[FEATURE] Add tool_input_size_bytes and tool_use_id to claude_code.tool_result OTel event

Resolved 💬 2 comments Opened Apr 22, 2026 by serkan-ozal Closed Apr 22, 2026

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

I'm building an observability pipeline that aggregates tool usage across many Claude Code sessions — tool name distributions, tool input/output size percentiles, correlation with session-level outcomes. I run two telemetry sources in parallel:

  1. OTel logs via CLAUDE_CODE_ENABLE_TELEMETRY=1 — the claude_code.tool_result event with tool_name, success, duration_ms, tool_result_size_bytes, etc.
  2. Hook-based events via PostToolUse / PostToolUseFailure stdin — carrying tool_use_id, tool_input, tool_response, hook-computed session context.

Two gaps block the work today:

  1. No tool_input_size_bytes.

claude_code.tool_result already exposes tool_result_size_bytes unconditionally — but the symmetric tool_input_size_bytes does not exist (https://code.claude.com/docs/en/monitoring-usage#tool-result-event).
The only way to learn the size of a tool's input is:

  • Set OTEL_LOG_TOOL_DETAILS=1 → get full tool_input / `tool_parameters (content), then measure length myself. But that content may contain sensitive data (file paths, commands, URLs, API args) the docs themselves caution against shipping downstream. For a fleet-wide dashboard where I only care about aggregate sizes, that's a disproportionate disclosure.
  • Derive nothing. Dashboards lose a useful signal ("which tools consume the most input tokens from the model's perspective").

I don't need the content — just the byte count. tool_result_size_bytes already establishes the pattern.

  1. No tool_use_id anywhere in OTel.

tool_use_id is present in every hook input schema (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest). So Claude Code already has this ID and threads it through hooks. But none of the OTel log events (tool_result, tool_decision) or spans (claude_code.tool.execution) include it.

As a result, I can't join OTel data with hook-emitted data at the individual tool-call level. The only correlation keys in OTel are tool_name + session + timestamp sequence — which doesn't reliably identify the same invocation when there are parallel or rapidly-sequential tool calls of the same name.

Proposed Solution

Add two attributes to the existing claude_code.tool_result event (and ideally mirror on the related spans):

claude_code.tool_result
├---- tool_use_id: string (← new, unconditional; matches hook tool_use_id verbatim)
├---- tool_input_size_bytes: number (← new, unconditional; UTF-8 byte length of serialized tool input)
├---- [existing attributes unchanged]

Semantics:

  • tool_input_size_bytes: byte length of the JSON-serialized tool input (same serialization the hook stdin would carry). Unconditional, no gating. Symmetric to the existing tool_result_size_bytes.
  • tool_use_id: the exact same string already sent to hooks via tool_use_id (e.g., "toolu_01ABC..."). Unconditional.

Propagating tool_use_id to claude_code.tool_decision and the claude_code.tool.execution span would round out the correlation surface, but the single biggest win is tool_result.

Alternative Solutions

Things I've considered or tried:

  • Enable OTEL_LOG_TOOL_DETAILS=1 and measure tool_input length downstream. Gets the signal but costs me a privacy trade-off I don't want: every tool input flows to the telemetry backend, truncated at 4 KB but potentially including API args, file contents, shell commands. The docs already warn to "filter or redact these attributes as needed." For size-only aggregation, shipping the content is overkill.
  • Parse transcript_path downstream to find matching tool_use_id's input size. Undocumented format, I/O cost, version-fragile.
  • Timestamp-and-tool-name heuristic for correlation. Fails when parallel tool calls of the same name happen, or when hooks and OTel have clock skew. Not reliable at scale.
  • Read tool_input_size from my hook-captured event only and live without OTel correlation. That's what I do today — but I lose the ability to cross-check hook data against OTel for completeness, and backends that only consume OTel (e.g., Grafana over OTLP) can't see the size dimension at all.

Priority

Critical - Blocking my work

Feature Category

Developer tools/SDK

Use Case Example

_No response_

Additional Context

Scenario: fleet-wide tool usage dashboard

  1. My organization runs Claude Code across hundreds of developer sessions per day.
  2. Every session ships:
  • OTel logs to a central collector (Grafana/Loki/Datadog).
  • Hook-captured events to an internal analytics pipeline (via PostToolUse / PostToolUseFailure).
  1. I want to answer questions like:
  • "What's the p99 tool input size for Read across our fleet?" — blocked by missing tool_input_size_bytes.
  • "For tool_call X recorded in my hook pipeline, what did OTel say about decision_source?" — blocked by missing tool_use_id.
  • "Which tool invocations had unusually large inputs AND failed?" — join of both would make this a one-liner SQL/PromQL. Currently not possible without dual logging of content.
  1. With the two attributes added:
  • OTel alone answers size-percentile queries (no content, privacy-safe).
  • OTel + hook events join cleanly on tool_use_id.
  • Cross-cutting views become trivial.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗