Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions .github/workflows/merge-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ jobs:
- name: Run tests
run: pnpm test:ci

# @sentry/mcp-server is a deprecation shim for `sentry mcp`; delete this
# step with the package after its final publish.
# @sentry/mcp-server: the stdio proxy to the hosted MCP.
- name: Package mcp-server
working-directory: packages/mcp-server
run: pnpm pack --pack-destination dist
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ toolkit/
│ ├── cli/ # Sentry CLI (see packages/cli/AGENTS.md)
│ ├── toolkit-core/ # Shared CLI/MCP primitives
│ ├── mcp-core/ # Core MCP implementation (private)
│ ├── mcp-server/ # Deprecated shim that runs `sentry mcp` (delete after final publish)
│ ├── mcp-server/ # stdio proxy to the hosted MCP (@sentry/mcp-server on npm)
│ ├── mcp-cloudflare/ # Web app + OAuth
│ ├── mcp-server-evals/ # AI evaluation tests
│ ├── mcp-server-mocks/ # MSW mocks
Expand Down Expand Up @@ -70,7 +70,7 @@ toolkit/

- docs/architecture/overview.md — MCP system design
- docs/operations/security.md — Authentication and security patterns
- docs/operations/stdio-auth.md — How `sentry mcp` reuses CLI auth and static tokens
- docs/operations/stdio-auth.md — How `sentry mcp` and the `@sentry/mcp-server` proxy authenticate
- docs/operations/oauth-signout-playbook.md — Remote OAuth diagnostic runbook
- docs/operations/embedded-agents.md — LLM provider configuration for AI-powered tools
- docs/operations/github-actions.md — MCP deployment workflows
Expand Down Expand Up @@ -98,7 +98,7 @@ toolkit/

**MCP releases:**

- docs/releases/stdio.md — `sentry mcp` release, registry, and the `@sentry/mcp-server` shim
- docs/releases/stdio.md — `sentry mcp` release, registry, and the `@sentry/mcp-server` proxy
- docs/releases/cloudflare.md — Cloudflare deployment

## Commands
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ While this repository is focused on acting as an MCP service, we also support a

**Note:** The AI-powered search tools (`search_errors`, `search_traces`, `search_logs`, `search_issues`, etc.) require an LLM provider (OpenAI, Azure OpenAI, Anthropic, or OpenRouter). These tools use natural language processing to translate queries into Sentry's query syntax. Without a configured provider, these specific tools will be unavailable, but all other tools will function normally.

The `stdio` transport is the `sentry mcp` command in the [`sentry` CLI](https://cli.sentry.dev). It uses your CLI login (`sentry auth login`). MCP client configurations written for the retired `@sentry/mcp-server` package keep working: `--access-token` and `SENTRY_ACCESS_TOKEN` still pass a static token, and every other flag is unchanged.
The `stdio` transport is the `sentry mcp` command in the [`sentry` CLI](https://cli.sentry.dev). It uses your CLI login (`sentry auth login`). It accepts the flags of the original `@sentry/mcp-server` local server: `--access-token` and `SENTRY_ACCESS_TOKEN` still pass a static token, and every other flag is unchanged. On sentry.io, `npx -y @sentry/mcp-server --access-token=TOKEN` is a lighter option: a small proxy to the hosted MCP that does not download the CLI.

To use a static token, create a User Auth Token in Sentry with the necessary scopes. As of writing this is:

Expand Down
17 changes: 11 additions & 6 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ packages/
├── cli/ # Sentry CLI, including the `sentry mcp` stdio server
├── toolkit-core/ # Shared CLI/MCP primitives
├── mcp-core/ # Core MCP implementation (private package)
├── mcp-server/ # Deprecated shim that runs `sentry mcp`
├── mcp-server/ # stdio proxy to the hosted MCP (@sentry/mcp-server)
├── mcp-cloudflare/ # Cloudflare Workers deployment
├── mcp-server-evals/ # Evaluation test suite
├── mcp-server-mocks/ # Shared mock data
Expand Down Expand Up @@ -65,11 +65,14 @@ The local stdio MCP server is the `sentry mcp` command in the Sentry CLI.
- `src/lib/mcp/` parses server flags and environment variables, configures the
embedded agent provider and telemetry, and starts mcp-core's stdio transport.

### packages/mcp-server (deprecated)
### packages/mcp-server

A deprecation shim for one last `@sentry/mcp-server` release. Its `sentry-mcp`
bin prints a notice and runs `sentry mcp` with the same arguments. Delete it after
the final publish; see [stdio release](../releases/stdio.md).
`@sentry/mcp-server` is a small stdio proxy for clients that only need Sentry's
tools over stdio, without downloading the CLI. Its `sentry-mcp` bin forwards
stdio to the hosted MCP's Streamable HTTP endpoint with the user's token as
`Authorization: Sentry-Bearer <token>`. It bundles the MCP SDK and nothing
else, and serves sentry.io only; for self-hosted Sentry it exits with an error that
points to `sentry mcp`. See [stdio release](../releases/stdio.md).

### packages/mcp-cloudflare

Expand Down Expand Up @@ -135,7 +138,9 @@ Two checks enforce this in `pnpm run lint`:
`packages/toolkit-core`.
- `scripts/check-workspace-deps.mjs` (`pnpm run lint:deps`) reads every
workspace `package.json` and fails on a dependency cycle or a forbidden edge,
such as mcp-core reaching `sentry` or toolkit-core reaching mcp-core.
such as mcp-core reaching `sentry` or toolkit-core reaching mcp-core. It
also keeps `@sentry/mcp-server` (the published stdio proxy) free of every
other workspace package, so its bundle holds only the MCP SDK.

## Key Architectural Decisions

Expand Down
42 changes: 31 additions & 11 deletions docs/operations/stdio-auth.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# Stdio Authentication

How the local stdio MCP server (`sentry mcp`) authenticates with Sentry.
How the two stdio MCP entry points authenticate with Sentry: the local server
(`sentry mcp`) and the `@sentry/mcp-server` proxy to the hosted MCP.

## `sentry mcp`

`sentry mcp` has no auth flow of its own. It reuses the Sentry CLI's
credentials, so `sentry auth login`, `sentry auth logout`, and
`sentry auth status` manage the MCP server's credentials too.

## Credential Sources
### Credential Sources

In order of precedence:

1. **Static token** — `--access-token` flag, then `SENTRY_ACCESS_TOKEN`. Kept
for MCP client configurations written for the retired `@sentry/mcp-server`
package. The token is used as given for the life of the server and is never
for MCP client configurations written for the original `@sentry/mcp-server`
local server. The token is used as given for the life of the server and is never
refreshed. Org auth tokens (`sntrys_`) record the Sentry URL they were
issued for; `sentry mcp` refuses to send one to a different host
(`HostScopeError`). `SENTRY_ACCESS_TOKEN` only affects `sentry mcp`; other
Expand All @@ -34,15 +37,30 @@ For self-hosted Sentry, set the host with `--host`/`--url` (or the CLI's
`SENTRY_HOST`/`SENTRY_URL`). Pair `--host` with `--insecure-http` for instances
that only expose plain HTTP.

## Migration From `@sentry/mcp-server`
## `@sentry/mcp-server` Proxy

The `sentry-mcp` bin has no auth flow and stores nothing. It needs a static
token from `--access-token` or `SENTRY_ACCESS_TOKEN` and sends it to the
hosted MCP as `Authorization: Sentry-Bearer <token>` on every request. The
hosted MCP routes that scheme before OAuth and passes the token to Sentry as
is: no validation, refresh, or storage. A rejected token comes back as the
JSON-RPC error of the failed request, and the proxy logs it to stderr.

Without a token the proxy exits 1 and tells the user to create one or run
`npx -y sentry mcp`, which can use `sentry auth login`. `sentry-mcp auth ...`
exits 1 with the same pointer.

The hosted MCP serves sentry.io only (regional and organization subdomains go
through sentry.io). A `--host`, `--url`, `SENTRY_HOST`, or `SENTRY_URL` for
any other host, or `--insecure-http`, exits 1 with an error that points to
`npx -y sentry mcp`.

The standalone package's device code flow, bundled client ID, and token cache
(`~/.sentry/mcp.json`, `SENTRY_MCP_AUTH_CACHE`) are gone. `sentry mcp` does not
read the old cache. Users who relied on it run `sentry auth login` once, or keep
passing `--access-token` / `SENTRY_ACCESS_TOKEN`.
### Migration From the Old Local Server

The final `@sentry/mcp-server` release is a shim that maps
`sentry-mcp auth login|logout|status` to `sentry auth login|logout|status`.
The old package's device code flow, bundled client ID, and token cache
(`~/.sentry/mcp.json`, `SENTRY_MCP_AUTH_CACHE`) are gone. Neither entry point
reads the old cache. Users who relied on it run `sentry auth login` once and
switch to `sentry mcp`, or pass `--access-token` / `SENTRY_ACCESS_TOKEN`.

## Key Files

Expand All @@ -53,3 +71,5 @@ The final `@sentry/mcp-server` release is a shim that maps
| `packages/cli/src/lib/db/auth.ts` | CLI token storage and `refreshToken()` |
| `packages/cli/src/lib/token-host.ts` | Host-scoping checks |
| `packages/mcp-core/src/transports/stdio.ts` | stdio transport and per-request auth context |
| `packages/mcp-server/lib/config.js` | Proxy flags, token, and host checks |
| `packages/mcp-server/lib/proxy.js` | Proxy stdio to Streamable HTTP forwarding |
84 changes: 60 additions & 24 deletions docs/releases/stdio.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# stdio Release

How the local stdio MCP server ships.
How the stdio MCP entry points ship.

## Overview

The stdio MCP server is the `sentry mcp` command in the `sentry` npm package
(`packages/cli`). It ships with every CLI release (the `packages/cli` Craft
workspace in `.craft.yml`); there is no separate MCP package to version or
publish.
There are two:

- **`sentry mcp`**, the full local server in the `sentry` npm package
(`packages/cli`). It ships with every CLI release (the `packages/cli` Craft
workspace in `.craft.yml`) and owns the MCP registry entry.
- **`@sentry/mcp-server`** (`packages/mcp-server`), a small stdio proxy to the
hosted MCP for sentry.io users who do not want to download the CLI.

`sentry mcp` bundles `@sentry/mcp-core` (tools, server, and the stdio
transport) and uses the CLI's own authentication:
Expand All @@ -16,26 +19,42 @@ transport) and uses the CLI's own authentication:
upstream 401)
- `SENTRY_AUTH_TOKEN` / `SENTRY_TOKEN`, as for every CLI command
- `--access-token` / `SENTRY_ACCESS_TOKEN`: a static token for `sentry mcp`
only, kept for configurations written for the retired `@sentry/mcp-server`
package. It is never refreshed, and org auth tokens (`sntrys_`) are still
only, kept for configurations written for the original `@sentry/mcp-server`
local server. It is never refreshed, and org auth tokens (`sntrys_`) are still
scoped to the host they were issued for.

## Retired `@sentry/mcp-server` package

`packages/mcp-server` is a deprecation shim kept for one last release. Its
`sentry-mcp` bin prints a one-line stderr notice and runs `sentry mcp` with the
same arguments (`auth login|logout|status` map to `sentry auth ...`). It depends
on `sentry` (`>=0.48.0`, the first release with full flag parity), so publish
it only after that CLI release is on npm.

Remaining maintainer steps:

1. Release `@sentry/mcp-server` once more through the `packages/mcp-server`
Craft workspace (`release/mcp` branches).
2. Deprecate it on npm with `npm deprecate @sentry/mcp-server "<message>"`,
pointing users at `npx -y sentry mcp`.
3. Delete `packages/mcp-server`, its Craft workspace, and the pack step in
`.github/workflows/merge-jobs.yml`.
## `@sentry/mcp-server` proxy

`packages/mcp-server` is plain JavaScript in `bin/` and `lib/`. Its only
library is `@modelcontextprotocol/sdk`, which tsdown bundles into
`dist/sentry-mcp.js` on `pnpm pack` (`prepack`), so the published package has
no runtime dependencies (about 80 kB packed, versus about 95 packages for an
unbundled SDK). `pnpm run lint:deps` fails if it gains a workspace dependency,
so the CLI and the MCP core never end up in it.

The `sentry-mcp` bin forwards every JSON-RPC message between stdio and
`https://mcp.sentry.dev/mcp` (Streamable HTTP) with
`Authorization: Sentry-Bearer <token>`. It maps the old local server's flags
onto the hosted URL:

| Flag or variable | Hosted MCP |
| ---------------------------------------------- | ------------------------------ |
| `--organization-slug`, `--project-slug` | `/mcp/<org>/<project>` |
| `--skills`, `MCP_SKILLS` | `?skills=` |
| `--disable-skills`, `MCP_DISABLE_SKILLS` | `?disable-skills=` |
| `--all-skills` | Default (no parameter) |
| `--experimental` | `?experimental=1` |
| `--mcp-url`, `MCP_URL` | Hosted MCP origin (testing) |
| `--host`, `--url`, `SENTRY_HOST`, `SENTRY_URL` | Must be sentry.io; else exit 1 |

`--insecure-http` and the embedded agent flags (`--agent-provider`,
`--openai-*`, `--anthropic-*`) exit 1 and point to `sentry mcp`. `--sentry-dsn`
and the agent environment variables (`OPENAI_API_KEY`, ...) print a warning
and are ignored.

It releases through the `packages/mcp-server` Craft workspace (`release/mcp`
branches) together with the other `packages/mcp-*` versions. CI packs it in
`.github/workflows/merge-jobs.yml`.

## MCP Registry

Expand Down Expand Up @@ -77,6 +96,20 @@ Users run the server through npx in their MCP client configuration:
Without a `sentry auth login` session, add `"env": { "SENTRY_ACCESS_TOKEN":
"sntryu_..." }`. Pin a version with `sentry@0.48.0`.

On sentry.io, the proxy is a smaller download:

```json
{
"mcpServers": {
"sentry": {
"command": "npx",
"args": ["-y", "@sentry/mcp-server@latest"],
"env": { "SENTRY_ACCESS_TOKEN": "sntryu_..." }
}
}
}
```

## Testing Before a Release

```bash
Expand All @@ -85,11 +118,14 @@ pnpm --silent --dir packages/cli run cli mcp --help

# Exercise it with the test client
pnpm -w run cli --transport stdio "who am I?"

# Proxy tests (unit plus end to end against a local Streamable HTTP server)
pnpm --filter @sentry/mcp-server test
```

## References

- Command entry: `packages/cli/src/lib/mcp.ts`
- Host (flags, env, telemetry): `packages/cli/src/lib/mcp/`
- stdio transport: `packages/mcp-core/src/transports/stdio.ts`
- Deprecation shim: `packages/mcp-server/`
- Proxy: `packages/mcp-server/`
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"scripts": {
"docs:check": "node scripts/check-doc-links.mjs",
"test:ci-projects": "node --test scripts/ci-projects.test.mjs scripts/check-workspace-deps.test.mjs scripts/cli-nightly-version.test.mjs scripts/cli-nightly-watchdog.test.mjs scripts/cloudflare-deployment.test.mjs scripts/deploy-workflow.test.mjs scripts/migrate-cloudflare-token.test.mjs scripts/oxc-config.test.mjs scripts/cli-lint-rules.test.mjs packages/mcp-server/lib/shim.test.js",
"test:ci-projects": "node --test scripts/ci-projects.test.mjs scripts/check-workspace-deps.test.mjs scripts/cli-nightly-version.test.mjs scripts/cli-nightly-watchdog.test.mjs scripts/cloudflare-deployment.test.mjs scripts/deploy-workflow.test.mjs scripts/migrate-cloudflare-token.test.mjs scripts/oxc-config.test.mjs scripts/cli-lint-rules.test.mjs packages/mcp-server/lib/config.test.js packages/mcp-server/lib/proxy.test.js",
"dev": "pnpm --filter '@sentry/mcp-cloudflare...' --if-present run build && dotenv -e .env -e .env.local -- pnpm --parallel --filter @sentry/mcp-cloudflare --filter @sentry/mcp-core --filter @sentry/mcp-server-mocks --if-present run dev",
"dev:stdio": "pnpm --filter '@sentry/mcp-core...' --if-present run build && dotenv -e .env -e .env.local -- pnpm --parallel --filter @sentry/mcp-core --filter @sentry/mcp-server-mocks --if-present run dev",
"build": "dotenv -e .env -e .env.local -- pnpm -r --filter '!sentry' --filter '!sentry-cli-docs' --if-present run build",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/env-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const ENV_VAR_REGISTRY: readonly EnvVarEntry[] = [
{
name: "SENTRY_ACCESS_TOKEN",
description:
"Static auth token for `sentry mcp`, kept for MCP client configurations written for the retired `@sentry/mcp-server` package (the env form of its `--access-token` flag). It replaces the CLI session for the MCP server only and is never refreshed. Other commands ignore it.",
"Static auth token for `sentry mcp`, kept for MCP client configurations written for the original `@sentry/mcp-server` local server (the env form of its `--access-token` flag). It replaces the CLI session for the MCP server only and is never refreshed. Other commands ignore it.",
example: "sntryu_YOUR_TOKEN_HERE",
},
// -- Targeting --
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/lib/mcp/args.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Argument and environment parsing for `sentry mcp`.
*
* Accepts the same flags and environment variables as the retired
* `@sentry/mcp-server` package, so existing MCP client configurations keep
* working when they switch to `sentry mcp`. Flags win over environment values.
* Accepts the same flags and environment variables as the original
* `@sentry/mcp-server` local server, so existing MCP client configurations
* keep working when they switch to `sentry mcp`. Flags win over environment values.
*/

import { parseArgs } from "node:util";
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ function initMcpTelemetry(
zodErrorsIntegration(),
vercelAIIntegration({ recordInputs: true, recordOutputs: true }),
],
// The retired package fixed this at build time; keep the same value.
// The standalone local server fixed this at build time; keep the same value.
environment: "stdio",
});
}
Expand Down
Loading
Loading