|
44 | 44 | - [Advanced Usage](#advanced-usage)
|
45 | 45 | - [Low-Level Server](#low-level-server)
|
46 | 46 | - [Writing MCP Clients](#writing-mcp-clients)
|
| 47 | + - [Parsing Tool Results](#parsing-tool-results) |
| 48 | + - [Client Display Utilities](#client-display-utilities) |
| 49 | + - [OAuth Authentication for Clients](#oauth-authentication-for-clients) |
47 | 50 | - [MCP Primitives](#mcp-primitives)
|
48 | 51 | - [Server Capabilities](#server-capabilities)
|
49 | 52 | - [Documentation](#documentation)
|
@@ -1467,6 +1470,158 @@ if __name__ == "__main__":
|
1467 | 1470 | _Full example: [examples/snippets/clients/streamable_basic.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/streamable_basic.py)_
|
1468 | 1471 | <!-- /snippet-source -->
|
1469 | 1472 |
|
| 1473 | +### Parsing Tool Results |
| 1474 | + |
| 1475 | +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. |
| 1476 | + |
| 1477 | +<!-- snippet-source examples/snippets/clients/parsing_tool_results.py --> |
| 1478 | +```python |
| 1479 | +""" |
| 1480 | +Example showing how to parse CallToolResult from MCP servers. |
| 1481 | +
|
| 1482 | +cd to the `examples/snippets` directory and run: |
| 1483 | + uv run parsing-tool-results |
| 1484 | +""" |
| 1485 | + |
| 1486 | +import asyncio |
| 1487 | +import os |
| 1488 | + |
| 1489 | +from pydantic import AnyUrl |
| 1490 | + |
| 1491 | +from mcp import ClientSession, StdioServerParameters, types |
| 1492 | +from mcp.client.stdio import stdio_client |
| 1493 | + |
| 1494 | +# Create server parameters for stdio connection |
| 1495 | +server_params = StdioServerParameters( |
| 1496 | + command="uv", # Using uv to run the server |
| 1497 | + args=["run", "server", "fastmcp_quickstart", "stdio"], |
| 1498 | + env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, |
| 1499 | +) |
| 1500 | + |
| 1501 | + |
| 1502 | +async def demonstrate_tool_result_parsing(): |
| 1503 | + """Demonstrate parsing different types of tool results.""" |
| 1504 | + async with stdio_client(server_params) as (read, write): |
| 1505 | + async with ClientSession(read, write) as session: |
| 1506 | + # Initialize the connection |
| 1507 | + await session.initialize() |
| 1508 | + |
| 1509 | + # Call a tool and parse the result |
| 1510 | + result = await session.call_tool("add", arguments={"a": 5, "b": 3}) |
| 1511 | + |
| 1512 | + # Example 1: Parsing unstructured content (backward compatible) |
| 1513 | + print("=== Parsing Unstructured Content ===") |
| 1514 | + for i, item in enumerate(result.content): |
| 1515 | + print(f"\nContent item {i + 1}:") |
| 1516 | + |
| 1517 | + if isinstance(item, types.TextContent): |
| 1518 | + # Most common case: plain text response |
| 1519 | + print(f" Type: TextContent") |
| 1520 | + print(f" Text: {item.text}") |
| 1521 | + |
| 1522 | + elif isinstance(item, types.ImageContent): |
| 1523 | + # Binary image data |
| 1524 | + print(f" Type: ImageContent") |
| 1525 | + print(f" MIME Type: {item.mimeType}") |
| 1526 | + print(f" Data (first 50 chars): {item.data[:50]}...") |
| 1527 | + |
| 1528 | + elif isinstance(item, types.EmbeddedResource): |
| 1529 | + # Embedded resource with either text or binary data |
| 1530 | + print(f" Type: EmbeddedResource") |
| 1531 | + print(f" Resource URI: {item.resource.uri}") |
| 1532 | + |
| 1533 | + if isinstance(item.resource, types.TextResourceContents): |
| 1534 | + print(f" Resource Type: Text") |
| 1535 | + print(f" Text: {item.resource.text}") |
| 1536 | + elif isinstance(item.resource, types.BlobResourceContents): |
| 1537 | + print(f" Resource Type: Blob") |
| 1538 | + print(f" MIME Type: {item.resource.mimeType}") |
| 1539 | + print(f" Blob data (first 50 chars): {item.resource.blob[:50]}...") |
| 1540 | + |
| 1541 | + elif isinstance(item, types.ResourceLink): |
| 1542 | + # Link to a resource that can be read separately |
| 1543 | + print(f" Type: ResourceLink") |
| 1544 | + print(f" URI: {item.uri}") |
| 1545 | + print(f" Name: {item.name}") |
| 1546 | + if item.description: |
| 1547 | + print(f" Description: {item.description}") |
| 1548 | + |
| 1549 | + else: |
| 1550 | + # Handle any future content types |
| 1551 | + print(f" Type: {type(item).__name__} (unknown)") |
| 1552 | + |
| 1553 | + # Example 2: Parsing structured content (spec revision 2025-06-18+) |
| 1554 | + print("\n\n=== Parsing Structured Content ===") |
| 1555 | + if result.structuredContent is not None: |
| 1556 | + print(f"Structured result: {result.structuredContent}") |
| 1557 | + # The structure depends on the tool's outputSchema |
| 1558 | + # For the 'add' tool, it might be: {"result": 8} |
| 1559 | + else: |
| 1560 | + print("No structured content (tool may not support it)") |
| 1561 | + |
| 1562 | + # Example 3: Error handling |
| 1563 | + print("\n\n=== Error Handling ===") |
| 1564 | + if result.isError: |
| 1565 | + print("The tool call resulted in an error!") |
| 1566 | + # Error details will be in the content field |
| 1567 | + for item in result.content: |
| 1568 | + if isinstance(item, types.TextContent): |
| 1569 | + print(f"Error message: {item.text}") |
| 1570 | + else: |
| 1571 | + print("Tool call completed successfully") |
| 1572 | + |
| 1573 | + |
| 1574 | +async def demonstrate_resource_parsing(): |
| 1575 | + """Show how embedded resources might appear in tool results.""" |
| 1576 | + print("\n\n=== Resource Examples ===") |
| 1577 | + |
| 1578 | + # Example of what a tool might return with embedded resources |
| 1579 | + # This is just for demonstration - actual results come from the server |
| 1580 | + |
| 1581 | + # Text resource example |
| 1582 | + text_resource = types.EmbeddedResource( |
| 1583 | + type="resource", |
| 1584 | + resource=types.TextResourceContents( |
| 1585 | + uri=AnyUrl("file:///example.txt"), |
| 1586 | + mimeType="text/plain", |
| 1587 | + text="This is the content of the file" |
| 1588 | + ) |
| 1589 | + ) |
| 1590 | + print(f"Text resource URI: {text_resource.resource.uri}") |
| 1591 | + print(f"Text resource content: {text_resource.resource.text}") |
| 1592 | + |
| 1593 | + # Binary resource example |
| 1594 | + blob_resource = types.EmbeddedResource( |
| 1595 | + type="resource", |
| 1596 | + resource=types.BlobResourceContents( |
| 1597 | + uri=AnyUrl("file:///image.png"), |
| 1598 | + mimeType="image/png", |
| 1599 | + blob="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" |
| 1600 | + ) |
| 1601 | + ) |
| 1602 | + print(f"\nBlob resource URI: {blob_resource.resource.uri}") |
| 1603 | + print(f"Blob resource MIME type: {blob_resource.resource.mimeType}") |
| 1604 | + print(f"Blob resource data (base64): {blob_resource.resource.blob[:30]}...") |
| 1605 | + |
| 1606 | + |
| 1607 | +async def run(): |
| 1608 | + """Run all demonstrations.""" |
| 1609 | + await demonstrate_tool_result_parsing() |
| 1610 | + await demonstrate_resource_parsing() |
| 1611 | + |
| 1612 | + |
| 1613 | +def main(): |
| 1614 | + """Entry point for the parsing examples.""" |
| 1615 | + asyncio.run(run()) |
| 1616 | + |
| 1617 | + |
| 1618 | +if __name__ == "__main__": |
| 1619 | + main() |
| 1620 | +``` |
| 1621 | + |
| 1622 | +_Full example: [examples/snippets/clients/parsing_tool_results.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/parsing_tool_results.py)_ |
| 1623 | +<!-- /snippet-source --> |
| 1624 | + |
1470 | 1625 | ### Client Display Utilities
|
1471 | 1626 |
|
1472 | 1627 | When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts:
|
|
0 commit comments