Skip to main content
Glama

Sentry MCP

Official
by getsentry
resolve.ts5.72 kB
import { ALL_SCOPES, parseScopes, resolveScopes, type Scope, } from "../permissions"; import { parseSkills, SKILLS, type Skill } from "../skills"; import { DEFAULT_SCOPES } from "../constants"; import { validateAndParseSentryUrlThrows, validateOpenAiBaseUrlThrows, validateSentryHostThrows, } from "../utils/url-utils"; import type { MergedArgs, ResolvedConfig } from "./types"; export function formatInvalid(invalid: string[], envName?: string): string { const where = envName ? `${envName} provided` : "Invalid scopes provided"; return `Error: ${where}: ${invalid.join(", ")}\nAvailable scopes: ${ALL_SCOPES.join(", ")}`; } export function formatInvalidSkills( invalid: string[], envName?: string, ): string { const where = envName ? `${envName} provided` : "Invalid skills provided"; const allSkills = Object.keys(SKILLS).join(", "); return `Error: ${where}: ${invalid.join(", ")}\nAvailable skills: ${allSkills}`; } export function finalize(input: MergedArgs): ResolvedConfig { // Access token required if (!input.accessToken) { throw new Error( "Error: No access token was provided. Pass one with `--access-token` or via `SENTRY_ACCESS_TOKEN`.", ); } // Determine host from url/host with validation let sentryHost = "sentry.io"; if (input.url) { sentryHost = validateAndParseSentryUrlThrows(input.url); } else if (input.host) { validateSentryHostThrows(input.host); sentryHost = input.host; } // Scopes resolution (LEGACY - deprecated in favor of skills) let finalScopes: Set<Scope> | undefined = undefined; if (input.allScopes) { console.warn( "⚠️ Warning: --all-scopes is deprecated. Consider using skills instead:", ); console.warn(" --skills=inspect,triage,project-management,seer,docs"); console.warn(" Learn more about skills in the documentation."); console.warn(""); finalScopes = new Set<Scope>(ALL_SCOPES as ReadonlyArray<Scope>); } else if (input.scopes || input.addScopes) { // Show deprecation warning if (input.scopes) { console.warn( "⚠️ Warning: --scopes is deprecated. Consider using skills instead:", ); console.warn( " For example, instead of --scopes=event:write,project:write", ); console.warn(" Use: --skills=triage,project-management"); console.warn(" Learn more about skills in the documentation."); console.warn(""); } else if (input.addScopes) { console.warn( "⚠️ Warning: --add-scopes is deprecated. Consider using --skills instead:", ); console.warn(" For example, instead of --add-scopes=event:write"); console.warn(" Use: --skills=triage"); console.warn(" Learn more about skills in the documentation."); console.warn(""); } // Strict validation: any invalid token is an error if (input.scopes) { const { valid, invalid } = parseScopes(input.scopes); if (invalid.length > 0) { throw new Error(formatInvalid(invalid)); } if (valid.size === 0) { throw new Error( "Error: Invalid scopes provided. No valid scopes found.", ); } finalScopes = resolveScopes({ override: valid, defaults: DEFAULT_SCOPES, }); } else if (input.addScopes) { const { valid, invalid } = parseScopes(input.addScopes); if (invalid.length > 0) { throw new Error(formatInvalid(invalid)); } if (valid.size === 0) { throw new Error( "Error: Invalid additional scopes provided. No valid scopes found.", ); } finalScopes = resolveScopes({ add: valid, defaults: DEFAULT_SCOPES }); } } // Skills resolution // // IMPORTANT: stdio (CLI) intentionally defaults to ALL skills when no --skills flag is provided // // This differs from the OAuth flow, which requires explicit user selection: // - stdio/CLI: Non-interactive, defaults to ALL skills (inspect, docs, seer, triage, project-management) // - OAuth: Interactive, requires user to explicitly select skills (with sensible defaults pre-checked) // // Rationale: // We don't want the MCP to break if users don't specify skills. stdio is typically used in // local development and CI/CD environments where maximum access by default is expected. // OAuth is used in multi-tenant hosted environments where users should consciously grant // permissions on a per-app basis. // // For OAuth validation that enforces minimum 1 skill selection, see: // packages/mcp-cloudflare/src/server/oauth/routes/callback.ts (lines 234-248) // let finalSkills: Set<Skill> | undefined = undefined; if (input.skills) { // Override: use only the specified skills const { valid, invalid } = parseSkills(input.skills); if (invalid.length > 0) { throw new Error(formatInvalidSkills(invalid)); } if (valid.size === 0) { throw new Error("Error: Invalid skills provided. No valid skills found."); } finalSkills = valid; } else { // Default: grant ALL skills when no flag is provided (see comment block above for rationale) const allSkills = Object.keys(SKILLS) as Skill[]; finalSkills = new Set<Skill>(allSkills); } const resolvedOpenAiBaseUrl = input.openaiBaseUrl ? validateOpenAiBaseUrlThrows(input.openaiBaseUrl) : undefined; return { accessToken: input.accessToken, sentryHost, mcpUrl: input.mcpUrl, sentryDsn: input.sentryDsn, openaiBaseUrl: resolvedOpenAiBaseUrl, openaiModel: input.openaiModel, finalScopes, finalSkills, organizationSlug: input.organizationSlug, projectSlug: input.projectSlug, }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/getsentry/sentry-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server