diff --git a/doc/connections/SENTRY.md b/doc/connections/SENTRY.md index 8a05839ee3d..b689834bd57 100644 --- a/doc/connections/SENTRY.md +++ b/doc/connections/SENTRY.md @@ -4,6 +4,25 @@ Paperclip connects to Sentry's [official MCP server](https://mcp.sentry.dev/) at `https://mcp.sentry.dev/mcp`. Browser sign-in uses the MCP authorization server. A normal connection does not require a customer-created OAuth app. +## User auth token + +Browser sign-in grants expire. A staging company connected on 2026-10-11 +needed reauthorization at 04:01 UTC on 2026-11-10, and its agents lost Sentry +access until a person signed in again. For unattended agents such as a triage +routine, use **Use a user auth token** instead. + +Create a [user auth token](https://sentry.io/settings/account/api/auth-tokens/) +with `org:read`, `project:read`, `project:write`, `team:read`, `team:write` and +`event:write`. Paperclip sends it to the same hosted server as +`Authorization: Sentry-Bearer `. The hosted server reserves `Bearer` for +its own OAuth tokens and forwards a `Sentry-Bearer` token to Sentry without +storing or refreshing it. See the +[Sentry MCP README](https://github.com/getsentry/sentry-mcp#remote-server). + +The token acts as the person who created it and is limited by that person's +organization role. It lasts until revoked, so rotate it if that person leaves +the organization. + ## Discovery The method pins the [MCP protected-resource metadata](https://mcp.sentry.dev/.well-known/oauth-protected-resource/mcp). diff --git a/doc/connections/tool-method-permission-reviews.json b/doc/connections/tool-method-permission-reviews.json index 3ee97832ed1..405ad058330 100644 --- a/doc/connections/tool-method-permission-reviews.json +++ b/doc/connections/tool-method-permission-reviews.json @@ -2016,6 +2016,22 @@ "liveProof": "not-run", "evidenceLimit": "Provider documentation was unavailable or not readable during this review; metadata/registered source only. Account-bound proof remains outstanding." }, + { + "app": "sentry", + "method": "mcp-user-token", + "auth": "api_key", + "policy": "provider-key", + "requestedScopes": [], + "capability": "provider-controlled", + "supportedActions": "Sentry API actions allowed by the token's scopes and its creator's organization role, including issue triage and project/team changes.", + "evidence": [ + "https://github.com/getsentry/sentry-mcp#remote-server", + "https://docs.sentry.io/account/auth-tokens/" + ], + "reviewedAt": "2026-11-11", + "liveProof": "not-run", + "keyPermissions": "Create a user auth token with org:read, project:read, project:write, team:read, team:write and event:write. The token acts as its creator; Paperclip cannot increase an existing token's permissions." + }, { "app": "shopify", "method": "ucp-commerce", diff --git a/packages/shared/src/app-definitions.test.ts b/packages/shared/src/app-definitions.test.ts index e0584a8d9eb..3c048c63a6d 100644 --- a/packages/shared/src/app-definitions.test.ts +++ b/packages/shared/src/app-definitions.test.ts @@ -717,6 +717,15 @@ describe("AppDefinition catalog", () => { discoveryUrl: "https://mcp.sentry.dev/.well-known/oauth-protected-resource/mcp", }, }); + // A user auth token does not expire with the 30-day browser grant. The + // hosted server reserves `Bearer` for its own OAuth tokens. + expect(APP_DEFINITIONS.find((app) => app.slug === "sentry")?.methods[1]).toMatchObject({ + key: "mcp-user-token", + transport: "mcp_remote", + auth: "api_key", + defaults: { serverUrl: "https://mcp.sentry.dev/mcp" }, + keyPlacement: { location: "header", name: "Authorization", prefix: "Sentry-Bearer " }, + }); expect(method("zapier")).toMatchObject({ key: "generated-url", auth: "none", diff --git a/packages/shared/src/app-definitions/sentry.json b/packages/shared/src/app-definitions/sentry.json index 5a85dd024f1..0afeca8c0e7 100644 --- a/packages/shared/src/app-definitions/sentry.json +++ b/packages/shared/src/app-definitions/sentry.json @@ -42,6 +42,46 @@ "project", "environment" ] + }, + { + "key": "mcp-user-token", + "transport": "mcp_remote", + "auth": "api_key", + "ownershipModes": [ + "customer" + ], + "whenToUse": "Use a user auth token when an unattended agent needs Sentry access that does not expire with a browser sign-in grant.", + "defaults": { + "serverUrl": "https://mcp.sentry.dev/mcp" + }, + "guidanceMd": "Connect with a Sentry user auth token for unattended agents. Browser sign-in grants expire and need the owner to sign in again; a user auth token lasts until you revoke it.", + "riskTier": "S2", + "label": "Use a user auth token", + "credentialFields": [ + { + "key": "authorization", + "label": "Sentry user auth token", + "type": "password", + "required": true, + "placeholder": "sntryu_...", + "secret": true, + "helperMd": "Create a user auth token with org:read, project:read, project:write, team:read, team:write and event:write. The token acts as its creator; Paperclip cannot increase an existing token's permissions." + } + ], + "keyPlacement": { + "location": "header", + "name": "Authorization", + "prefix": "Sentry-Bearer " + }, + "consoleLinks": { + "keys": "https://sentry.io/settings/account/api/auth-tokens/", + "docs": "https://github.com/getsentry/sentry-mcp#remote-server" + }, + "requiredResourceFilters": [ + "organization", + "project", + "environment" + ] } ], "docsUrl": "https://mcp.sentry.dev/.well-known/oauth-authorization-server", diff --git a/scripts/ingest-app-definitions.mjs b/scripts/ingest-app-definitions.mjs index 6eb77ca0ca9..782ad7e03f6 100644 --- a/scripts/ingest-app-definitions.mjs +++ b/scripts/ingest-app-definitions.mjs @@ -776,19 +776,51 @@ const apps = [ "developer", "sentry.io", ["https://mcp.sentry.dev/*"], - method( - "mcp-oauth", - "mcp_remote", - "oauth", - { - serverUrl: "https://mcp.sentry.dev/mcp", - discoveryUrl: - "https://mcp.sentry.dev/.well-known/oauth-protected-resource/mcp", - }, - "S2", - "Connect the Sentry organization and projects agents need for incident work.", - { requiredResourceFilters: ["organization", "project", "environment"] }, - ), + [ + method( + "mcp-oauth", + "mcp_remote", + "oauth", + { + serverUrl: "https://mcp.sentry.dev/mcp", + discoveryUrl: + "https://mcp.sentry.dev/.well-known/oauth-protected-resource/mcp", + }, + "S2", + "Connect the Sentry organization and projects agents need for incident work.", + { requiredResourceFilters: ["organization", "project", "environment"] }, + ), + method( + "mcp-user-token", + "mcp_remote", + "api_key", + { serverUrl: "https://mcp.sentry.dev/mcp" }, + "S2", + "Connect with a Sentry user auth token for unattended agents. Browser sign-in grants expire and need the owner to sign in again; a user auth token lasts until you revoke it.", + { + label: "Use a user auth token", + whenToUse: + "Use a user auth token when an unattended agent needs Sentry access that does not expire with a browser sign-in grant.", + credentialFields: [ + { + ...field("authorization", "Sentry user auth token", "sntryu_..."), + helperMd: + "Create a user auth token with org:read, project:read, project:write, team:read, team:write and event:write. The token acts as its creator, limited to that person's organization role.", + }, + ], + keyPlacement: { + location: "header", + name: "Authorization", + prefix: "Sentry-Bearer ", + }, + consoleLinks: { + keys: "https://sentry.io/settings/account/api/auth-tokens/", + docs: "https://github.com/getsentry/sentry-mcp#remote-server", + }, + requiredResourceFilters: ["organization", "project", "environment"], + }, + ), + ], ], [ "vercel", diff --git a/tests/runner-e2e/source-sdk-public-inputs.json b/tests/runner-e2e/source-sdk-public-inputs.json index b452ede9dde..ca7eb258c48 100644 --- a/tests/runner-e2e/source-sdk-public-inputs.json +++ b/tests/runner-e2e/source-sdk-public-inputs.json @@ -72,7 +72,7 @@ "packages/shared/src/app-definitions-url.test.ts": "7378d29fd8428e4807dd234b495aa9043f40e6d7", "packages/shared/src/app-definitions.generated.ts": "d74319a5c9684633b4a0a23c8bfa682e0d3bf267", "packages/shared/src/app-definitions.ingestion-report.json": "a7b60c7c16a484a411989315e9f08d48258fa647", - "packages/shared/src/app-definitions.test.ts": "e0584a8d9eb648d3cc42d594a2e5617240475dab", + "packages/shared/src/app-definitions.test.ts": "3c048c63a6d2c630a984f6f10e1dbdd9bf7c4f68", "packages/shared/src/app-definitions.ts": "eaadd6b7831cc23998a1a3ec6fd1d3a5032a503b", "packages/shared/src/app-definitions/1password.json": "c11a4777de2c78805b2d391da5eb075a9511e0f8", "packages/shared/src/app-definitions/agentmail.json": "edd0799b1980d0f5f43b89c8f7a1173dc623f7fa", @@ -159,7 +159,7 @@ "packages/shared/src/app-definitions/resend.json": "38f7595265f9ff6afdc84b9bce18602cd2c6ebfa", "packages/shared/src/app-definitions/responses-api.json": "29ad92e4c41a0dc0e3a3b07f518200bdc8e2e6ff", "packages/shared/src/app-definitions/sanity.json": "15968d52eb75008b170f45a82e79e0b77516b50d", - "packages/shared/src/app-definitions/sentry.json": "5a85dd024f11382a802557b0c5374b856e29b56a", + "packages/shared/src/app-definitions/sentry.json": "0afeca8c0e76be5d8324fa6d18fb970a38bb2b88", "packages/shared/src/app-definitions/shopify.json": "f6bff7db55083d96060d8bd46a08aa38b33c3080", "packages/shared/src/app-definitions/similarweb.json": "37a912287b7ad323cf783eb32d01b943a93e83fd", "packages/shared/src/app-definitions/slack.json": "733de01cdeec0dc66d732639925c9bb9fb7dc53d",