Skip to content

Commit 1fba897

Browse files
authored
Merge branch 'modelcontextprotocol:main' into modelcontextprotocol#552
2 parents 9a797c2 + 37b54b4 commit 1fba897

File tree

17 files changed

+1162
-35
lines changed

17 files changed

+1162
-35
lines changed

README.md

Lines changed: 9 additions & 3 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.
@@ -426,7 +428,7 @@ mcp = FastMCP(name="MathServer", stateless_http=True)
426428

427429

428430
@mcp.tool(description="A simple add tool")
429-
def add_two(n: int) -> str:
431+
def add_two(n: int) -> int:
430432
return n + 2
431433
```
432434

@@ -462,6 +464,8 @@ The streamable HTTP transport supports:
462464

463465
> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
464466
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+
465469
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.
466470

467471
```python
@@ -617,7 +621,7 @@ server = Server("example-server", lifespan=server_lifespan)
617621
# Access lifespan context in handlers
618622
@server.call_tool()
619623
async def query_db(name: str, arguments: dict) -> list:
620-
ctx = server.request_context
624+
ctx = server.get_context()
621625
db = ctx.lifespan_context["db"]
622626
return await db.query(arguments["query"])
623627
```
@@ -692,6 +696,8 @@ if __name__ == "__main__":
692696
asyncio.run(run())
693697
```
694698

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

697703
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):

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

examples/servers/simple-auth/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,31 @@ uv run mcp-simple-auth
4444

4545
The server will start on `http://localhost:8000`.
4646

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+
4772
## Available Tool
4873

4974
### get_user_profile
@@ -61,5 +86,6 @@ If the server fails to start, check:
6186
1. Environment variables `MCP_GITHUB_GITHUB_CLIENT_ID` and `MCP_GITHUB_GITHUB_CLIENT_SECRET` are set
6287
2. The GitHub OAuth app callback URL matches `http://localhost:8000/github/callback`
6388
3. No other service is using port 8000
89+
4. The transport specified is valid (`sse` or `streamable-http`)
6490

6591
You can use [Inspector](https://github.com/modelcontextprotocol/inspector) to test Auth

examples/servers/simple-auth/mcp_simple_auth/server.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import secrets
55
import time
6-
from typing import Any
6+
from typing import Any, Literal
77

88
import click
99
from pydantic import AnyHttpUrl
@@ -347,7 +347,13 @@ async def get_user_profile() -> dict[str, Any]:
347347
@click.command()
348348
@click.option("--port", default=8000, help="Port to listen on")
349349
@click.option("--host", default="localhost", help="Host to bind to")
350-
def main(port: int, host: str) -> int:
350+
@click.option(
351+
"--transport",
352+
default="sse",
353+
type=click.Choice(["sse", "streamable-http"]),
354+
help="Transport protocol to use ('sse' or 'streamable-http')",
355+
)
356+
def main(port: int, host: str, transport: Literal["sse", "streamable-http"]) -> int:
351357
"""Run the simple GitHub MCP server."""
352358
logging.basicConfig(level=logging.INFO)
353359

@@ -364,5 +370,6 @@ def main(port: int, host: str) -> int:
364370
return 1
365371

366372
mcp_server = create_simple_mcp_server(settings)
367-
mcp_server.run(transport="sse")
373+
logger.info(f"Starting server with {transport} transport")
374+
mcp_server.run(transport=transport)
368375
return 0

src/mcp/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .client.session import ClientSession
2+
from .client.session_group import ClientSessionGroup
23
from .client.stdio import StdioServerParameters, stdio_client
34
from .server.session import ServerSession
45
from .server.stdio import stdio_server
@@ -63,6 +64,7 @@
6364
"ClientRequest",
6465
"ClientResult",
6566
"ClientSession",
67+
"ClientSessionGroup",
6668
"CreateMessageRequest",
6769
"CreateMessageResult",
6870
"ErrorData",

src/mcp/cli/claude.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def get_claude_config_path() -> Path | None:
3131
return path
3232
return None
3333

34+
3435
def get_uv_path() -> str:
3536
"""Get the full path to the uv executable."""
3637
uv_path = shutil.which("uv")
@@ -42,6 +43,7 @@ def get_uv_path() -> str:
4243
return "uv" # Fall back to just "uv" if not found
4344
return uv_path
4445

46+
4547
def update_claude_config(
4648
file_spec: str,
4749
server_name: str,

src/mcp/cli/cli.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import subprocess
77
import sys
88
from pathlib import Path
9-
from typing import Annotated
9+
from typing import Annotated, Any
10+
11+
from mcp.server import FastMCP
12+
from mcp.server import Server as LowLevelServer
1013

1114
try:
1215
import typer
@@ -141,17 +144,48 @@ def _import_server(file: Path, server_object: str | None = None):
141144
module = importlib.util.module_from_spec(spec)
142145
spec.loader.exec_module(module)
143146

147+
def _check_server_object(server_object: Any, object_name: str):
148+
"""Helper function to check that the server object is supported
149+
150+
Args:
151+
server_object: The server object to check.
152+
153+
Returns:
154+
True if it's supported.
155+
"""
156+
if not isinstance(server_object, FastMCP):
157+
logger.error(
158+
f"The server object {object_name} is of type "
159+
f"{type(server_object)} (expecting {FastMCP})."
160+
)
161+
if isinstance(server_object, LowLevelServer):
162+
logger.warning(
163+
"Note that only FastMCP server is supported. Low level "
164+
"Server class is not yet supported."
165+
)
166+
return False
167+
return True
168+
144169
# If no object specified, try common server names
145170
if not server_object:
146171
# Look for the most common server object names
147172
for name in ["mcp", "server", "app"]:
148173
if hasattr(module, name):
174+
if not _check_server_object(getattr(module, name), f"{file}:{name}"):
175+
logger.error(
176+
f"Ignoring object '{file}:{name}' as it's not a valid "
177+
"server object"
178+
)
179+
continue
149180
return getattr(module, name)
150181

151182
logger.error(
152183
f"No server object found in {file}. Please either:\n"
153184
"1. Use a standard variable name (mcp, server, or app)\n"
154-
"2. Specify the object name with file:object syntax",
185+
"2. Specify the object name with file:object syntax"
186+
"3. If the server creates the FastMCP object within main() "
187+
" or another function, refactor the FastMCP object to be a "
188+
" global variable named mcp, server, or app.",
155189
extra={"file": str(file)},
156190
)
157191
sys.exit(1)
@@ -179,6 +213,9 @@ def _import_server(file: Path, server_object: str | None = None):
179213
)
180214
sys.exit(1)
181215

216+
if not _check_server_object(server, server_object):
217+
sys.exit(1)
218+
182219
return server
183220

184221

0 commit comments

Comments
 (0)