Skip to content

Commit 9e06753

Browse files
authored
Merge branch 'main' into main
2 parents 2ab4ad0 + 679b229 commit 9e06753

File tree

23 files changed

+2178
-789
lines changed

23 files changed

+2178
-789
lines changed

README.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -423,43 +423,42 @@ The `elicit()` method returns an `ElicitationResult` with:
423423

424424
Authentication can be used by servers that want to expose tools accessing protected resources.
425425

426-
`mcp.server.auth` implements an OAuth 2.0 server interface, which servers can use by
427-
providing an implementation of the `OAuthAuthorizationServerProvider` protocol.
426+
`mcp.server.auth` implements OAuth 2.1 resource server functionality, where MCP servers act as Resource Servers (RS) that validate tokens issued by separate Authorization Servers (AS). This follows the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) and implements RFC 9728 (Protected Resource Metadata) for AS discovery.
427+
428+
MCP servers can use authentication by providing an implementation of the `TokenVerifier` protocol:
428429

429430
```python
430431
from mcp import FastMCP
431-
from mcp.server.auth.provider import OAuthAuthorizationServerProvider
432-
from mcp.server.auth.settings import (
433-
AuthSettings,
434-
ClientRegistrationOptions,
435-
RevocationOptions,
436-
)
432+
from mcp.server.auth.provider import TokenVerifier, TokenInfo
433+
from mcp.server.auth.settings import AuthSettings
437434

438435

439-
class MyOAuthServerProvider(OAuthAuthorizationServerProvider):
440-
# See an example on how to implement at `examples/servers/simple-auth`
441-
...
436+
class MyTokenVerifier(TokenVerifier):
437+
# Implement token validation logic (typically via token introspection)
438+
async def verify_token(self, token: str) -> TokenInfo:
439+
# Verify with your authorization server
440+
...
442441

443442

444443
mcp = FastMCP(
445444
"My App",
446-
auth_server_provider=MyOAuthServerProvider(),
445+
token_verifier=MyTokenVerifier(),
447446
auth=AuthSettings(
448-
issuer_url="https://myapp.com",
449-
revocation_options=RevocationOptions(
450-
enabled=True,
451-
),
452-
client_registration_options=ClientRegistrationOptions(
453-
enabled=True,
454-
valid_scopes=["myscope", "myotherscope"],
455-
default_scopes=["myscope"],
456-
),
457-
required_scopes=["myscope"],
447+
issuer_url="https://auth.example.com",
448+
resource_server_url="http://localhost:3001",
449+
required_scopes=["mcp:read", "mcp:write"],
458450
),
459451
)
460452
```
461453

462-
See [OAuthAuthorizationServerProvider](src/mcp/server/auth/provider.py) for more details.
454+
For a complete example with separate Authorization Server and Resource Server implementations, see [`examples/servers/simple-auth/`](examples/servers/simple-auth/).
455+
456+
**Architecture:**
457+
- **Authorization Server (AS)**: Handles OAuth flows, user authentication, and token issuance
458+
- **Resource Server (RS)**: Your MCP server that validates tokens and serves protected resources
459+
- **Client**: Discovers AS through RFC 9728, obtains tokens, and uses them with the MCP server
460+
461+
See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation.
463462

464463
## Running Your Server
465464

examples/clients/simple-auth-client/mcp_simple_auth_client/main.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ async def connect(self):
160160
print(f"🔗 Attempting to connect to {self.server_url}...")
161161

162162
try:
163-
# Set up callback server
164-
callback_server = CallbackServer(port=3000)
163+
callback_server = CallbackServer(port=3030)
165164
callback_server.start()
166165

167166
async def callback_handler() -> tuple[str, str | None]:
@@ -175,7 +174,7 @@ async def callback_handler() -> tuple[str, str | None]:
175174

176175
client_metadata_dict = {
177176
"client_name": "Simple Auth Client",
178-
"redirect_uris": ["http://localhost:3000/callback"],
177+
"redirect_uris": ["http://localhost:3030/callback"],
179178
"grant_types": ["authorization_code", "refresh_token"],
180179
"response_types": ["code"],
181180
"token_endpoint_auth_method": "client_secret_post",
Lines changed: 104 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,143 @@
1-
# Simple MCP Server with GitHub OAuth Authentication
1+
# MCP OAuth Authentication Demo
22

3-
This is a simple example of an MCP server with GitHub OAuth authentication. It demonstrates the essential components needed for OAuth integration with just a single tool.
3+
This example demonstrates OAuth 2.0 authentication with the Model Context Protocol using **separate Authorization Server (AS) and Resource Server (RS)** to comply with the new RFC 9728 specification.
44

5-
This is just an example of a server that uses auth, an official GitHub mcp server is [here](https://github.com/github/github-mcp-server)
5+
---
66

7-
## Overview
7+
## Setup Requirements
88

9-
This simple demo to show to set up a server with:
10-
- GitHub OAuth2 authorization flow
11-
- Single tool: `get_user_profile` to retrieve GitHub user information
9+
**Create a GitHub OAuth App:**
10+
- Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App
11+
- **Authorization callback URL:** `http://localhost:9000/github/callback`
12+
- Note down your **Client ID** and **Client Secret**
1213

14+
**Set environment variables:**
15+
```bash
16+
export MCP_GITHUB_CLIENT_ID="your_client_id_here"
17+
export MCP_GITHUB_CLIENT_SECRET="your_client_secret_here"
18+
```
1319

14-
## Prerequisites
15-
16-
1. Create a GitHub OAuth App:
17-
- Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App
18-
- Application name: Any name (e.g., "Simple MCP Auth Demo")
19-
- Homepage URL: `http://localhost:8000`
20-
- Authorization callback URL: `http://localhost:8000/github/callback`
21-
- Click "Register application"
22-
- Note down your Client ID and Client Secret
20+
---
2321

24-
## Required Environment Variables
22+
## Running the Servers
2523

26-
You MUST set these environment variables before running the server:
24+
### Step 1: Start Authorization Server
2725

2826
```bash
29-
export MCP_GITHUB_GITHUB_CLIENT_ID="your_client_id_here"
30-
export MCP_GITHUB_GITHUB_CLIENT_SECRET="your_client_secret_here"
27+
# Navigate to the simple-auth directory
28+
cd examples/servers/simple-auth
29+
30+
# Start Authorization Server on port 9000
31+
uv run mcp-simple-auth-as --port=9000
3132
```
3233

33-
The server will not start without these environment variables properly set.
34+
**What it provides:**
35+
- OAuth 2.0 flows (registration, authorization, token exchange)
36+
- GitHub OAuth integration for user authentication
37+
- Token introspection endpoint for Resource Servers (`/introspect`)
38+
- User data proxy endpoint (`/github/user`)
3439

40+
---
3541

36-
## Running the Server
42+
### Step 2: Start Resource Server (MCP Server)
3743

3844
```bash
39-
# Set environment variables first (see above)
45+
# In another terminal, navigate to the simple-auth directory
46+
cd examples/servers/simple-auth
4047

41-
# Run the server
42-
uv run mcp-simple-auth
43-
```
48+
# Start Resource Server on port 8001, connected to Authorization Server
49+
uv run mcp-simple-auth-rs --port=8001 --auth-server=http://localhost:9000 --transport=streamable-http
50+
51+
# With RFC 8707 strict resource validation (recommended for production)
52+
uv run mcp-simple-auth-rs --port=8001 --auth-server=http://localhost:9000 --transport=streamable-http --oauth-strict
4453

45-
The server will start on `http://localhost:8000`.
54+
```
4655

47-
### Transport Options
4856

49-
This server supports multiple transport protocols that can run on the same port:
57+
### Step 3: Test with Client
5058

51-
#### SSE (Server-Sent Events) - Default
5259
```bash
53-
uv run mcp-simple-auth
54-
# or explicitly:
55-
uv run mcp-simple-auth --transport sse
60+
cd examples/clients/simple-auth-client
61+
# Start client with streamable HTTP
62+
MCP_SERVER_PORT=8001 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-client
5663
```
5764

58-
SSE transport provides endpoint:
59-
- `/sse`
6065

61-
#### Streamable HTTP
66+
## How It Works
67+
68+
### RFC 9728 Discovery
69+
70+
**Client → Resource Server:**
6271
```bash
63-
uv run mcp-simple-auth --transport streamable-http
72+
curl http://localhost:8001/.well-known/oauth-protected-resource
73+
```
74+
```json
75+
{
76+
"resource": "http://localhost:8001",
77+
"authorization_servers": ["http://localhost:9000"]
78+
}
6479
```
6580

66-
Streamable HTTP transport provides endpoint:
67-
- `/mcp`
81+
**Client → Authorization Server:**
82+
```bash
83+
curl http://localhost:9000/.well-known/oauth-authorization-server
84+
```
85+
```json
86+
{
87+
"issuer": "http://localhost:9000",
88+
"authorization_endpoint": "http://localhost:9000/authorize",
89+
"token_endpoint": "http://localhost:9000/token"
90+
}
91+
```
6892

93+
## Legacy MCP Server as Authorization Server (Backwards Compatibility)
6994

70-
This ensures backward compatibility without needing multiple server instances. When using SSE transport (`--transport sse`), only the `/sse` endpoint is available.
95+
For backwards compatibility with older MCP implementations, a legacy server is provided that acts as an Authorization Server (following the old spec where MCP servers could optionally provide OAuth):
7196

72-
## Available Tool
97+
### Running the Legacy Server
7398

74-
### get_user_profile
99+
```bash
100+
# Start legacy authorization server on port 8002
101+
uv run mcp-simple-auth-legacy --port=8002
102+
```
75103

76-
The only tool in this simple example. Returns the authenticated user's GitHub profile information.
104+
**Differences from the new architecture:**
105+
- **MCP server acts as AS:** The MCP server itself provides OAuth endpoints (old spec behavior)
106+
- **No separate RS:** The server handles both authentication and MCP tools
107+
- **Local token validation:** Tokens are validated internally without introspection
108+
- **No RFC 9728 support:** Does not provide `/.well-known/oauth-protected-resource`
109+
- **Direct OAuth discovery:** OAuth metadata is at the MCP server's URL
77110

78-
**Required scope**: `user`
111+
### Testing with Legacy Server
79112

80-
**Returns**: GitHub user profile data including username, email, bio, etc.
113+
```bash
114+
# Test with client (will automatically fall back to legacy discovery)
115+
cd examples/clients/simple-auth-client
116+
MCP_SERVER_PORT=8002 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-client
117+
```
81118

119+
The client will:
120+
1. Try RFC 9728 discovery at `/.well-known/oauth-protected-resource` (404 on legacy server)
121+
2. Fall back to direct OAuth discovery at `/.well-known/oauth-authorization-server`
122+
3. Complete authentication with the MCP server acting as its own AS
82123

83-
## Troubleshooting
124+
This ensures existing MCP servers (which could optionally act as Authorization Servers under the old spec) continue to work while the ecosystem transitions to the new architecture where MCP servers are Resource Servers only.
84125

85-
If the server fails to start, check:
86-
1. Environment variables `MCP_GITHUB_GITHUB_CLIENT_ID` and `MCP_GITHUB_GITHUB_CLIENT_SECRET` are set
87-
2. The GitHub OAuth app callback URL matches `http://localhost:8000/github/callback`
88-
3. No other service is using port 8000
89-
4. The transport specified is valid (`sse` or `streamable-http`)
126+
## Manual Testing
90127

91-
You can use [Inspector](https://github.com/modelcontextprotocol/inspector) to test Auth
128+
### Test Discovery
129+
```bash
130+
# Test Resource Server discovery endpoint (new architecture)
131+
curl -v http://localhost:8001/.well-known/oauth-protected-resource
132+
133+
# Test Authorization Server metadata
134+
curl -v http://localhost:9000/.well-known/oauth-authorization-server
135+
```
136+
137+
### Test Token Introspection
138+
```bash
139+
# After getting a token through OAuth flow:
140+
curl -X POST http://localhost:9000/introspect \
141+
-H "Content-Type: application/x-www-form-urlencoded" \
142+
-d "token=your_access_token"
143+
```

0 commit comments

Comments
 (0)