Skip to content

Commit 9964412

Browse files
committed
fix: address review feedback on PR strands-agents#1918
- Add echo_meta tool to echo_server.py that returns _meta from request context, so integration tests can verify metadata reaches the server - Update integration tests to use echo_meta and assert the server received the metadata, removing the spy-based approach - Update unit test to verify the key name is '_meta' (alias) in the reconstructed params dict instead of checking inject positional args
1 parent f46caa8 commit 9964412

3 files changed

Lines changed: 33 additions & 46 deletions

File tree

tests/strands/tools/mcp/test_mcp_instrumentation.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,10 @@ def test_patch_mcp_client_preserves_existing_meta_pydantic(self):
452452

453453
patch_function(mock_wrapped, None, [mock_request], {})
454454

455-
# inject should be called with the existing _meta dict (not a new empty one)
456-
inject_call_args = mock_textmap_instance.inject.call_args[0][0]
457-
assert inject_call_args.get("com.example/request_id") == "abc-123"
455+
# Verify the reconstructed params use the key "_meta" (alias) not "meta" (Python name)
456+
validated_params = mock_request.root.params.model_dump(by_alias=True)
457+
assert "_meta" in validated_params
458+
assert validated_params["_meta"]["com.example/request_id"] == "abc-123"
458459

459460
def test_patch_mcp_client_injects_context_dict_params(self):
460461
"""Test that the client patch injects OpenTelemetry context into dict params."""

tests_integ/mcp/echo_server.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from typing import Literal
2121

2222
from mcp.server import FastMCP
23+
from mcp.server.fastmcp import Context
2324
from mcp.types import BlobResourceContents, CallToolResult, EmbeddedResource, TextContent, TextResourceContents
2425
from pydantic import BaseModel
2526

@@ -48,6 +49,13 @@ def start_echo_server():
4849
def echo(to_echo: str) -> str:
4950
return to_echo
5051

52+
@mcp.tool(description="Echos back the _meta received in the request", structured_output=False)
53+
def echo_meta(ctx: Context) -> str:
54+
meta = ctx.request_context.meta
55+
if meta is None:
56+
return json.dumps(None)
57+
return json.dumps(meta.model_dump(exclude_none=True))
58+
5159
# FastMCP automatically constructs structured output schema from method signature
5260
@mcp.tool(description="Echos response back with structured content", structured_output=True)
5361
def echo_with_structured_content(to_echo: str) -> EchoResponse:

tests_integ/mcp/test_mcp_client.py

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -239,84 +239,62 @@ def test_mcp_client_without_structured_content():
239239

240240

241241
def test_call_tool_sync_with_meta():
242-
"""Test that call_tool_sync works correctly when meta is provided."""
242+
"""Test that call_tool_sync forwards meta to the MCP server."""
243243
stdio_mcp_client = MCPClient(
244244
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
245245
)
246246

247247
with stdio_mcp_client:
248248
result = stdio_mcp_client.call_tool_sync(
249249
tool_use_id="test-meta-sync",
250-
name="echo",
251-
arguments={"to_echo": "META_TEST"},
250+
name="echo_meta",
251+
arguments={},
252252
meta={"com.example/request_id": "abc-123"},
253253
)
254254

255255
assert result["status"] == "success"
256-
assert result["content"] == [{"text": "META_TEST"}]
256+
received_meta = json.loads(result["content"][0]["text"])
257+
assert received_meta["com.example/request_id"] == "abc-123"
257258

258259

259260
@pytest.mark.asyncio
260261
async def test_call_tool_async_with_meta():
261-
"""Test that call_tool_async works correctly when meta is provided."""
262+
"""Test that call_tool_async forwards meta to the MCP server."""
262263
stdio_mcp_client = MCPClient(
263264
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
264265
)
265266

266267
with stdio_mcp_client:
267268
result = await stdio_mcp_client.call_tool_async(
268269
tool_use_id="test-meta-async",
269-
name="echo",
270-
arguments={"to_echo": "META_ASYNC_TEST"},
270+
name="echo_meta",
271+
arguments={},
271272
meta={"com.example/request_id": "def-456"},
272273
)
273274

274275
assert result["status"] == "success"
275-
assert result["content"] == [{"text": "META_ASYNC_TEST"}]
276+
received_meta = json.loads(result["content"][0]["text"])
277+
assert received_meta["com.example/request_id"] == "def-456"
276278

277279

278280
def test_instrumentation_preserves_meta_on_tool_call():
279-
"""Test that OTel instrumentation correctly sets _meta on outgoing tool call requests."""
280-
captured_params = []
281-
282-
def spy_send_request(wrapped, instance, args, kwargs):
283-
if args:
284-
request = args[0]
285-
method = getattr(getattr(request, "root", None), "method", None)
286-
if method == "tools/call" and hasattr(request.root, "params"):
287-
params = request.root.params
288-
if hasattr(params, "model_dump"):
289-
captured_params.append(params.model_dump(by_alias=True))
290-
elif isinstance(params, dict):
291-
captured_params.append(params.copy())
292-
return wrapped(*args, **kwargs)
293-
281+
"""Test that OTel instrumentation sets _meta that reaches the MCP server."""
294282
stdio_mcp_client = MCPClient(
295283
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
296284
)
297285

298286
with stdio_mcp_client:
299-
from mcp.shared.session import BaseSession
300-
from wrapt import wrap_function_wrapper
301-
302-
original_send = BaseSession.send_request
303-
wrap_function_wrapper("mcp.shared.session", "BaseSession.send_request", spy_send_request)
304-
305-
try:
306-
result = stdio_mcp_client.call_tool_sync(
307-
tool_use_id="test-instrumentation",
308-
name="echo",
309-
arguments={"to_echo": "INSTRUMENTATION_TEST"},
310-
)
311-
312-
assert result["status"] == "success"
313-
assert len(captured_params) > 0
287+
result = stdio_mcp_client.call_tool_sync(
288+
tool_use_id="test-instrumentation",
289+
name="echo_meta",
290+
arguments={},
291+
)
314292

315-
params = captured_params[-1]
316-
assert "_meta" in params
317-
assert isinstance(params["_meta"], dict)
318-
finally:
319-
BaseSession.send_request = original_send
293+
assert result["status"] == "success"
294+
received_meta = json.loads(result["content"][0]["text"])
295+
# OTel instrumentation should have injected _meta with tracing context
296+
assert received_meta is not None
297+
assert isinstance(received_meta, dict)
320298

321299

322300
@pytest.mark.skipif(

0 commit comments

Comments
 (0)