Skip to content

Commit 8e1915e

Browse files
authored
fix: preserve static MCP meta in converted function tools (#2769)
1 parent 0cc4805 commit 8e1915e

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/agents/mcp/util.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,28 @@ def create_static_tool_filter(
177177
class MCPUtil:
178178
"""Set of utilities for interop between MCP and Agents SDK tools."""
179179

180+
@staticmethod
181+
def _extract_static_meta(tool: Any) -> dict[str, Any] | None:
182+
meta = getattr(tool, "meta", None)
183+
if isinstance(meta, dict):
184+
return copy.deepcopy(meta)
185+
186+
model_extra = getattr(tool, "model_extra", None)
187+
if isinstance(model_extra, dict):
188+
extra_meta = model_extra.get("meta")
189+
if isinstance(extra_meta, dict):
190+
return copy.deepcopy(extra_meta)
191+
192+
model_dump = getattr(tool, "model_dump", None)
193+
if callable(model_dump):
194+
dumped = model_dump()
195+
if isinstance(dumped, dict):
196+
dumped_meta = dumped.get("meta")
197+
if isinstance(dumped_meta, dict):
198+
return copy.deepcopy(dumped_meta)
199+
200+
return None
201+
180202
@classmethod
181203
async def get_all_function_tools(
182204
cls,
@@ -251,7 +273,13 @@ def to_function_tool(
251273
policies. If the server uses a callable approval policy, approvals default
252274
to required to avoid bypassing dynamic checks.
253275
"""
254-
invoke_func_impl = functools.partial(cls.invoke_mcp_tool, server, tool)
276+
static_meta = cls._extract_static_meta(tool)
277+
invoke_func_impl = functools.partial(
278+
cls.invoke_mcp_tool,
279+
server,
280+
tool,
281+
meta=static_meta,
282+
)
255283
effective_failure_error_function = server._get_failure_error_function(
256284
failure_error_function
257285
)

tests/mcp/test_mcp_util.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,62 @@ def resolve_meta(context):
149149
assert args == {"foo": "bar"}
150150

151151

152+
@pytest.mark.asyncio
153+
async def test_to_function_tool_passes_static_mcp_meta():
154+
server = FakeMCPServer()
155+
tool = MCPTool(
156+
name="test_tool_1",
157+
inputSchema={},
158+
_meta={"locale": "en", "extra": "value"},
159+
)
160+
161+
function_tool = MCPUtil.to_function_tool(tool, server, convert_schemas_to_strict=False)
162+
tool_context = ToolContext(
163+
context=None,
164+
tool_name="test_tool_1",
165+
tool_call_id="test_call_static_meta",
166+
tool_arguments="{}",
167+
)
168+
169+
await function_tool.on_invoke_tool(tool_context, "{}")
170+
171+
assert server.tool_metas[-1] == {"locale": "en", "extra": "value"}
172+
173+
174+
@pytest.mark.asyncio
175+
async def test_to_function_tool_merges_static_mcp_meta_with_resolver():
176+
captured: dict[str, Any] = {}
177+
178+
def resolve_meta(context):
179+
captured["run_context"] = context.run_context
180+
captured["server_name"] = context.server_name
181+
captured["tool_name"] = context.tool_name
182+
captured["arguments"] = context.arguments
183+
return {"request_id": "req-123", "locale": "ja"}
184+
185+
server = FakeMCPServer(tool_meta_resolver=resolve_meta)
186+
tool = MCPTool(
187+
name="test_tool_1",
188+
inputSchema={},
189+
_meta={"locale": "en", "extra": "value"},
190+
)
191+
192+
function_tool = MCPUtil.to_function_tool(tool, server, convert_schemas_to_strict=False)
193+
tool_context = ToolContext(
194+
context={"request_id": "req-123"},
195+
tool_name="test_tool_1",
196+
tool_call_id="test_call_static_meta_with_resolver",
197+
tool_arguments="{}",
198+
)
199+
200+
await function_tool.on_invoke_tool(tool_context, "{}")
201+
202+
assert server.tool_metas[-1] == {"request_id": "req-123", "locale": "en", "extra": "value"}
203+
assert captured["server_name"] == server.name
204+
assert captured["tool_name"] == "test_tool_1"
205+
assert captured["arguments"] == {}
206+
207+
152208
@pytest.mark.asyncio
153209
async def test_mcp_invoke_bad_json_errors(caplog: pytest.LogCaptureFixture):
154210
caplog.set_level(logging.DEBUG)

0 commit comments

Comments
 (0)