File size: 5,149 Bytes
2f9831b 1ec9387 cecc310 1ec9387 1d99038 877dbfb 2f9831b 75ce927 1ec9387 75ce927 877dbfb 1d99038 877dbfb 1ec9387 2f9831b 1ec9387 877dbfb 2f9831b 1d99038 cecc310 1d99038 cecc310 1d99038 877dbfb 1d99038 877dbfb 75ce927 877dbfb 75ce927 1ec9387 877dbfb 1ec9387 877dbfb 1d99038 1ec9387 877dbfb 1ec9387 877dbfb 1ec9387 2f9831b 877dbfb 75ce927 877dbfb 75ce927 1ec9387 1d99038 1ec9387 877dbfb 1ec9387 877dbfb 1ec9387 877dbfb 1ec9387 877dbfb 2f9831b 1ec9387 1d99038 1ec9387 877dbfb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
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
# -------------------------------------------------
# CONFIGURATION
# -------------------------------------------------
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") # your Space URL
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",
)
# -------------------------------------------------
# CORS MIDDLEWARE (needed for Google AI Studio / front-end calls)
# -------------------------------------------------
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # for demo; restrict later if desired
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# -------------------------------------------------
# AUTHENTICATION
# -------------------------------------------------
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:
# open mode
return True
if api_key != AGENTKIT_API_KEY:
raise HTTPException(status_code=401, detail="Invalid or missing API key")
return True
# -------------------------------------------------
# MCP MANIFEST (for AgentKit autodiscovery)
# -------------------------------------------------
@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)
# -------------------------------------------------
# TOOL: Fetch Purchase Orders
# -------------------------------------------------
@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}")
# -------------------------------------------------
# HEALTH CHECK
# -------------------------------------------------
@app.get("/health", tags=["System"])
async def health():
return {"status": "ok", "message": "SAP MCP Server is running"}
# -------------------------------------------------
# ROOT (friendly landing page)
# -------------------------------------------------
@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."
}
|