python-sdk/examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py
2025-06-23 14:19:03 +01:00

152 lines
5.1 KiB
Python

"""
Legacy Combined Authorization Server + Resource Server for MCP.
This server implements the old spec where MCP servers could act as both AS and RS.
Used for backwards compatibility testing with the new split AS/RS architecture.
NOTE: this is a simplified example for demonstration purposes.
This is not a production-ready implementation.
Usage:
python -m mcp_simple_auth.legacy_as_server --port=8002
"""
import logging
from typing import Any, Literal
import click
from pydantic import AnyHttpUrl, BaseModel
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import RedirectResponse, Response
from mcp.server.auth.middleware.auth_context import get_access_token
from mcp.server.auth.settings import AuthSettings, ClientRegistrationOptions
from mcp.server.fastmcp.server import FastMCP
from .github_oauth_provider import GitHubOAuthProvider, GitHubOAuthSettings
logger = logging.getLogger(__name__)
class ServerSettings(BaseModel):
"""Settings for the simple GitHub MCP server."""
# Server settings
host: str = "localhost"
port: int = 8000
server_url: AnyHttpUrl = AnyHttpUrl("http://localhost:8000")
github_callback_path: str = "http://localhost:8000/github/callback"
class SimpleGitHubOAuthProvider(GitHubOAuthProvider):
"""GitHub OAuth provider for legacy MCP server."""
def __init__(self, github_settings: GitHubOAuthSettings, github_callback_path: str):
super().__init__(github_settings, github_callback_path)
def create_simple_mcp_server(server_settings: ServerSettings, github_settings: GitHubOAuthSettings) -> FastMCP:
"""Create a simple FastMCP server with GitHub OAuth."""
oauth_provider = SimpleGitHubOAuthProvider(github_settings, server_settings.github_callback_path)
auth_settings = AuthSettings(
issuer_url=server_settings.server_url,
client_registration_options=ClientRegistrationOptions(
enabled=True,
valid_scopes=[github_settings.mcp_scope],
default_scopes=[github_settings.mcp_scope],
),
required_scopes=[github_settings.mcp_scope],
# No resource_server_url parameter in legacy mode
resource_server_url=None,
)
app = FastMCP(
name="Simple GitHub MCP Server",
instructions="A simple MCP server with GitHub OAuth authentication",
auth_server_provider=oauth_provider,
host=server_settings.host,
port=server_settings.port,
debug=True,
auth=auth_settings,
)
@app.custom_route("/github/callback", methods=["GET"])
async def github_callback_handler(request: Request) -> Response:
"""Handle GitHub OAuth callback."""
code = request.query_params.get("code")
state = request.query_params.get("state")
if not code or not state:
raise HTTPException(400, "Missing code or state parameter")
redirect_uri = await oauth_provider.handle_github_callback(code, state)
return RedirectResponse(status_code=302, url=redirect_uri)
def get_github_token() -> str:
"""Get the GitHub token for the authenticated user."""
access_token = get_access_token()
if not access_token:
raise ValueError("Not authenticated")
# Get GitHub token from mapping
github_token = oauth_provider.token_mapping.get(access_token.token)
if not github_token:
raise ValueError("No GitHub token found for user")
return github_token
@app.tool()
async def get_user_profile() -> dict[str, Any]:
"""Get the authenticated user's GitHub profile information.
This is the only tool in our simple example. It requires the 'user' scope.
"""
access_token = get_access_token()
if not access_token:
raise ValueError("Not authenticated")
return await oauth_provider.get_github_user_info(access_token.token)
return app
@click.command()
@click.option("--port", default=8000, help="Port to listen on")
@click.option(
"--transport",
default="streamable-http",
type=click.Choice(["sse", "streamable-http"]),
help="Transport protocol to use ('sse' or 'streamable-http')",
)
def main(port: int, transport: Literal["sse", "streamable-http"]) -> int:
"""Run the simple GitHub MCP server."""
logging.basicConfig(level=logging.INFO)
# Load GitHub settings from environment variables
github_settings = GitHubOAuthSettings()
# Validate required fields
if not github_settings.github_client_id or not github_settings.github_client_secret:
raise ValueError("GitHub credentials not provided")
# Create server settings
host = "localhost"
server_url = f"http://{host}:{port}"
server_settings = ServerSettings(
host=host,
port=port,
server_url=AnyHttpUrl(server_url),
github_callback_path=f"{server_url}/github/callback",
)
mcp_server = create_simple_mcp_server(server_settings, github_settings)
logger.info(f"Starting server with {transport} transport")
mcp_server.run(transport=transport)
return 0
if __name__ == "__main__":
main() # type: ignore[call-arg]