Skip to content

feat(mcp): improve metric discovery, add quiet-saturation eval scenario - #2861

Open
karl-power wants to merge 1 commit into
mainfrom
karl/improve-metric-mcp-adoption
Open

feat(mcp): improve metric discovery, add quiet-saturation eval scenario#2861
karl-power wants to merge 1 commit into
mainfrom
karl/improve-metric-mcp-adoption

Conversation

@karl-power

@karl-power karl-power commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Why

Eval transcripts showed agents solving investigations without ever touching metrics — not because metrics weren't useful, but because they were the only signal behind a discovery wall. After list_sources, logs and traces are queryable immediately (key columns are in the catalog), while a metric source showed only opaque table names: querying it cost 1–3 extra calls (describe_source/list_metrics) just to learn what exists. Under a turn budget, agents rationally skipped it — even when explicitly nudged (we tested this: prompt- and output-level hints naming the exact metrics were ignored in 7/7 deliveries when metrics weren't on the efficient path).

What changed

  • clickstack_list_sources: metric sources now include metricNamesPreview — up to 10 recently-reported metric names per kind, sampled from the team's own tables — plus a usage note that metrics are queried directly via clickstack_table/clickstack_timeseries with metricType + metricName, no describe hop needed. Best-effort under a 3s wall-clock budget with a concurrency pool and per-table dedup; omitted silently on timeout.
  • metricNames.ts (new): the metric-name sampler extracted from describeSource.ts into a shared module, with a widening lookback (24h → 30d, first non-empty window wins) so sparse or batch-emitted metrics still produce a sample. describe_source uses the same sampler, so its per-kind sample gets the lookback too.

Measured wins

Benchmarked with the hdx-eval framework on quiet-saturation (connection-pool leak where metric history is the efficient diagnostic path), branch vs main, identical seed/anchor/prompts, claude-fable-5 + claude-opus-4-6, 3 runs/cell, two independent batches.

First tool call whose args name a target metric (the load-bearing pool gauges), every run, both batches:

arm batch 1 batch 2 range
branch / fable-5 6, 5, 4 5, 5, 6 4–6
branch / opus-4-6 6, 7, 6 6, 7, 7 6–7
main / fable-5 8, 10, 9 8, 8, 8 8–10
main / opus-4-6 16, 14, 13 14, 13, 21 13–21

Zero overlap across all 24 runs (branch ≤7, main ≥8; p ≈ 0.001 by permutation): the preview removes the discovery hops entirely (list_sources → query), where main inserts list_metrics/describe_source detours or trace-grinds first.

Headline metrics from the second batch (which ran exactly this minimal build):

branch/fable main/fable branch/opus main/opus
Combined score 96% 91% 82% 84%
LLM-judge (weighted) 96% 92% 92% 84%
Programmatic checks 96% 96% 97% 93%
Pre-symptom onset attributed (distinguishes_true_onset) 100% 100% 67% 33%
Tool calls (mean) 13.0 13.0 19.7 20.7
Tool errors (mean) 0.0 0.3 2.3 0.7
Metric adoption 100% 100% 100% 100%
  • fable-5: +5pp combined here, +9pp in batch 1 (12.3 vs 15.7 calls) — consistently equal-or-fewer calls, zero tool errors, faster wall clock.
  • opus-4-6: the freed budget converts to depth rather than speed — judge +8pp and the pre-symptom onset attributed twice as often. Its combined score dips below main only because of a 12pp tool-error penalty from unrelated agent-side query-syntax mistakes (one run retried an identical malformed select 4×).
  • Metric adoption is 100% on both arms — the change lowers the cost of the metric path rather than nudging inclination; agents reach the load-bearing signal roughly twice as fast.

@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8a7ad67

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/hdx-eval Patch
@hyperdx/api Patch
@hyperdx/app Patch
@hyperdx/otel-collector Patch

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

@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 12, 2026 9:21am
hyperdx-storybook Ready Ready Preview Aug 12, 2026 9:21am

Request Review

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 630 production lines changed (Tier 2 max: < 250)

Additional context: 8 file(s) in private internal-tooling packages, excluded from the line count

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 3
  • Production lines changed: 630 (+ 586 in test files, excluded from tier calculation)
  • Branch: karl/improve-metric-mcp-adoption
  • Author: karl-power

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR makes metric names discoverable directly from the MCP source catalog and adds a quiet-saturation evaluation scenario.

  • Adds bounded, concurrent metric-name preview sampling with widening lookback windows.
  • Reuses the shared sampler in source descriptions.
  • Adds informational adoption checks and corresponding aggregate/Markdown reporting.
  • Adds deterministic quiet-saturation telemetry, grading criteria, and tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/api/src/mcp/tools/sources/listSources.ts Adds team-scoped, timeout-bounded metric preview collection and direct-query guidance to the source catalog.
packages/api/src/mcp/tools/sources/metricNames.ts Extracts metric-name sampling into a shared helper and widens sparse-metric discovery from 24 hours to 30 days.
packages/api/src/mcp/tools/sources/describeSource.ts Replaces the local metric sampler with the shared widening-lookback implementation.
packages/hdx-eval/src/grading/programmatic.ts Evaluates informational adoption checks while excluding their zero weight from adoption scoring.
packages/hdx-eval/src/reports/aggregate.ts Propagates informational adoption metadata into aggregate reporting.
packages/hdx-eval/src/reports/markdown.ts Labels informational checks and explains their exclusion from adoption scores.
packages/hdx-eval/src/scenarios/quiet-saturation/generate.ts Generates deterministic telemetry for diagnosing a gradual connection-pool leak and its distractors.
packages/hdx-eval/src/scenarios/quiet-saturation/ground-truth.json Defines outcome, judge, and metric-adoption criteria for the new scenario.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[clickstack_list_sources] --> B[Load team sources and connections]
  B --> C[Build source summaries]
  C --> D[Sample metric tables concurrently]
  D --> E[Try 24-hour lookback]
  E -->|Empty| F[Try 30-day lookback]
  E -->|Names found| G[Attach metricNamesPreview]
  F -->|Names found| G
  F -->|Empty or timeout| H[Return summary without preview]
  G --> I[Return catalog and metrics usage guidance]
  H --> I
Loading

Reviews (3): Last reviewed commit: "feat(mcp): improve metric discovery, add..." | Re-trigger Greptile

Comment thread packages/api/src/mcp/tools/sources/listSources.ts Outdated
Comment thread packages/hdx-eval/src/scenarios/quiet-saturation/generate.ts
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

The change is defensively built: metric-name preview sampling in clickstack_list_sources is wrapped in a hard 3s AbortController budget raced against a bounded worker pool, every ClickHouse call is per-task try/catch'd so individual failures never fail the catalog call, the enrichment SQL is fully parameterized via the chSql DSL (customer-controlled metric names bind as String params, identifiers via tableExpr), and cross-team connection resolution correctly returns null and skips. The informational-adoption-check scoring guards its divisor (totalWeight === 0 ? 0 : hitWeight / totalWeight), so an all-informational rubric cannot divide by zero.

No P0/P1/P2 findings with a concrete failure mode surfaced from the completed analysis.

🔵 P3 nitpicks (1)
  • packages/api/src/mcp/tools/sources/listSources.ts:90 -- ClickhouseClient instances created per distinct connection in attachMetricNamePreviews are never disposed after the preview pass, mirroring the existing un-closed-client pattern in describeSource.ts.
    • Fix: If ClickhouseClient holds pollable transport resources, dispose the per-connection clients in a finally after the sampling race resolves; otherwise leave a short comment noting the transport is stateless so future readers don't re-flag it.

Reviewers (1): security (completed, no findings). The full panel (correctness, performance, reliability, adversarial, testing, maintainability, api-contract, project-standards, agent-native, learnings) was dispatched but had not returned at synthesis time; the assessment above reflects the security reviewer plus direct analysis of every changed production file (metricNames.ts, listSources.ts, describeSource.ts, and the hdx-eval grading/scenario changes). The 976-line scenarios/quiet-saturation/generate.ts is new eval-only scaffolding and was not line-by-line verified.

Testing gaps:

  • No test asserts a source whose connectionId belongs to another team is skipped (getConnectionById returns null) during preview attachment.
  • No test covers metric names containing SQL metacharacters/quotes to lock in String-param binding against future regressions.
  • The 3s timeout/abort path (preview silently omitted on timeout) and the per-(connection,db,table,timestampExpr) dedup cache are not directly exercised.
  • sampleMetricNamesWithLookback widening is not covered for the 24h-empty→30d-hit and both-empty→[] cases.

Residual risk: under a quiet environment where the 24h window is empty across many metric sources, every kind falls through to a 30d scan; the client-side 3s budget bounds list_sources wall-clock, but ClickHouse may not honor the abort signal, so abandoned 30d scans can keep running server-side. Worth confirming the ClickHouse client actually cancels on abort.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 280 passed • 1 skipped • 1093s

Status Count
✅ Passed 280
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@karl-power
karl-power force-pushed the karl/improve-metric-mcp-adoption branch from 2e3c3f7 to 3b1a262 Compare August 12, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant