<!-- provider-gap-audit: anthropic-messages-tool-use-no-span -->
Summary
The Anthropic integration (py/src/braintrust/integrations/anthropic/) creates dedicated child SpanTypeAttribute.TOOL spans for server-side tools (_log_server_tool_spans, e.g. web_search_tool_result, code execution) and for the beta Managed Agents surface (agent.tool_use/agent.mcp_tool_use), but a standard client.messages.create() call that uses client-side tool calling (tool_use/tool_result content blocks — Anthropic's primary, most widely used tool-calling mechanism) produces no dedicated tool span at all. The tool_use block is only ever visible embedded inside the parent LLM span's output.content/input array.
This is Anthropic's flagship, most heavily documented execution surface (https://docs.anthropic.com/en/docs/build-with-claude/tool-use) and is instrumented with materially less structural detail here than the equivalent surface in every other multi-tool-call provider integration already present in this same repo.
Verification
_is_server_tool_result_type (py/src/braintrust/integrations/anthropic/tracing.py:1304-1305) explicitly excludes the plain "tool_result" type: item_type.endswith("_tool_result") and item_type != "tool_result". Only types like web_search_tool_result, code_execution_tool_result, etc. reach _log_server_tool_spans (tracing.py:1424 onward).
- Regular
tool_use blocks are converted only via the generic content-block pass-through used for _log_message_to_span (tracing.py:1474-1508) — they appear as plain JSON inside the LLM span's output, with no tool_use_id/name/input broken out into a queryable child span, no separate latency, and no per-call error surfacing.
- Confirmed no other code path in this file creates a
SpanTypeAttribute.TOOL span keyed off a bare tool_use/tool_result pair (only _MANAGED_AGENTS_CALL_TYPES at tracing.py:868 and the server-tool path at tracing.py:1301-1471 produce TOOL spans).
Comparison with other tool-calling surfaces in this repo
| Integration/surface |
Client-executed tool calls get dedicated TOOL spans? |
| OpenAI Responses API |
Yes — per-item spans for function_call, web_search_call, code_interpreter_call, etc. (integrations/openai/tracing.py:842-974) |
| Cohere (chat) |
Yes — dedicated child TOOL spans (integrations/cohere/tracing.py:634-648) |
| Mistral (chat/conversations) |
Yes — tool spans for both completion-style and conversation-style tool calls (integrations/mistral/tracing.py:990-1067) |
| Google GenAI (Interactions API) |
Yes — live tool-call spans across turns (integrations/google_genai/tracing.py:798-993) |
| Claude Agent SDK |
Yes — TOOL spans keyed off tool_use/tool_result (integrations/claude_agent_sdk/tracing.py:275-996) |
| Anthropic server-side tools |
Yes — _log_server_tool_spans (tracing.py:1402-1471) |
| Anthropic Managed Agents (beta) |
Yes — _log_managed_agents_tool_spans (tracing.py:1126-1169) |
Anthropic standard Messages API client tool_use |
No — embedded in output only |
| OpenAI Chat Completions (base) |
No (same asymmetry, out of scope for this issue) |
Anthropic's own integration already contains three separate TOOL-span code paths (server tools, managed agents, and — per the Claude Agent SDK integration in the same repo — regular tool_use/tool_result), making the omission for the single most common case (plain client-side tool calling in messages.create()) an inconsistency rather than a deliberate scope boundary.
What should be instrumented
For a messages.create()/messages.stream() response whose content contains tool_use blocks, log a child SpanTypeAttribute.TOOL span per tool call (mirroring _log_server_tool_span's shape) capturing:
| Span field |
Content |
| name |
tool name from the tool_use block |
| input |
tool_use.input |
| metadata |
tool_use_id |
| output |
the matching tool_result block content from the next request's input, when available in the same call context |
Braintrust docs status
unclear. The Anthropic integration docs (https://www.braintrust.dev/docs/providers/anthropic) describe the Messages API tracing as capturing "input messages, system prompt, model, request parameters, response content, stop reason, and stop sequence" — tool_use is only implied as part of generic "response content," with no explicit statement about dedicated tool-call spans. The same docs do describe "beta tool-runner spans" (a different, opt-in feature) capturing "task input, tools, response messages, and aggregated metrics across iterations," confirming Braintrust considers structured tool-call spans a meaningful capability elsewhere in the same integration.
Upstream sources
Local repo files inspected
py/src/braintrust/integrations/anthropic/tracing.py:1301-1471 — server-tool span logic (_is_server_tool_result_type, _log_server_tool_spans), confirmed to exclude plain tool_result
py/src/braintrust/integrations/anthropic/tracing.py:868-1176 — Managed Agents tool span logic (separate code path, beta-only)
py/src/braintrust/integrations/anthropic/tracing.py:1474-1508 — _message_output/_log_message_to_span, the only path that touches regular tool_use content, with no span creation
py/src/braintrust/integrations/openai/tracing.py:842-974, integrations/cohere/tracing.py:634-648, integrations/mistral/tracing.py:990-1067, integrations/google_genai/tracing.py:798-993, integrations/claude_agent_sdk/tracing.py:275-996 — comparable dedicated tool-span handling in other integrations
<!-- provider-gap-audit: anthropic-messages-tool-use-no-span -->
Summary
The Anthropic integration (
py/src/braintrust/integrations/anthropic/) creates dedicated childSpanTypeAttribute.TOOLspans for server-side tools (_log_server_tool_spans, e.g.web_search_tool_result, code execution) and for the beta Managed Agents surface (agent.tool_use/agent.mcp_tool_use), but a standardclient.messages.create()call that uses client-side tool calling (tool_use/tool_resultcontent blocks — Anthropic's primary, most widely used tool-calling mechanism) produces no dedicated tool span at all. Thetool_useblock is only ever visible embedded inside the parent LLM span'soutput.content/inputarray.This is Anthropic's flagship, most heavily documented execution surface (https://docs.anthropic.com/en/docs/build-with-claude/tool-use) and is instrumented with materially less structural detail here than the equivalent surface in every other multi-tool-call provider integration already present in this same repo.
Verification
_is_server_tool_result_type(py/src/braintrust/integrations/anthropic/tracing.py:1304-1305) explicitly excludes the plain"tool_result"type:item_type.endswith("_tool_result") and item_type != "tool_result". Only types likeweb_search_tool_result,code_execution_tool_result, etc. reach_log_server_tool_spans(tracing.py:1424onward).tool_useblocks are converted only via the generic content-block pass-through used for_log_message_to_span(tracing.py:1474-1508) — they appear as plain JSON inside the LLM span'soutput, with notool_use_id/name/inputbroken out into a queryable child span, no separate latency, and no per-call error surfacing.SpanTypeAttribute.TOOLspan keyed off a baretool_use/tool_resultpair (only_MANAGED_AGENTS_CALL_TYPESattracing.py:868and the server-tool path attracing.py:1301-1471produce TOOL spans).Comparison with other tool-calling surfaces in this repo
function_call,web_search_call,code_interpreter_call, etc. (integrations/openai/tracing.py:842-974)integrations/cohere/tracing.py:634-648)integrations/mistral/tracing.py:990-1067)integrations/google_genai/tracing.py:798-993)tool_use/tool_result(integrations/claude_agent_sdk/tracing.py:275-996)_log_server_tool_spans(tracing.py:1402-1471)_log_managed_agents_tool_spans(tracing.py:1126-1169)tool_useAnthropic's own integration already contains three separate TOOL-span code paths (server tools, managed agents, and — per the Claude Agent SDK integration in the same repo — regular
tool_use/tool_result), making the omission for the single most common case (plain client-side tool calling inmessages.create()) an inconsistency rather than a deliberate scope boundary.What should be instrumented
For a
messages.create()/messages.stream()response whosecontentcontainstool_useblocks, log a childSpanTypeAttribute.TOOLspan per tool call (mirroring_log_server_tool_span's shape) capturing:namefrom thetool_useblocktool_use.inputtool_use_idtool_resultblock content from the next request's input, when available in the same call contextBraintrust docs status
unclear. The Anthropic integration docs (https://www.braintrust.dev/docs/providers/anthropic) describe the Messages API tracing as capturing "input messages, system prompt, model, request parameters, response content, stop reason, and stop sequence" —
tool_useis only implied as part of generic "response content," with no explicit statement about dedicated tool-call spans. The same docs do describe "beta tool-runner spans" (a different, opt-in feature) capturing "task input, tools, response messages, and aggregated metrics across iterations," confirming Braintrust considers structured tool-call spans a meaningful capability elsewhere in the same integration.Upstream sources
tool_use/tool_resultcontent block shapes): https://docs.anthropic.com/en/api/messagespy/pyproject.toml[tool.braintrust.matrix.anthropic]:min_version = "0.48.0",latest = "0.116.0"Local repo files inspected
py/src/braintrust/integrations/anthropic/tracing.py:1301-1471— server-tool span logic (_is_server_tool_result_type,_log_server_tool_spans), confirmed to exclude plaintool_resultpy/src/braintrust/integrations/anthropic/tracing.py:868-1176— Managed Agents tool span logic (separate code path, beta-only)py/src/braintrust/integrations/anthropic/tracing.py:1474-1508—_message_output/_log_message_to_span, the only path that touches regulartool_usecontent, with no span creationpy/src/braintrust/integrations/openai/tracing.py:842-974,integrations/cohere/tracing.py:634-648,integrations/mistral/tracing.py:990-1067,integrations/google_genai/tracing.py:798-993,integrations/claude_agent_sdk/tracing.py:275-996— comparable dedicated tool-span handling in other integrations