README - replace code snippets with examples - direct execution and display utilities (#1137)
This commit is contained in:
parent
9bf5160eea
commit
a10fec7fa6
5 changed files with 254 additions and 8 deletions
94
README.md
94
README.md
|
|
@ -338,6 +338,8 @@ to the `@tool` decorator.
|
|||
```python
|
||||
"""Example showing structured output with tools."""
|
||||
|
||||
from typing import TypedDict
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
|
@ -365,12 +367,8 @@ def get_weather(city: str) -> WeatherData:
|
|||
condition="sunny",
|
||||
wind_speed=5.2,
|
||||
)
|
||||
```
|
||||
|
||||
_Full example: [examples/snippets/servers/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/structured_output.py)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
```python
|
||||
# Using TypedDict for simpler structures
|
||||
class LocationInfo(TypedDict):
|
||||
latitude: float
|
||||
|
|
@ -437,6 +435,9 @@ def get_temperature(city: str) -> float:
|
|||
# Returns: {"result": 22.5}
|
||||
```
|
||||
|
||||
_Full example: [examples/snippets/servers/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/structured_output.py)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
### Prompts
|
||||
|
||||
Prompts are reusable templates that help LLMs interact with your server effectively:
|
||||
|
|
@ -814,21 +815,46 @@ uv run mcp install server.py -f .env
|
|||
|
||||
For advanced scenarios like custom deployments:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/direct_execution.py -->
|
||||
```python
|
||||
"""Example showing direct execution of an MCP server.
|
||||
|
||||
This is the simplest way to run an MCP server directly.
|
||||
cd to the `examples/snippets` directory and run:
|
||||
uv run direct-execution-server
|
||||
or
|
||||
python servers/direct_execution.py
|
||||
"""
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My App")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@mcp.tool()
|
||||
def hello(name: str = "World") -> str:
|
||||
"""Say hello to someone."""
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for the direct execution server."""
|
||||
mcp.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
_Full example: [examples/snippets/servers/direct_execution.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/direct_execution.py)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
Run it with:
|
||||
|
||||
```bash
|
||||
python server.py
|
||||
python servers/direct_execution.py
|
||||
# or
|
||||
uv run mcp run server.py
|
||||
uv run mcp run servers/direct_execution.py
|
||||
```
|
||||
|
||||
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():
|
|||
|
||||
When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts:
|
||||
|
||||
<!-- snippet-source examples/snippets/clients/display_utilities.py -->
|
||||
```python
|
||||
"""Client display utilities example.
|
||||
|
||||
This example shows how to use the SDK's display utilities to show
|
||||
human-readable names for tools, resources, and prompts.
|
||||
|
||||
cd to the `examples/snippets` directory and run:
|
||||
uv run display-utilities-client
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
from mcp.shared.metadata_utils import get_display_name
|
||||
from mcp.client.session import ClientSession
|
||||
|
||||
# Create server parameters for stdio connection
|
||||
server_params = StdioServerParameters(
|
||||
command="uv", # Using uv to run the server
|
||||
args=["run", "server", "fastmcp_quickstart", "stdio"],
|
||||
env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
|
||||
)
|
||||
|
||||
|
||||
async def display_tools(session: ClientSession):
|
||||
|
|
@ -1301,8 +1348,39 @@ async def display_resources(session: ClientSession):
|
|||
for resource in resources_response.resources:
|
||||
display_name = get_display_name(resource)
|
||||
print(f"Resource: {display_name} ({resource.uri})")
|
||||
|
||||
templates_response = await session.list_resource_templates()
|
||||
for template in templates_response.resourceTemplates:
|
||||
display_name = get_display_name(template)
|
||||
print(f"Resource Template: {display_name}")
|
||||
|
||||
|
||||
async def run():
|
||||
"""Run the display utilities example."""
|
||||
async with stdio_client(server_params) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
# Initialize the connection
|
||||
await session.initialize()
|
||||
|
||||
print("=== Available Tools ===")
|
||||
await display_tools(session)
|
||||
|
||||
print("\n=== Available Resources ===")
|
||||
await display_resources(session)
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for the display utilities client."""
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
_Full example: [examples/snippets/clients/display_utilities.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/display_utilities.py)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
The `get_display_name()` function implements the proper precedence rules for displaying names:
|
||||
|
||||
- For tools: `title` > `annotations.title` > `name`
|
||||
|
|
|
|||
71
examples/snippets/clients/display_utilities.py
Normal file
71
examples/snippets/clients/display_utilities.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Client display utilities example.
|
||||
|
||||
This example shows how to use the SDK's display utilities to show
|
||||
human-readable names for tools, resources, and prompts.
|
||||
|
||||
cd to the `examples/snippets` directory and run:
|
||||
uv run display-utilities-client
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
from mcp.shared.metadata_utils import get_display_name
|
||||
|
||||
# Create server parameters for stdio connection
|
||||
server_params = StdioServerParameters(
|
||||
command="uv", # Using uv to run the server
|
||||
args=["run", "server", "fastmcp_quickstart", "stdio"],
|
||||
env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
|
||||
)
|
||||
|
||||
|
||||
async def display_tools(session: ClientSession):
|
||||
"""Display available tools with human-readable names"""
|
||||
tools_response = await session.list_tools()
|
||||
|
||||
for tool in tools_response.tools:
|
||||
# get_display_name() returns the title if available, otherwise the name
|
||||
display_name = get_display_name(tool)
|
||||
print(f"Tool: {display_name}")
|
||||
if tool.description:
|
||||
print(f" {tool.description}")
|
||||
|
||||
|
||||
async def display_resources(session: ClientSession):
|
||||
"""Display available resources with human-readable names"""
|
||||
resources_response = await session.list_resources()
|
||||
|
||||
for resource in resources_response.resources:
|
||||
display_name = get_display_name(resource)
|
||||
print(f"Resource: {display_name} ({resource.uri})")
|
||||
|
||||
templates_response = await session.list_resource_templates()
|
||||
for template in templates_response.resourceTemplates:
|
||||
display_name = get_display_name(template)
|
||||
print(f"Resource Template: {display_name}")
|
||||
|
||||
|
||||
async def run():
|
||||
"""Run the display utilities example."""
|
||||
async with stdio_client(server_params) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
# Initialize the connection
|
||||
await session.initialize()
|
||||
|
||||
print("=== Available Tools ===")
|
||||
await display_tools(session)
|
||||
|
||||
print("\n=== Available Resources ===")
|
||||
await display_resources(session)
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for the display utilities client."""
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -18,3 +18,5 @@ packages = ["servers", "clients"]
|
|||
server = "servers:run_server"
|
||||
client = "clients.stdio_client:main"
|
||||
completion-client = "clients.completion_client:main"
|
||||
direct-execution-server = "servers.direct_execution:main"
|
||||
display-utilities-client = "clients.display_utilities:main"
|
||||
|
|
|
|||
27
examples/snippets/servers/direct_execution.py
Normal file
27
examples/snippets/servers/direct_execution.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"""Example showing direct execution of an MCP server.
|
||||
|
||||
This is the simplest way to run an MCP server directly.
|
||||
cd to the `examples/snippets` directory and run:
|
||||
uv run direct-execution-server
|
||||
or
|
||||
python servers/direct_execution.py
|
||||
"""
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My App")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def hello(name: str = "World") -> str:
|
||||
"""Say hello to someone."""
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for the direct execution server."""
|
||||
mcp.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
"""Example showing structured output with tools."""
|
||||
|
||||
from typing import TypedDict
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
|
@ -27,3 +29,69 @@ def get_weather(city: str) -> WeatherData:
|
|||
condition="sunny",
|
||||
wind_speed=5.2,
|
||||
)
|
||||
|
||||
|
||||
# Using TypedDict for simpler structures
|
||||
class LocationInfo(TypedDict):
|
||||
latitude: float
|
||||
longitude: float
|
||||
name: str
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_location(address: str) -> LocationInfo:
|
||||
"""Get location coordinates"""
|
||||
return LocationInfo(latitude=51.5074, longitude=-0.1278, name="London, UK")
|
||||
|
||||
|
||||
# Using dict[str, Any] for flexible schemas
|
||||
@mcp.tool()
|
||||
def get_statistics(data_type: str) -> dict[str, float]:
|
||||
"""Get various statistics"""
|
||||
return {"mean": 42.5, "median": 40.0, "std_dev": 5.2}
|
||||
|
||||
|
||||
# Ordinary classes with type hints work for structured output
|
||||
class UserProfile:
|
||||
name: str
|
||||
age: int
|
||||
email: str | None = None
|
||||
|
||||
def __init__(self, name: str, age: int, email: str | None = None):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.email = email
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_user(user_id: str) -> UserProfile:
|
||||
"""Get user profile - returns structured data"""
|
||||
return UserProfile(name="Alice", age=30, email="alice@example.com")
|
||||
|
||||
|
||||
# Classes WITHOUT type hints cannot be used for structured output
|
||||
class UntypedConfig:
|
||||
def __init__(self, setting1, setting2):
|
||||
self.setting1 = setting1
|
||||
self.setting2 = setting2
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_config() -> UntypedConfig:
|
||||
"""This returns unstructured output - no schema generated"""
|
||||
return UntypedConfig("value1", "value2")
|
||||
|
||||
|
||||
# Lists and other types are wrapped automatically
|
||||
@mcp.tool()
|
||||
def list_cities() -> list[str]:
|
||||
"""Get a list of cities"""
|
||||
return ["London", "Paris", "Tokyo"]
|
||||
# Returns: {"result": ["London", "Paris", "Tokyo"]}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_temperature(city: str) -> float:
|
||||
"""Get temperature as a simple float"""
|
||||
return 22.5
|
||||
# Returns: {"result": 22.5}
|
||||
|
|
|
|||
Loading…
Reference in a new issue