Skip to content

Commit 754095a

Browse files
authored
Merge branch 'main' into fix-fastmcp-integration-tests
2 parents ab970eb + 4d45bb8 commit 754095a

File tree

24 files changed

+2279
-1594
lines changed

24 files changed

+2279
-1594
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: 91 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,129 @@
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
87

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
8+
## Running the Servers
129

10+
### Step 1: Start Authorization Server
1311

14-
## Prerequisites
12+
```bash
13+
# Navigate to the simple-auth directory
14+
cd examples/servers/simple-auth
15+
16+
# Start Authorization Server on port 9000
17+
uv run mcp-simple-auth-as --port=9000
18+
```
1519

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+
**What it provides:**
21+
- OAuth 2.0 flows (registration, authorization, token exchange)
22+
- Simple credential-based authentication (no external provider needed)
23+
- Token introspection endpoint for Resource Servers (`/introspect`)
2324

24-
## Required Environment Variables
25+
---
2526

26-
You MUST set these environment variables before running the server:
27+
### Step 2: Start Resource Server (MCP Server)
2728

2829
```bash
29-
export MCP_GITHUB_GITHUB_CLIENT_ID="your_client_id_here"
30-
export MCP_GITHUB_GITHUB_CLIENT_SECRET="your_client_secret_here"
31-
```
30+
# In another terminal, navigate to the simple-auth directory
31+
cd examples/servers/simple-auth
3232

33-
The server will not start without these environment variables properly set.
33+
# Start Resource Server on port 8001, connected to Authorization Server
34+
uv run mcp-simple-auth-rs --port=8001 --auth-server=http://localhost:9000 --transport=streamable-http
3435

36+
# With RFC 8707 strict resource validation (recommended for production)
37+
uv run mcp-simple-auth-rs --port=8001 --auth-server=http://localhost:9000 --transport=streamable-http --oauth-strict
3538

36-
## Running the Server
39+
```
3740

38-
```bash
39-
# Set environment variables first (see above)
4041

41-
# Run the server
42-
uv run mcp-simple-auth
42+
### Step 3: Test with Client
43+
44+
```bash
45+
cd examples/clients/simple-auth-client
46+
# Start client with streamable HTTP
47+
MCP_SERVER_PORT=8001 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-client
4348
```
4449

45-
The server will start on `http://localhost:8000`.
4650

47-
### Transport Options
51+
## How It Works
4852

49-
This server supports multiple transport protocols that can run on the same port:
53+
### RFC 9728 Discovery
5054

51-
#### SSE (Server-Sent Events) - Default
55+
**Client → Resource Server:**
5256
```bash
53-
uv run mcp-simple-auth
54-
# or explicitly:
55-
uv run mcp-simple-auth --transport sse
57+
curl http://localhost:8001/.well-known/oauth-protected-resource
58+
```
59+
```json
60+
{
61+
"resource": "http://localhost:8001",
62+
"authorization_servers": ["http://localhost:9000"]
63+
}
5664
```
5765

58-
SSE transport provides endpoint:
59-
- `/sse`
60-
61-
#### Streamable HTTP
66+
**Client → Authorization Server:**
6267
```bash
63-
uv run mcp-simple-auth --transport streamable-http
68+
curl http://localhost:9000/.well-known/oauth-authorization-server
69+
```
70+
```json
71+
{
72+
"issuer": "http://localhost:9000",
73+
"authorization_endpoint": "http://localhost:9000/authorize",
74+
"token_endpoint": "http://localhost:9000/token"
75+
}
6476
```
6577

66-
Streamable HTTP transport provides endpoint:
67-
- `/mcp`
6878

79+
## Legacy MCP Server as Authorization Server (Backwards Compatibility)
6980

70-
This ensures backward compatibility without needing multiple server instances. When using SSE transport (`--transport sse`), only the `/sse` endpoint is available.
81+
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):
7182

72-
## Available Tool
83+
### Running the Legacy Server
7384

74-
### get_user_profile
85+
```bash
86+
# Start legacy authorization server on port 8002
87+
uv run mcp-simple-auth-legacy --port=8002
88+
```
89+
90+
**Differences from the new architecture:**
91+
- **MCP server acts as AS:** The MCP server itself provides OAuth endpoints (old spec behavior)
92+
- **No separate RS:** The server handles both authentication and MCP tools
93+
- **Local token validation:** Tokens are validated internally without introspection
94+
- **No RFC 9728 support:** Does not provide `/.well-known/oauth-protected-resource`
95+
- **Direct OAuth discovery:** OAuth metadata is at the MCP server's URL
7596

76-
The only tool in this simple example. Returns the authenticated user's GitHub profile information.
97+
### Testing with Legacy Server
98+
99+
```bash
100+
# Test with client (will automatically fall back to legacy discovery)
101+
cd examples/clients/simple-auth-client
102+
MCP_SERVER_PORT=8002 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-client
103+
```
77104

78-
**Required scope**: `user`
105+
The client will:
106+
1. Try RFC 9728 discovery at `/.well-known/oauth-protected-resource` (404 on legacy server)
107+
2. Fall back to direct OAuth discovery at `/.well-known/oauth-authorization-server`
108+
3. Complete authentication with the MCP server acting as its own AS
79109

80-
**Returns**: GitHub user profile data including username, email, bio, etc.
110+
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.
81111

112+
## Manual Testing
82113

83-
## Troubleshooting
114+
### Test Discovery
115+
```bash
116+
# Test Resource Server discovery endpoint (new architecture)
117+
curl -v http://localhost:8001/.well-known/oauth-protected-resource
84118

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`)
119+
# Test Authorization Server metadata
120+
curl -v http://localhost:9000/.well-known/oauth-authorization-server
121+
```
90122

91-
You can use [Inspector](https://github.com/modelcontextprotocol/inspector) to test Auth
123+
### Test Token Introspection
124+
```bash
125+
# After getting a token through OAuth flow:
126+
curl -X POST http://localhost:9000/introspect \
127+
-H "Content-Type: application/x-www-form-urlencoded" \
128+
-d "token=your_access_token"
129+
```

0 commit comments

Comments
 (0)