Skip to content

Commit 82058da

Browse files
committed
implemented-api-key-based-auth-for-mcp
1 parent f269cf1 commit 82058da

2 files changed

Lines changed: 52 additions & 6 deletions

File tree

requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
# spendee client
12
requests
23
python-dotenv
34
google-cloud-firestore
45
google-auth
56
jwt
7+
8+
# mcp
69
mcp[cli]
7-
httpx
10+
fastapi

spendee/spendee_mcp.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
from mcp.server.fastmcp import FastMCP
1+
2+
import os
23
import logging
4+
import contextlib
5+
import uvicorn
6+
from fastapi import FastAPI, HTTPException, Request
7+
from mcp.server.fastmcp import FastMCP
8+
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
39

410
# to start (after .venv setup):
511
# python spendee/spendee_mcp.py
@@ -10,20 +16,57 @@
1016
# setup "Transport Type" to "Streamable HTTP"
1117
# and "Server URL" to "http://localhost:8000/mcp"
1218

19+
ACCEPTED_TOKEN = os.environ.get("MCP_TOKEN", "spendee-token")
20+
1321
logging.basicConfig(level=logging.DEBUG)
14-
mcp = FastMCP("spendee", host="0.0.0.0", port=8000)
22+
logger = logging.getLogger(__name__)
23+
24+
mcp = FastMCP("spendee")
1525

1626
@mcp.tool()
1727
def get_wallets():
1828
# Hardcoded example wallets
19-
example_wallets = [
29+
return [
2030
{"id": 1, "name": "Main Account", "currency": "USD", "balance": 1234.56},
2131
{"id": 2, "name": "Savings", "currency": "EUR", "balance": 7890.12},
2232
]
23-
return example_wallets
2433

34+
# --- Extra authentication layer ---
35+
session_manager = StreamableHTTPSessionManager(
36+
app=mcp._mcp_server, # Use the underlying MCPServer instance
37+
)
38+
39+
async def auth_middleware(scope, receive, send):
40+
request = Request(scope, receive)
41+
auth_header = request.headers.get("authorization")
42+
43+
if not auth_header or not auth_header.lower().startswith("bearer "):
44+
logger.warning("Missing or invalid Authorization header.")
45+
raise HTTPException(401, "Missing or invalid Authorization header.")
46+
47+
token = auth_header.split(" ", 1)[1]
48+
if token != ACCEPTED_TOKEN:
49+
logger.warning("Invalid or expired token.")
50+
raise HTTPException(401, "Invalid or expired token.")
51+
52+
await session_manager.handle_request(scope, receive, send)
53+
54+
@contextlib.asynccontextmanager
55+
async def lifespan(app: FastAPI):
56+
async with session_manager.run():
57+
logger.info("StreamableHTTPSessionManager started.")
58+
yield
59+
logger.info("StreamableHTTPSessionManager stopped.")
60+
61+
app = FastAPI(lifespan=lifespan)
62+
app.mount("/mcp", auth_middleware)
63+
64+
# --- Server Execution ---
2565
if __name__ == "__main__":
26-
mcp.run(transport="streamable-http")
66+
logger.info("Starting Spendee MCP Server with Bearer Token Authentication")
67+
logger.info("Access the MCP endpoint at http://0.0.0.0:8000/mcp")
68+
uvicorn.run(app, host="0.0.0.0", port=8000)
69+
2770

2871
# relevant URLs for learning:
2972
# - https://modelcontextprotocol.io/specification/2025-06-18/server/tools

0 commit comments

Comments
 (0)