Skip to content

Commit dc8c68f

Browse files
committed
renamed to proxy-oauth
Signed-off-by: Jesse Sanford <[email protected]>
1 parent 90436c1 commit dc8c68f

File tree

14 files changed

+571
-269
lines changed

14 files changed

+571
-269
lines changed

examples/servers/proxy-auth/README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# OAuth Proxy Server
2+
3+
This is a minimal OAuth proxy server example for the MCP Python SDK that demonstrates how to create a transparent OAuth proxy for existing OAuth providers.
4+
5+
## Installation
6+
7+
```bash
8+
# Navigate to the proxy-auth directory
9+
cd examples/servers/proxy-auth
10+
11+
# Install the package in development mode
12+
uv add -e .
13+
```
14+
15+
## Configuration
16+
17+
The servers can be configured using either:
18+
19+
1. **Command-line arguments** (take precedence when provided)
20+
2. **Environment variables** (loaded from `.env` file when present)
21+
22+
Example `.env` file:
23+
```
24+
# Auth Server Configuration
25+
AUTH_SERVER_HOST=localhost
26+
AUTH_SERVER_PORT=9000
27+
AUTH_SERVER_URL=http://localhost:9000
28+
29+
# Resource Server Configuration
30+
RESOURCE_SERVER_HOST=localhost
31+
RESOURCE_SERVER_PORT=8001
32+
RESOURCE_SERVER_URL=http://localhost:8001
33+
34+
# Combo Server Configuration
35+
COMBO_SERVER_HOST=localhost
36+
COMBO_SERVER_PORT=8000
37+
38+
# OAuth Provider Configuration
39+
UPSTREAM_AUTHORIZE=https://github.com/login/oauth/authorize
40+
UPSTREAM_TOKEN=https://github.com/login/oauth/access_token
41+
CLIENT_ID=your-client-id
42+
CLIENT_SECRET=your-client-secret
43+
DEFAULT_SCOPE=openid
44+
```
45+
46+
## Running the Servers
47+
48+
The example consists of three server components that can be run using the project scripts defined in pyproject.toml:
49+
50+
### Step 1: Start Authorization Server
51+
52+
```bash
53+
# Start Authorization Server on port 9000
54+
uv run mcp-proxy-auth-as --port=9000
55+
56+
# Or rely on environment variables from .env file
57+
uv run mcp-proxy-auth-as
58+
```
59+
60+
**What it provides:**
61+
- OAuth 2.0 flows (authorization, token exchange)
62+
- Token introspection endpoint for Resource Servers (`/introspect`)
63+
- Client registration endpoint (`/register`)
64+
65+
### Step 2: Start Resource Server (MCP Server)
66+
67+
```bash
68+
# In another terminal, start Resource Server on port 8001
69+
uv run mcp-proxy-auth-rs --port=8001 --auth-server=http://localhost:9000 --transport=streamable-http
70+
71+
# Or rely on environment variables from .env file
72+
uv run mcp-proxy-auth-rs
73+
```
74+
75+
### Step 3: Alternatively, Run Combined Server
76+
77+
For simpler testing, you can run a combined proxy server that handles both authentication and resource access:
78+
79+
```bash
80+
# Run the combined proxy server on port 8000
81+
uv run mcp-proxy-auth-combo --port=8000 --transport=streamable-http
82+
83+
# Or rely on environment variables from .env file
84+
uv run mcp-proxy-auth-combo
85+
```
86+
87+
## How It Works
88+
89+
The proxy OAuth server acts as a transparent proxy between:
90+
1. Client applications requesting OAuth tokens
91+
2. Upstream OAuth providers (like GitHub, Google, etc.)
92+
93+
This allows MCP servers to leverage existing OAuth providers without implementing their own authentication systems.
94+
95+
The server code is organized in the `proxy_auth` package for better modularity.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""OAuth Proxy Server for MCP."""
2+
3+
__version__ = "0.1.0"
4+
5+
# Import key components for easier access
6+
from .auth_server import main as auth_server_main
7+
from .auth_server import run_server as run_auth_server
8+
from .combo_server import main as combo_server_main
9+
from .combo_server import mcp as proxy_server
10+
from .resource_server import main as resource_server_main
11+
from .resource_server import mcp as resource_server
12+
from .token_verifier import IntrospectionTokenVerifier
13+
14+
__all__ = [
15+
"run_auth_server",
16+
"resource_server",
17+
"proxy_server",
18+
"IntrospectionTokenVerifier",
19+
"auth_server_main",
20+
"resource_server_main",
21+
"combo_server_main",
22+
]
23+
24+
# Aliases for the script entry points
25+
main = combo_server_main
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Main entry point for Combo Proxy OAuth Resource+Auth MCP server."""
2+
3+
import sys
4+
5+
from .combo_server import main
6+
7+
sys.exit(main()) # type: ignore[call-arg]

examples/servers/proxy_oauth/auth_server.py renamed to examples/servers/proxy-auth/proxy_auth/auth_server.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# pyright: reportMissingImports=false
2+
import argparse
3+
import asyncio
24
import logging
35
import os
46
import time
57

68
from dotenv import load_dotenv # type: ignore
7-
from mcp.server.auth.provider import AccessToken, OAuthToken
9+
from mcp.server.auth.provider import AccessToken
810
from mcp.server.auth.providers.transparent_proxy import (
911
ProxySettings, # type: ignore
10-
TransparentOAuthProxyProvider,
1112
ProxyTokenHandler,
13+
TransparentOAuthProxyProvider,
1214
)
1315
from mcp.server.auth.routes import cors_middleware, create_auth_routes
1416
from mcp.server.auth.settings import ClientRegistrationOptions
@@ -32,7 +34,7 @@
3234
)
3335

3436
# Dedicated logger for this server module
35-
logger = logging.getLogger("proxy_oauth.auth_server")
37+
logger = logging.getLogger("proxy_auth.auth_server")
3638

3739
# Suppress noisy INFO messages from the FastMCP low-level server unless we are
3840
# explicitly running in DEBUG mode. These logs (e.g. "Processing request of type
@@ -139,6 +141,7 @@ def _mask_secret(secret: str | None) -> str | None: # noqa: D401
139141
proxy_token_handler = ProxyTokenHandler(oauth_provider)
140142
routes.append(Route("/token", endpoint=proxy_token_handler.handle, methods=["POST"]))
141143

144+
142145
# Add token introspection endpoint for Resource Servers
143146
async def introspect_handler(request: Request) -> Response:
144147
"""
@@ -183,22 +186,48 @@ async def introspect_handler(request: Request) -> Response:
183186
auth_app = Starlette(routes=routes)
184187

185188

186-
async def run_server():
189+
async def run_server(host: str = AUTH_SERVER_HOST, port: int = AUTH_SERVER_PORT):
187190
"""Run the Authorization Server."""
188191
config = Config(
189192
auth_app,
190-
host=AUTH_SERVER_HOST,
191-
port=AUTH_SERVER_PORT,
193+
host=host,
194+
port=port,
192195
log_level="info",
193196
)
194197
server = Server(config)
195198

196-
logger.info(f"🚀 MCP Authorization Server running on {AUTH_SERVER_URL}")
199+
logger.info(f"🚀 MCP Authorization Server running on http://{host}:{port}")
197200

198201
await server.serve()
199202

200203

201-
if __name__ == "__main__":
202-
import asyncio
204+
def main():
205+
"""Command-line entry point for the Authorization Server."""
206+
parser = argparse.ArgumentParser(description="MCP OAuth Proxy Authorization Server")
207+
parser.add_argument(
208+
"--host",
209+
default=None,
210+
help="Host to bind to (overrides AUTH_SERVER_HOST env var)",
211+
)
212+
parser.add_argument(
213+
"--port",
214+
type=int,
215+
default=None,
216+
help="Port to bind to (overrides AUTH_SERVER_PORT env var)",
217+
)
203218

204-
asyncio.run(run_server())
219+
args = parser.parse_args()
220+
221+
# Use command-line arguments only if provided, otherwise use environment variables
222+
host = args.host or AUTH_SERVER_HOST
223+
port = args.port or AUTH_SERVER_PORT
224+
225+
# Log the configuration being used
226+
logger.info(f"Starting Authorization Server with host={host}, port={port}")
227+
logger.info("Using environment variables from .env file if present")
228+
229+
asyncio.run(run_server(host=host, port=port))
230+
231+
232+
if __name__ == "__main__":
233+
main()

0 commit comments

Comments
 (0)