Repository navigation
feat(mcp): record response size of each tool call - #3408
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 6401b60 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Duplicate code analysisNo new duplicate code introduced by this PR. jscpd compared this branch against |
[Medium impact] The PR appears safe to merge; no actionable issues remain. SummaryAdds UTF-8 response-byte counts to MCP spans, a histogram, and completion/error logs.
Reviews (3) · Last reviewed commit: "Merge branch 'main' into brandon/mcp-out..." · Reviewed by Greptile |
E2E Test Results✅ All tests passed • 440 passed • 1 skipped • 1615s
Tests ran across 4 shards in parallel. |
🔵 Tier 2 — Low RiskSmall, isolated change with no API route or data model modifications. Why this tier:
Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns. Stats
|
| 'Count of MCP tool invocations that returned an error or threw an exception.', | ||
| }); | ||
|
|
||
| const toolResponseSizeHistogram = getHistogram( |
There was a problem hiding this comment.
🟠 major — Byte histogram uses the default bucket boundaries, which stop at 10,000, so every response over about 10 KB lands in the overflow bucket
Pass explicit byte-scale buckets when creating the instrument, e.g. getHistogram('hyperdx.mcp.tool.response.size_bytes', { ..., unit: 'By', advice: { explicitBucketBoundaries: [256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304] } }). @opentelemetry/api ^1.8 supports advice. Nothing in the repo sets a View or OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION; getHistogram in packages/api/src/utils/instrumentation.ts just calls meter.createHistogram, so the SDK's default explicit buckets apply. Those are [0,5,10,25,...,7500,10000], which suit milliseconds. Search and query tool results are often tens to hundreds of KB, so they all fall into the single >10000 bucket. Percentiles then can't tell a 12 KB response from a 2 MB one, which defeats the PR's stated purpose of checking whether output trimming works.
Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
PR Review
1 finding(s): 🔴 0 critical · 🟠 1 major · 🔵 0 minor 1 posted as inline comment(s) on the changed lines. Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides. |
Deep Review✅ No critical issues found. This is a tightly-scoped, well-tested observability change. Response size is computed by a pure guarded helper ( 🔵 P3 nitpicks (3)
Reviewers (10): correctness, testing, maintainability, project-standards, kieran-typescript, reliability, performance, previous-comments, agent-native, learnings-researcher. Testing gaps: No assertion that No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond |
Summary
Each MCP tool call now records how many bytes of text it returns to the model. Response size stands in for token usage, so this gives a per-tool KPI for tracking context cost and checking whether output trimming works.
mcp.tool.response.size_bytes(UTF-8 bytes of the result's text blocks)hyperdx.mcp.tool.response.size_bytes, attributestoolandoutcome(success|error)responseByteson the "MCP tool completed" and "MCP tool error" log linesBytes rather than tokens: bytes are exact and model-agnostic, and any tokenizer we could bundle would only estimate Claude's counts.
Calls that throw record no size, since there is no response.
How to reproduce
Starting point:
yarn dev, with an MCP client connected to/api/mcpusing a personal API access key.On
main:clickstack_list_sources.mcp.tool.clickstack_list_sourcesspan has no response size.On this branch:
mcp.tool.response.size_bytes, and thehyperdx.mcp.tool.response.size_byteshistogram has a point fortool=clickstack_list_sources.How to test on Vercel preview
N/A — non-UI change.
References