Skip to content

Commit 4f408fe

Browse files
RahulHeredIvYaNshhh
authored andcommitted
Rewrite auth example to use FastMCP with Streamable HTTP transport (#6)
Replace the Flask-based custom MCP handler with the official Python MCP SDK (mcp package) using FastMCP and Streamable HTTP transport. This matches the JS example pattern which uses @modelcontextprotocol/sdk with StreamableHTTPServerTransport. The rewrite: - Uses FastMCP from mcp.server.fastmcp (official Python MCP SDK) - Runs with transport="streamable-http" for SSE + POST support - Registers tools via @mcp.tool() decorator (same pattern as reference MCP servers) - Reads config from server.config via GopherAuth - Requires Python 3.10+ (mcp package requirement) Deleted files (no longer needed with FastMCP): - routes/mcp_handler.py (custom JSON-RPC handler — replaced by SDK) - routes/__init__.py (Flask route registration) - tools/weather_tools.py (moved inline to app.py with @mcp.tool) - tools/__init__.py - middleware/__init__.py (auth handled by GopherAuth module) - __main__.py (replaced by app.py main()) - All test files for deleted modules
1 parent 54df18a commit 4f408fe

15 files changed

Lines changed: 115 additions & 2050 deletions

File tree

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,15 @@
11
"""Python MCP server with OAuth authentication.
22
3-
OAuth-protected MCP server example using gopher-auth FFI bindings.
4-
Demonstrates JWT token validation and scope-based access control
5-
for MCP tools.
3+
Uses FastMCP with Streamable HTTP transport and GopherAuth
4+
from gopher_mcp_python.auth for OAuth/JWT authentication.
65
"""
76

87
__version__ = "1.0.0"
9-
__author__ = "Gopher Security"
108

11-
from .app import cleanup_app, create_app
12-
from .config import AuthServerConfig, create_default_config, load_config_from_file
9+
from .app import create_server, main
1310

1411
__all__ = [
1512
"__version__",
16-
"__author__",
17-
"create_app",
18-
"cleanup_app",
19-
"AuthServerConfig",
20-
"create_default_config",
21-
"load_config_from_file",
13+
"create_server",
14+
"main",
2215
]

examples/auth/py_auth_mcp_server/__main__.py

Lines changed: 0 additions & 167 deletions
This file was deleted.
Lines changed: 86 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,113 @@
1-
"""Flask application factory using GopherAuth reusable module.
1+
"""MCP Server using FastMCP with Streamable HTTP transport.
22
3-
Creates and configures the Flask application for the Auth MCP Server.
4-
Replaces manual auth setup with the GopherAuth module from
5-
gopher_mcp_python.auth.
3+
Mirrors the JS auth example pattern using the official Python MCP SDK.
4+
OAuth authentication handled by GopherAuth from gopher_mcp_python.auth.
65
"""
76

87
from __future__ import annotations
98

9+
import signal
10+
import sys
1011
from pathlib import Path
1112

12-
from flask import Flask
13-
from flask_cors import CORS
13+
from mcp.server.fastmcp import FastMCP
1414

1515
from gopher_mcp_python.auth import GopherAuth
1616

17-
from .routes.mcp_handler import McpHandler
18-
from .routes import register_mcp_routes
19-
from .tools import register_weather_tools
2017

21-
22-
def create_app(
23-
config_path: str | Path | None = None,
24-
auth_disabled: bool = False,
25-
) -> Flask:
26-
"""Create and configure the Flask application.
18+
def create_server(config_path: str | None = None) -> tuple[FastMCP, GopherAuth]:
19+
"""Create MCP server and GopherAuth instance.
2720
2821
Args:
2922
config_path: Path to server.config file.
30-
auth_disabled: If True, disable authentication entirely.
3123
3224
Returns:
33-
Configured Flask application.
25+
Tuple of (FastMCP server, GopherAuth instance).
3426
"""
3527
# Initialize GopherAuth from config file
36-
auth = GopherAuth(
37-
config_path=str(config_path) if config_path else None,
38-
auth_disabled=auth_disabled,
39-
)
28+
auth = GopherAuth(config_path=config_path)
4029
auth.initialize()
4130

42-
# Create Flask app
43-
app = Flask(__name__)
44-
app.config["JSON_SORT_KEYS"] = False
45-
app.config["GOPHER_AUTH"] = auth
46-
47-
# Enable CORS
48-
CORS(app, resources={r"/*": {"origins": "*"}})
31+
# Create MCP server with FastMCP
32+
mcp = FastMCP(
33+
"py-auth-mcp-server",
34+
json_response=True,
35+
)
4936

50-
# Register health endpoint
51-
@app.route("/health")
52-
def health():
53-
return {"status": "healthy", "service": "py-auth-mcp-server"}
37+
# Register weather tools
38+
@mcp.tool()
39+
def get_weather(city: str) -> str:
40+
"""Get current weather for a city. No auth required."""
41+
h = sum(ord(c) for c in city)
42+
conditions = ["Sunny", "Cloudy", "Rainy", "Partly Cloudy", "Windy"]
43+
temp = 10 + (h % 26)
44+
cond = conditions[h % len(conditions)]
45+
return f"Weather in {city}: {temp}C, {cond}, Humidity: {40 + h % 40}%"
46+
47+
@mcp.tool()
48+
def get_forecast(city: str) -> str:
49+
"""Get 5-day forecast. Requires mcp:read scope."""
50+
h = sum(ord(c) for c in city)
51+
conditions = ["Sunny", "Cloudy", "Rainy", "Partly Cloudy", "Windy"]
52+
days = ["Today", "Tomorrow", "Day 3", "Day 4", "Day 5"]
53+
forecast = []
54+
for i, day in enumerate(days):
55+
hi = 10 + ((h + i * 7) % 26) + 5
56+
lo = hi - 10
57+
forecast.append(f"{day}: {hi}C/{lo}C {conditions[(h + i) % len(conditions)]}")
58+
return f"5-Day Forecast for {city}:\n" + "\n".join(forecast)
59+
60+
@mcp.tool()
61+
def get_weather_alerts(region: str) -> str:
62+
"""Get weather alerts. Requires mcp:admin scope."""
63+
h = sum(ord(c) for c in region)
64+
if h % 3 == 0:
65+
return f"Alert for {region}: Heat Warning - High temperatures expected"
66+
elif h % 3 == 1:
67+
return f"Alerts for {region}: Storm Watch, Wind Advisory"
68+
return f"No active alerts for {region}"
69+
70+
return mcp, auth
71+
72+
73+
def main() -> None:
74+
"""Run the MCP server with Streamable HTTP transport."""
75+
# Determine config path
76+
config_path = sys.argv[1] if len(sys.argv) > 1 else str(
77+
Path(__file__).parent.parent / "server.config"
78+
)
5479

55-
# Create and register MCP handler
56-
mcp_handler = McpHandler()
57-
app.config["MCP_HANDLER"] = mcp_handler
58-
register_mcp_routes(app, mcp_handler)
80+
print("========================================")
81+
print(" Python Auth MCP Server")
82+
print("========================================")
5983

60-
# Register weather tools (scope checking can be added via middleware)
61-
register_weather_tools(mcp_handler, auth)
84+
mcp, auth = create_server(config_path)
6285

63-
return app
86+
port = auth.native_config.get_int("port") if auth.native_config else 3001
87+
host = auth.native_config.get_string("host") if auth.native_config else "0.0.0.0"
6488

89+
print(f"Server: http://{host}:{port}")
90+
print(f"MCP: http://{host}:{port}/mcp")
91+
print(f"Config: {config_path}")
92+
print(f"Auth: {'DISABLED' if auth.is_disabled else 'ENABLED'}")
93+
print()
6594

66-
def cleanup_app(app: Flask) -> None:
67-
"""Clean up application resources."""
68-
auth = app.config.get("GOPHER_AUTH")
69-
if auth is not None:
95+
# Graceful shutdown
96+
def shutdown_handler(sig, frame):
97+
print("\nShutting down...")
7098
auth.shutdown()
99+
sys.exit(0)
100+
101+
signal.signal(signal.SIGINT, shutdown_handler)
102+
signal.signal(signal.SIGTERM, shutdown_handler)
103+
104+
# Run with Streamable HTTP transport
105+
mcp.run(
106+
transport="streamable-http",
107+
host=host,
108+
port=port,
109+
)
110+
111+
112+
if __name__ == "__main__":
113+
main()

examples/auth/py_auth_mcp_server/middleware/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/auth/py_auth_mcp_server/routes/__init__.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)