Skip to content

Render mcp_list_tools spans instead of leaving them blank - #330

Open
laminar-coding-agent[bot] wants to merge 1 commit into
mainfrom
fix/lam-2162-agents-mcp-span-io
Open

Render mcp_list_tools spans instead of leaving them blank#330
laminar-coding-agent[bot] wants to merge 1 commit into
mainfrom
fix/lam-2162-agents-mcp-span-io

Conversation

@laminar-coding-agent

@laminar-coding-agent laminar-coding-agent Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Why

On any Agents-SDK run with an MCP server, every agents.mcp_tools span renders empty in the trace view. A 15-turn chi-bench run produces 15 blank TOOL spans, which reads as broken instrumentation.

The data was never missing. _apply_mcp_span_data wrote it to openai.agents.mcp.server / openai.agents.mcp.result, and the trace view reads lmnr.span.input / lmnr.span.output. It was the only _apply_*_span_data handler in the file that never called set_lmnr_span_io.

What changed

_apply_mcp_span_data now also sets span I/O — the server name as input, the returned tool list as output. The openai.agents.mcp.* attributes are unchanged, so anything querying them in SQL keeps working.

Verification

New tests/test_openai_agents_span_data.py (3 tests, hermetic — no SDK or OTEL provider needed):

3 passed, 5 warnings in 7.31s

Covers the populated case plus the two partial ones (no server, no result), asserting that a None field records no attribute rather than a null payload.

Confirmed against a production trace before the fix — all 15 spans had input len: 0 | output len: 0 while carrying:

openai.agents.mcp.server = chi_bench
openai.agents.mcp.result = ["chart.get_patient_chart_by_patient", "chart.search_patients", ...]  (30 tools)

Also documented in CLAUDE.md

Three related non-obvious behaviours found while diagnosing this, none of them bugs:

  • mcp_tools spans are emitted once per turn even with cache_tools_list=True — the span wraps server.list_tools(), which checks the cache internally. Measured: first span 11ms (real round trip), the other 14 ~0.3ms.
  • The first turn's mcp_tools span parents to the trace root, not the agent span — agents/run.py calls get_all_tools() before it starts agent_span(...). One sibling-of-the-agent span per run is upstream ordering, not a parenting bug.
  • A custom openai.agents.* attribute is not visible in the trace view at all, which is the general form of the bug fixed here.

🤖 Generated with Claude Code


Note

Low Risk
Localized instrumentation change with unit tests; no auth, export, or API surface changes.

Overview
Fixes blank mcp_tools / mcp_list_tools spans in the Laminar trace UI. Those spans already had openai.agents.mcp.server and .result, but the view only shows lmnr.span.input / lmnr.span.output.

_apply_mcp_span_data now calls set_lmnr_span_io: server name as input (when present) and the tool list as output. The custom openai.agents.mcp.* attributes are unchanged for SQL queries.

Adds hermetic tests/test_openai_agents_span_data.py (full case, no server, no result). CLAUDE.md documents OpenAI Agents tracing pitfalls (trace view vs custom attrs, per-turn mcp_tools spans, first-turn parenting, tool defs on Responses only).

Reviewed by Cursor Bugbot for commit fdafd3c. Bugbot is set up for automated code reviews on this repo. Configure here.

`_apply_mcp_span_data` recorded the MCP server name and the tool list it
returned as `openai.agents.mcp.*` attributes and stopped there. The trace
view reads `lmnr.span.input` / `lmnr.span.output`, so every one of those
spans showed up empty even though the data was on it — on a 15-turn agent
run that is 15 blank TOOL spans that look like broken instrumentation.

Every other handler in the file already calls `set_lmnr_span_io`; this one
now does too, with the server as input and the tool list as output. The
custom attributes stay for anyone querying them in SQL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant