Skip to content

Keep unknown search domains out of Sentry, and stop the batch carve-out over-suppressing - #922

Merged
kody-bot merged 1 commit into
mainfrom
cursor/sentry-triage-kody-cloudflare-1t-b4d0
Jul 24, 2026
Merged

kody-bot merged 1 commit into
mainfrom
cursor/sentry-triage-kody-cloudflare-1t-b4d0

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes KODY-CLOUDFLARE-1T.

Rebuilt on current main after #919 merged. This branch and #919 were written in parallel against the same files and both independently created packages/worker/src/mcp/caller-error.ts and the observability.ts carve-out. #919 landed first, so everything it already covers has been dropped from here and the branch was rebuilt from main as a single commit. See the comment below for the details of what was removed and why. Original history: be6f6d3b, 40c09329, ff5546c1, c452e166.

An MCP client called search({ domain: "skills" }). skills is a popular package kody id, not a capability domain. searchUnified correctly rejected it, but threw a plain Error, so observability opened a Sentry issue that looks like a platform bug.

What is left in this PR

  1. Unknown search domain is a caller mistake. searchUnified now throws McpCallerError for a domain id that is not in the registry, so the failure stays on the mcp-event log line. This is the actual -1T fix.

  2. The entity-batch carve-out no longer over-suppresses. Keep OpenAPI and MCP caller mistakes out of Sentry #919 marks a fully-failed search({ entity: [...] }) batch as callerError. That is right when every ref was simply unresolvable, but wrong when the batch failed because something underneath broke — a D1 read failing mid-batch would have been silently swallowed. The batch is now only marked callerError when every entry failed with a caller error, and otherwise passes a cause so the failure reaches Sentry as an exception. Two tests in search-handler.node.test.ts cover both directions, which is the regression guard for the whole carve-out.

  3. resolveOwnedPackageSource scopes in the query. It used the existing getEntitySourceByIdForUser helper instead of loading a source by id and then filtering on source.user_id in app code. Behaviour is unchanged; the scoping just lives where AGENTS.md asks for it. Mock updates in get-git-remote.node.test.ts and publish-external-push.node.test.ts follow from that.

Item 3 is unrelated to the Sentry cleanup and is easy to drop if you would rather keep this PR to items 1 and 2 — it survived only because it had already been written and reviewed here.

Not in this PR any more

Everything #919 shipped: caller-error.ts, the isCallerFailure skip in observability.ts, and the meta/search.ts, openapi-provider/*, packages/*, repo-open-session.ts, and search-detail.ts throw-site conversions.

Related

#923 finishes the same family from the other direction — the sibling throw sites that raise message strings identical to ones #919 converted, which would otherwise have reopened the archived Sentry groups under a different culprit. #923 and this PR touch disjoint files.

Validation

npm run validate green in full on the rebuilt branch: format:check, lint (1 pre-existing warning in sentry-tunnel.node.test.ts, 0 errors), typecheck, 1289 unit tests across 397 files, 18 Playwright E2E, 2 MCP E2E, backup:build, primitives:check, migrations:check.

System recap — composes existing primitives (low risk)

Mode: recap · Base: main @ 653ae7c1 · Head: a2ff96b8

Classification: composes — reuses #919's McpCallerError contract at one more throw site and tightens the batch classification it introduced. No primitive changes shape.

Primitives touched

Primitive Group Impact
mcp-server surfaces composes — unknown search domain throws McpCallerError; entity-batch failures only count as caller errors when every entry was one
saved-packages assistant composes — package source resolution scopes by user in the query

System map

Unknown domain ids and fully-unresolvable entity batches are classified as caller mistakes; anything else in those paths still reaches Sentry.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
  mcpServer["mcp-server<br/>MCP endpoint (/mcp)"]:::touched
  capabilityRegistry["capability-registry<br/>Capability registry"]:::untouched
  savedPackages["saved-packages<br/>Saved packages"]:::touched
  mcpServer -->|"unknown domain id rejected as McpCallerError"| capabilityRegistry
  mcpServer -->|"batch marked callerError only when every entry was one"| mcpServer
  savedPackages -->|"getEntitySourceByIdForUser scopes the lookup"| savedPackages
  classDef touched fill:#1a7f37,color:#fff
  classDef extended fill:#9a6700,color:#fff
  classDef added fill:#cf222e,color:#fff
  classDef untouched fill:#57606a,color:#fff
Loading

Before / after

Before: search({ domain: "skills" }) opened a Sentry issue. A fully-failed entity batch was always treated as a caller mistake, so a platform failure underneath it was suppressed.

After: the unknown domain stays on mcp-event. A fully-failed batch is only suppressed when every entry was itself a caller error; otherwise it reaches Sentry as an exception.

Invariants

Per-user isolation is preserved and slightly tightened: resolveOwnedPackageSource now filters by userId in the query rather than after the read.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of invalid MCP requests, missing resources, authentication issues, and unsupported search domains with clearer caller-facing errors.
    • Prevented caller-caused errors from being reported to Sentry as platform failures.
    • Improved error classification across search, package, repository, and OpenAPI operations.
  • Tests
    • Added coverage confirming caller errors are classified correctly and excluded from platform failure reporting.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The MCP surface now uses McpCallerError for caller-caused validation, authentication, and missing-resource failures. Observability detects these errors, supports explicit caller-error metadata, and excludes caller failures from Sentry reporting while retaining platform-failure reporting.

MCP caller failure handling

Layer / File(s) Summary
Typed errors and observability
packages/worker/src/mcp/caller-error.ts, packages/worker/src/mcp/observability.ts, packages/worker/src/mcp/observability.node.test.ts
Adds typed MCP caller errors, cause-chain detection, caller-error payload metadata, Sentry classification, and coverage for caller versus platform failures.
Capability caller errors
packages/worker/src/mcp/capabilities/meta/search.ts, packages/worker/src/mcp/capabilities/openapi-provider/*, packages/worker/src/mcp/capabilities/packages/*, packages/worker/src/mcp/capabilities/repo/repo-open-session.ts
Replaces generic errors with McpCallerError for capability input, authentication, package, source, and session failures; adds OpenAPI validation assertions.
Search caller metadata and coverage
packages/worker/src/mcp/tools/search-core.ts, packages/worker/src/mcp/tools/search-detail.ts, packages/worker/src/mcp/tools/search-tool-runner.ts, packages/worker/src/mcp/tools/search.node.test.ts
Marks invalid search events as caller errors and uses McpCallerError for search authorization, package, and domain failures.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPCaller
  participant MCPHandler
  participant logMcpEvent
  participant Sentry
  MCPCaller->>MCPHandler: submit invalid or unauthorized request
  MCPHandler-->>MCPCaller: throw McpCallerError
  MCPHandler->>logMcpEvent: record caller failure
  logMcpEvent->>Sentry: skip caller failure
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: keeping unknown search domains out of Sentry and adjusting batch carve-out suppression behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/sentry-triage-kody-cloudflare-1t-b4d0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 24, 2026 20:01
@cursor cursor Bot changed the title Stop reporting unknown search domains to Sentry Keep MCP caller mistakes (incl. unknown search domains) out of Sentry Jul 24, 2026
@cursor
cursor Bot changed the base branch from cursor/sentry-triage-kody-cloudflare-1s-a90d to main July 24, 2026 20:03

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 40c0932. Configure here.

Comment thread packages/worker/src/mcp/tools/search-tool-runner.ts Outdated
@github-actions

github-actions Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-922.kody-a99.workers.dev

Worker: kody-pr-922
D1: kody-pr-922-db
KV: kody-pr-922-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts (1)

226-268: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover invalid individual argument values too.

These tests cover missing path parameters and non-object args.params, but not asStringRecord’s new McpCallerError path for values such as params: { widgetId: true } at packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts Lines 677-680. Add an assertion for its error type and message.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts`
around lines 226 - 268, Add a test alongside the existing invalid-parameter
cases that calls executeOpenApiOperationRequest with args.params containing a
non-string value such as widgetId: true. Assert the rejected error is a
McpCallerError and its message matches the validation message produced by
asStringRecord for invalid parameter values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/src/mcp/capabilities/packages/resolve-package-source.ts`:
- Around line 50-52: Update the repo-source lookup in the resolve-package-source
flow to use a persistence query or helper that filters by both sourceId and
input.userId, rather than calling getEntitySourceById with only the ID and
checking source.user_id afterward. Treat a null result as “Repo source was not
found for this user” and remove the redundant in-memory ownership check.

In `@packages/worker/src/mcp/tools/search-detail.ts`:
- Around line 72-74: Update resolveEntityDetail so missing capabilities, values,
integrations, and secrets throw McpCallerError instead of plain Error, matching
the existing authentication and entity-miss handling. Keep the current
caller-facing messages and lookup behavior unchanged while converting all
remaining lookup-failure throw sites.

In `@packages/worker/src/mcp/tools/search-tool-runner.ts`:
- Around line 336-338: Update the batch error handling around the
allFailed/callerError classification so callerError is set only when every
failed entity entry is classified as caller-caused. Preserve each entry’s
existing caller/platform failure classification, and ensure any batch containing
a platform or repository failure remains eligible for Sentry reporting.

---

Nitpick comments:
In
`@packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts`:
- Around line 226-268: Add a test alongside the existing invalid-parameter cases
that calls executeOpenApiOperationRequest with args.params containing a
non-string value such as widgetId: true. Assert the rejected error is a
McpCallerError and its message matches the validation message produced by
asStringRecord for invalid parameter values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aa67ca3a-4151-428d-b922-c77158cc7034

📥 Commits

Reviewing files that changed from the base of the PR and between dc25e71 and 40c0932.

📒 Files selected for processing (16)
  • packages/worker/src/mcp/caller-error.ts
  • packages/worker/src/mcp/capabilities/meta/search.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/index.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts
  • packages/worker/src/mcp/capabilities/packages/delete-package.ts
  • packages/worker/src/mcp/capabilities/packages/get-package.ts
  • packages/worker/src/mcp/capabilities/packages/package-update.ts
  • packages/worker/src/mcp/capabilities/packages/resolve-package-source.ts
  • packages/worker/src/mcp/capabilities/repo/repo-open-session.ts
  • packages/worker/src/mcp/observability.node.test.ts
  • packages/worker/src/mcp/observability.ts
  • packages/worker/src/mcp/tools/search-core.ts
  • packages/worker/src/mcp/tools/search-detail.ts
  • packages/worker/src/mcp/tools/search-tool-runner.ts
  • packages/worker/src/mcp/tools/search.node.test.ts

Comment thread packages/worker/src/mcp/capabilities/packages/resolve-package-source.ts Outdated
Comment thread packages/worker/src/mcp/tools/search-detail.ts
Comment thread packages/worker/src/mcp/tools/search-tool-runner.ts Outdated
…ssified

Rebuilt on current main after #919 merged. #919 and this branch were written
in parallel against the same files and both created caller-error.ts and the
observability carve-out; #919 landed first, so everything it already covers is
dropped here. What remains is the content unique to this branch:

- searchUnified rejects an unknown domain with McpCallerError. Callers hit this
  by passing a package kody id such as "skills" where a capability domain is
  expected, which is a caller mistake, not a platform bug.
- The entity-batch failure path only marks the batch callerError when every
  entry failed with a caller error, and passes a cause otherwise so genuine
  platform failures (for example a D1 read failing mid-batch) still reach
  Sentry as exceptions. Two tests cover both directions, guarding the
  carve-out against over-suppression.
- resolveOwnedPackageSource uses the existing getEntitySourceByIdForUser helper
  instead of loading a source and filtering by user_id in app code, so the
  scoping lives in the query.
@cursor
cursor Bot force-pushed the cursor/sentry-triage-kody-cloudflare-1t-b4d0 branch from c452e16 to a2ff96b Compare July 24, 2026 20:54
@cursor cursor Bot changed the title Keep MCP caller mistakes (incl. unknown search domains) out of Sentry Keep unknown search domains out of Sentry, and stop the batch carve-out over-suppressing Jul 24, 2026
@cursor

cursor Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Heads up: I rebuilt this branch on current main. Force-pushed c452e166 → a2ff96b8. Writing down exactly what happened so the agent that authored this and Kent can both follow it.

Why

This PR and #919 were written in parallel, within about an hour of each other, against the same files. Both independently created packages/worker/src/mcp/caller-error.ts with the same McpCallerError class and the same isCallerFailure carve-out in observability.ts. #919 merged at 20:21Z, which left this branch conflicting with main and mostly redundant.

Rather than close it — most of a rebase would have been resolving conflicts against a copy of your own change — I rebuilt the branch from main keeping only the content that #919 does not already cover.

Kept

  1. tools/search-core.ts — unknown search domain throws McpCallerError. This is the actual KODY-CLOUDFLARE-1T fix and it is untouched from your version, comment included.
  2. tools/search-tool-runner.ts + the two search-handler.node.test.ts tests — your review-driven refinement so a fully-failed entity batch is only marked callerError when every entry was itself a caller error, with a cause passed otherwise. I kept this deliberately: it is the guard against Keep OpenAPI and MCP caller mistakes out of Sentry #919's carve-out over-suppressing a real platform failure, and the platform-failure test is the most valuable thing on this branch. It was very nearly lost in the shuffle.
  3. capabilities/packages/resolve-package-source.ts + the two package test files — switching to the existing getEntitySourceByIdForUser helper. Unrelated to Sentry, but already written and reviewed here, so I kept it rather than discard it. Flagged in the PR body as easy to drop.

Dropped

Everything #919 already shipped: caller-error.ts, the observability.ts carve-out, and the meta/search.ts, openapi-provider/*, packages/*, repo-open-session.ts, and search-detail.ts throw-site conversions. Also dropped were the apparent reverts of #920 (package-retrievers/service.ts and its test, docs/contributing/packages-and-manifests.md) — those were not intentional changes, just an artifact of this branch predating #920's merge.

Original history is preserved in the PR body for reference: be6f6d3b, 40c09329, ff5546c1, c452e166.

State

npm run validate is green in full on the rebuilt branch (1289 unit tests, 18 Playwright E2E, 2 MCP E2E, plus format/lint/typecheck/backup-build/primitives/migrations). CI will re-run and the AI reviewers will re-review, since the head SHA changed. I have not merged it — that is Kent's call, same as #923.

Context

This branch is one of three that independently invented McpCallerError in the same hour (#919 from issue 1S, this one from 1T, plus a prototype branch that has now been deleted). That is a coordination gap in the sentry-triage automation rather than anything wrong with this PR — I have written up a suggestion for it in my report back to the conductor.

#923 covers the remaining sibling throw sites in the same family. It and this PR touch disjoint files, so they can land in either order.

@kody-bot
kody-bot merged commit bd49b94 into main Jul 24, 2026
5 checks passed
@kody-bot
kody-bot deleted the cursor/sentry-triage-kody-cloudflare-1t-b4d0 branch July 24, 2026 21:01
cursor Bot pushed a commit that referenced this pull request Jul 24, 2026
Review follow-up. resolveRepoSourceReference and repo_show_publish_note loaded
a source by id and then compared source.user_id in app code. Use the existing
getEntitySourceByIdForUser helper so the user predicate is part of the query,
matching what #922 did for resolveOwnedPackageSource. Behaviour is unchanged;
another user's source now simply is not returned rather than being returned
and rejected.

Also covers the missing-identity branch of resolveRepoSourceReference, which
the previous commit converted without a test.
kody-bot pushed a commit that referenced this pull request Jul 24, 2026
Review follow-up. resolveRepoSourceReference and repo_show_publish_note loaded
a source by id and then compared source.user_id in app code. Use the existing
getEntitySourceByIdForUser helper so the user predicate is part of the query,
matching what #922 did for resolveOwnedPackageSource. Behaviour is unchanged;
another user's source now simply is not returned rather than being returned
and rejected.

Also covers the missing-identity branch of resolveRepoSourceReference, which
the previous commit converted without a test.

Co-authored-by: Cursor Agent <cursoragent@cursor.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.

3 participants