Skip to content

Commit f51d257

Browse files
authored
Merge branch 'main' into ylassoued/feat-request
2 parents 2c33194 + 4cd4fe0 commit f51d257

File tree

62 files changed

+5490
-127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+5490
-127
lines changed

.github/workflows/shared.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ jobs:
3737
run: uv run --no-sync pyright
3838

3939
test:
40-
runs-on: ubuntu-latest
40+
runs-on: ${{ matrix.os }}
4141
strategy:
4242
matrix:
4343
python-version: ["3.10", "3.11", "3.12", "3.13"]
44+
os: [ubuntu-latest, windows-latest]
4445

4546
steps:
4647
- uses: actions/checkout@v4
@@ -55,3 +56,4 @@ jobs:
5556

5657
- name: Run pytest
5758
run: uv run --no-sync pytest
59+
continue-on-error: true

README.md

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ providing an implementation of the `OAuthServerProvider` protocol.
318318

319319
```
320320
mcp = FastMCP("My App",
321-
auth_provider=MyOAuthServerProvider(),
321+
auth_server_provider=MyOAuthServerProvider(),
322322
auth=AuthSettings(
323323
issuer_url="https://myapp.com",
324324
revocation_options=RevocationOptions(
@@ -387,6 +387,8 @@ python server.py
387387
mcp run server.py
388388
```
389389

390+
Note that `mcp run` or `mcp dev` only supports server using FastMCP and not the low-level server variant.
391+
390392
### Streamable HTTP Transport
391393

392394
> **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
@@ -400,6 +402,9 @@ mcp = FastMCP("StatefulServer")
400402
# Stateless server (no session persistence)
401403
mcp = FastMCP("StatelessServer", stateless_http=True)
402404

405+
# Stateless server (no session persistence, no sse stream with supported client)
406+
mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
407+
403408
# Run server with streamable_http transport
404409
mcp.run(transport="streamable-http")
405410
```
@@ -426,21 +431,28 @@ mcp = FastMCP(name="MathServer", stateless_http=True)
426431

427432

428433
@mcp.tool(description="A simple add tool")
429-
def add_two(n: int) -> str:
434+
def add_two(n: int) -> int:
430435
return n + 2
431436
```
432437

433438
```python
434439
# main.py
440+
import contextlib
435441
from fastapi import FastAPI
436442
from mcp.echo import echo
437443
from mcp.math import math
438444

439445

440-
app = FastAPI()
446+
# Create a combined lifespan to manage both session managers
447+
@contextlib.asynccontextmanager
448+
async def lifespan(app: FastAPI):
449+
async with contextlib.AsyncExitStack() as stack:
450+
await stack.enter_async_context(echo.mcp.session_manager.run())
451+
await stack.enter_async_context(math.mcp.session_manager.run())
452+
yield
441453

442-
# Use the session manager's lifespan
443-
app = FastAPI(lifespan=lambda app: echo.mcp.session_manager.run())
454+
455+
app = FastAPI(lifespan=lifespan)
444456
app.mount("/echo", echo.mcp.streamable_http_app())
445457
app.mount("/math", math.mcp.streamable_http_app())
446458
```
@@ -462,6 +474,8 @@ The streamable HTTP transport supports:
462474

463475
> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
464476
477+
By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below.
478+
465479
You can mount the SSE server to an existing ASGI server using the `sse_app` method. This allows you to integrate the SSE server with other ASGI applications.
466480

467481
```python
@@ -617,7 +631,7 @@ server = Server("example-server", lifespan=server_lifespan)
617631
# Access lifespan context in handlers
618632
@server.call_tool()
619633
async def query_db(name: str, arguments: dict) -> list:
620-
ctx = server.request_context
634+
ctx = server.get_context()
621635
db = ctx.lifespan_context["db"]
622636
return await db.query(arguments["query"])
623637
```
@@ -692,6 +706,8 @@ if __name__ == "__main__":
692706
asyncio.run(run())
693707
```
694708

709+
Caution: The `mcp run` and `mcp dev` tool doesn't support low-level server.
710+
695711
### Writing MCP Clients
696712

697713
The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports):
@@ -780,6 +796,60 @@ async def main():
780796
tool_result = await session.call_tool("echo", {"message": "hello"})
781797
```
782798

799+
### OAuth Authentication for Clients
800+
801+
The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for connecting to protected MCP servers:
802+
803+
```python
804+
from mcp.client.auth import OAuthClientProvider, TokenStorage
805+
from mcp.client.session import ClientSession
806+
from mcp.client.streamable_http import streamablehttp_client
807+
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
808+
809+
810+
class CustomTokenStorage(TokenStorage):
811+
"""Simple in-memory token storage implementation."""
812+
813+
async def get_tokens(self) -> OAuthToken | None:
814+
pass
815+
816+
async def set_tokens(self, tokens: OAuthToken) -> None:
817+
pass
818+
819+
async def get_client_info(self) -> OAuthClientInformationFull | None:
820+
pass
821+
822+
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
823+
pass
824+
825+
826+
async def main():
827+
# Set up OAuth authentication
828+
oauth_auth = OAuthClientProvider(
829+
server_url="https://api.example.com",
830+
client_metadata=OAuthClientMetadata(
831+
client_name="My Client",
832+
redirect_uris=["http://localhost:3000/callback"],
833+
grant_types=["authorization_code", "refresh_token"],
834+
response_types=["code"],
835+
),
836+
storage=CustomTokenStorage(),
837+
redirect_handler=lambda url: print(f"Visit: {url}"),
838+
callback_handler=lambda: ("auth_code", None),
839+
)
840+
841+
# Use with streamable HTTP client
842+
async with streamablehttp_client(
843+
"https://api.example.com/mcp", auth=oauth_auth
844+
) as (read, write, _):
845+
async with ClientSession(read, write) as session:
846+
await session.initialize()
847+
# Authenticated session ready
848+
```
849+
850+
For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).
851+
852+
783853
### MCP Primitives
784854

785855
The MCP protocol defines three core primitives that servers can implement:
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Simple Auth Client Example
2+
3+
A demonstration of how to use the MCP Python SDK with OAuth authentication over streamable HTTP or SSE transport.
4+
5+
## Features
6+
7+
- OAuth 2.0 authentication with PKCE
8+
- Support for both StreamableHTTP and SSE transports
9+
- Interactive command-line interface
10+
11+
## Installation
12+
13+
```bash
14+
cd examples/clients/simple-auth-client
15+
uv sync --reinstall
16+
```
17+
18+
## Usage
19+
20+
### 1. Start an MCP server with OAuth support
21+
22+
```bash
23+
# Example with mcp-simple-auth
24+
cd path/to/mcp-simple-auth
25+
uv run mcp-simple-auth --transport streamable-http --port 3001
26+
```
27+
28+
### 2. Run the client
29+
30+
```bash
31+
uv run mcp-simple-auth-client
32+
33+
# Or with custom server URL
34+
MCP_SERVER_PORT=3001 uv run mcp-simple-auth-client
35+
36+
# Use SSE transport
37+
MCP_TRANSPORT_TYPE=sse uv run mcp-simple-auth-client
38+
```
39+
40+
### 3. Complete OAuth flow
41+
42+
The client will open your browser for authentication. After completing OAuth, you can use commands:
43+
44+
- `list` - List available tools
45+
- `call <tool_name> [args]` - Call a tool with optional JSON arguments
46+
- `quit` - Exit
47+
48+
## Example
49+
50+
```
51+
🔐 Simple MCP Auth Client
52+
Connecting to: http://localhost:3001
53+
54+
Please visit the following URL to authorize the application:
55+
http://localhost:3001/authorize?response_type=code&client_id=...
56+
57+
✅ Connected to MCP server at http://localhost:3001
58+
59+
mcp> list
60+
📋 Available tools:
61+
1. echo - Echo back the input text
62+
63+
mcp> call echo {"text": "Hello, world!"}
64+
🔧 Tool 'echo' result:
65+
Hello, world!
66+
67+
mcp> quit
68+
👋 Goodbye!
69+
```
70+
71+
## Configuration
72+
73+
- `MCP_SERVER_PORT` - Server URL (default: 8000)
74+
- `MCP_TRANSPORT_TYPE` - Transport type: `streamable_http` (default) or `sse`
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)