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."
    }