Skip to content

fix: make the MCP error tools answer the no-unknown-errors policy - #763

Merged
Makisuo merged 5 commits into
mainfrom
fix/mcp-error-triage-tooling
Sep 4, 2026
Merged

fix: make the MCP error tools answer the no-unknown-errors policy#763
Makisuo merged 5 commits into
mainfrom
fix/mcp-error-triage-tooling

Conversation

@Makisuo

@Makisuo Makisuo commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Why

Triaging maple-api errors against the "no unknown or unexpected error" policy took a dozen MCP calls, and two of the tools returned wrong or empty answers along the way. This PR fixes the two product bugs found and closes the tooling gaps that made the triage slow.

Bugs fixed

  • Log severity filter missed every Effect service. severity: "ERROR" compiled to SeverityText = 'ERROR', but Effect's logger writes Title Case (Error), OTel SDKs upper-case, and pino-style shims lower-case. search_logs, dashboard severity filters and alert rules on maple-api/alerting silently matched nothing. The scalar level now expands to every spelling as an IN list (keeps the sorting-key prefix on logs_aggregates_hourly); facet arrays stay exact. The raw-SQL catalog text and dashboard example that told agents the opposite now point at SeverityNumber ranges.
  • Model-call spans recorded AI.Error. The upstream package's tag leaked onto the chat <model> span because the mapping to @maple/llm/LlmCallError ran outside Stream.withSpan. Both the chat turn and the compaction call now map inside the span.

MCP tooling

  • find_errors: one sample message per fingerprint; identity="unexpected" (+ namespace_prefix) narrows to labels outside the namespace plus the 5xx / unexpected-envelope markers — a one-call answer to the policy question.
  • error_detail: reports the failing span (name, service, status, model/route/tool/query attributes) rather than an arbitrary span's status message, which was empty for most traces. Next step points at inspect_span for that span.
  • list_error_issues: last_seen_after, a Fingerprint column, and compact=true for a narrow table and payload. The 50-row default used to exceed client output caps.
  • inspect_trace: errors_only and max_spans.

Not in this PR

Fingerprinting transport failures by tag + reason instead of message (so a retried LlmCallError lands on the existing issue) needs a change to the error_events_mv materialization and a partial hash rotation; it is worth its own change.

Verification

  • packages/query-engine: errors, logs, pipe-dispatch, error-detail, search-logs tests — 171 passed.
  • apps/api: span-tree, render-trace, turn, registry, tool-output tests — 83 passed.
  • tsc --noEmit clean for apps/api, packages/query-engine, packages/domain.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…res as Maple errors

`severity: "ERROR"` compiled to `SeverityText = 'ERROR'`, but Effect services write
Title Case (`Error`), so the MCP log search, dashboard filters and alert rules on
Maple's own services matched nothing. The scalar level now expands to every spelling
as an `IN` list, which keeps the sorting-key prefix on `logs_aggregates_hourly`. Facet
arrays stay exact. The raw-SQL catalog and dashboard example, which told agents the
opposite, now point at `SeverityNumber` ranges.

The model-call span closed with the upstream package's `AI.Error` tag because the
mapping to `LlmCallError` ran outside `Stream.withSpan`. Both the chat turn and the
compaction call now map before the span records the failure.
…was it expected"

Triage through the MCP tools needed several round-trips for facts the warehouse
already had:

- `find_errors` prints one sample message per fingerprint, so a tag that owns a
  dozen fingerprints no longer needs an `error_detail` call each. `identity="unexpected"`
  narrows to identities that break a no-unknown-errors policy: labels outside
  `namespace_prefix` (default `@maple/`) plus the 5xx and unexpected-envelope markers.
- `error_detail` reports the failing span (name, service, status message, and the
  model/route/tool/query attributes it carries) instead of an arbitrary span's status,
  and points at `inspect_span` for it. `any(StatusMessage)` used to pick a healthy span.
- `list_error_issues` takes `last_seen_after`, prints the fingerprint so an issue can go
  straight to `error_detail`, and `compact=true` trims the table and payload to
  identity, state and volume.
- `inspect_trace` takes `errors_only` (error spans, their ancestors and roots) and
  `max_spans`, so a 400-span trace's failure fits in one response.
…QL baseline

`ErrorDetailTrace` gained `errorSpan`; the CLI's remote mode builds that shape from
the v2 trace, so it now picks the first `Error` span and the same attribute allowlist
the warehouse query projects. The SQL baseline records the new error-span columns and
the severity spellings, and the catalog sweeps the `identity="unexpected"` shape so
the `NOT LIKE ... OR IN` predicate is DESCRIBE-checked against real ClickHouse.
`scripts/bench-queries.ts` loads `scripts/bench-suites/*.ts` by path through a
dynamic import off its `--suite` argument, an edge knip cannot follow, so the
unified-catalog commit left `bench-suites/services.ts` reported as unused and
turned main red.
@Makisuo
Makisuo merged commit 5b34959 into main Sep 4, 2026
40 checks passed
@Makisuo
Makisuo deleted the fix/mcp-error-triage-tooling branch September 4, 2026 23:54
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🍁 Maple PR preview

Warning

Preview cleanup could not be confirmed. The Alchemy teardown outcome was skipped.

Final commit 5b38ce5 · View workflow run

Makisuo added a commit that referenced this pull request Sep 5, 2026
One conflict, and it is the good kind: main's #763 landed the same knip fix
this branch carried a commit earlier, so both sides added
`scripts/bench-suites/*.ts` to the apps/api entry list. The entry arrays are
byte-identical; only the explanatory comment differed.

Took main's comment because it is more accurate, and checked before adopting
it. Mine said nothing ever imports a bench suite; main's says they are loaded
by path from `scripts/bench-queries.ts` via a dynamic import off the CLI's
`--suite` argument, which is why knip cannot see the edge. That import is real
— `bench-queries.ts:659`, `import(pathToFileURL(resolve(path)).href)` — so
main's version describes the actual mechanism and mine described its symptom.

This branch's own knip commit (c6dd7d9) is now redundant with main and does
nothing on top of it; it stays in history as part of the merge rather than
being rewritten out.

Verified: `bun knip` exits 0, the full test-packages lane is 16/16,
`bun typecheck` 40/40, `bun run lint` clean, ClickHouse schema / local-manifest
(v17) / Tinybird manifest gates up to date, apps/api 2586, apps/web 2370,
apps/cli 538.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant