Repository navigation
docs(openspec): Propose a shared Sentry API layer for the CLI and MCP - #1669
Open
sentry-junior[bot] wants to merge 5 commits into
Open
sentry-junior[bot] wants to merge 5 commits into
sentry-junior[bot] wants to merge 5 commits into
Conversation
Share pagination, response caching, and request transport between the CLI and MCP through toolkit-core on top of @sentry/api, let sentry mcp supply the CLI's authenticated transport, and add a tiered (memory + R2), zstd-compressed cache for the hosted MCP. Co-Authored-By: BYK <burak.kaya@sentry.io>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Benchmark zstd, brotli, gzip and trained zstd dictionaries on real API responses. Keep plain zstd for now, cap entries on compressed size, and move dictionary support to a follow-up gated on safe training data and Workers support (workerd ignores the zstd dictionary option). Co-Authored-By: BYK <burak.kaya@sentry.io>
BYK
added a commit
that referenced
this pull request
Oct 26, 2026
…ry (#1671) `search_issues` was sending AND/OR to the issues endpoint, which rejects them with `Boolean statements containing "OR" or "AND" are not supported in this search`. That's the top `search_issues` failure in `sentry/mcp-server`: about 93K a week across many users, mostly `is:X OR is:Y`. Two things in MCP let them through: - The repair prompt (`search-issues/config.ts`) listed `AND, OR, NOT` as valid syntax and told the agent to keep explicit tokens. - With no agent provider configured, queries went to the API unchecked. **What changes** - **The CLI sanitizer moves to `@sentry/toolkit-core/search-query`.** `rewriteSearchQuery` returns `{ query, notes }` and throws `SearchQueryError`. Each product decides how to report: the CLI wrapper (`cli/src/lib/search-query.ts`) keeps its stderr warning and `ValidationError`, and MCP throws `UserInputError`. CLI behavior is unchanged except one error line, which now reads `key:[val1,val2]` instead of `--query "key:[val1,val2]"`. - **`search_issues` runs it after the optional agent repair**, right before `listIssues`, so it covers the no-provider path too. When it rewrites a query, the result says so and shows the query it ran. Queries it can't rewrite fail with a `UserInputError` that lists alternatives, before any API call. - **New `issueSearch` option** (MCP only for now): - `is:A OR is:B` becomes `status:[A,B]` or `substatus:[A,B]`. `is:` rejects in-lists, but the status and substatus filters it maps to accept them. - Substatus lists must share one parent status, because the endpoint adds a status filter for the first substatus only (`expand_substatus_query_values`). So `is:ongoing OR is:archived_forever` is still rejected. - Parentheses that reduce to plain terms are unwrapped: `is:unresolved (is:regressed OR is:escalating)` becomes `is:unresolved substatus:[regressed,escalating]`. - **The agent prompt and tool description now say** there's no AND/OR/NOT or parentheses, and give the in-list, `status:`, and `substatus:` forms instead. **Checked against the live API** (sentry org, `junior` project, 14d, `X-Hits`): - `status:[unresolved,resolved]` returned 165, which is 159 (`is:unresolved`) + 6 (`is:resolved`). - `substatus:[new,ongoing]` returned 159, which is 53 (`is:new`) + 106 (`is:ongoing`). - `substatus:[ongoing,archived_forever]` returned only unresolved issues, and `[archived_forever,ongoing]` returned none. That's the first-substatus behavior the parent-status check guards against. - `(is:unresolved)` returned `[]` and `NOT is:resolved` returned `[]` (NOT is searched as free text), both with a 200. That's why the prompt now says `!` instead of NOT, and why parentheses get unwrapped. - `is:[unresolved,resolved]` returned a 400: `"in" syntax invalid for "is" search`. **Reviewing the move** Git shows the core file as new, because the CLI path still holds the wrapper. To see only what changed in the moved code: ``` git diff main:packages/cli/src/lib/search-query.ts HEAD:packages/toolkit-core/src/search-query.ts ``` **Not in this PR** - The CLI's `issue list` could opt into `issueSearch`. I left it off to keep CLI behavior unchanged. - MCP's other query tools don't use the shared sanitizer yet. - The 429 retry in `get_sentry_resource` is task 4.1 of #1669. **Checks** - `pnpm run tsc` and `pnpm run lint` pass. - Tests pass for `toolkit-core` (356), `mcp-core` (1833), and the CLI search-query, issue, log, span, and replay tests (532). - The full CLI `test/lib` + `test/commands` run has 6 failures in filesystem-permission tests (completions, upgrade lock, code scanner, file-changes). The completions and upgrade-lock failures also happen on `main` in this sandbox, so they look environment-specific. <!-- junior-request-attribution:start --> via @BYK. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1792772794.175999 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com> Co-authored-by: Andrew Liu <andrew.liu@sentry.io>
The getsentry/sentry#128818 spike found that no event or trace endpoint is safe to cache as immutable. Issue and project event details embed next/previous IDs and user reports, `latest` resolves to different events over time, and full traces gain spans. The CLI's 24-hour tier still matches all of them, including `/issues/<id>/events/latest/`. Replace the immutable tier with a 5-minute detail tier limited to organization event details and project trace-item details, and move issue events, project events, and full traces to 60 seconds. Add the CLI tier correction as its own task group ahead of the toolkit-core move. R2 now persists only detail-tier entries that the server marks `private, max-age`, for at most 5 minutes, and needs a security sign-off before production. Use zstd level 3 for every tier instead of level 9 for R2. Co-Authored-By: BYK <burak.kaya@sentry.io>
BYK
added a commit
that referenced
this pull request
Oct 26, 2026
…1672) The CLI and MCP core pinned different `@sentry/api` versions, so the two products could disagree on generated types and request helpers. This adds `@sentry/api: 0.400.0` to the pnpm catalog and points `packages/cli` and `packages/mcp-core` at `catalog:`. The lockfile now resolves a single `@sentry/api` version. MCP core needed no code changes for the bump. This is task 1 of the `unify-sentry-api-layer` OpenSpec change in #1669. Checks run locally: - `pnpm install --frozen-lockfile` - `pnpm --filter @sentry/mcp-core run tsc` - `pnpm --filter @sentry/mcp-core run test` (131 files, 1,831 passed, 6 skipped) - `pnpm --filter @sentry/mcp-core run build` - `tsc` for `@sentry/mcp-server` and `@sentry/mcp-cloudflare` - Formatter check on changed files The full root quality gate and the CLI suite were not run locally; CI covers them. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3A0ab4fc2bd5a2e23f2c25efcf --> -- [View Junior Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A0ab4fc2bd5a2e23f2c25efcf/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <burak.kaya@sentry.io>
…ders Neither product handles 429s well. The CLI retries every method, POSTs included, and ignores rate-limit headers. The MCP retries only GET 502/503/504, so the ~32.6K rate-limited GETs it sees a week fail immediately. Add task 5.0 for a pure `toolkit-core/src/api-retry.ts` policy: retry only GET/HEAD/OPTIONS unless a request opts in as idempotent, wait for Retry-After or X-Sentry-Rate-Limit-Reset with a clock-skew guard and caps, fall back to jittered backoff, and sleep abortably. The MCP adopts it first. Task 5.1 now moves idempotent-only retry through it and calls the CLI's all-methods retry a bug fixed separately; 6.1 drops the MCP's own retry once it is on the transport so retries don't stack. Add a sentry-api-transport spec delta for these rules. Co-Authored-By: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
Contributor
Author
|
Plan update (a81439e): retries and 429s.
|
…uses Sentry's RatelimitMiddleware rejects a 429 in process_request, before the view runs, so replaying one cannot repeat a write. The sentry-api-transport spec now retries 429s for any method, and retries 5xx, timeouts, and network errors only for GET/HEAD/OPTIONS or an explicit idempotent opt-in. The retried statuses are 429, 500, 502, 503, and 504. Other 5xx codes are deterministic. The clock-skew wording now matches the Date-bounded delay from #1673. The long requirements are split so `openspec validate --strict` passes.
Contributor
Author
|
Correction to my earlier plan comment (b5523f5), following review on #1673:
The |
This was referenced Oct 27, 2026
Merged
BYK
added a commit
that referenced
this pull request
Oct 27, 2026
…tions on missing org (#1678) ## Problem Telemetry from `sentry/mcp-server` (spans over 7 days, extrapolated from 0.3 sampling, so user counts are lower bounds): - **Missing `organizationSlug`:** about 23.8K failures from 861+ users. One user sent ~17.7K of them, all `search_issues` with `period:"1h"`. Without them it's ~6.1K failures across ~860 users. All of these came in on the unscoped `/mcp` URL. They fail in the MCP SDK's input validation before any handler runs, so the zod error message is the only place to add guidance without changing the schema. - **`period: "1h"` on `search_issues`:** about 25.6K failures from ~50 users, mostly that same user. - **Minute periods:** values like `30m`, `15m` and `10m` on `search_errors`, `search_logs` and `search_traces` were never accepted. That's about 1.5K failures from ~300 users. Sentry reads `m` as minutes (read-only GET check: `30m` returned 28 errors, against 1,269 for `24h`), and the issues endpoint accepts `statsPeriod=1h`. ## Changes - **`SEARCH_ISSUES_PERIOD_VALUES`** now includes `1h`. - **`ParamPeriod`** now accepts minutes (`/^\d+[mhdw]$/`), and the rejection message says "(minutes)". - **A missing or null `organizationSlug`** now returns: `Required. Call find_organizations() to list your organizations, then retry with organizationSlug set to one of their slugs.` This works on SDK v1 and v2 and doesn't change the JSON schema. - **Tool definitions** are regenerated. Token overhead goes up by 13 (10,066 → 10,079). ## Held back - **Defaulting to the user's only org.** This would mean making `organizationSlug` optional on ~50 tools, and fetching the org list plus a region lookup on every call that has no org. That's the org endpoint, cache and region-routing code that #1669 reworks, so it's left for that work. - **Listing the user's orgs in the error, with a ready-to-copy retry.** Same blocker: the SDK rejects the call before our handler sees it. ## Checks - `pnpm run tsc` and `pnpm run lint` pass. - mcp-core: 1847 passed, 6 skipped. ## Risk A model that means months by `3m` would get 3 minutes of data. Telemetry shows callers mean minutes; there are only a handful of `6m`/`12m`. None of this fixes the stuck script. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1792772794.175999 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
BYK
added a commit
that referenced
this pull request
Oct 27, 2026
…#1679) ## Problem These are the most common field and sort errors in `sentry/mcp-server` telemetry (7 days, extrapolated from 0.3 sampling, so user counts are lower bounds). Each fix was checked against the live API. - **`search_logs` with `min/max(timestamp)`:** ~2.6K failures from ~470 users. Log timestamps are strings, so the fix is `min/max(timestamp_precise)` (nanoseconds). Metrics behaves the same way (43 failures). - **`search_errors` with `first_seen()`:** ~2.3K failures from ~590 users. That function doesn't exist; use `min(timestamp)`, plus `max(timestamp)` or `last_seen()`. About 700 of these came from fields the embedded agent picked. - **`search_errors` sorted by `issue`:** ~1K failures from ~270 users. Sentry rejects this sort even when `issue` is selected; use `-count()` or `issue.id`. ## Changes - **New `tools/support/search-events/field-hints.ts`.** It adds a hint only when the request actually sent the rejected expression and Sentry's error message matches it. - Hints are fixed text and never include caller input, and Sentry's own message is kept. - Anything that doesn't match is re-thrown unchanged as `ApiValidationError`. - **Hints are added in two places:** the validation failure, and the final Events API 400. The second is needed because the validate endpoint accepts the `first_seen()` and `issue`-sort cases. - **`min(timestamp)`** is added to the embedded agent's errors field list. ## Not covered - `timestamp.to_day` / `to_hour` on spans and logs (~600 failures): there's no clear one-line replacement. - Metrics `count()` arity (~350), `p10`/`p25` (~150), and `count(span)` (0). ## Checks - tsc, lint (oxfmt) and all mcp-core tests pass (1837 passed, 6 skipped). - Tool definitions are unchanged (10,066 tokens). ## Relation to #1669 Task 7.3 moves the explore, traces and logs endpoints onto `@sentry/api`. These hints only depend on the error being an `ApiValidationError` and on its message, so they should carry over as long as 400s still map to that class. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1792772794.175999 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the OpenSpec change
unify-sentry-api-layer. It covers the proposal, three capability specs, the design, and the tasks. It is docs only; implementation lands in follow-up PRs.What it proposes
/issues/<id>/events/latest/), project events, and full traces. Those change after they are first read. A 5-minutedetailtier keeps only organization event details and project trace-item details; the rest drop to 60 seconds. This lands as its own CLI PR (task group 2).toolkit-core, on top of@sentry/api. The CLI and MCP then share one implementation, with credentials, storage, and telemetry supplied by each host.runMcpServer()accepts a host-suppliedfetch.sentry mcppasses the CLI's authenticated transport, so local MCP sessions reuse the CLI login, refresh, and on-disk cache.private, max-age, for at most 5 minutes. Enabling it in production needs a security sign-off on the stale window after access revocation or deletion.freshinput, like the CLI's--fresh.@sentry/apioperations one domain at a time, and both products use one@sentry/apiversion from the catalog.Why not
createSentrySDK()@sentry/mcp-server, so the MCP calling the SDK would create a dependency cycle.Evidence behind the design
mcp-core/src/api-client/client.tshave operations in@sentry/api@0.404.0. This was a pattern match, so treat it as approximate.dictionaryoption, and a dictionary trained on real data embeds verbatim strings, so it must not ship in a public package.node:zlibzstd works in local workerd under the current compatibility date. Task 7.2 confirms it on a deployed preview.immutable, and only organization event details and project trace-item details safe forprivate, max-age=300. Prototype: feat(api): Add opt-in max-age mode to ConditionalGetResponseMixin sentry#128828.Checks:
openspec validate unify-sentry-api-layer --strict,oxfmt --check, andpnpm run docs:checkall pass.via BYK.
--
View Junior Session in Sentry