Add optional content parameter to ToolError for rich error responses

Closed
SU
2mo ago
+132-10

Summary

Fixes #348.

Adds an optional content keyword argument to ToolError, allowing tool implementations to return rich error responses (images, structured data) alongside error messages — matching what the MCP protocol already supports in CallToolResult.

Changes

src/mcp/server/mcpserver/exceptions.py

  • Added content: list[ContentBlock] | None attribute to ToolError
  • Added __init__ with optional content kwarg (defaults to None)

src/mcp/server/mcpserver/server.py

  • In _handle_call_tool: when a caught ToolError has content, uses it directly in CallToolResult instead of wrapping str(exc) as text
  • Falls back to existing behavior when content is None

src/mcp/server/mcpserver/tools/base.py

  • Preserves content attribute through the re-raise in Tool.run() (was lost when wrapping the error message with tool name)

tests/server/mcpserver/test_server.py

  • 4 new tests: rich content round-trip, backward compatibility with plain string, content preservation through call_tool(), logging behavior unchanged

Backward compatibility

Zero breaking changes. raise ToolError("msg") works exactly as before — content defaults to None and the existing text-wrapping path runs.

Usage

raise ToolError(
    "rendering failed",
    content=[
        TextContent(type="text", text="partial render completed"),
        ImageContent(type="image", data=b64_png, mimeType="image/png"),
    ],
)

Test plan

  • All 170 existing tests pass
  • 4 new tests cover the feature, backward compatibility, propagation, and logging
  • Ruff and pyright clean