|
|
import os |
|
|
import requests |
|
|
from fastapi import FastAPI, HTTPException, Depends |
|
|
from fastapi.responses import JSONResponse |
|
|
from fastapi.security.api_key import APIKeyHeader |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HUGGINGFACE_BACKEND = os.getenv( |
|
|
"SAP_BACKEND_URL", |
|
|
"https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata4/" |
|
|
"sap/api_purchaseorder_2/srvd_a2x/sap/purchaseorder/0001/PurchaseOrder?$top=10" |
|
|
) |
|
|
AGENTKIT_API_KEY = os.getenv("AGENTKIT_API_KEY", None) |
|
|
BASE_URL = os.getenv("PUBLIC_URL", "https://pd03-agentkit.hf.space") |
|
|
|
|
|
app = FastAPI( |
|
|
title="SAP MCP Server", |
|
|
description="MCP-compatible FastAPI server exposing live SAP Purchase Orders for demo and AgentKit integration.", |
|
|
version="2.0.0", |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
api_key_header = APIKeyHeader(name="x-agentkit-api-key", auto_error=False) |
|
|
|
|
|
def verify_api_key(api_key: str = Depends(api_key_header)): |
|
|
"""Verifies the x-agentkit-api-key header, if configured.""" |
|
|
if AGENTKIT_API_KEY is None: |
|
|
|
|
|
return True |
|
|
if api_key != AGENTKIT_API_KEY: |
|
|
raise HTTPException(status_code=401, detail="Invalid or missing API key") |
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/.well-known/mcp/manifest.json", include_in_schema=False) |
|
|
async def get_manifest(): |
|
|
"""Manifest describing this MCP server and its tools.""" |
|
|
manifest = { |
|
|
"name": "sap_mcp_server", |
|
|
"description": "MCP server exposing a tool for retrieving SAP purchase orders from the SAP Sandbox API.", |
|
|
"version": "2.0.0", |
|
|
"auth": { |
|
|
"type": "api_key", |
|
|
"location": "header", |
|
|
"header_name": "x-agentkit-api-key", |
|
|
"description": "Custom header used to authenticate MCP requests." |
|
|
}, |
|
|
"tools": [ |
|
|
{ |
|
|
"name": "get_purchase_orders", |
|
|
"description": "Fetches the top 10 purchase orders from the SAP Sandbox API.", |
|
|
"input_schema": {"type": "object", "properties": {}}, |
|
|
"output_schema": {"type": "object"}, |
|
|
"http": {"method": "GET", "url": f"{BASE_URL}/tools/get_purchase_orders"} |
|
|
} |
|
|
] |
|
|
} |
|
|
return JSONResponse(content=manifest) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/tools/get_purchase_orders", tags=["MCP Tools"]) |
|
|
async def get_purchase_orders(auth=Depends(verify_api_key)): |
|
|
""" |
|
|
Fetch the top purchase orders from SAP Sandbox API. |
|
|
Requires SAP_API_KEY secret and a valid SAP_BACKEND_URL. |
|
|
""" |
|
|
sap_api_key = os.getenv("SAP_API_KEY") |
|
|
if not sap_api_key: |
|
|
raise HTTPException(status_code=500, detail="SAP_API_KEY not set in environment") |
|
|
|
|
|
headers = {"APIKey": sap_api_key} |
|
|
|
|
|
try: |
|
|
print(f"π‘ Calling SAP Sandbox: {HUGGINGFACE_BACKEND}") |
|
|
resp = requests.get(HUGGINGFACE_BACKEND, headers=headers, timeout=60) |
|
|
resp.raise_for_status() |
|
|
data = resp.json() |
|
|
|
|
|
records = data.get("value", []) |
|
|
print(f"β
SAP API returned {len(records)} records") |
|
|
|
|
|
return { |
|
|
"source": "SAP Sandbox", |
|
|
"count": len(records), |
|
|
"data": records |
|
|
} |
|
|
|
|
|
except requests.exceptions.HTTPError as e: |
|
|
print(f"β SAP API HTTP error: {e}") |
|
|
raise HTTPException(status_code=resp.status_code, detail=f"SAP API error: {e.response.text}") |
|
|
except Exception as e: |
|
|
print(f"β SAP API general error: {e}") |
|
|
raise HTTPException(status_code=500, detail=f"Failed to call SAP API: {e}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/health", tags=["System"]) |
|
|
async def health(): |
|
|
return {"status": "ok", "message": "SAP MCP Server is running"} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/", tags=["Root"]) |
|
|
async def root(): |
|
|
return { |
|
|
"message": "π Welcome to the SAP MCP Server", |
|
|
"available_endpoints": { |
|
|
"health": "/health", |
|
|
"manifest": "/.well-known/mcp/manifest.json", |
|
|
"purchase_orders": "/tools/get_purchase_orders" |
|
|
}, |
|
|
"instructions": "Use /tools/get_purchase_orders with header x-agentkit-api-key to fetch live SAP sandbox data." |
|
|
} |
|
|
|