-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add meta support to ToolResult #2283
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
Conversation
WalkthroughFastMCP adds optional metadata to tool results: Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant FastMCP as FastMCP Server
participant Tool
participant Cache as Caching Middleware
participant CallResult as CallToolResult
Client->>FastMCP: call_tool(tool_name, args)
FastMCP->>Tool: invoke tool(...)
Tool->>Tool: produce ToolResult(content, structured_content, meta)
Tool->>FastMCP: ToolResult.to_mcp_result()
note right of FastMCP #f3f4f6: if meta present → returns CallToolResult
FastMCP->>Cache: wrap(CallToolResult) — preserve meta
Cache-->>FastMCP: unwrap() → CallToolResult (meta preserved)
FastMCP->>Client: return CallToolResult(content, structured_content, data, meta)
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🔇 Additional comments (1)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/servers/tools.mdx (1)
621-631: Example not runnable: missing TextContent import.Add the import so users can copy/paste.
Apply:
from fastmcp.tools.tool import ToolResult +from mcp.types import TextContent
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
docs/servers/tools.mdx(2 hunks)examples/tool_result_echo.py(1 hunks)pyproject.toml(1 hunks)src/fastmcp/client/client.py(2 hunks)src/fastmcp/server/middleware/caching.py(1 hunks)src/fastmcp/tools/tool.py(3 hunks)tests/server/middleware/test_caching.py(2 hunks)tests/server/test_server_interactions.py(1 hunks)tests/tools/test_tool.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Use Python ≥ 3.10 and provide full type annotations for library code
Files:
src/fastmcp/client/client.pysrc/fastmcp/server/middleware/caching.pysrc/fastmcp/tools/tool.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare except; always catch specific exception types
Files:
src/fastmcp/client/client.pytests/server/middleware/test_caching.pysrc/fastmcp/server/middleware/caching.pytests/tools/test_tool.pysrc/fastmcp/tools/tool.pyexamples/tool_result_echo.pytests/server/test_server_interactions.py
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write instructions and procedures in second person ("you")
Use active voice over passive voice
Use present tense for current states and future tense for outcomes
Maintain consistent terminology across the documentation
Keep sentences concise while preserving necessary context
Use parallel structure in lists, headings, and procedures
Lead with the most important information (inverted pyramid)
Use progressive disclosure: basic concepts before advanced ones
Break complex procedures into numbered steps
Include prerequisites and context before instructions
Provide expected outcomes for each major step
End sections with next steps or related information
Use descriptive, keyword-rich headings for navigation and SEO
Focus on user goals and outcomes rather than system features
Anticipate common questions and address them proactively
Include troubleshooting for likely failure points
Offer multiple pathways when appropriate (beginner vs advanced) and provide an opinionated recommended path
Use for supplementary information that supports the main content
Use for expert advice, shortcuts, or best practices
Use for critical cautions, breaking changes, or destructive actions
Use for neutral background or contextual information
Use to confirm success or completion
Provide single code examples using fenced code blocks with language (and filename when relevant)
Use to present the same concept in multiple languages
For API docs, use to show requests
For API docs, use to show responses
Use and to document procedures and sequential instructions
Use and for platform-specific or alternative approaches
Use / for supplementary content that might interrupt flow
In API docs, use for parameters (path, body, query, header) with type and required/default as appropria...
Files:
docs/servers/tools.mdx
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
docs/**/*.{md,mdx}: Documentation code examples should be explained before the code and be fully runnable, including imports
Use clear headers with logical H2/H3 hierarchy to form navigation
Write user-focused content that motivates the why before the how
Prefer prose for important information over code comments in docs
Files:
docs/servers/tools.mdx
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Be brief and to the point in written materials; avoid regurgitating obvious code details
Avoid defensive constructions like "This isn't X" or "Not just X, but Y"; state what it is directly
Files:
docs/servers/tools.mdx
tests/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
tests/**/*.py: Tests must be atomic, self-contained, and cover a single functionality
Use pytest parameterization for multiple examples of the same functionality
Use separate tests for distinct pieces of functionality
Always put imports at the top of test files; do not import inside test bodies
Do not add @pytest.mark.asyncio; asyncio_mode = "auto" is set globally
Prefer in-memory transport for tests; use HTTP transport only when explicitly testing networking
For slow/long-running tests, mark them as integration or optimize (default timeout is 5s)
In tests, use # type: ignore[attr-defined] for MCP results instead of type assertions
Files:
tests/server/middleware/test_caching.pytests/tools/test_tool.pytests/server/test_server_interactions.py
🧬 Code graph analysis (7)
src/fastmcp/client/client.py (1)
src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py (2)
CallToolRequestResult(25-48)from_call_tool_result(37-48)
tests/server/middleware/test_caching.py (2)
src/fastmcp/server/middleware/caching.py (5)
CachableToolResult(63-81)wrap(52-53)wrap(69-74)unwrap(56-60)unwrap(76-81)src/fastmcp/tools/tool.py (1)
ToolResult(66-112)
src/fastmcp/server/middleware/caching.py (1)
src/fastmcp/tools/tool.py (1)
ToolResult(66-112)
tests/tools/test_tool.py (3)
src/fastmcp/client/client.py (1)
call_tool(865-934)src/fastmcp/tools/tool.py (1)
ToolResult(66-112)src/fastmcp/tools/tool_manager.py (1)
call_tool(153-172)
src/fastmcp/tools/tool.py (3)
src/fastmcp/client/client.py (1)
CallToolResult(946-951)src/fastmcp/contrib/mcp_mixin/mcp_mixin.py (1)
mcp_tool(25-52)src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py (1)
CallToolRequestResult(25-48)
examples/tool_result_echo.py (1)
src/fastmcp/tools/tool.py (1)
ToolResult(66-112)
tests/server/test_server_interactions.py (2)
tests/contrib/test_mcp_mixin.py (2)
test_tool_with_title_and_meta(257-279)MyToolWithMeta(263-269)tests/server/test_server.py (1)
test_tool_decorator_with_meta(434-448)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (11)
src/fastmcp/server/middleware/caching.py (2)
66-75: Meta preserved in cache wrapper. LGTM.
78-81: Unwrap returns ToolResult with meta. LGTM.tests/server/middleware/test_caching.py (2)
26-30: New import for CachableToolResult — OK.
511-523: Round‑trip test for meta/content/structured_content — OK.tests/server/test_server_interactions.py (1)
1182-1183: Snapshot updated to include meta=None — OK.Keeps equality stable after dataclass change.
src/fastmcp/client/client.py (1)
931-931: Code change verified—no issues found.The script confirms that
result.metais available in the installed MCP SDK'sCallToolResultmodel. The code correctly accesses the field without errors. The original review's approval stands.pyproject.toml (1)
10-10: Constraint is correctly configured in pyproject.toml and uv.lock, but runtime mcp 1.16.0 violates the ≥1.19.0 floor.✓ What's correct:
- pyproject.toml pins
mcp>=1.19.0,<2.0.0(line 10)- uv.lock resolves to mcp 1.19.0
- CI matrix includes
run_tests_lowest_directjob that enforces floor version (Python 3.10 viauv sync --resolution lowest-direct)- Both ubuntu-latest and windows-latest tested
✗ Critical gap:
Runtime resolved to mcp 1.16.0, which violates the constraint and risksCallToolResult.metaattribute mismatches. Verify that:
- Deployment environments use
uv.lockor respectpyproject.tomlconstraints- No cached/stale environments are bypassing the floor version
- All dependency resolution (pip, poetry, uv) respects the ≥1.19.0 constraint
examples/tool_result_echo.py (1)
1-21: LGTM! Clear demonstration of the new meta field.This example effectively demonstrates the new
metasupport inToolResult. The usage of all three fields (content, structured_content, and meta) clearly illustrates the intended API for users.src/fastmcp/tools/tool.py (3)
19-19: LGTM! Necessary import for meta support.The
CallToolResultimport frommcp.typesis required for returning tool results with metadata, aligning with the MCP SDK v1.19.0 changes.
67-79: LGTM! Proper parameter addition and storage.The
metaparameter is correctly added with appropriate type annotations (dict[str, Any] | None) and default value (None), ensuring backwards compatibility. The storage onself.metais straightforward and correctly typed.
99-112: LGTM! Correct implementation of meta support.The
to_mcp_resultmethod correctly handles the newmetafield:
- When
metais present, returns aCallToolResultwith all three fields, using the correct MCP SDK conventions (structuredContentin camelCase,_metawith underscore prefix)- Falls back to existing behavior when
metaisNone- Return type annotation properly reflects the new
CallToolResultoptionThe logic correctly prioritizes meta handling and maintains backwards compatibility.
| class CallToolResult: | ||
| content: list[mcp.types.ContentBlock] | ||
| structured_content: dict[str, Any] | None | ||
| meta: dict[str, Any] | None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Dataclass extended with meta — update downstream extenders.
Classes extending/deriving from CallToolResult should propagate structured_content and meta. Example fix for bulk caller:
class CallToolRequestResult(CallToolResult):
@@
def from_call_tool_result(
cls, result: CallToolResult, tool: str, arguments: dict[str, Any]
) -> "CallToolRequestResult":
return cls(
tool=tool,
arguments=arguments,
- isError=result.isError,
- content=result.content,
+ isError=result.is_error,
+ content=result.content,
+ structured_content=result.structured_content,
+ meta=result.meta,
)Search for similar helpers to update:
🏁 Script executed:
#!/bin/bash
rg -nP 'from_call_tool_result|class\s+CallToolResult|result\.(structuredContent|meta)\b' -C2Length of output: 5210
🏁 Script executed:
#!/bin/bash
# View the implementation of from_call_tool_result
cat src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py | head -60Length of output: 1693
🏁 Script executed:
#!/bin/bash
# View the CallToolResult dataclass definition
sed -n '945,960p' src/fastmcp/client/client.pyLength of output: 200
Update CallToolRequestResult.from_call_tool_result() to propagate all fields.
The method currently attempts to access result.isError (which doesn't exist) and fails to propagate structured_content and meta. Fix:
@classmethod
def from_call_tool_result(
cls, result: CallToolResult, tool: str, arguments: dict[str, Any]
) -> "CallToolRequestResult":
"""
Create a CallToolRequestResult from a CallToolResult.
"""
return cls(
tool=tool,
arguments=arguments,
- isError=result.isError,
+ is_error=result.is_error,
content=result.content,
+ structured_content=result.structured_content,
+ meta=result.meta,
)Location: src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py lines 37–48
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/fastmcp/client/client.py around line 949, update
CallToolRequestResult.from_call_tool_result to stop referencing the non-existent
result.isError and instead propagate the actual fields from the incoming result:
use result.error (or set is_error = bool(result.error)) for error state, and
copy result.structured_content and result.meta into the returned
CallToolRequestResult object so all fields are preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't changed in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BrandonShar You're absolutely right! The bulk_tool_caller.py file wasn't modified in this PR. I was flagging a downstream compatibility issue that exists in that separate module—since CallToolResult now includes the meta field, the CallToolRequestResult.from_call_tool_result() method in that file should ideally be updated to propagate it.
Since this is outside the scope of your current changes, would you like me to open a follow-up issue to track updating the bulk tool caller to support the new meta field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
examples/tool_result_echo.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare except; always catch specific exception types
Files:
examples/tool_result_echo.py
🧬 Code graph analysis (1)
examples/tool_result_echo.py (2)
src/fastmcp/server/server.py (1)
FastMCP(148-2693)src/fastmcp/tools/tool.py (1)
ToolResult(66-112)
🔇 Additional comments (1)
examples/tool_result_echo.py (1)
1-8: LGTM!The imports are appropriate for demonstrating the new
metafeature in ToolResult.
jlowin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @BrandonShar!
Description
As part of supporting OpenAI's Apps SDK, the underlying MCP python sdk added support for
metain tool call payloads in v1.19.0.This PR modifies the
ToolResultobject to additionally support themetafield. It makes the same change toCachedToolResultto support the caching middleware and the localCallToolResultdataclass in the client.It also adds some additional testing for
ToolResultandCachedToolResultContributors Checklist
Review Checklist
Summary by CodeRabbit
New Features
Documentation
Tests
Chores