Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
79ba47b
docs(openspec): propose a shared Sentry API layer for the CLI and MCP
sentry-junior[bot] Oct 26, 2026
7f3a0b9
docs(openspec): pick codecs from real-response benchmarks
sentry-junior[bot] Oct 26, 2026
e8d161e
docs(openspec): narrow cache tiers to what the server spike allows
sentry-junior[bot] Oct 26, 2026
5530d22
docs(openspec): plan a shared retry policy that honors Sentry 429 hea…
sentry-junior[bot] Oct 26, 2026
c7a8b18
docs(openspec): Retry 429s for any method and add 500 to retried stat…
sentry-junior[bot] Oct 27, 2026
01deb0a
docs(openspec): mark version alignment done (#1672)
sentry-junior[bot] Oct 27, 2026
62dfdb5
docs(openspec): Fold the detail tier into stable and allowlist R2 ins…
sentry-junior[bot] Oct 27, 2026
53358ba
docs(openspec): Mark shared pagination done (#1683)
sentry-junior[bot] Oct 27, 2026
67524c2
docs(openspec): Mark the shared cache policy done (#1688)
sentry-junior[bot] Oct 27, 2026
9eed4c0
docs(openspec): Mark the cache body codec done (#1695)
sentry-junior[bot] Oct 28, 2026
db7010f
docs(openspec): Mark the CacheStore task in review (#1696)
sentry-junior[bot] Oct 28, 2026
2f78136
docs(openspec): Mark the CacheStore done (#1696) and the CLI store in…
sentry-junior[bot] Oct 28, 2026
e05b6c4
docs(openspec): Mark the CLI store done (#1697) and revalidation in r…
sentry-junior[bot] Oct 28, 2026
852b91b
docs(openspec): Mark revalidation done (#1698) and add the shared cac…
sentry-junior[bot] Oct 28, 2026
d430a6e
docs(openspec): Mark the shared cache policy done (#1704)
sentry-junior[bot] Oct 28, 2026
2c263bb
docs(openspec): Mark task 5.0 done
sentry-junior[bot] Oct 29, 2026
be2fd27
docs(openspec): Mark issues and events on @sentry/api done (#1713)
sentry-junior[bot] Oct 29, 2026
95c2a0e
docs(openspec): Mark the shared transport done (#1719)
sentry-junior[bot] Oct 29, 2026
bd1fb8c
docs(openspec): Plan the shared API client in two steps
sentry-junior[bot] Oct 29, 2026
0e44e74
docs(openspec): Validate shared API responses through Standard Schema
sentry-junior[bot] Oct 29, 2026
8a0b84e
docs(openspec): Keep MCP dependencies pointing down
sentry-junior[bot] Oct 29, 2026
9139c19
docs(openspec): Land step 1 as stacked PRs by layer
sentry-junior[bot] Oct 29, 2026
b5a97ab
docs(openspec): Mark the api-client foundation done (#1732) and track…
sentry-junior[bot] Oct 29, 2026
86ce9ba
docs(openspec): Make MCP tools a portable face of the CLI
sentry-junior[bot] Oct 29, 2026
0211f1d
docs(openspec): Mark 8.3 done (#1736)
sentry-junior[bot] Oct 29, 2026
32fe3b5
docs(openspec): Keep @sentry/mcp-server as a stdio proxy
sentry-junior[bot] Oct 29, 2026
e68ce60
docs(openspec): Record @sentry/mcp-server as a slim bundle of sentry mcp
sentry-junior[bot] Oct 29, 2026
de407d5
docs(openspec): Keep the MCP registry entry on @sentry/mcp-server
sentry-junior[bot] Oct 29, 2026
e43f6ee
docs(openspec): Refresh the checklist after #1731, #1737, #1741 and #…
sentry-junior[bot] Nov 3, 2026
2a72851
Merge remote-tracking branch 'origin/main' into feat/unify-sentry-api…
sentry-junior[bot] Nov 3, 2026
8b69f44
docs(openspec): Plan the MCP as Code Mode over the CLI and move the A…
sentry-junior[bot] Nov 3, 2026
77b487c
docs(openspec): Expose CLI commands through search and execute, and r…
sentry-junior[bot] Nov 3, 2026
311b1fe
docs(openspec): Run the hosted SDK per request with persistence off
sentry-junior[bot] Nov 3, 2026
610f023
docs(openspec): Split execute by permission class and link the implem…
sentry-junior[bot] Nov 3, 2026
19bc066
Merge remote-tracking branch 'origin/main' into feat/unify-sentry-api…
sentry-junior[bot] Nov 3, 2026
f4aaef0
docs(openspec): Record the CLI SDK on workerd spike results
sentry-junior[bot] Nov 3, 2026
6cf1dc3
docs(openspec): Serve each command as its own tool in cli-first-mcp
sentry-junior[bot] Nov 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openspec/changes/cli-first-mcp/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-11-03
466 changes: 466 additions & 0 deletions openspec/changes/cli-first-mcp/design.md

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions openspec/changes/cli-first-mcp/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Proposal

## Why

Sentry's product logic for agents lives in two places. The CLI has about 40
command groups built on `@sentry/api`. The MCP server has 75 hand-written
catalog tools in `mcp-core/src/tools/catalog/`, with their own endpoint
wrappers (`mcp-core/src/api-client/client.ts`, about 5,000 lines), their own
Zod output formatting, and embedded natural-language search agents that need
an OpenAI or Anthropic key. Every capability is built twice, or exists in
only one product.

The earlier plan (`unify-sentry-api-layer`, group 11) kept both surfaces and
moved each MCP tool into the CLI as a portable tool definition. That still
means one hand-written MCP tool per command.

If the CLI is the only product surface, the MCP can be a thin layer that
exposes the CLI's commands as tools, found through the CLI's own
introspection and run through the CLI SDK.

## What Changes

1. **CLI parity.** Every MCP catalog tool gets a CLI command. New Sentry
capabilities are added as CLI commands only; no new hand-written MCP
tools. Commands call the API through `@sentry/api`
(`unify-sentry-api-layer`).
2. **The MCP exposes the CLI's commands.** Each exposed command is its own
tool (`sentry_issue_list`), run with JSON parameters through
`createSentrySDK()`. There are no search or execute meta-tools:
harnesses search large tool lists themselves. Each tool carries its
command's permission class as hints, so MCP hosts can allowlist reads
and ask before writes (#1254). No sandbox is involved: the agent can
only run commands that exist, with parameters their schema accepts.
3. **One introspection source.** Tool names, descriptions, and input schemas
come from metadata generated from the CLI's route tree
(`lib/introspect.ts`) at build time, on stdio and hosted.
4. **Every published API route.** `sentry api` and `sentry schema` are
commands like any other, so their tools reach every published
`@sentry/api` operation with no extra tool.
5. **Completion for agents.** The shell completion system
(`lib/complete.ts`, `lib/completion-target.ts`) suggests values when a
parameter fails validation, next to the static suggestions in each
parameter's schema `examples`: organization and project slugs, issue IDs, dataset fields
and aggregates, and search query terms.
6. **The CLI SDK runs on the hosted MCP.** Hosted creates one SDK instance
per request with the OAuth grant's token and runs it with persistence
off: no SQLite, no config directory, no caches. Module-level state on
the exposed commands' import graph becomes per-request. The hosted MCP
then runs commands through the same SDK as `sentry mcp`.
7. **The catalog goes away after an eval gate.** The command surface ships
next to the catalog. When the MCP eval suite shows it matching the
catalog, it becomes the default, and the catalog tools, their endpoint
wrappers, and the embedded search agents are deleted from `mcp-core`.

HTTP Code Mode (#1670), where the agent writes JavaScript that a sandbox
runs, is not part of this plan's critical path. It can be added later as a
tool over the same SDK, for multi-step work that benefits from code.

## Capabilities

### New Capabilities

- `mcp-command-surface`: The MCP's tool surface: discovery, execution,
permissions, and completion over the CLI's commands.

### Modified Capabilities

None. The project-management tool contract in
`refine-project-management-tools` is superseded: those operations become CLI
commands.

## Impact

- `packages/cli`: new commands for the parity gaps; agent metadata on
commands (permission class, hosted availability); a no-persistence
mode; per-request SDK state; a Workers build of the SDK.
- `packages/mcp-core`: keeps MCP plumbing (registration, transports,
constraints, the command tool definitions). Loses the catalog tools, the
`SentryApiService` endpoint wrappers they use, the search agents, and the
agent provider configuration.
- `packages/mcp-cloudflare`: runs commands through the CLI SDK, one
instance per request, with persistence off. OAuth consent maps to command permission classes.
- `packages/mcp-server`: the slim bundle needs the command handlers it
exposes; its module guard and size budget change.
- `packages/mcp-server-evals`: command-surface and catalog runs of the full
suite, then the command surface only.
- Docs: mcp.sentry.dev setup, `docs/` architecture pages, and the agent
skills (#1357 skills audit).
120 changes: 120 additions & 0 deletions openspec/changes/cli-first-mcp/specs/mcp-command-surface/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Spec Delta

## Purpose

Defines the MCP's tool surface as the CLI's commands: one tool per exposed
command, how a call runs, what each connection may do, and how completion
helps agents build valid calls.

## ADDED Requirements

### Requirement: Commands are the product surface

Every Sentry capability the MCP offers SHALL be a CLI command. The MCP SHALL
NOT define Sentry operations of its own.

#### Scenario: A new capability

- **WHEN** a Sentry capability is added for agents
- **THEN** it is added as a CLI command
- **THEN** it is listed as an MCP tool with no MCP code change

### Requirement: One tool per exposed command

The MCP SHALL list one tool per exposed command, named from the command's
path, with the command's brief and examples as its description and the
command's parameters as its input schema, from the CLI's introspection
metadata. It SHALL NOT add search or execute meta-tools; harnesses search
the tool list themselves. The hosted server SHALL read the generated
catalog, and SHALL list only commands its build can run.

#### Scenario: Tool list matches the catalog

- **WHEN** a connection lists tools
- **THEN** the list has one tool per exposed command, such as `sentry_issue_list` for `sentry issue list`
- **THEN** each tool's input schema is the command's `{ positional, flags }` JSON Schema
- **THEN** the list has no `search_sentry`, `execute_sentry_read`, or `execute_sentry_write`

### Requirement: Execution runs only exposed commands

Command tools SHALL run only commands marked exposed, with parameters that
validate against the command's schema, through the CLI SDK. They SHALL NOT
run arbitrary code.

#### Scenario: Hidden command

- **WHEN** a connection lists tools
- **THEN** there is no tool for `monitor run`, a hidden command

#### Scenario: Invalid parameter value

- **WHEN** an agent passes an organization slug that does not exist
- **THEN** the call fails with the allowed values or completion candidates for that parameter

### Requirement: Annotations from the permission class

Each command tool SHALL carry hints from its command's permission class:
read commands `readOnlyHint`, write and destructive commands
`destructiveHint`. `sentry api`, whose class comes from each call's method,
SHALL be marked destructive, and read-only on a read-only connection. A
read-only connection SHALL NOT list write tools.

#### Scenario: A host allowlists reads

- **WHEN** a host allowlists the read-only tools
- **THEN** read commands such as `sentry_issue_list` run without approval
- **THEN** `sentry_issue_resolve` still asks for approval

#### Scenario: Read-only connection lists tools

- **WHEN** a read-only connection lists tools
- **THEN** the list has the read command tools and `sentry_api`, and no write tools

### Requirement: Permission classes

Every command SHALL have a permission class (read, write, or destructive)
that agrees with the API operations it calls. A read-only connection SHALL
run only read commands, and `sentry api` calls only with read methods.
Session constraints SHALL pin organization and project parameters.

#### Scenario: Read-only connection runs a write command

- **WHEN** a read-only connection calls `issue resolve`
- **THEN** the call is refused without sending an API request

#### Scenario: Constrained session

- **WHEN** a session is constrained to an organization and a call names another organization
- **THEN** the call is refused

### Requirement: Isolation on the hosted server

When the hosted server runs the CLI SDK, no user's credentials,
constraints, or cached data SHALL be visible to another user's request.

#### Scenario: Concurrent identities

- **WHEN** two identities run commands concurrently in one isolate
- **THEN** each call uses only its own credentials, constraints, and cache entries

### Requirement: Completion for agents

Completion SHALL reach agents through parameter schema `examples` and
validation errors, with no completion tool. A call with an invalid value
SHALL fail with completion candidates for that parameter, from the same
completers as shell completion, with a data source suited to the host.

#### Scenario: Dataset field completion

- **WHEN** an agent calls `sentry_explore` with `--dataset spans` and a `--field` that does not exist, such as `span.dur`
- **THEN** the error includes span attribute keys close to `span.dur`

### Requirement: Eval-gated catalog removal

The catalog tools SHALL NOT be removed until the MCP eval suite shows the
command surface matching the catalog on task success.

#### Scenario: Default flip

- **WHEN** the command surface matches the catalog on the eval suite
- **THEN** it becomes the default and the catalog stays behind a flag for one release
69 changes: 69 additions & 0 deletions openspec/changes/cli-first-mcp/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Tasks

The API work these tasks build on is `unify-sentry-api-layer`. New commands
use `@sentry/api` operations and do not wait for the shared client to move
there.

## 1. Command metadata for agents

- [ ] 1.1 (#1880) Add a permission class (read, write, destructive) to every command, derived from its operations in `command-api.json` and its `destructive` flag, with a lint check that fails when a class is missing or disagrees with the operations. Verify the check fails on a planted GET-only command marked write and a write command marked read.
- [ ] 1.2 (#1880) Add an exposure (exposed, stdio-only, hidden) to every command, with a lint check that fails when one is missing. Mark session management, CLI maintenance, `local`, and `monitor run` hidden, and local-file commands stdio-only.
- [ ] 1.3 (#1881) Generate `command-catalog.json` from the route tree: path, brief, JSON Schema for parameters, completion kinds, examples, output fields, API operations, permission class, and exposure. Verify a test that it matches `lib/introspect.ts` output for the full tree.

## 2. CLI parity with the MCP catalog

One PR per domain. Each adds the commands from the parity table in design.md, on `@sentry/api` operations, with unit tests, and ticks its rows. Pending MCP tool PRs for the same capability are redirected here.

- [ ] 2.1 (#1883) Projects and teams: `project edit`, `team create`, `project team add/remove`. Supersedes `refine-project-management-tools`.
- [ ] 2.2 (#1884) DSNs: `dsn create`, `dsn edit`.
- [ ] 2.3 (#1885) Issues: `issue assign`, `issue comment`, `issue activity`; breadcrumbs and user reports where `event view` and `feedback list` fall short.
- [ ] 2.4 (#1886) Events: `event attachment list/download`; dropped events (#1812).
- [ ] 2.5 (#1887) Monitors: `monitor view`, `monitor env delete`, `monitor uptime list/view/create/edit/delete` (the detectors work in #1837 moves here).
- [ ] 2.6 (#1381) Profiles: `profile list/view`.
- [ ] 2.7 (#1888) Snapshots, alert options, and the metrics dataset in `explore`: check against the MCP tools and fill the gaps.
- [ ] 2.8 (#1889) `sentry open <url>`: resolve any Sentry URL to the matching resource and command.
- [ ] 2.9 (#1382) Mark every row in the parity table done or dropped, with the reason.

## 3. The command surface on stdio

- [ ] 3.1 (#1890) Add per-command tools to `mcp-core`: one MCP tool per exposed command in `command-catalog.json`, named from its path (`sentry issue list` → `sentry_issue_list`), with the catalog's brief, examples, and `{ positional, flags }` JSON Schema. Annotations from the permission class (read → `readOnlyHint`, write and destructive → `destructiveHint`; `sentry api` is one tool marked destructive, classed per call by `--method`). No `search_sentry` or `execute_*` meta-tools; harnesses search tools themselves (#1891 is folded in). Each call gets schema validation, session constraints, a result size cap that keeps JSON valid, and cursors. Verify `pnpm run measure-tokens` for the full list.
- [ ] 3.2 (#1890) Serve them from `sentry mcp --commands`, next to the catalog, running commands through `createSentrySDK()` with the session's credentials. Refuse hidden and stdio-only commands, local-path flags, and `--web`; reject streaming flags; buffer multi-yield commands. Add `--read-only`, which drops the write tools and limits `sentry api` to GET, HEAD, and OPTIONS. Verify tests for each permission class, constraint pinning, an invalid parameter returning the allowed values, a hidden or stdio-only command not listed, write tools absent under `--read-only`, the tool list matching the catalog's exposed commands, and `/mcp-qa` over stdio.
- 3.3 (#1891) Folded into 3.1 and 3.2: every exposed command is its own tool, and there is no separate search mode.
- [ ] 3.4 (#1892) Add `format: "markdown"` for commands with a plain markdown renderer. Verify a test for `issue view` and `trace view`.

## 4. API routes

- [ ] 4.1 (#1893) Expose `sentry api` and `sentry schema`. A `sentry api` call's permission class comes from its method, and hosted checks the path against the published operation list with #1807's shared matcher. Verify a raw read, a raw write denied under `--read-only`, and an unpublished path refused on hosted.
- [ ] 4.2 (#1894) Move `sentry schema`'s index onto `@sentry/api`'s operation catalog. Verify `sentry schema` output is unchanged.

## 5. Completion for agents

- [ ] 5.1 (#1895) Split the completers in `lib/complete.ts` into completers over a data source: the SQLite cache with a live API fallback on stdio, the live API, uncached, on hosted. Verify the shell completion tests pass unchanged.
- [ ] 5.2 (#1895) Wire them into the command tools' validation errors, returning candidates with descriptions; static suggestions stay in each parameter's schema `examples`. There is no completion tool. Verify tests for slugs, issue IDs, dataset fields and aggregates, and query keys and values.
- [ ] 5.3 (#1896) Document the query syntax in the tool descriptions of the search commands, replacing the embedded search agents' prompts.

## 6. The CLI SDK on the hosted MCP

- [ ] 6.1 (#1882) Spike: bundle `createSentrySDK()` with the exposed commands for workerd under `nodejs_compat` and a current compatibility date. Record what fails to bundle or run, by module and command, and the bundle size against the Workers script limit, in design.md. Mark blocked commands stdio-only.
- [ ] 6.2 (#1897) Add a no-persistence mode to the SDK: `getDatabase()` callers get a null store (reads miss, writes do nothing), and the response cache, `.sentryclirc` and config-directory reads, custom CA and header files, version checks, and telemetry state are off. Hosted rejects the `next`/`prev` cursor keywords; agents pass the raw `nextCursor`. Verify tests that run an exposed read command, a write command, and a paginated list with persistence off and no config directory.
- [ ] 6.3 (#1898) Audit module-level state on the exposed commands' import graph (for example `regionCache` in `lib/region.ts`, `loginTrustAnchor` in `lib/token-host.ts`, cached headers in `lib/custom-headers.ts`), and scope each item to the invocation or turn it off with persistence. Verify a test that runs two identities concurrently in one isolate and checks that neither sees the other's credentials, constraints, or results.
- [ ] 6.4 (#1899) In `mcp-cloudflare`'s Hono app, create one SDK instance per request with the OAuth grant's token and the session constraints, persistence off. Verify Workers tests for an exposed read command, a write command, and constraint pinning.
- [ ] 6.5 (#1899) Serve the command tools from `mcp-cloudflare` on the SDK, next to the catalog, listing only commands the hosted build can run. Verify the Workers tests and the #1675 harness against the command surface.
- [ ] 6.6 (#1907) Map hosted consent to permission classes: API Read, API Write, and the existing skills as command groups. Verify a test per grant.
- [ ] 6.7 For any domain the spike blocks entirely, split its commands into input resolution and a portable operation (formerly `unify-sentry-api-layer` 11.1), for that domain only.
- [ ] 6.8 (#1908) Compare the CLI's OAuth scopes with the MCP `SCOPES` and widen the CLI's if needed. Verify a test that the CLI's scopes cover every exposed command's operations. (Formerly 11.5.)

## 7. Slim stdio bundle

- [ ] 7.1 (#1909) Bundle the exposed command handlers and the SDK into `@sentry/mcp-server`, keeping the guard against stdio-only and hidden commands, Ink, the terminal renderer, and SQLite WASM. Re-measure the size budget. The package stays. Verify the bundle test (`initialize`, `tools/list`, a `sentry_issue_list` call against mocks).

## 8. Eval gate and cutover

- [ ] 8.1 (#1910) Run the full `mcp-server-evals` suite on the catalog and the command tools, with at least two clients or models. Record task success, tokens, and turns in `docs/testing/`.
- [ ] 8.2 (#1911) Make the command surface the default on stdio and hosted once it matches the catalog. Keep the catalog behind `--catalog` and a hosted skill for one release.
- [ ] 8.3 (#1911) Delete the catalog tools, their `SentryApiService` endpoint wrappers, the embedded search agents, and the agent provider configuration from `mcp-core`. Verify `pnpm run tsc && pnpm run lint && pnpm run test`.
- [ ] 8.4 (#1911) Update mcp.sentry.dev setup, `docs/` architecture and operations pages, and the agent skills for the command surface.

## 9. Later: Code Mode over the SDK (optional)

- [ ] 9.1 If 8.1 shows multi-step tasks losing to Code Mode (#1670), add `execute_sentry_code` whose `sentry` object calls the same execution path, permission checks included, in #1670's sandbox on hosted. Decide the stdio sandbox then.
2 changes: 2 additions & 0 deletions openspec/changes/unify-sentry-api-layer/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-26
Loading
Loading