Skip to content

Commit 76c8ef8

Browse files
Merge branch 'main' into pcarleton/conformance-auth-client
2 parents 08ee14c + b19fa6f commit 76c8ef8

32 files changed

+1755
-136
lines changed

README.md

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,14 @@ Let's create a simple MCP server that exposes a calculator tool and some data:
132132
"""
133133
FastMCP quickstart example.
134134
135-
cd to the `examples/snippets/clients` directory and run:
136-
uv run server fastmcp_quickstart stdio
135+
Run from the repository root:
136+
uv run examples/snippets/servers/fastmcp_quickstart.py
137137
"""
138138

139139
from mcp.server.fastmcp import FastMCP
140140

141141
# Create an MCP server
142-
mcp = FastMCP("Demo")
142+
mcp = FastMCP("Demo", json_response=True)
143143

144144

145145
# Add an addition tool
@@ -167,23 +167,36 @@ def greet_user(name: str, style: str = "friendly") -> str:
167167
}
168168

169169
return f"{styles.get(style, styles['friendly'])} for someone named {name}."
170+
171+
172+
# Run with streamable HTTP transport
173+
if __name__ == "__main__":
174+
mcp.run(transport="streamable-http")
170175
```
171176

172177
_Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/fastmcp_quickstart.py)_
173178
<!-- /snippet-source -->
174179

175-
You can install this server in [Claude Desktop](https://claude.ai/download) and interact with it right away by running:
180+
You can install this server in [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp) and interact with it right away. First, run the server:
176181

177182
```bash
178-
uv run mcp install server.py
183+
uv run --with mcp examples/snippets/servers/fastmcp_quickstart.py
179184
```
180185

181-
Alternatively, you can test it with the MCP Inspector:
186+
Then add it to Claude Code:
182187

183188
```bash
184-
uv run mcp dev server.py
189+
claude mcp add --transport http my-server http://localhost:8000/mcp
185190
```
186191

192+
Alternatively, you can test it with the MCP Inspector. Start the server as above, then in a separate terminal:
193+
194+
```bash
195+
npx -y @modelcontextprotocol/inspector
196+
```
197+
198+
In the inspector UI, connect to `http://localhost:8000/mcp`.
199+
187200
## What is MCP?
188201

189202
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
@@ -873,8 +886,8 @@ async def generate_poem(topic: str, ctx: Context[ServerSession, None]) -> str:
873886
max_tokens=100,
874887
)
875888

876-
if result.content.type == "text":
877-
return result.content.text
889+
if all(c.type == "text" for c in result.content_as_list):
890+
return "\n".join(c.text for c in result.content_as_list if c.type == "text")
878891
return str(result.content)
879892
```
880893

@@ -943,6 +956,7 @@ class SimpleTokenVerifier(TokenVerifier):
943956
# Create FastMCP instance as a Resource Server
944957
mcp = FastMCP(
945958
"Weather Service",
959+
json_response=True,
946960
# Token verifier for authentication
947961
token_verifier=SimpleTokenVerifier(),
948962
# Auth settings for RFC 9728 Protected Resource Metadata
@@ -1158,7 +1172,7 @@ Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMC
11581172

11591173
### Streamable HTTP Transport
11601174

1161-
> **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
1175+
> **Note**: Streamable HTTP transport is the recommended transport for production deployments. Use `stateless_http=True` and `json_response=True` for optimal scalability.
11621176
11631177
<!-- snippet-source examples/snippets/servers/streamable_config.py -->
11641178
```python
@@ -1169,15 +1183,15 @@ Run from the repository root:
11691183

11701184
from mcp.server.fastmcp import FastMCP
11711185

1172-
# Stateful server (maintains session state)
1173-
mcp = FastMCP("StatefulServer")
1186+
# Stateless server with JSON responses (recommended)
1187+
mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
11741188

11751189
# Other configuration options:
1176-
# Stateless server (no session persistence)
1190+
# Stateless server with SSE streaming responses
11771191
# mcp = FastMCP("StatelessServer", stateless_http=True)
11781192

1179-
# Stateless server (no session persistence, no sse stream with supported client)
1180-
# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
1193+
# Stateful server with session persistence
1194+
# mcp = FastMCP("StatefulServer")
11811195

11821196

11831197
# Add a simple tool to demonstrate the server
@@ -1212,7 +1226,7 @@ from starlette.routing import Mount
12121226
from mcp.server.fastmcp import FastMCP
12131227

12141228
# Create the Echo server
1215-
echo_mcp = FastMCP(name="EchoServer", stateless_http=True)
1229+
echo_mcp = FastMCP(name="EchoServer", stateless_http=True, json_response=True)
12161230

12171231

12181232
@echo_mcp.tool()
@@ -1222,7 +1236,7 @@ def echo(message: str) -> str:
12221236

12231237

12241238
# Create the Math server
1225-
math_mcp = FastMCP(name="MathServer", stateless_http=True)
1239+
math_mcp = FastMCP(name="MathServer", stateless_http=True, json_response=True)
12261240

12271241

12281242
@math_mcp.tool()
@@ -1323,7 +1337,7 @@ from starlette.routing import Mount
13231337
from mcp.server.fastmcp import FastMCP
13241338

13251339
# Create MCP server
1326-
mcp = FastMCP("My App")
1340+
mcp = FastMCP("My App", json_response=True)
13271341

13281342

13291343
@mcp.tool()
@@ -1360,7 +1374,7 @@ from starlette.routing import Host
13601374
from mcp.server.fastmcp import FastMCP
13611375

13621376
# Create MCP server
1363-
mcp = FastMCP("MCP Host App")
1377+
mcp = FastMCP("MCP Host App", json_response=True)
13641378

13651379

13661380
@mcp.tool()
@@ -1397,8 +1411,8 @@ from starlette.routing import Mount
13971411
from mcp.server.fastmcp import FastMCP
13981412

13991413
# Create multiple MCP servers
1400-
api_mcp = FastMCP("API Server")
1401-
chat_mcp = FastMCP("Chat Server")
1414+
api_mcp = FastMCP("API Server", json_response=True)
1415+
chat_mcp = FastMCP("Chat Server", json_response=True)
14021416

14031417

14041418
@api_mcp.tool()
@@ -1448,7 +1462,11 @@ from mcp.server.fastmcp import FastMCP
14481462

14491463
# Configure streamable_http_path during initialization
14501464
# This server will mount at the root of wherever it's mounted
1451-
mcp_at_root = FastMCP("My Server", streamable_http_path="/")
1465+
mcp_at_root = FastMCP(
1466+
"My Server",
1467+
json_response=True,
1468+
streamable_http_path="/",
1469+
)
14521470

14531471

14541472
@mcp_at_root.tool()

docs/index.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Here's a simple MCP server that exposes a tool, resource, and prompt:
1717
```python title="server.py"
1818
from mcp.server.fastmcp import FastMCP
1919

20-
mcp = FastMCP("Test Server")
20+
mcp = FastMCP("Test Server", json_response=True)
2121

2222

2323
@mcp.tool()
@@ -36,12 +36,22 @@ def get_greeting(name: str) -> str:
3636
def greet_user(name: str, style: str = "friendly") -> str:
3737
"""Generate a greeting prompt"""
3838
return f"Write a {style} greeting for someone named {name}."
39+
40+
41+
if __name__ == "__main__":
42+
mcp.run(transport="streamable-http")
43+
```
44+
45+
Run the server:
46+
47+
```bash
48+
uv run --with mcp server.py
3949
```
4050

41-
Test it with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
51+
Then open the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) and connect to `http://localhost:8000/mcp`:
4252

4353
```bash
44-
uv run mcp dev server.py
54+
npx -y @modelcontextprotocol/inspector
4555
```
4656

4757
## Getting Started

examples/clients/simple-auth-client/mcp_simple_auth_client/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ async def callback_handler() -> tuple[str, str | None]:
177177
"redirect_uris": ["http://localhost:3030/callback"],
178178
"grant_types": ["authorization_code", "refresh_token"],
179179
"response_types": ["code"],
180-
"token_endpoint_auth_method": "client_secret_post",
181180
}
182181

183182
async def _default_redirect_handler(authorization_url: str) -> None:

examples/servers/everything-server/mcp_everything_server/server.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ async def test_sampling(prompt: str, ctx: Context[ServerSession, None]) -> str:
134134
max_tokens=100,
135135
)
136136

137-
if result.content.type == "text":
138-
model_response = result.content.text
137+
if any(c.type == "text" for c in result.content_as_list):
138+
model_response = "\n".join(c.text for c in result.content_as_list if c.type == "text")
139139
else:
140140
model_response = "No response"
141141

@@ -198,6 +198,65 @@ async def test_elicitation_sep1034_defaults(ctx: Context[ServerSession, None]) -
198198
return f"Elicitation not supported or error: {str(e)}"
199199

200200

201+
class EnumSchemasTestSchema(BaseModel):
202+
"""Schema for testing enum schema variations (SEP-1330)"""
203+
204+
untitledSingle: str = Field(
205+
description="Simple enum without titles", json_schema_extra={"enum": ["active", "inactive", "pending"]}
206+
)
207+
titledSingle: str = Field(
208+
description="Enum with titled options (oneOf)",
209+
json_schema_extra={
210+
"oneOf": [
211+
{"const": "low", "title": "Low Priority"},
212+
{"const": "medium", "title": "Medium Priority"},
213+
{"const": "high", "title": "High Priority"},
214+
]
215+
},
216+
)
217+
untitledMulti: list[str] = Field(
218+
description="Multi-select without titles",
219+
json_schema_extra={"items": {"type": "string", "enum": ["read", "write", "execute"]}},
220+
)
221+
titledMulti: list[str] = Field(
222+
description="Multi-select with titled options",
223+
json_schema_extra={
224+
"items": {
225+
"anyOf": [
226+
{"const": "feature", "title": "New Feature"},
227+
{"const": "bug", "title": "Bug Fix"},
228+
{"const": "docs", "title": "Documentation"},
229+
]
230+
}
231+
},
232+
)
233+
legacyEnum: str = Field(
234+
description="Legacy enum with enumNames",
235+
json_schema_extra={
236+
"enum": ["small", "medium", "large"],
237+
"enumNames": ["Small Size", "Medium Size", "Large Size"],
238+
},
239+
)
240+
241+
242+
@mcp.tool()
243+
async def test_elicitation_sep1330_enums(ctx: Context[ServerSession, None]) -> str:
244+
"""Tests elicitation with enum schema variations per SEP-1330"""
245+
try:
246+
result = await ctx.elicit(
247+
message="Please select values using different enum schema types", schema=EnumSchemasTestSchema
248+
)
249+
250+
if result.action == "accept":
251+
content = result.data.model_dump_json()
252+
else:
253+
content = "{}"
254+
255+
return f"Elicitation completed: action={result.action}, content={content}"
256+
except Exception as e:
257+
return f"Elicitation not supported or error: {str(e)}"
258+
259+
201260
@mcp.tool()
202261
def test_error_handling() -> str:
203262
"""Tests error response handling"""

examples/snippets/servers/fastmcp_quickstart.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""
22
FastMCP quickstart example.
33
4-
cd to the `examples/snippets/clients` directory and run:
5-
uv run server fastmcp_quickstart stdio
4+
Run from the repository root:
5+
uv run examples/snippets/servers/fastmcp_quickstart.py
66
"""
77

88
from mcp.server.fastmcp import FastMCP
99

1010
# Create an MCP server
11-
mcp = FastMCP("Demo")
11+
mcp = FastMCP("Demo", json_response=True)
1212

1313

1414
# Add an addition tool
@@ -36,3 +36,8 @@ def greet_user(name: str, style: str = "friendly") -> str:
3636
}
3737

3838
return f"{styles.get(style, styles['friendly'])} for someone named {name}."
39+
40+
41+
# Run with streamable HTTP transport
42+
if __name__ == "__main__":
43+
mcp.run(transport="streamable-http")

examples/snippets/servers/oauth_server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ async def verify_token(self, token: str) -> AccessToken | None:
2020
# Create FastMCP instance as a Resource Server
2121
mcp = FastMCP(
2222
"Weather Service",
23+
json_response=True,
2324
# Token verifier for authentication
2425
token_verifier=SimpleTokenVerifier(),
2526
# Auth settings for RFC 9728 Protected Resource Metadata

examples/snippets/servers/sampling.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ async def generate_poem(topic: str, ctx: Context[ServerSession, None]) -> str:
2020
max_tokens=100,
2121
)
2222

23-
if result.content.type == "text":
24-
return result.content.text
23+
if all(c.type == "text" for c in result.content_as_list):
24+
return "\n".join(c.text for c in result.content_as_list if c.type == "text")
2525
return str(result.content)

examples/snippets/servers/streamable_config.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
from mcp.server.fastmcp import FastMCP
77

8-
# Stateful server (maintains session state)
9-
mcp = FastMCP("StatefulServer")
8+
# Stateless server with JSON responses (recommended)
9+
mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
1010

1111
# Other configuration options:
12-
# Stateless server (no session persistence)
12+
# Stateless server with SSE streaming responses
1313
# mcp = FastMCP("StatelessServer", stateless_http=True)
1414

15-
# Stateless server (no session persistence, no sse stream with supported client)
16-
# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
15+
# Stateful server with session persistence
16+
# mcp = FastMCP("StatefulServer")
1717

1818

1919
# Add a simple tool to demonstrate the server

examples/snippets/servers/streamable_http_basic_mounting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from mcp.server.fastmcp import FastMCP
1212

1313
# Create MCP server
14-
mcp = FastMCP("My App")
14+
mcp = FastMCP("My App", json_response=True)
1515

1616

1717
@mcp.tool()

examples/snippets/servers/streamable_http_host_mounting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from mcp.server.fastmcp import FastMCP
1212

1313
# Create MCP server
14-
mcp = FastMCP("MCP Host App")
14+
mcp = FastMCP("MCP Host App", json_response=True)
1515

1616

1717
@mcp.tool()

0 commit comments

Comments
 (0)