Skip to content

Commit 1dc16b4

Browse files
committed
renamed to proxy-oauth
Signed-off-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com>
1 parent b40d3a9 commit 1dc16b4

File tree

11 files changed

+219
-226
lines changed

11 files changed

+219
-226
lines changed
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.

examples/servers/proxy-auth/proxy_auth/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
__version__ = "0.1.0"
44

55
# Import key components for easier access
6+
<<<<<<< HEAD
67
from .auth_server import auth_server as auth_server
78
from .auth_server import main as auth_server_main
89
from .combo_server import combo_server as combo_server
@@ -15,6 +16,20 @@
1516
"auth_server",
1617
"resource_server",
1718
"combo_server",
19+
=======
20+
from .auth_server import main as auth_server_main
21+
from .auth_server import run_server as run_auth_server
22+
from .combo_server import main as combo_server_main
23+
from .combo_server import mcp as proxy_server
24+
from .resource_server import main as resource_server_main
25+
from .resource_server import mcp as resource_server
26+
from .token_verifier import IntrospectionTokenVerifier
27+
28+
__all__ = [
29+
"run_auth_server",
30+
"resource_server",
31+
"proxy_server",
32+
>>>>>>> 37d81fe (renamed to proxy-oauth)
1833
"IntrospectionTokenVerifier",
1934
"auth_server_main",
2035
"resource_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/resource_server.py renamed to examples/servers/proxy-auth/proxy_auth/resource_server.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# pyright: reportMissingImports=false
2+
import argparse
23
import base64
34
import json
45
import logging
@@ -321,5 +322,95 @@ def main():
321322
resource_server.run(transport=args.transport)
322323

323324

325+
def create_resource_server(
326+
host: str = RESOURCE_SERVER_HOST,
327+
port: int = RESOURCE_SERVER_PORT,
328+
auth_server_url: str = AUTH_SERVER_URL,
329+
validate_resource: bool = False,
330+
) -> FastMCP:
331+
"""Create a resource server instance with the given configuration."""
332+
resource_server_url = f"http://{host}:{port}"
333+
334+
# Create token verifier that uses the auth server's introspection endpoint
335+
token_verifier = IntrospectionTokenVerifier(
336+
introspection_endpoint=f"{auth_server_url}/introspect",
337+
server_url=resource_server_url,
338+
validate_resource=validate_resource,
339+
)
340+
341+
# Create FastMCP resource server instance
342+
server = FastMCP(
343+
name="MCP Resource Server",
344+
host=host,
345+
port=port,
346+
auth=AuthSettings(
347+
issuer_url=AnyHttpUrl(auth_server_url),
348+
resource_server_url=AnyHttpUrl(resource_server_url),
349+
required_scopes=["openid"],
350+
),
351+
token_verifier=token_verifier,
352+
)
353+
354+
# Add tools
355+
@server.tool()
356+
def echo(message: str) -> str:
357+
return f"Echo: {message}"
358+
359+
# Add other tools...
360+
361+
return server
362+
363+
364+
def main():
365+
"""Command-line entry point for the Resource Server."""
366+
parser = argparse.ArgumentParser(description="MCP OAuth Proxy Resource Server")
367+
parser.add_argument(
368+
"--host",
369+
default=None,
370+
help="Host to bind to (overrides RESOURCE_SERVER_HOST env var)",
371+
)
372+
parser.add_argument(
373+
"--port",
374+
type=int,
375+
default=None,
376+
help="Port to bind to (overrides RESOURCE_SERVER_PORT env var)",
377+
)
378+
parser.add_argument(
379+
"--auth-server",
380+
default=None,
381+
help="URL of the authorization server (overrides AUTH_SERVER_URL env var)",
382+
)
383+
parser.add_argument(
384+
"--oauth-strict",
385+
action="store_true",
386+
help="Enable strict RFC 8707 resource validation",
387+
)
388+
parser.add_argument(
389+
"--transport",
390+
default="streamable-http",
391+
help="Transport type (streamable-http or websocket)",
392+
)
393+
394+
args = parser.parse_args()
395+
396+
# Use command-line arguments only if provided, otherwise use environment variables
397+
host = args.host or RESOURCE_SERVER_HOST
398+
port = args.port or RESOURCE_SERVER_PORT
399+
auth_server = args.auth_server or AUTH_SERVER_URL
400+
401+
# Log the configuration being used
402+
logger.info(
403+
f"Starting Resource Server with host={host}, port={port}, "
404+
f"auth_server={auth_server}"
405+
)
406+
logger.info("Using environment variables from .env file if present")
407+
408+
# Create a server with the specified configuration if needed
409+
# For now, we'll use the global mcp instance
410+
411+
logger.info(f"🚀 MCP Resource Server running on http://{host}:{port}")
412+
mcp.run(transport=args.transport)
413+
414+
324415
if __name__ == "__main__":
325416
resource_server.run(transport="streamable-http")
File renamed without changes.

examples/servers/proxy_oauth/pyproject.toml renamed to examples/servers/proxy-auth/pyproject.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[project]
2-
name = "proxy_oauth"
2+
name = "proxy_auth"
33
version = "0.1.0"
44
description = "OAuth Proxy Server"
55
authors = [{ name = "Your Name" }]
@@ -14,15 +14,20 @@ dev = [
1414
"pytest>=6.0",
1515
]
1616

17+
[project.scripts]
18+
mcp-proxy-auth-rs = "proxy_auth.resource_server:main"
19+
mcp-proxy-auth-as = "proxy_auth.auth_server:main"
20+
mcp-proxy-auth-combo = "proxy_auth.combo_server:main"
21+
1722
[build-system]
1823
requires = ["hatchling"]
1924
build-backend = "hatchling.build"
2025

2126
[tool.hatch.build.targets.wheel]
22-
packages = ["proxy_oauth"]
27+
packages = ["proxy_auth"]
2328

2429
[tool.pyright]
25-
include = ["proxy_oauth"]
30+
include = ["proxy_auth"]
2631
venvPath = "."
2732
venv = ".venv"
2833

examples/servers/proxy_oauth/README.md

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

0 commit comments

Comments
 (0)