Skip to content

Commit c09119f

Browse files
authored
Merge branch 'main' into main
2 parents c0adeb8 + b8f7b02 commit c09119f

File tree

45 files changed

+3308
-293
lines changed

Some content is hidden

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

45 files changed

+3308
-293
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: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ The Model Context Protocol allows applications to provide context for LLMs in a
6666

6767
- Build MCP clients that can connect to any MCP server
6868
- Create MCP servers that expose resources, prompts and tools
69-
- Use standard transports like stdio and SSE
69+
- Use standard transports like stdio, SSE, and Streamable HTTP
7070
- Handle all MCP protocol messages and lifecycle events
7171

7272
## Installation
@@ -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(
@@ -334,7 +334,7 @@ mcp = FastMCP("My App",
334334
)
335335
```
336336

337-
See [OAuthServerProvider](mcp/server/auth/provider.py) for more details.
337+
See [OAuthServerProvider](src/mcp/server/auth/provider.py) for more details.
338338

339339
## Running Your Server
340340

@@ -387,8 +387,85 @@ 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+
392+
### Streamable HTTP Transport
393+
394+
> **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
395+
396+
```python
397+
from mcp.server.fastmcp import FastMCP
398+
399+
# Stateful server (maintains session state)
400+
mcp = FastMCP("StatefulServer")
401+
402+
# Stateless server (no session persistence)
403+
mcp = FastMCP("StatelessServer", stateless_http=True)
404+
405+
# Run server with streamable_http transport
406+
mcp.run(transport="streamable-http")
407+
```
408+
409+
You can mount multiple FastMCP servers in a FastAPI application:
410+
411+
```python
412+
# echo.py
413+
from mcp.server.fastmcp import FastMCP
414+
415+
mcp = FastMCP(name="EchoServer", stateless_http=True)
416+
417+
418+
@mcp.tool(description="A simple echo tool")
419+
def echo(message: str) -> str:
420+
return f"Echo: {message}"
421+
```
422+
423+
```python
424+
# math.py
425+
from mcp.server.fastmcp import FastMCP
426+
427+
mcp = FastMCP(name="MathServer", stateless_http=True)
428+
429+
430+
@mcp.tool(description="A simple add tool")
431+
def add_two(n: int) -> int:
432+
return n + 2
433+
```
434+
435+
```python
436+
# main.py
437+
from fastapi import FastAPI
438+
from mcp.echo import echo
439+
from mcp.math import math
440+
441+
442+
app = FastAPI()
443+
444+
# Use the session manager's lifespan
445+
app = FastAPI(lifespan=lambda app: echo.mcp.session_manager.run())
446+
app.mount("/echo", echo.mcp.streamable_http_app())
447+
app.mount("/math", math.mcp.streamable_http_app())
448+
```
449+
450+
For low level server with Streamable HTTP implementations, see:
451+
- Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/)
452+
- Stateless server: [`examples/servers/simple-streamablehttp-stateless/`](examples/servers/simple-streamablehttp-stateless/)
453+
454+
455+
456+
The streamable HTTP transport supports:
457+
- Stateful and stateless operation modes
458+
- Resumability with event stores
459+
- JSON or SSE response formats
460+
- Better scalability for multi-node deployments
461+
462+
390463
### Mounting to an Existing ASGI Server
391464

465+
> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
466+
467+
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.
468+
392469
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.
393470

394471
```python
@@ -410,6 +487,43 @@ app = Starlette(
410487
app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app()))
411488
```
412489

490+
When mounting multiple MCP servers under different paths, you can configure the mount path in several ways:
491+
492+
```python
493+
from starlette.applications import Starlette
494+
from starlette.routing import Mount
495+
from mcp.server.fastmcp import FastMCP
496+
497+
# Create multiple MCP servers
498+
github_mcp = FastMCP("GitHub API")
499+
browser_mcp = FastMCP("Browser")
500+
curl_mcp = FastMCP("Curl")
501+
search_mcp = FastMCP("Search")
502+
503+
# Method 1: Configure mount paths via settings (recommended for persistent configuration)
504+
github_mcp.settings.mount_path = "/github"
505+
browser_mcp.settings.mount_path = "/browser"
506+
507+
# Method 2: Pass mount path directly to sse_app (preferred for ad-hoc mounting)
508+
# This approach doesn't modify the server's settings permanently
509+
510+
# Create Starlette app with multiple mounted servers
511+
app = Starlette(
512+
routes=[
513+
# Using settings-based configuration
514+
Mount("/github", app=github_mcp.sse_app()),
515+
Mount("/browser", app=browser_mcp.sse_app()),
516+
# Using direct mount path parameter
517+
Mount("/curl", app=curl_mcp.sse_app("/curl")),
518+
Mount("/search", app=search_mcp.sse_app("/search")),
519+
]
520+
)
521+
522+
# Method 3: For direct execution, you can also pass the mount path to run()
523+
if __name__ == "__main__":
524+
search_mcp.run(transport="sse", mount_path="/search")
525+
```
526+
413527
For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
414528

415529
## Examples
@@ -507,7 +621,7 @@ server = Server("example-server", lifespan=server_lifespan)
507621
# Access lifespan context in handlers
508622
@server.call_tool()
509623
async def query_db(name: str, arguments: dict) -> list:
510-
ctx = server.request_context
624+
ctx = server.get_context()
511625
db = ctx.lifespan_context["db"]
512626
return await db.query(arguments["query"])
513627
```
@@ -582,9 +696,11 @@ if __name__ == "__main__":
582696
asyncio.run(run())
583697
```
584698

699+
Caution: The `mcp run` and `mcp dev` tool doesn't support low-level server.
700+
585701
### Writing MCP Clients
586702

587-
The SDK provides a high-level client interface for connecting to MCP servers:
703+
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):
588704

589705
```python
590706
from mcp import ClientSession, StdioServerParameters, types
@@ -648,6 +764,28 @@ if __name__ == "__main__":
648764
asyncio.run(run())
649765
```
650766

767+
Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):
768+
769+
```python
770+
from mcp.client.streamable_http import streamablehttp_client
771+
from mcp import ClientSession
772+
773+
774+
async def main():
775+
# Connect to a streamable HTTP server
776+
async with streamablehttp_client("example/mcp") as (
777+
read_stream,
778+
write_stream,
779+
_,
780+
):
781+
# Create a session using the client streams
782+
async with ClientSession(read_stream, write_stream) as session:
783+
# Initialize the connection
784+
await session.initialize()
785+
# Call a tool
786+
tool_result = await session.call_tool("echo", {"message": "hello"})
787+
```
788+
651789
### MCP Primitives
652790

653791
The MCP protocol defines three core primitives that servers can implement:

examples/clients/simple-chatbot/README.MD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into
2525
```plaintext
2626
LLM_API_KEY=your_api_key_here
2727
```
28+
**Note:** The current implementation is configured to use the Groq API endpoint (`https://api.groq.com/openai/v1/chat/completions`) with the `llama-3.2-90b-vision-preview` model. If you plan to use a different LLM provider, you'll need to modify the `LLMClient` class in `main.py` to use the appropriate endpoint URL and model parameters.
2829

2930
3. **Configure servers:**
3031

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Simple MCP Server with GitHub OAuth Authentication
2+
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.
4+
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)
6+
7+
## Overview
8+
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
12+
13+
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
23+
24+
## Required Environment Variables
25+
26+
You MUST set these environment variables before running the server:
27+
28+
```bash
29+
export MCP_GITHUB_GITHUB_CLIENT_ID="your_client_id_here"
30+
export MCP_GITHUB_GITHUB_CLIENT_SECRET="your_client_secret_here"
31+
```
32+
33+
The server will not start without these environment variables properly set.
34+
35+
36+
## Running the Server
37+
38+
```bash
39+
# Set environment variables first (see above)
40+
41+
# Run the server
42+
uv run mcp-simple-auth
43+
```
44+
45+
The server will start on `http://localhost:8000`.
46+
47+
### Transport Options
48+
49+
This server supports multiple transport protocols that can run on the same port:
50+
51+
#### SSE (Server-Sent Events) - Default
52+
```bash
53+
uv run mcp-simple-auth
54+
# or explicitly:
55+
uv run mcp-simple-auth --transport sse
56+
```
57+
58+
SSE transport provides endpoint:
59+
- `/sse`
60+
61+
#### Streamable HTTP
62+
```bash
63+
uv run mcp-simple-auth --transport streamable-http
64+
```
65+
66+
Streamable HTTP transport provides endpoint:
67+
- `/mcp`
68+
69+
70+
This ensures backward compatibility without needing multiple server instances. When using SSE transport (`--transport sse`), only the `/sse` endpoint is available.
71+
72+
## Available Tool
73+
74+
### get_user_profile
75+
76+
The only tool in this simple example. Returns the authenticated user's GitHub profile information.
77+
78+
**Required scope**: `user`
79+
80+
**Returns**: GitHub user profile data including username, email, bio, etc.
81+
82+
83+
## Troubleshooting
84+
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`)
90+
91+
You can use [Inspector](https://github.com/modelcontextprotocol/inspector) to test Auth
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Simple MCP server with GitHub OAuth authentication."""
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Main entry point for simple MCP server with GitHub OAuth authentication."""
2+
3+
import sys
4+
5+
from mcp_simple_auth.server import main
6+
7+
sys.exit(main())

0 commit comments

Comments
 (0)