From df35989ee40353aad49ff83adf997f498b85e8d2 Mon Sep 17 00:00:00 2001 From: Dagang Wei Date: Mon, 26 May 2025 22:09:47 -0700 Subject: [PATCH 1/4] Docs: Update CallToolResult parsing in README The example in README.md for parsing the result of `session.call_tool` has been updated to reflect the structure of `CallToolResult`. This change aligns the README example with the type definitions in `mcp/types.py`. --- README.md | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2cb90770c..918a3658b 100644 --- a/README.md +++ b/README.md @@ -1410,11 +1410,22 @@ async def run(): # Call a tool (add tool from fastmcp_quickstart) result = await session.call_tool("add", arguments={"a": 5, "b": 3}) - result_unstructured = result.content[0] - if isinstance(result_unstructured, types.TextContent): - print(f"Tool result: {result_unstructured.text}") - result_structured = result.structuredContent - print(f"Structured tool result: {result_structured}") + # Parse the result (type: CallToolResult) + for item in result.content: + if isinstance(item, types.TextContent): + # Extract text directly from TextContent + print(f"Tool output (TextContent): {item.text}") + elif isinstance(item, types.EmbeddedResource): + # Check if the embedded resource contains text + if isinstance(item.resource, types.TextResourceContents): + print(f"Tool output (EmbeddedResource - Text): {item.resource.text}") + elif isinstance(item.resource, types.BlobResourceContents): + print(f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}") + elif isinstance(item, types.ImageContent): + # Showing only a snippet of image data + print(f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}...") + else: + print(f"Tool output (Unknown Content Type): {type(item)}") def main(): @@ -1459,6 +1470,25 @@ async def main(): tools = await session.list_tools() print(f"Available tools: {[tool.name for tool in tools.tools]}") + # Call a tool + tool_result = await session.call_tool("echo", {"message": "hello"}) + # Parse the result (type: CallToolResult) + for item in tool_result.content: + if isinstance(item, types.TextContent): + # Extract text directly from TextContent + print(f"Tool output (TextContent): {item.text}") + elif isinstance(item, types.EmbeddedResource): + # Check if the embedded resource contains text + if isinstance(item.resource, types.TextResourceContents): + print(f"Tool output (EmbeddedResource - Text): {item.resource.text}") + elif isinstance(item.resource, types.BlobResourceContents): + print(f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}") + elif isinstance(item, types.ImageContent): + # Showing only a snippet of image data + print(f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}...") + else: + print(f"Tool output (Unknown Content Type): {type(item)}") + if __name__ == "__main__": asyncio.run(main()) From 4b8f55891d51ba78bba966b98ec86134a5d71890 Mon Sep 17 00:00:00 2001 From: Dagang Wei Date: Mon, 26 May 2025 22:13:10 -0700 Subject: [PATCH 2/4] Docs: add import for mcp.type --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 918a3658b..6f3b2c6fa 100644 --- a/README.md +++ b/README.md @@ -1451,7 +1451,7 @@ Run from the repository root: import asyncio -from mcp import ClientSession +from mcp import ClientSession, types from mcp.client.streamable_http import streamablehttp_client From fc6117d3531aa1c46f59b2fcc676e92f595d49e0 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 17 Jul 2025 11:35:44 +0100 Subject: [PATCH 3/4] Add dedicated 'Parsing Tool Results' section to documentation - Add new section under Advanced Usage for parsing CallToolResult objects - Create comprehensive example showing all content types (text, structured, embedded resources, images, errors) - Move parsing documentation to a dedicated section instead of inline examples - Improve discoverability and clarity of tool result parsing patterns --- README.md | 93 +++++++++++++++++-- .../snippets/clients/parsing_tool_results.py | 60 ++++++++++++ 2 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 examples/snippets/clients/parsing_tool_results.py diff --git a/README.md b/README.md index 6f3b2c6fa..fd03eaa3c 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ - [Advanced Usage](#advanced-usage) - [Low-Level Server](#low-level-server) - [Writing MCP Clients](#writing-mcp-clients) + - [Parsing Tool Results](#parsing-tool-results) - [MCP Primitives](#mcp-primitives) - [Server Capabilities](#server-capabilities) - [Documentation](#documentation) @@ -1418,12 +1419,18 @@ async def run(): elif isinstance(item, types.EmbeddedResource): # Check if the embedded resource contains text if isinstance(item.resource, types.TextResourceContents): - print(f"Tool output (EmbeddedResource - Text): {item.resource.text}") + print( + f"Tool output (EmbeddedResource - Text): {item.resource.text}" + ) elif isinstance(item.resource, types.BlobResourceContents): - print(f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}") + print( + f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}" + ) elif isinstance(item, types.ImageContent): # Showing only a snippet of image data - print(f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}...") + print( + f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}..." + ) else: print(f"Tool output (Unknown Content Type): {type(item)}") @@ -1480,12 +1487,18 @@ async def main(): elif isinstance(item, types.EmbeddedResource): # Check if the embedded resource contains text if isinstance(item.resource, types.TextResourceContents): - print(f"Tool output (EmbeddedResource - Text): {item.resource.text}") + print( + f"Tool output (EmbeddedResource - Text): {item.resource.text}" + ) elif isinstance(item.resource, types.BlobResourceContents): - print(f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}") + print( + f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}" + ) elif isinstance(item, types.ImageContent): # Showing only a snippet of image data - print(f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}...") + print( + f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}..." + ) else: print(f"Tool output (Unknown Content Type): {type(item)}") @@ -1635,6 +1648,74 @@ async def main(): For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/). +### Parsing Tool Results + +When calling tools through MCP, the `CallToolResult` object contains the tool's response in a structured format. Understanding how to parse this result is essential for properly handling tool outputs. + +```python +"""examples/snippets/clients/parsing_tool_results.py""" + +import asyncio + +from mcp import ClientSession, StdioServerParameters, types +from mcp.client.stdio import stdio_client + + +async def parse_tool_results(): + """Demonstrates how to parse different types of content in CallToolResult.""" + server_params = StdioServerParameters( + command="python", args=["path/to/mcp_server.py"] + ) + + async with stdio_client(server_params) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + # Example 1: Parsing text content + result = await session.call_tool("get_data", {"format": "text"}) + for content in result.content: + if isinstance(content, types.TextContent): + print(f"Text: {content.text}") + + # Example 2: Parsing structured content from JSON tools + result = await session.call_tool("get_user", {"id": "123"}) + if hasattr(result, "structuredContent") and result.structuredContent: + # Access structured data directly + user_data = result.structuredContent + print(f"User: {user_data.get('name')}, Age: {user_data.get('age')}") + + # Example 3: Parsing embedded resources + result = await session.call_tool("read_config", {}) + for content in result.content: + if isinstance(content, types.EmbeddedResource): + resource = content.resource + if isinstance(resource, types.TextResourceContents): + print(f"Config from {resource.uri}: {resource.text}") + elif isinstance(resource, types.BlobResourceContents): + print(f"Binary data from {resource.uri}") + + # Example 4: Parsing image content + result = await session.call_tool("generate_chart", {"data": [1, 2, 3]}) + for content in result.content: + if isinstance(content, types.ImageContent): + print(f"Image ({content.mimeType}): {len(content.data)} bytes") + + # Example 5: Handling errors + result = await session.call_tool("failing_tool", {}) + if result.isError: + print("Tool execution failed!") + for content in result.content: + if isinstance(content, types.TextContent): + print(f"Error: {content.text}") + + +async def main(): + await parse_tool_results() + + +if __name__ == "__main__": + asyncio.run(main()) +``` ### MCP Primitives The MCP protocol defines three core primitives that servers can implement: diff --git a/examples/snippets/clients/parsing_tool_results.py b/examples/snippets/clients/parsing_tool_results.py new file mode 100644 index 000000000..0a3b3997c --- /dev/null +++ b/examples/snippets/clients/parsing_tool_results.py @@ -0,0 +1,60 @@ +"""examples/snippets/clients/parsing_tool_results.py""" + +import asyncio + +from mcp import ClientSession, StdioServerParameters, types +from mcp.client.stdio import stdio_client + + +async def parse_tool_results(): + """Demonstrates how to parse different types of content in CallToolResult.""" + server_params = StdioServerParameters(command="python", args=["path/to/mcp_server.py"]) + + async with stdio_client(server_params) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + # Example 1: Parsing text content + result = await session.call_tool("get_data", {"format": "text"}) + for content in result.content: + if isinstance(content, types.TextContent): + print(f"Text: {content.text}") + + # Example 2: Parsing structured content from JSON tools + result = await session.call_tool("get_user", {"id": "123"}) + if hasattr(result, "structuredContent") and result.structuredContent: + # Access structured data directly + user_data = result.structuredContent + print(f"User: {user_data.get('name')}, Age: {user_data.get('age')}") + + # Example 3: Parsing embedded resources + result = await session.call_tool("read_config", {}) + for content in result.content: + if isinstance(content, types.EmbeddedResource): + resource = content.resource + if isinstance(resource, types.TextResourceContents): + print(f"Config from {resource.uri}: {resource.text}") + elif isinstance(resource, types.BlobResourceContents): + print(f"Binary data from {resource.uri}") + + # Example 4: Parsing image content + result = await session.call_tool("generate_chart", {"data": [1, 2, 3]}) + for content in result.content: + if isinstance(content, types.ImageContent): + print(f"Image ({content.mimeType}): {len(content.data)} bytes") + + # Example 5: Handling errors + result = await session.call_tool("failing_tool", {}) + if result.isError: + print("Tool execution failed!") + for content in result.content: + if isinstance(content, types.TextContent): + print(f"Error: {content.text}") + + +async def main(): + await parse_tool_results() + + +if __name__ == "__main__": + asyncio.run(main()) From c313b9073b534e1c169c70eaf2e8409e6334d35e Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 17 Jul 2025 12:07:53 +0100 Subject: [PATCH 4/4] Remove redundant inline parsing examples The original author's inline parsing examples are now redundant since we have a dedicated 'Parsing Tool Results' section under Advanced Usage. This commit removes all the duplicated content, keeping the documentation clean and focused. --- README.md | 55 +++++++------------------------------------------------ 1 file changed, 7 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index fd03eaa3c..ce162e898 100644 --- a/README.md +++ b/README.md @@ -1411,28 +1411,11 @@ async def run(): # Call a tool (add tool from fastmcp_quickstart) result = await session.call_tool("add", arguments={"a": 5, "b": 3}) - # Parse the result (type: CallToolResult) - for item in result.content: - if isinstance(item, types.TextContent): - # Extract text directly from TextContent - print(f"Tool output (TextContent): {item.text}") - elif isinstance(item, types.EmbeddedResource): - # Check if the embedded resource contains text - if isinstance(item.resource, types.TextResourceContents): - print( - f"Tool output (EmbeddedResource - Text): {item.resource.text}" - ) - elif isinstance(item.resource, types.BlobResourceContents): - print( - f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}" - ) - elif isinstance(item, types.ImageContent): - # Showing only a snippet of image data - print( - f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}..." - ) - else: - print(f"Tool output (Unknown Content Type): {type(item)}") + result_unstructured = result.content[0] + if isinstance(result_unstructured, types.TextContent): + print(f"Tool result: {result_unstructured.text}") + result_structured = result.structuredContent + print(f"Structured tool result: {result_structured}") def main(): @@ -1458,7 +1441,7 @@ Run from the repository root: import asyncio -from mcp import ClientSession, types +from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client @@ -1477,31 +1460,6 @@ async def main(): tools = await session.list_tools() print(f"Available tools: {[tool.name for tool in tools.tools]}") - # Call a tool - tool_result = await session.call_tool("echo", {"message": "hello"}) - # Parse the result (type: CallToolResult) - for item in tool_result.content: - if isinstance(item, types.TextContent): - # Extract text directly from TextContent - print(f"Tool output (TextContent): {item.text}") - elif isinstance(item, types.EmbeddedResource): - # Check if the embedded resource contains text - if isinstance(item.resource, types.TextResourceContents): - print( - f"Tool output (EmbeddedResource - Text): {item.resource.text}" - ) - elif isinstance(item.resource, types.BlobResourceContents): - print( - f"Tool output (EmbeddedResource - Blob): URI {item.resource.uri}, MIME Type {item.resource.mimeType}" - ) - elif isinstance(item, types.ImageContent): - # Showing only a snippet of image data - print( - f"Tool output (ImageContent): MIME Type {item.mimeType}, Data (base64): {item.data[:30]}..." - ) - else: - print(f"Tool output (Unknown Content Type): {type(item)}") - if __name__ == "__main__": asyncio.run(main()) @@ -1716,6 +1674,7 @@ async def main(): if __name__ == "__main__": asyncio.run(main()) ``` + ### MCP Primitives The MCP protocol defines three core primitives that servers can implement: