Verify that calling a tool produces client and server spans with correct attributes.
(capfire: CaptureLogfire)
| 11 | |
| 12 | |
| 13 | async def test_client_and_server_spans(capfire: CaptureLogfire): |
| 14 | """Verify that calling a tool produces client and server spans with correct attributes.""" |
| 15 | server = MCPServer("test") |
| 16 | |
| 17 | @server.tool() |
| 18 | def greet(name: str) -> str: |
| 19 | """Greet someone.""" |
| 20 | return f"Hello, {name}!" |
| 21 | |
| 22 | async with Client(server, mode="legacy") as client: |
| 23 | result = await client.call_tool("greet", {"name": "World"}) |
| 24 | |
| 25 | assert isinstance(result.content[0], types.TextContent) |
| 26 | assert result.content[0].text == "Hello, World!" |
| 27 | |
| 28 | spans = capfire.exporter.exported_spans_as_dict() |
| 29 | span_names = {s["name"] for s in spans} |
| 30 | |
| 31 | assert "MCP send tools/call greet" in span_names |
| 32 | assert "MCP handle tools/call greet" in span_names |
| 33 | |
| 34 | client_span = next(s for s in spans if s["name"] == "MCP send tools/call greet") |
| 35 | server_span = next(s for s in spans if s["name"] == "MCP handle tools/call greet") |
| 36 | |
| 37 | assert client_span["attributes"]["mcp.method.name"] == "tools/call" |
| 38 | assert server_span["attributes"]["mcp.method.name"] == "tools/call" |
| 39 | |
| 40 | # Server span should be in the same trace as the client span (context propagation). |
| 41 | assert server_span["context"]["trace_id"] == client_span["context"]["trace_id"] |