Skip to main content
Glama

Sentry MCP

Official
by getsentry
url-utils.ts8.44 kB
/** * Determines if a Sentry instance is SaaS or self-hosted based on the host. * @param host The Sentry host (e.g., "sentry.io" or "sentry.company.com") * @returns true if SaaS instance, false if self-hosted */ export function isSentryHost(host: string): boolean { return host === "sentry.io" || host.endsWith(".sentry.io"); } /** * Generates a Sentry issue URL. * @param host The Sentry host (may include regional subdomain for API access) * @param organizationSlug Organization identifier * @param issueId Issue identifier (e.g., "PROJECT-123") * @returns The complete issue URL */ export function getIssueUrl( host: string, organizationSlug: string, issueId: string, ): string { const isSaas = isSentryHost(host); // For SaaS instances, always use sentry.io for web UI URLs regardless of region // Regional subdomains (e.g., us.sentry.io) are only for API endpoints const webHost = isSaas ? "sentry.io" : host; return isSaas ? `https://${organizationSlug}.${webHost}/issues/${issueId}` : `https://${host}/organizations/${organizationSlug}/issues/${issueId}`; } /** * Generates a Sentry issues search URL. * @param host The Sentry host (may include regional subdomain for API access) * @param organizationSlug Organization identifier * @param query Optional search query * @param projectSlugOrId Optional project slug or ID * @returns The complete issues search URL */ export function getIssuesSearchUrl( host: string, organizationSlug: string, query?: string | null, projectSlugOrId?: string, ): string { const isSaas = isSentryHost(host); // For SaaS instances, always use sentry.io for web UI URLs regardless of region // Regional subdomains (e.g., us.sentry.io) are only for API endpoints const webHost = isSaas ? "sentry.io" : host; let url = isSaas ? `https://${organizationSlug}.${webHost}/issues/` : `https://${host}/organizations/${organizationSlug}/issues/`; const params = new URLSearchParams(); if (projectSlugOrId) { params.append("project", projectSlugOrId); } if (query) { params.append("query", query); } const queryString = params.toString(); if (queryString) { url += `?${queryString}`; } return url; } /** * Generates a Sentry trace URL for performance investigation. * @param host The Sentry host (may include regional subdomain for API access) * @param organizationSlug Organization identifier * @param traceId Trace identifier * @returns The complete trace URL */ export function getTraceUrl( host: string, organizationSlug: string, traceId: string, ): string { const isSaas = isSentryHost(host); // For SaaS instances, always use sentry.io for web UI URLs regardless of region // Regional subdomains (e.g., us.sentry.io) are only for API endpoints const webHost = isSaas ? "sentry.io" : host; return isSaas ? `https://${organizationSlug}.${webHost}/explore/traces/trace/${traceId}` : `https://${host}/organizations/${organizationSlug}/explore/traces/trace/${traceId}`; } /** * Generates a Sentry events explorer URL. * @param host The Sentry host (may include regional subdomain for API access) * @param organizationSlug Organization identifier * @param query Search query * @param dataset Dataset type * @param projectSlug Optional project slug * @param fields Optional fields to display * @returns The complete events explorer URL */ export function getEventsExplorerUrl( host: string, organizationSlug: string, query: string, dataset: "spans" | "errors" | "logs" = "spans", projectSlug?: string, fields?: string[], ): string { const isSaas = isSentryHost(host); // For SaaS instances, always use sentry.io for web UI URLs regardless of region // Regional subdomains (e.g., us.sentry.io) are only for API endpoints const webHost = isSaas ? "sentry.io" : host; let url = isSaas ? `https://${organizationSlug}.${webHost}/explore/` : `https://${host}/organizations/${organizationSlug}/explore/`; const params = new URLSearchParams(); params.append("query", query); params.append("dataset", dataset); params.append("layout", "table"); if (projectSlug) { params.append("project", projectSlug); } if (fields && fields.length > 0) { for (const field of fields) { params.append("field", field); } } url += `?${params.toString()}`; return url; } /** * Internal validation function that checks if a SENTRY_HOST value contains only hostname (no protocol). * Throws an error if validation fails instead of exiting the process. * * @param host The hostname to validate * @throws {Error} If the host contains a protocol */ function _validateSentryHostInternal(host: string): void { if (host.startsWith("http://") || host.startsWith("https://")) { throw new Error( "SENTRY_HOST should only contain a hostname (e.g., sentry.example.com). Use SENTRY_URL if you want to provide a full URL.", ); } } /** * Internal validation function that checks if a SENTRY_URL value is a valid HTTPS URL and extracts the hostname. * Throws an error if validation fails instead of exiting the process. * * @param url The HTTPS URL to validate and parse * @returns The extracted hostname from the URL * @throws {Error} If the URL is invalid or not HTTPS */ function _validateAndParseSentryUrlInternal(url: string): string { if (!url.startsWith("https://")) { throw new Error( "SENTRY_URL must be a full HTTPS URL (e.g., https://sentry.example.com).", ); } try { const parsedUrl = new URL(url); return parsedUrl.host; } catch (error) { throw new Error( "SENTRY_URL must be a valid HTTPS URL (e.g., https://sentry.example.com).", ); } } /** * Validates that a SENTRY_HOST value contains only hostname (no protocol). * Exits the process with error code 1 if validation fails (CLI behavior). * * @param host The hostname to validate */ export function validateSentryHost(host: string): void { try { _validateSentryHostInternal(host); } catch (error) { console.error(`Error: ${(error as Error).message}`); process.exit(1); } } /** * Validates that a SENTRY_URL value is a valid HTTPS URL and extracts the hostname. * Exits the process with error code 1 if validation fails (CLI behavior). * * @param url The HTTPS URL to validate and parse * @returns The extracted hostname from the URL */ export function validateAndParseSentryUrl(url: string): string { try { return _validateAndParseSentryUrlInternal(url); } catch (error) { console.error(`Error: ${(error as Error).message}`); process.exit(1); } } /** * Validates that a SENTRY_HOST value contains only hostname (no protocol). * Throws an error instead of exiting the process (for testing). * * @param host The hostname to validate * @throws {Error} If the host contains a protocol */ export function validateSentryHostThrows(host: string): void { _validateSentryHostInternal(host); } /** * Validates that a SENTRY_URL value is a valid HTTPS URL and extracts the hostname. * Throws an error instead of exiting the process (for testing). * * @param url The HTTPS URL to validate and parse * @returns The extracted hostname from the URL * @throws {Error} If the URL is invalid or not HTTPS */ export function validateAndParseSentryUrlThrows(url: string): string { return _validateAndParseSentryUrlInternal(url); } /** * Validates that the provided OpenAI base URL is a valid HTTP(S) URL and returns a normalized string. * * @param url The URL to validate and normalize * @returns The normalized URL string * @throws {Error} If the URL is empty, invalid, or uses an unsupported protocol */ export function validateOpenAiBaseUrlThrows(url: string): string { const trimmed = url.trim(); if (trimmed.length === 0) { throw new Error("OPENAI base URL must not be empty."); } let parsed: URL; try { parsed = new URL(trimmed); } catch (error) { throw new Error( "OPENAI base URL must be a valid HTTP or HTTPS URL (e.g., https://example.com/v1).", { cause: error }, ); } if (parsed.protocol !== "https:" && parsed.protocol !== "http:") { throw new Error( "OPENAI base URL must use http or https scheme (e.g., https://example.com/v1).", ); } // Preserve the exact path to support Azure or proxy endpoints that include version/path segments return parsed.toString(); }

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