Skip to content

Commit a10fec7

Browse files
authored
README - replace code snippets with examples - direct execution and display utilities (#1137)
1 parent 9bf5160 commit a10fec7

File tree

5 files changed

+254
-8
lines changed

5 files changed

+254
-8
lines changed

README.md

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ to the `@tool` decorator.
338338
```python
339339
"""Example showing structured output with tools."""
340340

341+
from typing import TypedDict
342+
341343
from pydantic import BaseModel, Field
342344

343345
from mcp.server.fastmcp import FastMCP
@@ -365,12 +367,8 @@ def get_weather(city: str) -> WeatherData:
365367
condition="sunny",
366368
wind_speed=5.2,
367369
)
368-
```
369370

370-
_Full example: [examples/snippets/servers/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/structured_output.py)_
371-
<!-- /snippet-source -->
372371

373-
```python
374372
# Using TypedDict for simpler structures
375373
class LocationInfo(TypedDict):
376374
latitude: float
@@ -437,6 +435,9 @@ def get_temperature(city: str) -> float:
437435
# Returns: {"result": 22.5}
438436
```
439437

438+
_Full example: [examples/snippets/servers/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/structured_output.py)_
439+
<!-- /snippet-source -->
440+
440441
### Prompts
441442

442443
Prompts are reusable templates that help LLMs interact with your server effectively:
@@ -814,21 +815,46 @@ uv run mcp install server.py -f .env
814815

815816
For advanced scenarios like custom deployments:
816817

818+
<!-- snippet-source examples/snippets/servers/direct_execution.py -->
817819
```python
820+
"""Example showing direct execution of an MCP server.
821+
822+
This is the simplest way to run an MCP server directly.
823+
cd to the `examples/snippets` directory and run:
824+
uv run direct-execution-server
825+
or
826+
python servers/direct_execution.py
827+
"""
828+
818829
from mcp.server.fastmcp import FastMCP
819830

820831
mcp = FastMCP("My App")
821832

822-
if __name__ == "__main__":
833+
834+
@mcp.tool()
835+
def hello(name: str = "World") -> str:
836+
"""Say hello to someone."""
837+
return f"Hello, {name}!"
838+
839+
840+
def main():
841+
"""Entry point for the direct execution server."""
823842
mcp.run()
843+
844+
845+
if __name__ == "__main__":
846+
main()
824847
```
825848

849+
_Full example: [examples/snippets/servers/direct_execution.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/direct_execution.py)_
850+
<!-- /snippet-source -->
851+
826852
Run it with:
827853

828854
```bash
829-
python server.py
855+
python servers/direct_execution.py
830856
# or
831-
uv run mcp run server.py
857+
uv run mcp run servers/direct_execution.py
832858
```
833859

834860
Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMCP and not the low-level server variant.
@@ -1277,9 +1303,30 @@ async def main():
12771303

12781304
When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts:
12791305

1306+
<!-- snippet-source examples/snippets/clients/display_utilities.py -->
12801307
```python
1308+
"""Client display utilities example.
1309+
1310+
This example shows how to use the SDK's display utilities to show
1311+
human-readable names for tools, resources, and prompts.
1312+
1313+
cd to the `examples/snippets` directory and run:
1314+
uv run display-utilities-client
1315+
"""
1316+
1317+
import asyncio
1318+
import os
1319+
1320+
from mcp import ClientSession, StdioServerParameters
1321+
from mcp.client.stdio import stdio_client
12811322
from mcp.shared.metadata_utils import get_display_name
1282-
from mcp.client.session import ClientSession
1323+
1324+
# Create server parameters for stdio connection
1325+
server_params = StdioServerParameters(
1326+
command="uv", # Using uv to run the server
1327+
args=["run", "server", "fastmcp_quickstart", "stdio"],
1328+
env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
1329+
)
12831330

12841331

12851332
async def display_tools(session: ClientSession):
@@ -1301,8 +1348,39 @@ async def display_resources(session: ClientSession):
13011348
for resource in resources_response.resources:
13021349
display_name = get_display_name(resource)
13031350
print(f"Resource: {display_name} ({resource.uri})")
1351+
1352+
templates_response = await session.list_resource_templates()
1353+
for template in templates_response.resourceTemplates:
1354+
display_name = get_display_name(template)
1355+
print(f"Resource Template: {display_name}")
1356+
1357+
1358+
async def run():
1359+
"""Run the display utilities example."""
1360+
async with stdio_client(server_params) as (read, write):
1361+
async with ClientSession(read, write) as session:
1362+
# Initialize the connection
1363+
await session.initialize()
1364+
1365+
print("=== Available Tools ===")
1366+
await display_tools(session)
1367+
1368+
print("\n=== Available Resources ===")
1369+
await display_resources(session)
1370+
1371+
1372+
def main():
1373+
"""Entry point for the display utilities client."""
1374+
asyncio.run(run())
1375+
1376+
1377+
if __name__ == "__main__":
1378+
main()
13041379
```
13051380

1381+
_Full example: [examples/snippets/clients/display_utilities.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/display_utilities.py)_
1382+
<!-- /snippet-source -->
1383+
13061384
The `get_display_name()` function implements the proper precedence rules for displaying names:
13071385

13081386
- For tools: `title` > `annotations.title` > `name`
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Client display utilities example.
2+
3+
This example shows how to use the SDK's display utilities to show
4+
human-readable names for tools, resources, and prompts.
5+
6+
cd to the `examples/snippets` directory and run:
7+
uv run display-utilities-client
8+
"""
9+
10+
import asyncio
11+
import os
12+
13+
from mcp import ClientSession, StdioServerParameters
14+
from mcp.client.stdio import stdio_client
15+
from mcp.shared.metadata_utils import get_display_name
16+
17+
# Create server parameters for stdio connection
18+
server_params = StdioServerParameters(
19+
command="uv", # Using uv to run the server
20+
args=["run", "server", "fastmcp_quickstart", "stdio"],
21+
env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
22+
)
23+
24+
25+
async def display_tools(session: ClientSession):
26+
"""Display available tools with human-readable names"""
27+
tools_response = await session.list_tools()
28+
29+
for tool in tools_response.tools:
30+
# get_display_name() returns the title if available, otherwise the name
31+
display_name = get_display_name(tool)
32+
print(f"Tool: {display_name}")
33+
if tool.description:
34+
print(f" {tool.description}")
35+
36+
37+
async def display_resources(session: ClientSession):
38+
"""Display available resources with human-readable names"""
39+
resources_response = await session.list_resources()
40+
41+
for resource in resources_response.resources:
42+
display_name = get_display_name(resource)
43+
print(f"Resource: {display_name} ({resource.uri})")
44+
45+
templates_response = await session.list_resource_templates()
46+
for template in templates_response.resourceTemplates:
47+
display_name = get_display_name(template)
48+
print(f"Resource Template: {display_name}")
49+
50+
51+
async def run():
52+
"""Run the display utilities example."""
53+
async with stdio_client(server_params) as (read, write):
54+
async with ClientSession(read, write) as session:
55+
# Initialize the connection
56+
await session.initialize()
57+
58+
print("=== Available Tools ===")
59+
await display_tools(session)
60+
61+
print("\n=== Available Resources ===")
62+
await display_resources(session)
63+
64+
65+
def main():
66+
"""Entry point for the display utilities client."""
67+
asyncio.run(run())
68+
69+
70+
if __name__ == "__main__":
71+
main()

examples/snippets/pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ packages = ["servers", "clients"]
1818
server = "servers:run_server"
1919
client = "clients.stdio_client:main"
2020
completion-client = "clients.completion_client:main"
21+
direct-execution-server = "servers.direct_execution:main"
22+
display-utilities-client = "clients.display_utilities:main"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Example showing direct execution of an MCP server.
2+
3+
This is the simplest way to run an MCP server directly.
4+
cd to the `examples/snippets` directory and run:
5+
uv run direct-execution-server
6+
or
7+
python servers/direct_execution.py
8+
"""
9+
10+
from mcp.server.fastmcp import FastMCP
11+
12+
mcp = FastMCP("My App")
13+
14+
15+
@mcp.tool()
16+
def hello(name: str = "World") -> str:
17+
"""Say hello to someone."""
18+
return f"Hello, {name}!"
19+
20+
21+
def main():
22+
"""Entry point for the direct execution server."""
23+
mcp.run()
24+
25+
26+
if __name__ == "__main__":
27+
main()

examples/snippets/servers/structured_output.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Example showing structured output with tools."""
22

3+
from typing import TypedDict
4+
35
from pydantic import BaseModel, Field
46

57
from mcp.server.fastmcp import FastMCP
@@ -27,3 +29,69 @@ def get_weather(city: str) -> WeatherData:
2729
condition="sunny",
2830
wind_speed=5.2,
2931
)
32+
33+
34+
# Using TypedDict for simpler structures
35+
class LocationInfo(TypedDict):
36+
latitude: float
37+
longitude: float
38+
name: str
39+
40+
41+
@mcp.tool()
42+
def get_location(address: str) -> LocationInfo:
43+
"""Get location coordinates"""
44+
return LocationInfo(latitude=51.5074, longitude=-0.1278, name="London, UK")
45+
46+
47+
# Using dict[str, Any] for flexible schemas
48+
@mcp.tool()
49+
def get_statistics(data_type: str) -> dict[str, float]:
50+
"""Get various statistics"""
51+
return {"mean": 42.5, "median": 40.0, "std_dev": 5.2}
52+
53+
54+
# Ordinary classes with type hints work for structured output
55+
class UserProfile:
56+
name: str
57+
age: int
58+
email: str | None = None
59+
60+
def __init__(self, name: str, age: int, email: str | None = None):
61+
self.name = name
62+
self.age = age
63+
self.email = email
64+
65+
66+
@mcp.tool()
67+
def get_user(user_id: str) -> UserProfile:
68+
"""Get user profile - returns structured data"""
69+
return UserProfile(name="Alice", age=30, email="alice@example.com")
70+
71+
72+
# Classes WITHOUT type hints cannot be used for structured output
73+
class UntypedConfig:
74+
def __init__(self, setting1, setting2):
75+
self.setting1 = setting1
76+
self.setting2 = setting2
77+
78+
79+
@mcp.tool()
80+
def get_config() -> UntypedConfig:
81+
"""This returns unstructured output - no schema generated"""
82+
return UntypedConfig("value1", "value2")
83+
84+
85+
# Lists and other types are wrapped automatically
86+
@mcp.tool()
87+
def list_cities() -> list[str]:
88+
"""Get a list of cities"""
89+
return ["London", "Paris", "Tokyo"]
90+
# Returns: {"result": ["London", "Paris", "Tokyo"]}
91+
92+
93+
@mcp.tool()
94+
def get_temperature(city: str) -> float:
95+
"""Get temperature as a simple float"""
96+
return 22.5
97+
# Returns: {"result": 22.5}

0 commit comments

Comments
 (0)