Skip to content

feat(cli): Add a permission class and an MCP exposure to every command - #1916

Open
sentry-junior[bot] wants to merge 5 commits into
mainfrom
feat/command-access-metadata
Open

sentry-junior[bot] wants to merge 5 commits into
mainfrom
feat/command-access-metadata

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Closes #1880 (cli-first-mcp tasks 1.1 and 1.2). With this, execute_sentry_read and execute_sentry_write (#1890) and command-catalog.json (#1881) can tell what each command may do and where it may run.

Permission class: derived, declared only where needed

generate:command-api now also writes src/generated/command-api-access.json, which records whether each command's Sentry API requests are read-only (GET/HEAD/OPTIONS) or include writes. These are the same requests behind command-api.json, plus the 23 requests outside the public catalog that command-api.json leaves out. Without those, dashboard restore and build upload would come out as read. lib/introspect.ts combines that file with destructive and any declared permission to get each command's permission: read, write, destructive, or method.

I derive the class instead of declaring it on all 144 commands because a declaration that only repeats what the requests show can drift. check:command-access accepts a permission declaration only where the requests can't decide the class:

  • the command also sends a write method outside the Sentry API: event send (DSN ingest) declares write; docs list/docs query (POST to the docs service) declare read;
  • the analysis can't follow its source, or can't read a request's method (no current exposed command hits this);
  • sentry api declares method, meaning each call gets the class of its --method. GET/HEAD/OPTIONS is read and anything else is write; Expose sentry api and sentry schema through the MCP #1893 resolves it per call.

The check rejects any other declaration. A GET-only command marked write, a write command marked read, a declaration that repeats the derived class, and permission on a destructive command all fail, and so does a missing exposure. The tests plant each case. Hidden commands need no declaration, but any declaration they make is still checked. A request whose method can't be read counts as write at runtime.

Exposure

The builder types require exposure: buildCommand, buildListCommand, buildDeleteCommand, and buildOrgListCommand (which sets exposed). The check also walks hidden routes.

hidden: auth login, auth logout, cli upgrade, cli setup, cli uninstall, local (all 16), monitor run, plus these, for review:

  • auth refresh, auth switch, auth token, auth (hidden default): session management; auth token prints the raw token
  • mcp: serves MCP on stdout
  • send-envelope, bash-hook: hidden legacy aliases

stdio-only: init, wasm-split, event send (sends env vars and logfiles), build upload/download, code-mappings upload, dart-symbol-map upload, debug-files (all 6), proguard upload/uuid, react-native gradle/xcode, replay download, snapshots diff/download/upload, sourcemap inject/upload/resolve, and these, for review:

  • release propose-version, release set-commits: local git
  • auth list, auth status (local credential store; auth status --show-token), info (local config)
  • cli completion, cli defaults, cli feedback, cli fix, cli import

Everything else is exposed, including api, schema, help, docs, status show, and platform list.

Flags that take local paths

FlagInfo.localPath is set for flags whose parser is parseFilePath/parseDirectoryPath, so #1890 can reject them outside stdio. No per-flag declaration is needed. The only exposed command with one is sentry api --input.

Open for #1890 (not handled here):

  • issue resolve --in @commit needs local git. It is a flag value, not a flag, so the execute path has to reject it.
  • Many exposed commands fall back to DSN detection from the cwd when the target is omitted. On hosted they need an explicit or session-pinned org/project.
  • event view --web/log view --web open a browser.

The permission class covers effects on Sentry only. Local side effects (config, credentials, files) are what exposure handles.

via @BYK.

--

View Session in Sentry

sentry-junior Bot and others added 2 commits November 3, 2026 21:52
Every command now declares where agents may run it (`exposure`:
exposed, stdio-only, or hidden), and introspection reports its
permission class (read, write, destructive, or `method` for
`sentry api`). The class comes from the Sentry API requests that
`generate:command-api` finds, so commands declare `permission` only
where those requests can't settle it. `check:command-access` fails when
a command has no exposure or a declaration contradicts its requests.

Closes #1880

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
…etadata

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Error Error Nov 4, 2026 9:46am UTC

Request Review

…etadata

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
…etadata

# Conflicts:
#	packages/cli/script/generate-command-api.ts
#	packages/cli/src/lib/introspect.ts

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>

This branch had an error being deployed

1 failed deployment
Preview — 2725cf09 Deployed Nov 4, 2026 by vercel[bot]
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.

Add a permission class and an MCP exposure to every CLI command

1 participant