PD03 commited on
Commit
1cd1723
·
verified ·
1 Parent(s): 37118c1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -85
app.py CHANGED
@@ -1,93 +1,124 @@
1
- from mcp.server.fastmcp import FastMCP
2
- import httpx
 
 
3
  import os
4
- import uvicorn
5
-
6
- # SAP API Configuration
7
- SAP_API_URL = "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata4/sap/api_purchaserequisition_2/srvd_a2x/sap/purchaserequisition/0001/PurchaseReqn"
8
- SAP_API_KEY = os.getenv("SAP_API_KEY", "your_sap_api_key_here")
9
-
10
- # Initialize FastMCP server
11
- mcp = FastMCP("SAP_PurchaseRequisition")
12
-
13
- @mcp.tool()
14
- async def get_purchase_requisitions(top: int = 50) -> dict:
15
- """
16
- Fetch purchase requisitions from SAP S/4HANA Cloud API.
17
-
18
- Args:
19
- top: Number of records to fetch (default 50, max 100)
20
-
21
- Returns:
22
- Dictionary containing purchase requisition data
23
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  headers = {
25
- "APIKey": SAP_API_KEY,
26
- "Accept": "application/json"
27
- }
28
-
29
- params = {
30
- "$top": min(top, 100)
31
  }
32
-
 
 
 
 
 
33
  async with httpx.AsyncClient(timeout=30.0) as client:
34
- try:
35
- response = await client.get(
36
- SAP_API_URL,
37
- headers=headers,
38
- params=params
39
- )
40
- response.raise_for_status()
41
- data = response.json()
42
- return {
43
- "status": "success",
44
- "data": data,
45
- "count": len(data.get("value", []))
46
- }
47
- except httpx.HTTPStatusError as e:
48
- return {
49
- "status": "error",
50
- "message": f"HTTP error occurred: {e.response.status_code}",
51
- "details": str(e)
52
- }
53
- except Exception as e:
54
- return {
55
- "status": "error",
56
- "message": "Failed to fetch purchase requisitions",
57
- "details": str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  }
 
 
 
 
 
 
 
 
59
 
60
- @mcp.tool()
61
- async def get_purchase_requisition_summary(top: int = 10) -> str:
62
- """
63
- Get a summary of recent purchase requisitions.
64
-
65
- Args:
66
- top: Number of records to summarize (default 10)
67
-
68
- Returns:
69
- Formatted summary string
70
- """
71
- result = await get_purchase_requisitions(top)
72
-
73
- if result["status"] == "error":
74
- return f"Error: {result['message']}"
75
-
76
- items = result["data"].get("value", [])
77
- if not items:
78
- return "No purchase requisitions found."
79
-
80
- summary = f"Found {result['count']} purchase requisitions:\n\n"
81
- for idx, item in enumerate(items, 1):
82
- pr_number = item.get("PurchaseRequisition", "N/A")
83
- description = item.get("PurchaseRequisitionDescription", "No description")
84
- summary += f"{idx}. PR#{pr_number}: {description}\n"
85
-
86
- return summary
87
-
88
- # Create ASGI app
89
- app = mcp.http_app()
90
 
91
  if __name__ == "__main__":
92
- port = int(os.getenv("PORT", 7860))
93
- uvicorn.run(app, host="0.0.0.0", port=port)
 
 
1
+ """FastAPI-based MCP server for SAP Purchase Requisition API."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
  import os
6
+ from typing import Optional
7
+
8
+ import httpx
9
+ from dotenv import load_dotenv
10
+ from fastapi import FastAPI, HTTPException
11
+ from fastapi.responses import JSONResponse
12
+ from model_context_protocol.fastapi import FastAPIMCPServer
13
+
14
+ load_dotenv()
15
+
16
+ API_KEY_ENV_VAR = "SAP_API_KEY"
17
+ SAP_PURCHASE_REQUISITION_URL = (
18
+ "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata4/"
19
+ "sap/api_purchaserequisition_2/srvd_a2x/sap/purchaserequisition/0001/"
20
+ "PurchaseReqn"
21
+ )
22
+
23
+ app = FastAPI(title="SAP Purchase Requisition MCP Server", version="0.1.0")
24
+ server = FastAPIMCPServer(app)
25
+
26
+
27
+ def _get_api_key() -> str:
28
+ """Load the SAP API key from the environment."""
29
+ api_key = os.getenv(API_KEY_ENV_VAR)
30
+ if not api_key:
31
+ raise HTTPException(
32
+ status_code=500,
33
+ detail=(
34
+ "The SAP API key is not configured. Set the environment variable "
35
+ f"{API_KEY_ENV_VAR} before calling this tool."
36
+ ),
37
+ )
38
+ return api_key
39
+
40
+
41
+ async def _call_sap_api(
42
+ *,
43
+ api_key: str,
44
+ top: int,
45
+ select: Optional[str],
46
+ filter_: Optional[str],
47
+ ) -> dict:
48
+ """Fetch purchase requisition data from the SAP sandbox API."""
49
  headers = {
50
+ "APIKey": api_key,
51
+ "Accept": "application/json",
52
+ "Accept-Language": "en",
 
 
 
53
  }
54
+ params = {"$top": top}
55
+ if select:
56
+ params["$select"] = select
57
+ if filter_:
58
+ params["$filter"] = filter_
59
+
60
  async with httpx.AsyncClient(timeout=30.0) as client:
61
+ response = await client.get(
62
+ SAP_PURCHASE_REQUISITION_URL,
63
+ headers=headers,
64
+ params=params,
65
+ )
66
+
67
+ try:
68
+ response.raise_for_status()
69
+ except httpx.HTTPStatusError as exc:
70
+ raise HTTPException(
71
+ status_code=exc.response.status_code,
72
+ detail=f"SAP API request failed: {exc.response.text}",
73
+ ) from exc
74
+
75
+ try:
76
+ data = response.json()
77
+ except ValueError as exc: # pragma: no cover - unexpected API response
78
+ raise HTTPException(status_code=502, detail="SAP API returned invalid JSON") from exc
79
+
80
+ return data
81
+
82
+
83
+ @server.tool(
84
+ name="list_purchase_requisitions",
85
+ description=(
86
+ "Retrieve purchase requisitions from the SAP sandbox OData endpoint. "
87
+ "Supports optional $select and $filter expressions to trim the payload."
88
+ ),
89
+ )
90
+ async def list_purchase_requisitions(
91
+ top: int = 50,
92
+ select: Optional[str] = None,
93
+ filter: Optional[str] = None,
94
+ ):
95
+ """Return purchase requisition data from SAP."""
96
+ if top <= 0 or top > 200:
97
+ raise HTTPException(
98
+ status_code=400,
99
+ detail="Parameter 'top' must be between 1 and 200 for sandbox usage.",
100
+ )
101
+
102
+ api_key = _get_api_key()
103
+ data = await _call_sap_api(api_key=api_key, top=top, select=select, filter_=filter)
104
+
105
+ return {
106
+ "content": [
107
+ {
108
+ "type": "text",
109
+ "text": json.dumps(data, indent=2),
110
  }
111
+ ]
112
+ }
113
+
114
+
115
+ @app.get("/", response_class=JSONResponse)
116
+ async def root():
117
+ """Simple health endpoint."""
118
+ return {"status": "ok", "mcp": "available"}
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  if __name__ == "__main__":
122
+ import uvicorn
123
+
124
+ uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "8000")))