[FEATURE] Add triggering_llm_request_id attribute to claude_code.tool spans for exact cost attribution
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Claude Code's OpenTelemetry layer emits claude_code.tool spans as siblings of their triggering claude_code.llm_request spans under a shared claude_code.interaction parent. The tool span carries tool_name, duration_ms, result_tokens, but no correlation key back to the specific LLM request that decided to call it.
This makes exact per-tool cost attribution impossible for observability pipelines. Cost is billed at the llm_request level; without a link from tool back to its triggering request, downstream systems can only approximate attribution by chronological sibling-ordering (exact for single-tool turns, proportional-by-duration for multi-tool fan-outs).
What I've verified
Span schema today (from live Tempo traces on @anthropic-ai/claude-code v2.1.118 with CLAUDE_CODE_ENABLE_TELEMETRY=1):
claude_code.interaction
├── claude_code.llm_request (attrs: model, input_tokens, output_tokens,
│ cache_read_tokens, cache_creation_tokens,
│ request_id, duration_ms)
├── claude_code.llm_request
├── claude_code.tool ← NO triggering_llm_request_id
│ └── claude_code.tool.execution
├── claude_code.tool ← still no link — order-dependent only
└── claude_code.llm_request (response generation — no tools)
Anthropic Messages API does not expose per-tool-use-block cost either — verified against anthropic-sdk-python v0.96.0 Usage type at https://github.com/anthropics/anthropic-sdk-python/blob/v0.96.0/src/anthropic/types/usage.py. Available fields: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens, cache_creation (TTL breakdown), server_tool_use (hosted-only), service_tier, inference_geo. No per-tool-use-block breakdown in any beta header, streaming event, or Advisor API surface.
The only upstream correlation key Claude Code CLI could expose is client-side: the request_id of the llm_request that produced each tool_use block. The CLI already has this internally (the response that contained the tool_use block is the same response that carries the request_id).
Related but distinct open issues:
- #35953 requests
CLAUDE_TOOL_USE_IDas env var in Bash subprocesses (outbound trace propagation) - #14859 requests agent hierarchy in hook events
- #16184 requested
parent_tool_use_idin hook payloads (closed, not_planned)
None add the llm_request → tool correlation I'm describing here.
Proposed Solution
Add a single string attribute to every claude_code.tool span:
triggering_llm_request_id: <string> // the request_id of the llm_request
whose tool_use block produced this tool span
This is the exact correlation key the CLI already has internally. Adding it as an OTel attribute is a minimal instrumentation change (assuming the tool-use-block handler has access to the enclosing response's request_id).
Why this unblocks accurate cost attribution
With triggering_llm_request_id present, an observability pipeline can:
- For each interaction trace, group tool spans by their triggering LLM request
- Single-tool turn: tool gets 100% of that llm_request's cost (exact)
- Multi-tool fan-out turn: prorate cost by output_token share of each tool_use block (Anthropic doesn't expose per-block usage, but response token share is a defensible proxy)
- No-tool llm_request (pure reasoning/response): cost stays unattributed to any tool
Without triggering_llm_request_id, step 1 requires heuristic sibling-ordering which cannot distinguish parallel tool_use blocks from the same llm_request from sequential calls. In my observed sessions, ~10% of LLM turns have multi-tool fan-outs (max observed: 10 tools in one turn).
Current workaround I've implemented
I run a Python post-processor that walks Tempo traces, sorts children of each claude_code.interaction by start_time_unix_nano, and groups tool spans with their nearest preceding llm_request sibling. This is chronologically correct for sequential tool calls but cannot distinguish parallel tool_use blocks from the same llm_request without the correlation attribute. Attribution drops from "exact" to "approximate" for the ~10% fan-out case.
With triggering_llm_request_id on each tool span, the heuristic disappears and attribution becomes exact regardless of fan-out.
Priority
Medium — enables accurate cost attribution for organizations building observability on Claude Code telemetry. Approximations work for aggregate rankings; exact attribution matters for per-team chargeback and tool ROI analysis.
Feature Category
OpenTelemetry instrumentation
Additional context
- Related doc: https://code.claude.com/docs/en/monitoring-usage (span schema page)
- Community attribution project: https://github.com/TechNickAI/claude_telemetry
- This request is complementary to #35953 (outbound context): that feature gets the tool_use_id out to subprocesses; this one gets the triggering request_id into the tool span so downstream correlation is lossless.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗