Address PR feedback: preserve fields and improve type hints

- Fix no_return_tool docstring to document None return
- Preserve _meta and structuredContent in CallToolRequestResult
- Remove intermediate CallToolResult creation
- Use from_call_tool_result helper in deprecated code
- Add TYPE_CHECKING import and __dir__ for better IDE support
- Update test snapshots to reflect structuredContent preservation

Co-authored-by: William Easton <strawgate@users.noreply.github.com>
This commit is contained in:
claude[bot] 2025-10-27 15:24:55 +00:00
commit a11e3aba16
5 changed files with 40 additions and 29 deletions

View file

@ -118,9 +118,6 @@ class BulkToolCaller(MCPMixin):
async with Client(self.connection) as client:
result = await client.call_tool_mcp(name=tool, arguments=arguments)
return CallToolRequestResult(
tool=tool,
arguments=arguments,
isError=result.isError,
content=result.content,
return CallToolRequestResult.from_call_tool_result(
result, tool=tool, arguments=arguments
)

View file

@ -1,9 +1,14 @@
from typing import TYPE_CHECKING
from .middleware import (
Middleware,
MiddlewareContext,
CallNext,
)
if TYPE_CHECKING:
from .bulk_tool_caller import BulkToolCallerMiddleware
def __getattr__(name: str):
if name == "BulkToolCallerMiddleware":
@ -13,6 +18,11 @@ def __getattr__(name: str):
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def __dir__() -> list[str]:
"""Ensure BulkToolCallerMiddleware shows up in dir() output."""
return sorted([*globals().keys(), "BulkToolCallerMiddleware"])
__all__ = [
"BulkToolCallerMiddleware",
"CallNext",

View file

@ -2,7 +2,7 @@
from typing import Annotated
from mcp.types import CallToolResult, TextContent
from mcp.types import TextContent
from fastmcp.server.context import Context
from fastmcp.server.middleware.bulk_tool_caller_types import (
@ -46,16 +46,15 @@ async def call_tools_bulk(
key=tool_call.tool, arguments=tool_call.arguments
)
# Convert ToolResult to CallToolResult
# Don't set isError - it defaults to None for successful calls
call_tool_result = CallToolResult(
content=tool_result.content,
)
# Convert to CallToolRequestResult
# Convert ToolResult to CallToolRequestResult, preserving all fields
# Note: ToolResult doesn't have isError - it's only on CallToolResult
# For successful calls, we don't set isError (defaults to None)
results.append(
CallToolRequestResult.from_call_tool_result(
call_tool_result, tool_call.tool, tool_call.arguments
CallToolRequestResult(
tool=tool_call.tool,
arguments=tool_call.arguments,
content=tool_result.content,
structuredContent=tool_result.structured_content,
)
)
except Exception as e:
@ -111,16 +110,15 @@ async def call_tool_bulk(
key=tool, arguments=args
)
# Convert ToolResult to CallToolResult
# Don't set isError - it defaults to None for successful calls
call_tool_result = CallToolResult(
content=tool_result.content,
)
# Convert to CallToolRequestResult
# Convert ToolResult to CallToolRequestResult, preserving all fields
# Note: ToolResult doesn't have isError - it's only on CallToolResult
# For successful calls, we don't set isError (defaults to None)
results.append(
CallToolRequestResult.from_call_tool_result(
call_tool_result, tool, args
CallToolRequestResult(
tool=tool,
arguments=args,
content=tool_result.content,
structuredContent=tool_result.structured_content,
)
)
except Exception as e:

View file

@ -36,4 +36,6 @@ class CallToolRequestResult(CallToolResult):
arguments=arguments,
isError=result.isError,
content=result.content,
_meta=getattr(result, "_meta", None),
structuredContent=getattr(result, "structuredContent", None),
)

View file

@ -29,7 +29,11 @@ def server_with_tools():
@mcp.tool
async def no_return_tool(arg1: str) -> None:
"""A simple tool that returns nothing."""
"""A simple tool that returns nothing.
Returns:
None: This tool does not return any value.
"""
@mcp.tool
def add(a: int, b: int) -> int:
@ -78,7 +82,7 @@ class TestBulkToolCallerMiddleware:
"_meta": None,
}
],
"structuredContent": None,
"structuredContent": {"result": "value1"},
"isError": False,
"tool": "echo_tool",
"arguments": {"arg1": "value1"},
@ -110,7 +114,7 @@ class TestBulkToolCallerMiddleware:
"_meta": None,
}
],
"structuredContent": None,
"structuredContent": {"result": "value1"},
"isError": False,
"tool": "echo_tool",
"arguments": {"arg1": "value1"},
@ -125,7 +129,7 @@ class TestBulkToolCallerMiddleware:
"_meta": None,
}
],
"structuredContent": None,
"structuredContent": {"result": "value2"},
"isError": False,
"tool": "echo_tool",
"arguments": {"arg1": "value2"},
@ -197,7 +201,7 @@ class TestBulkToolCallerMiddleware:
"_meta": None,
}
],
"structuredContent": None,
"structuredContent": {"result": "value1"},
"isError": False,
"tool": "echo_tool",
"arguments": {"arg1": "value1"},