Skip to content

README - replace code snippets with examples - direct execution and display utilities #1137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 86 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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):
Expand All @@ -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`
Expand Down
71 changes: 71 additions & 0 deletions examples/snippets/clients/display_utilities.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions examples/snippets/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 27 additions & 0 deletions examples/snippets/servers/direct_execution.py
Original file line number Diff line number Diff line change
@@ -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()
68 changes: 68 additions & 0 deletions examples/snippets/servers/structured_output.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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="[email protected]")


# 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