Skip to content

Commit 926745a

Browse files
committed
simplify server examples, address comments
1 parent 9b2e3df commit 926745a

File tree

5 files changed

+125
-165
lines changed

5 files changed

+125
-165
lines changed

examples/servers/simple-auth/mcp_simple_auth/auth_server.py

Lines changed: 76 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
This server handles OAuth flows, client registration, and token issuance.
55
Can be replaced with enterprise authorization servers like Auth0, Entra ID, etc.
66
7+
NOTE: this is a simplified example for demonstration purposes.
8+
This is not a production-ready implementation.
9+
710
Usage:
811
python -m mcp_simple_auth.auth_server --port=9000
912
"""
@@ -13,8 +16,7 @@
1316
import time
1417

1518
import click
16-
from pydantic import AnyHttpUrl
17-
from pydantic_settings import SettingsConfigDict
19+
from pydantic import AnyHttpUrl, BaseModel
1820
from starlette.applications import Starlette
1921
from starlette.exceptions import HTTPException
2022
from starlette.requests import Request
@@ -30,21 +32,15 @@
3032
logger = logging.getLogger(__name__)
3133

3234

33-
class AuthServerSettings(GitHubOAuthSettings):
35+
class AuthServerSettings(BaseModel):
3436
"""Settings for the Authorization Server."""
3537

36-
model_config = SettingsConfigDict(env_prefix="MCP_")
37-
3838
# Server settings
3939
host: str = "localhost"
4040
port: int = 9000
4141
server_url: AnyHttpUrl = AnyHttpUrl("http://localhost:9000")
4242
github_callback_path: str = "http://localhost:9000/github/callback"
4343

44-
def __init__(self, **data):
45-
"""Initialize settings with values from environment variables."""
46-
super().__init__(**data)
47-
4844

4945
class GitHubProxyAuthProvider(GitHubOAuthProvider):
5046
"""
@@ -56,22 +52,22 @@ class GitHubProxyAuthProvider(GitHubOAuthProvider):
5652
3. Maps MCP tokens to GitHub tokens for API access
5753
"""
5854

59-
def __init__(self, settings: AuthServerSettings):
60-
super().__init__(settings, settings.github_callback_path)
55+
def __init__(self, github_settings: GitHubOAuthSettings, github_callback_path: str):
56+
super().__init__(github_settings, github_callback_path)
6157

6258

63-
def create_authorization_server(settings: AuthServerSettings) -> Starlette:
59+
def create_authorization_server(server_settings: AuthServerSettings, github_settings: GitHubOAuthSettings) -> Starlette:
6460
"""Create the Authorization Server application."""
65-
oauth_provider = GitHubProxyAuthProvider(settings)
61+
oauth_provider = GitHubProxyAuthProvider(github_settings, server_settings.github_callback_path)
6662

6763
auth_settings = AuthSettings(
68-
issuer_url=settings.server_url,
64+
issuer_url=server_settings.server_url,
6965
client_registration_options=ClientRegistrationOptions(
7066
enabled=True,
71-
valid_scopes=[settings.mcp_scope],
72-
default_scopes=[settings.mcp_scope],
67+
valid_scopes=[github_settings.mcp_scope],
68+
default_scopes=[github_settings.mcp_scope],
7369
),
74-
required_scopes=[settings.mcp_scope],
70+
required_scopes=[github_settings.mcp_scope],
7571
authorization_servers=None,
7672
)
7773

@@ -93,20 +89,8 @@ async def github_callback_handler(request: Request) -> Response:
9389
if not code or not state:
9490
raise HTTPException(400, "Missing code or state parameter")
9591

96-
try:
97-
redirect_uri = await oauth_provider.handle_github_callback(code, state)
98-
return RedirectResponse(url=redirect_uri, status_code=302)
99-
except HTTPException:
100-
raise
101-
except Exception as e:
102-
logger.error("Unexpected error", exc_info=e)
103-
return JSONResponse(
104-
status_code=500,
105-
content={
106-
"error": "server_error",
107-
"error_description": "Unexpected error",
108-
},
109-
)
92+
redirect_uri = await oauth_provider.handle_github_callback(code, state)
93+
return RedirectResponse(url=redirect_uri, status_code=302)
11094

11195
routes.append(Route("/github/callback", endpoint=github_callback_handler, methods=["GET"]))
11296

@@ -118,32 +102,27 @@ async def introspect_handler(request: Request) -> Response:
118102
Resource Servers call this endpoint to validate tokens without
119103
needing direct access to token storage.
120104
"""
121-
try:
122-
form = await request.form()
123-
token = form.get("token")
124-
if not token or not isinstance(token, str):
125-
return JSONResponse({"active": False}, status_code=400)
126-
127-
# Look up token in provider
128-
access_token = await oauth_provider.load_access_token(token)
129-
if not access_token:
130-
return JSONResponse({"active": False})
131-
132-
# Return token info for Resource Server
133-
return JSONResponse(
134-
{
135-
"active": True,
136-
"client_id": access_token.client_id,
137-
"scope": " ".join(access_token.scopes),
138-
"exp": access_token.expires_at,
139-
"iat": int(time.time()),
140-
"token_type": "Bearer",
141-
}
142-
)
143-
144-
except Exception as e:
145-
logger.exception("Token introspection error")
146-
return JSONResponse({"active": False, "error": str(e)}, status_code=500)
105+
form = await request.form()
106+
token = form.get("token")
107+
if not token or not isinstance(token, str):
108+
return JSONResponse({"active": False}, status_code=400)
109+
110+
# Look up token in provider
111+
access_token = await oauth_provider.load_access_token(token)
112+
if not access_token:
113+
return JSONResponse({"active": False})
114+
115+
# Return token info for Resource Server
116+
return JSONResponse(
117+
{
118+
"active": True,
119+
"client_id": access_token.client_id,
120+
"scope": " ".join(access_token.scopes),
121+
"exp": access_token.expires_at,
122+
"iat": int(time.time()),
123+
"token_type": "Bearer",
124+
}
125+
)
147126

148127
routes.append(
149128
Route(
@@ -161,28 +140,16 @@ async def github_user_handler(request: Request) -> Response:
161140
Resource Servers call this with MCP tokens to get GitHub user data
162141
without exposing GitHub tokens to clients.
163142
"""
164-
try:
165-
# Extract Bearer token
166-
auth_header = request.headers.get("authorization", "")
167-
if not auth_header.startswith("Bearer "):
168-
return JSONResponse({"error": "unauthorized"}, status_code=401)
169-
170-
mcp_token = auth_header[7:]
171-
172-
# Get GitHub user info using the provider method
173-
try:
174-
user_info = await oauth_provider.get_github_user_info(mcp_token)
175-
return JSONResponse(user_info)
176-
except ValueError as e:
177-
if "No GitHub token found" in str(e):
178-
return JSONResponse({"error": "no_github_token"}, status_code=404)
179-
elif "GitHub API error" in str(e):
180-
return JSONResponse({"error": "github_api_error"}, status_code=502)
181-
raise
182-
183-
except Exception as e:
184-
logger.exception("GitHub user info error")
185-
return JSONResponse({"error": str(e)}, status_code=500)
143+
# Extract Bearer token
144+
auth_header = request.headers.get("authorization", "")
145+
if not auth_header.startswith("Bearer "):
146+
return JSONResponse({"error": "unauthorized"}, status_code=401)
147+
148+
mcp_token = auth_header[7:]
149+
150+
# Get GitHub user info using the provider method
151+
user_info = await oauth_provider.get_github_user_info(mcp_token)
152+
return JSONResponse(user_info)
186153

187154
routes.append(
188155
Route(
@@ -192,36 +159,36 @@ async def github_user_handler(request: Request) -> Response:
192159
)
193160
)
194161

195-
return Starlette(debug=True, routes=routes)
162+
return Starlette(routes=routes)
196163

197164

198-
async def run_server(settings: AuthServerSettings):
165+
async def run_server(server_settings: AuthServerSettings, github_settings: GitHubOAuthSettings):
199166
"""Run the Authorization Server."""
200-
auth_server = create_authorization_server(settings)
167+
auth_server = create_authorization_server(server_settings, github_settings)
201168

202169
config = Config(
203170
auth_server,
204-
host=settings.host,
205-
port=settings.port,
171+
host=server_settings.host,
172+
port=server_settings.port,
206173
log_level="info",
207174
)
208175
server = Server(config)
209176

210177
logger.info("=" * 80)
211178
logger.info("MCP AUTHORIZATION SERVER")
212179
logger.info("=" * 80)
213-
logger.info(f"Server URL: {settings.server_url}")
180+
logger.info(f"Server URL: {server_settings.server_url}")
214181
logger.info("Endpoints:")
215-
logger.info(f" - OAuth Metadata: {settings.server_url}/.well-known/oauth-authorization-server")
216-
logger.info(f" - Client Registration: {settings.server_url}/register")
217-
logger.info(f" - Authorization: {settings.server_url}/authorize")
218-
logger.info(f" - Token Exchange: {settings.server_url}/token")
219-
logger.info(f" - Token Introspection: {settings.server_url}/introspect")
220-
logger.info(f" - GitHub Callback: {settings.server_url}/github/callback")
221-
logger.info(f" - GitHub User Proxy: {settings.server_url}/github/user")
182+
logger.info(f" - OAuth Metadata: {server_settings.server_url}/.well-known/oauth-authorization-server")
183+
logger.info(f" - Client Registration: {server_settings.server_url}/register")
184+
logger.info(f" - Authorization: {server_settings.server_url}/authorize")
185+
logger.info(f" - Token Exchange: {server_settings.server_url}/token")
186+
logger.info(f" - Token Introspection: {server_settings.server_url}/introspect")
187+
logger.info(f" - GitHub Callback: {server_settings.server_url}/github/callback")
188+
logger.info(f" - GitHub User Proxy: {server_settings.server_url}/github/user")
222189
logger.info("")
223190
logger.info("Resource Servers should use /introspect to validate tokens")
224-
logger.info("Configure GitHub App callback URL: " + settings.github_callback_path)
191+
logger.info("Configure GitHub App callback URL: " + server_settings.github_callback_path)
225192
logger.info("=" * 80)
226193

227194
await server.serve()
@@ -242,16 +209,23 @@ def main(port: int, host: str) -> int:
242209
"""
243210
logging.basicConfig(level=logging.INFO)
244211

245-
try:
246-
settings = AuthServerSettings(host=host, port=port)
247-
except ValueError as e:
248-
logger.error("Failed to load settings. Make sure environment variables are set:")
249-
logger.error(" MCP_GITHUB_CLIENT_ID=<your-client-id>")
250-
logger.error(" MCP_GITHUB_CLIENT_SECRET=<your-client-secret>")
251-
logger.error(f"Error: {e}")
252-
return 1
212+
# Load GitHub settings from environment variables
213+
github_settings = GitHubOAuthSettings()
214+
215+
# Validate required fields
216+
if not github_settings.github_client_id or not github_settings.github_client_secret:
217+
raise ValueError("GitHub credentials not provided")
218+
219+
# Create server settings
220+
server_url = f"http://{host}:{port}"
221+
server_settings = AuthServerSettings(
222+
host=host,
223+
port=port,
224+
server_url=AnyHttpUrl(server_url),
225+
github_callback_path=f"{server_url}/github/callback",
226+
)
253227

254-
asyncio.run(run_server(settings))
228+
asyncio.run(run_server(server_settings, github_settings))
255229
return 0
256230

257231

examples/servers/simple-auth/mcp_simple_auth/github_oauth_provider.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
44
This module contains the common GitHub OAuth functionality used by both
55
the standalone authorization server and the legacy combined server.
6+
7+
NOTE: this is a simplified example for demonstration purposes.
8+
This is not a production-ready implementation.
9+
610
"""
711

812
import logging
@@ -11,7 +15,7 @@
1115
from typing import Any
1216

1317
from pydantic import AnyHttpUrl
14-
from pydantic_settings import BaseSettings
18+
from pydantic_settings import BaseSettings, SettingsConfigDict
1519
from starlette.exceptions import HTTPException
1620

1721
from mcp.server.auth.provider import (
@@ -31,9 +35,11 @@
3135
class GitHubOAuthSettings(BaseSettings):
3236
"""Common GitHub OAuth settings."""
3337

38+
model_config = SettingsConfigDict(env_prefix="MCP_")
39+
3440
# GitHub OAuth settings - MUST be provided via environment variables
35-
github_client_id: str # MCP_GITHUB_CLIENT_ID env var
36-
github_client_secret: str # MCP_GITHUB_CLIENT_SECRET env var
41+
github_client_id: str | None = None
42+
github_client_secret: str | None = None
3743

3844
# GitHub OAuth URLs
3945
github_auth_url: str = "https://github.com/login/oauth/authorize"

0 commit comments

Comments
 (0)