Skip to content

Commit 94bcd73

Browse files
committed
initial commit
1 parent 6353dd1 commit 94bcd73

File tree

10 files changed

+2044
-9
lines changed

10 files changed

+2044
-9
lines changed

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,86 @@ async def main():
796796
tool_result = await session.call_tool("echo", {"message": "hello"})
797797
```
798798

799+
### OAuth Authentication for Clients
800+
801+
The SDK supports OAuth 2.0 client authentication for secure access to MCP servers that require authentication:
802+
803+
```python
804+
from mcp.client.auth import OAuthClientProvider, UnauthorizedError
805+
from mcp.client.oauth_providers import InMemoryOAuthProvider
806+
from mcp.client.streamable_http import streamablehttp_client
807+
from mcp.shared.auth import OAuthClientMetadata
808+
from mcp import ClientSession
809+
810+
# Create an OAuth provider
811+
oauth_provider = InMemoryOAuthProvider(
812+
redirect_url="http://localhost:8080/callback",
813+
client_metadata=OAuthClientMetadata(
814+
redirect_uris=["http://localhost:8080/callback"],
815+
client_name="My MCP Client",
816+
scope="tools resources", # Request specific scopes
817+
),
818+
)
819+
820+
async def main():
821+
# Connect with OAuth authentication
822+
async with streamablehttp_client(
823+
"https://example.com/mcp",
824+
auth_provider=oauth_provider,
825+
) as (read_stream, write_stream, _):
826+
# Create a session
827+
async with ClientSession(read_stream, write_stream) as session:
828+
# Initialize (this may trigger OAuth flow)
829+
try:
830+
await session.initialize()
831+
# Use authenticated session
832+
result = await session.call_tool("protected_tool", {"arg": "value"})
833+
except UnauthorizedError:
834+
# Handle authorization required
835+
print("Authorization required. Check your browser.")
836+
837+
# Handle OAuth callback after user authorization
838+
async def handle_callback(authorization_code: str):
839+
from mcp.client.streamable_http import StreamableHTTPTransport
840+
841+
# Create a transport instance to handle auth completion
842+
transport = StreamableHTTPTransport(
843+
url="https://example.com/mcp",
844+
auth_provider=oauth_provider,
845+
)
846+
847+
# Exchange authorization code for tokens
848+
await transport.finish_auth(authorization_code)
849+
print("Authorization successful!")
850+
```
851+
852+
#### Custom OAuth Providers
853+
854+
You can implement custom OAuth storage by creating your own provider:
855+
856+
```python
857+
from mcp.client.oauth_providers import InMemoryOAuthProvider
858+
859+
class DatabaseOAuthProvider(InMemoryOAuthProvider):
860+
async def save_tokens(self, tokens):
861+
# Save to database
862+
await db.save_tokens(self.client_id, tokens)
863+
864+
async def tokens(self):
865+
# Load from database
866+
return await db.load_tokens(self.client_id)
867+
868+
# Implement other methods as needed...
869+
```
870+
871+
The OAuth client implementation supports:
872+
873+
- Dynamic client registration
874+
- Authorization code flow with PKCE
875+
- Token refresh
876+
- Multiple storage providers (in-memory and file-based included)
877+
- Automatic token management and retry logic
878+
799879
### MCP Primitives
800880

801881
The MCP protocol defines three core primitives that servers can implement:
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Simple OAuth Client Example
2+
3+
This example demonstrates how to create an MCP client that connects to an OAuth-protected server using the Python SDK's OAuth client support.
4+
5+
## Overview
6+
7+
This client connects to the [simple-auth server](../../servers/simple-auth/) and demonstrates:
8+
- OAuth 2.0 client authentication with PKCE
9+
- Token storage and management
10+
- Handling the OAuth authorization flow
11+
- Making authenticated requests to protected tools
12+
13+
## Prerequisites
14+
15+
1. **Set up the simple-auth server first**:
16+
- Follow the instructions in [../../servers/simple-auth/README.md](../../servers/simple-auth/README.md)
17+
- Make sure the server is running on `http://localhost:8000`
18+
19+
2. **Create a GitHub OAuth App** (if not done already):
20+
- Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App
21+
- Application name: "Simple MCP Auth Demo"
22+
- Homepage URL: `http://localhost:8000`
23+
- Authorization callback URL: `http://localhost:8080/callback`
24+
- Note: The client uses port 8080 for its callback, while the server uses 8000
25+
26+
## Installation
27+
28+
```bash
29+
# Install dependencies with uv
30+
uv install
31+
32+
# Or install in development mode with dev dependencies
33+
uv install --dev
34+
```
35+
36+
## Running the Client
37+
38+
```bash
39+
# Basic usage - will start OAuth flow if not authenticated
40+
uv run mcp-simple-auth-client
41+
42+
# Specify custom server URL
43+
uv run mcp-simple-auth-client --server-url http://localhost:8000
44+
45+
# Specify custom callback port (if port 8080 is in use)
46+
uv run mcp-simple-auth-client --callback-port 8081
47+
48+
# Use file-based token storage instead of in-memory
49+
uv run mcp-simple-auth-client --use-file-storage
50+
51+
# Run with debug logging
52+
uv run mcp-simple-auth-client --debug
53+
```
54+
55+
## How It Works
56+
57+
1. **First Run**: If no tokens are stored, the client will:
58+
- Start a local HTTP server to handle the OAuth callback
59+
- Open your default browser to the GitHub authorization page
60+
- Wait for you to authorize the application
61+
- Exchange the authorization code for tokens
62+
- Save the tokens for future use
63+
64+
2. **Subsequent Runs**: The client will:
65+
- Load existing tokens from storage
66+
- Use them to authenticate with the server
67+
- Automatically refresh tokens if needed
68+
69+
3. **Making Requests**: Once authenticated, the client can:
70+
- Call the `get_user_profile` tool
71+
- Display the GitHub user information
72+
73+
## Example Output
74+
75+
```
76+
$ uv run mcp-simple-auth-client
77+
Starting OAuth client...
78+
No existing tokens found. Starting OAuth flow...
79+
Opening authorization URL in browser...
80+
Starting callback server on http://localhost:8080...
81+
Waiting for OAuth callback...
82+
Authorization successful!
83+
Connecting to MCP server...
84+
Calling get_user_profile tool...
85+
86+
GitHub User Profile:
87+
{
88+
"login": "username",
89+
"id": 12345,
90+
"name": "John Doe",
91+
"email": "[email protected]",
92+
"bio": "Developer",
93+
"public_repos": 42,
94+
"followers": 100,
95+
"following": 50
96+
}
97+
98+
Done!
99+
```
100+
101+
## OAuth Flow
102+
103+
```
104+
Client Browser GitHub MCP Server
105+
| | | |
106+
|-- Opens auth URL ------>| | |
107+
| |-- User authorizes ------>| |
108+
| | |<-- Auth code ------------|
109+
|<-- Callback ------------| | |
110+
| | | |
111+
|-- Exchange code for tokens ------------------------>| |
112+
|<-- Access token -----------------------------------| |
113+
| | | |
114+
|-- Authenticated request -------------------------------------------------------->|
115+
|<-- Protected resource -----------------------------------------------------------|
116+
```
117+
118+
## Development
119+
120+
```bash
121+
# Run linting
122+
uv run ruff check .
123+
124+
# Run type checking
125+
uv run pyright
126+
127+
# Run with development dependencies
128+
uv run --dev pytest
129+
```
130+
131+
## Troubleshooting
132+
133+
**Client fails to connect:**
134+
- Make sure the server is running: `uv run mcp-simple-auth` in the server directory
135+
- Check that the server URL is correct (default: http://localhost:8000)
136+
- Verify OAuth configuration matches between client and server
137+
138+
**OAuth flow fails:**
139+
- Ensure GitHub OAuth app callback URL matches the client's callback URL
140+
- Check that no other service is using the callback port (default: 8080)
141+
- Make sure your browser allows opening localhost URLs
142+
143+
**Token issues:**
144+
- Delete stored tokens to restart the OAuth flow: `rm oauth_*.json`
145+
- Check that the server is configured with valid GitHub OAuth credentials
146+
147+
## Security Notes
148+
149+
- Tokens are stored locally in files or memory
150+
- The local callback server only runs during the OAuth flow
151+
- Consider using HTTPS in production environments
152+
- Implement proper token encryption for sensitive applications
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Simple OAuth client for MCP simple-auth server."""

0 commit comments

Comments
 (0)