PD03 commited on
Commit
877dbfb
Β·
verified Β·
1 Parent(s): cecc310

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -30
app.py CHANGED
@@ -3,21 +3,34 @@ import requests
3
  from fastapi import FastAPI, HTTPException, Depends
4
  from fastapi.responses import JSONResponse
5
  from fastapi.security.api_key import APIKeyHeader
 
6
 
7
  # -------------------------------------------------
8
  # CONFIGURATION
9
  # -------------------------------------------------
10
- HUGGINGFACE_BACKEND = os.getenv("SAP_BACKEND_URL", "https://pd03-agentkit.hf.space/purchase-orders")
 
 
 
 
11
  AGENTKIT_API_KEY = os.getenv("AGENTKIT_API_KEY", None)
12
- BASE_URL = os.getenv("PUBLIC_URL", "https://<your-space>.hf.space") # Replace if testing locally
13
 
14
  app = FastAPI(
15
  title="SAP MCP Server",
16
- description=(
17
- "MCP-compatible FastAPI server exposing the SAP Purchase Order API tool "
18
- "for OpenAI AgentKit (October 2025+)."
19
- ),
20
- version="1.2.0",
 
 
 
 
 
 
 
 
21
  )
22
 
23
  # -------------------------------------------------
@@ -28,25 +41,23 @@ api_key_header = APIKeyHeader(name="x-agentkit-api-key", auto_error=False)
28
  def verify_api_key(api_key: str = Depends(api_key_header)):
29
  """Verifies the x-agentkit-api-key header, if configured."""
30
  if AGENTKIT_API_KEY is None:
31
- # Open mode (no auth configured)
32
  return True
33
  if api_key != AGENTKIT_API_KEY:
34
  raise HTTPException(status_code=401, detail="Invalid or missing API key")
35
  return True
36
 
 
37
  # -------------------------------------------------
38
- # MCP MANIFEST (AgentKit autodiscovery)
39
  # -------------------------------------------------
40
  @app.get("/.well-known/mcp/manifest.json", include_in_schema=False)
41
  async def get_manifest():
42
- """
43
- Returns the MCP manifest that AgentKit uses to discover tools.
44
- The manifest defines auth and the tool's absolute URL.
45
- """
46
  manifest = {
47
  "name": "sap_mcp_server",
48
- "description": "MCP server exposing a tool for retrieving SAP purchase orders.",
49
- "version": "1.2.0",
50
  "auth": {
51
  "type": "api_key",
52
  "location": "header",
@@ -56,36 +67,53 @@ async def get_manifest():
56
  "tools": [
57
  {
58
  "name": "get_purchase_orders",
59
- "description": (
60
- "Fetches the top 50 purchase orders from the SAP Sandbox API "
61
- "via the Hugging Face backend."
62
- ),
63
  "input_schema": {"type": "object", "properties": {}},
64
  "output_schema": {"type": "object"},
65
- "http": {
66
- "method": "GET",
67
- "url": f"{BASE_URL}/tools/get_purchase_orders"
68
- }
69
  }
70
  ]
71
  }
72
  return JSONResponse(content=manifest)
73
 
 
74
  # -------------------------------------------------
75
- # TOOL ENDPOINT
76
  # -------------------------------------------------
77
  @app.get("/tools/get_purchase_orders", tags=["MCP Tools"])
78
  async def get_purchase_orders(auth=Depends(verify_api_key)):
79
  """
80
- Fetches SAP purchase orders via the Hugging Face backend.
81
- Requires a valid x-agentkit-api-key header if configured.
82
  """
 
 
 
 
 
 
83
  try:
84
- resp = requests.get(HUGGINGFACE_BACKEND, timeout=30)
 
85
  resp.raise_for_status()
86
- return resp.json()
87
- except requests.exceptions.RequestException as e:
88
- raise HTTPException(status_code=500, detail=f"Failed to call backend: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
  # -------------------------------------------------
91
  # HEALTH CHECK
@@ -93,3 +121,19 @@ async def get_purchase_orders(auth=Depends(verify_api_key)):
93
  @app.get("/health", tags=["System"])
94
  async def health():
95
  return {"status": "ok", "message": "SAP MCP Server is running"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from fastapi import FastAPI, HTTPException, Depends
4
  from fastapi.responses import JSONResponse
5
  from fastapi.security.api_key import APIKeyHeader
6
+ from fastapi.middleware.cors import CORSMiddleware
7
 
8
  # -------------------------------------------------
9
  # CONFIGURATION
10
  # -------------------------------------------------
11
+ HUGGINGFACE_BACKEND = os.getenv(
12
+ "SAP_BACKEND_URL",
13
+ "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata4/"
14
+ "sap/api_purchaseorder_2/srvd_a2x/sap/purchaseorder/0001/PurchaseOrder?$top=10"
15
+ )
16
  AGENTKIT_API_KEY = os.getenv("AGENTKIT_API_KEY", None)
17
+ BASE_URL = os.getenv("PUBLIC_URL", "https://pd03-agentkit.hf.space") # your Space URL
18
 
19
  app = FastAPI(
20
  title="SAP MCP Server",
21
+ description="MCP-compatible FastAPI server exposing live SAP Purchase Orders for demo and AgentKit integration.",
22
+ version="2.0.0",
23
+ )
24
+
25
+ # -------------------------------------------------
26
+ # CORS MIDDLEWARE (needed for Google AI Studio / front-end calls)
27
+ # -------------------------------------------------
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"], # for demo; restrict later if desired
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
  )
35
 
36
  # -------------------------------------------------
 
41
  def verify_api_key(api_key: str = Depends(api_key_header)):
42
  """Verifies the x-agentkit-api-key header, if configured."""
43
  if AGENTKIT_API_KEY is None:
44
+ # open mode
45
  return True
46
  if api_key != AGENTKIT_API_KEY:
47
  raise HTTPException(status_code=401, detail="Invalid or missing API key")
48
  return True
49
 
50
+
51
  # -------------------------------------------------
52
+ # MCP MANIFEST (for AgentKit autodiscovery)
53
  # -------------------------------------------------
54
  @app.get("/.well-known/mcp/manifest.json", include_in_schema=False)
55
  async def get_manifest():
56
+ """Manifest describing this MCP server and its tools."""
 
 
 
57
  manifest = {
58
  "name": "sap_mcp_server",
59
+ "description": "MCP server exposing a tool for retrieving SAP purchase orders from the SAP Sandbox API.",
60
+ "version": "2.0.0",
61
  "auth": {
62
  "type": "api_key",
63
  "location": "header",
 
67
  "tools": [
68
  {
69
  "name": "get_purchase_orders",
70
+ "description": "Fetches the top 10 purchase orders from the SAP Sandbox API.",
 
 
 
71
  "input_schema": {"type": "object", "properties": {}},
72
  "output_schema": {"type": "object"},
73
+ "http": {"method": "GET", "url": f"{BASE_URL}/tools/get_purchase_orders"}
 
 
 
74
  }
75
  ]
76
  }
77
  return JSONResponse(content=manifest)
78
 
79
+
80
  # -------------------------------------------------
81
+ # TOOL: Fetch Purchase Orders
82
  # -------------------------------------------------
83
  @app.get("/tools/get_purchase_orders", tags=["MCP Tools"])
84
  async def get_purchase_orders(auth=Depends(verify_api_key)):
85
  """
86
+ Fetch the top purchase orders from SAP Sandbox API.
87
+ Requires SAP_API_KEY secret and a valid SAP_BACKEND_URL.
88
  """
89
+ sap_api_key = os.getenv("SAP_API_KEY")
90
+ if not sap_api_key:
91
+ raise HTTPException(status_code=500, detail="SAP_API_KEY not set in environment")
92
+
93
+ headers = {"APIKey": sap_api_key}
94
+
95
  try:
96
+ print(f"πŸ“‘ Calling SAP Sandbox: {HUGGINGFACE_BACKEND}")
97
+ resp = requests.get(HUGGINGFACE_BACKEND, headers=headers, timeout=60)
98
  resp.raise_for_status()
99
+ data = resp.json()
100
+
101
+ records = data.get("value", [])
102
+ print(f"βœ… SAP API returned {len(records)} records")
103
+
104
+ return {
105
+ "source": "SAP Sandbox",
106
+ "count": len(records),
107
+ "data": records
108
+ }
109
+
110
+ except requests.exceptions.HTTPError as e:
111
+ print(f"❌ SAP API HTTP error: {e}")
112
+ raise HTTPException(status_code=resp.status_code, detail=f"SAP API error: {e.response.text}")
113
+ except Exception as e:
114
+ print(f"❌ SAP API general error: {e}")
115
+ raise HTTPException(status_code=500, detail=f"Failed to call SAP API: {e}")
116
+
117
 
118
  # -------------------------------------------------
119
  # HEALTH CHECK
 
121
  @app.get("/health", tags=["System"])
122
  async def health():
123
  return {"status": "ok", "message": "SAP MCP Server is running"}
124
+
125
+
126
+ # -------------------------------------------------
127
+ # ROOT (friendly landing page)
128
+ # -------------------------------------------------
129
+ @app.get("/", tags=["Root"])
130
+ async def root():
131
+ return {
132
+ "message": "πŸ‘‹ Welcome to the SAP MCP Server",
133
+ "available_endpoints": {
134
+ "health": "/health",
135
+ "manifest": "/.well-known/mcp/manifest.json",
136
+ "purchase_orders": "/tools/get_purchase_orders"
137
+ },
138
+ "instructions": "Use /tools/get_purchase_orders with header x-agentkit-api-key to fetch live SAP sandbox data."
139
+ }