Skip to content
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
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def validated_tool() -> Annotated[CallToolResult, ValidationModel]:
"""Return CallToolResult with structured output validation."""
return CallToolResult(
content=[TextContent(type="text", text="Validated response")],
structuredContent={"status": "success", "data": {"result": 42}},
structured_content={"status": "success", "data": {"result": 42}},
_meta={"internal": "metadata"},
)

Expand Down Expand Up @@ -757,8 +757,8 @@ async def run():
# List available resource templates
templates = await session.list_resource_templates()
print("Available resource templates:")
for template in templates.resourceTemplates:
print(f" - {template.uriTemplate}")
for template in templates.resource_templates:
print(f" - {template.uri_template}")

# List available prompts
prompts = await session.list_prompts()
Expand All @@ -767,20 +767,20 @@ async def run():
print(f" - {prompt.name}")

# Complete resource template arguments
if templates.resourceTemplates:
template = templates.resourceTemplates[0]
print(f"\nCompleting arguments for resource template: {template.uriTemplate}")
if templates.resource_templates:
template = templates.resource_templates[0]
print(f"\nCompleting arguments for resource template: {template.uri_template}")

# Complete without context
result = await session.complete(
ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate),
ref=ResourceTemplateReference(type="ref/resource", uri=template.uri_template),
argument={"name": "owner", "value": "model"},
)
print(f"Completions for 'owner' starting with 'model': {result.completion.values}")

# Complete with context - repo suggestions based on owner
result = await session.complete(
ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate),
ref=ResourceTemplateReference(type="ref/resource", uri=template.uri_template),
argument={"name": "repo", "value": ""},
context_arguments={"owner": "modelcontextprotocol"},
)
Expand Down Expand Up @@ -910,7 +910,7 @@ async def connect_service(service_name: str, ctx: Context[ServerSession, None])
mode="url",
message=f"Authorization required to connect to {service_name}",
url=f"https://{service_name}.example.com/oauth/authorize?elicit={elicitation_id}",
elicitationId=elicitation_id,
elicitation_id=elicitation_id,
)
]
)
Expand Down Expand Up @@ -1706,7 +1706,7 @@ async def handle_list_tools() -> list[types.Tool]:
types.Tool(
name="query_db",
description="Query the database",
inputSchema={
input_schema={
"type": "object",
"properties": {"query": {"type": "string", "description": "SQL query to execute"}},
"required": ["query"],
Expand Down Expand Up @@ -1867,12 +1867,12 @@ async def list_tools() -> list[types.Tool]:
types.Tool(
name="get_weather",
description="Get current weather for a city",
inputSchema={
input_schema={
"type": "object",
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"],
},
outputSchema={
output_schema={
"type": "object",
"properties": {
"temperature": {"type": "number", "description": "Temperature in Celsius"},
Expand Down Expand Up @@ -1970,7 +1970,7 @@ async def list_tools() -> list[types.Tool]:
types.Tool(
name="advanced_tool",
description="Tool with full control including _meta field",
inputSchema={
input_schema={
"type": "object",
"properties": {"message": {"type": "string"}},
"required": ["message"],
Expand All @@ -1986,7 +1986,7 @@ async def handle_call_tool(name: str, arguments: dict[str, Any]) -> types.CallTo
message = str(arguments.get("message", ""))
return types.CallToolResult(
content=[types.TextContent(type="text", text=f"Processed: {message}")],
structuredContent={"result": "success", "message": message},
structured_content={"result": "success", "message": message},
_meta={"hidden": "data for client applications only"},
)

Expand Down Expand Up @@ -2062,7 +2062,7 @@ async def list_resources_paginated(request: types.ListResourcesRequest) -> types
# Determine next cursor
next_cursor = str(end) if end < len(ITEMS) else None

return types.ListResourcesResult(resources=page_items, nextCursor=next_cursor)
return types.ListResourcesResult(resources=page_items, next_cursor=next_cursor)
```

_Full example: [examples/snippets/servers/pagination_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/pagination_example.py)_
Expand Down Expand Up @@ -2103,8 +2103,8 @@ async def list_all_resources() -> None:
print(f"Fetched {len(result.resources)} resources")

# Check if there are more pages
if result.nextCursor:
cursor = result.nextCursor
if result.next_cursor:
cursor = result.next_cursor
else:
break

Expand Down Expand Up @@ -2167,7 +2167,7 @@ async def handle_sampling_message(
text="Hello, world! from model",
),
model="gpt-3.5-turbo",
stopReason="endTurn",
stop_reason="endTurn",
)


Expand Down Expand Up @@ -2205,7 +2205,7 @@ async def run():
result_unstructured = result.content[0]
if isinstance(result_unstructured, types.TextContent):
print(f"Tool result: {result_unstructured.text}")
result_structured = result.structuredContent
result_structured = result.structured_content
print(f"Structured tool result: {result_structured}")


Expand Down Expand Up @@ -2306,7 +2306,7 @@ async def display_resources(session: ClientSession):
print(f"Resource: {display_name} ({resource.uri})")

templates_response = await session.list_resource_templates()
for template in templates_response.resourceTemplates:
for template in templates_response.resource_templates:
display_name = get_display_name(template)
print(f"Resource Template: {display_name}")

Expand Down
2 changes: 1 addition & 1 deletion examples/clients/simple-chatbot/mcp_simple_chatbot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async def list_tools(self) -> list[Tool]:

for item in tools_response:
if item[0] == "tools":
tools.extend(Tool(tool.name, tool.description, tool.inputSchema, tool.title) for tool in item[1])
tools.extend(Tool(tool.name, tool.description, tool.input_schema, tool.title) for tool in item[1])

return tools

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ async def run(url: str) -> None:
arguments={},
ttl=60000,
)
task_id = result.task.taskId
task_id = result.task.task_id
print(f"Task created: {task_id}")

status = None
# Poll until done (respects server's pollInterval hint)
async for status in session.experimental.poll_task(task_id):
print(f" Status: {status.status} - {status.statusMessage or ''}")
print(f" Status: {status.status} - {status.status_message or ''}")

# Check final status
if status and status.status != "completed":
Expand Down
2 changes: 1 addition & 1 deletion examples/clients/simple-task-interactive-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def sampling_callback(context, params) -> CreateMessageResult:
```python
# Call a tool as a task (returns immediately with task reference)
result = await session.experimental.call_tool_as_task("tool_name", {"arg": "value"})
task_id = result.task.taskId
task_id = result.task.task_id

# Get result - this delivers elicitation/sampling requests and blocks until complete
final = await session.experimental.get_task_result(task_id, CallToolResult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async def run(url: str) -> None:
print("Calling confirm_delete tool...")

elicit_task = await session.experimental.call_tool_as_task("confirm_delete", {"filename": "important.txt"})
elicit_task_id = elicit_task.task.taskId
elicit_task_id = elicit_task.task.task_id
print(f"Task created: {elicit_task_id}")

# Poll until terminal, calling tasks/result on input_required
Expand All @@ -112,7 +112,7 @@ async def run(url: str) -> None:
print("Calling write_haiku tool...")

sampling_task = await session.experimental.call_tool_as_task("write_haiku", {"topic": "autumn leaves"})
sampling_task_id = sampling_task.task.taskId
sampling_task_id = sampling_task.task.task_id
print(f"Task created: {sampling_task_id}")

# Poll until terminal, calling tasks/result on input_required
Expand Down
2 changes: 1 addition & 1 deletion examples/fastmcp/direct_call_tool_result_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ class EchoResponse(BaseModel):
def echo(text: str) -> Annotated[CallToolResult, EchoResponse]:
"""Echo the input text with structure and metadata"""
return CallToolResult(
content=[TextContent(type="text", text=text)], structuredContent={"text": text}, _meta={"some": "metadata"}
content=[TextContent(type="text", text=text)], structured_content={"text": text}, _meta={"some": "metadata"}
)
8 changes: 4 additions & 4 deletions examples/fastmcp/icons_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
icon_data = base64.standard_b64encode(icon_path.read_bytes()).decode()
icon_data_uri = f"data:image/png;base64,{icon_data}"

icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes=["64x64"])
icon_data = Icon(src=icon_data_uri, mime_type="image/png", sizes=["64x64"])

# Create server with icons in implementation
mcp = FastMCP("Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data])
Expand All @@ -40,9 +40,9 @@ def prompt_with_icon(text: str) -> str:

@mcp.tool(
icons=[
Icon(src=icon_data_uri, mimeType="image/png", sizes=["16x16"]),
Icon(src=icon_data_uri, mimeType="image/png", sizes=["32x32"]),
Icon(src=icon_data_uri, mimeType="image/png", sizes=["64x64"]),
Icon(src=icon_data_uri, mime_type="image/png", sizes=["16x16"]),
Icon(src=icon_data_uri, mime_type="image/png", sizes=["32x32"]),
Icon(src=icon_data_uri, mime_type="image/png", sizes=["64x64"]),
]
)
def multi_icon_tool(action: str) -> str:
Expand Down
18 changes: 9 additions & 9 deletions examples/fastmcp/weather_structured.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,32 +161,32 @@ async def test() -> None:
# Test get_weather
result = await client.call_tool("get_weather", {"city": "London"})
print("\nWeather in London:")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Test get_weather_summary
result = await client.call_tool("get_weather_summary", {"city": "Paris"})
print("\nWeather summary for Paris:")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Test get_weather_metrics
result = await client.call_tool("get_weather_metrics", {"cities": ["Tokyo", "Sydney", "Mumbai"]})
print("\nWeather metrics:")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Test get_weather_alerts
result = await client.call_tool("get_weather_alerts", {"region": "California"})
print("\nWeather alerts for California:")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Test get_temperature
result = await client.call_tool("get_temperature", {"city": "Berlin", "unit": "fahrenheit"})
print("\nTemperature in Berlin:")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Test get_weather_stats
result = await client.call_tool("get_weather_stats", {"city": "Seattle", "days": 30})
print("\nWeather stats for Seattle (30 days):")
print(json.dumps(result.structuredContent, indent=2))
print(json.dumps(result.structured_content, indent=2))

# Also show the text content for comparison
print("\nText content for last result:")
Expand All @@ -204,11 +204,11 @@ async def print_schemas() -> None:
print(f"\nTool: {tool.name}")
print(f"Description: {tool.description}")
print("Input Schema:")
print(json.dumps(tool.inputSchema, indent=2))
print(json.dumps(tool.input_schema, indent=2))

if tool.outputSchema:
if tool.output_schema:
print("Output Schema:")
print(json.dumps(tool.outputSchema, indent=2))
print(json.dumps(tool.output_schema, indent=2))
else:
print("Output Schema: None (returns unstructured content)")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ def test_simple_text() -> str:
@mcp.tool()
def test_image_content() -> list[ImageContent]:
"""Tests image content response"""
return [ImageContent(type="image", data=TEST_IMAGE_BASE64, mimeType="image/png")]
return [ImageContent(type="image", data=TEST_IMAGE_BASE64, mime_type="image/png")]


@mcp.tool()
def test_audio_content() -> list[AudioContent]:
"""Tests audio content response"""
return [AudioContent(type="audio", data=TEST_AUDIO_BASE64, mimeType="audio/wav")]
return [AudioContent(type="audio", data=TEST_AUDIO_BASE64, mime_type="audio/wav")]


@mcp.tool()
Expand All @@ -115,7 +115,7 @@ def test_embedded_resource() -> list[EmbeddedResource]:
type="resource",
resource=TextResourceContents(
uri="test://embedded-resource",
mimeType="text/plain",
mime_type="text/plain",
text="This is an embedded resource content.",
),
)
Expand All @@ -127,12 +127,12 @@ def test_multiple_content_types() -> list[TextContent | ImageContent | EmbeddedR
"""Tests response with multiple content types (text, image, resource)"""
return [
TextContent(type="text", text="Multiple content types test:"),
ImageContent(type="image", data=TEST_IMAGE_BASE64, mimeType="image/png"),
ImageContent(type="image", data=TEST_IMAGE_BASE64, mime_type="image/png"),
EmbeddedResource(
type="resource",
resource=TextResourceContents(
uri="test://mixed-content-resource",
mimeType="application/json",
mime_type="application/json",
text='{"test": "data", "value": 123}',
),
),
Expand Down Expand Up @@ -164,7 +164,7 @@ async def test_tool_with_progress(ctx: Context[ServerSession, None]) -> str:
await ctx.report_progress(progress=100, total=100, message="Completed step 100 of 100")

# Return progress token as string
progress_token = ctx.request_context.meta.progressToken if ctx.request_context and ctx.request_context.meta else 0
progress_token = ctx.request_context.meta.progress_token if ctx.request_context and ctx.request_context.meta else 0
return str(progress_token)


Expand Down Expand Up @@ -373,7 +373,7 @@ def test_prompt_with_embedded_resource(resourceUri: str) -> list[UserMessage]:
type="resource",
resource=TextResourceContents(
uri=resourceUri,
mimeType="text/plain",
mime_type="text/plain",
text="Embedded resource content for testing.",
),
),
Expand All @@ -386,7 +386,7 @@ def test_prompt_with_embedded_resource(resourceUri: str) -> list[UserMessage]:
def test_prompt_with_image() -> list[UserMessage]:
"""A prompt that includes image content"""
return [
UserMessage(role="user", content=ImageContent(type="image", data=TEST_IMAGE_BASE64, mimeType="image/png")),
UserMessage(role="user", content=ImageContent(type="image", data=TEST_IMAGE_BASE64, mime_type="image/png")),
UserMessage(role="user", content=TextContent(type="text", text="Please analyze the image above.")),
]

Expand Down Expand Up @@ -427,7 +427,7 @@ async def _handle_completion(
"""Handle completion requests"""
# Basic completion support - returns empty array for conformance
# Real implementations would provide contextual suggestions
return Completion(values=[], total=0, hasMore=False)
return Completion(values=[], total=0, has_more=False)


# CLI
Expand Down
Loading