Skip to main content
Glama

Sentry MCP

Official
by getsentry
index.ts3.86 kB
import * as Sentry from "@sentry/cloudflare"; import OAuthProvider from "@cloudflare/workers-oauth-provider"; import app from "./app"; import { SCOPES } from "../constants"; import type { Env } from "./types"; import getSentryConfig from "./sentry.config"; import { tokenExchangeCallback } from "./oauth"; import sentryMcpHandler from "./lib/mcp-handler"; import { checkRateLimit } from "./utils/rate-limiter"; import { getClientIp } from "./utils/client-ip"; // Public metadata endpoints that should be accessible from any origin const PUBLIC_METADATA_PATHS = [ "/.well-known/", // OAuth discovery endpoints "/robots.txt", // Search engine directives "/llms.txt", // LLM/AI agent directives ]; const isPublicMetadataEndpoint = (pathname: string): boolean => { return PUBLIC_METADATA_PATHS.some((path) => path.endsWith("/") ? pathname.startsWith(path) : pathname === path, ); }; const addCorsHeaders = (response: Response): Response => { const newResponse = new Response(response.body, response); newResponse.headers.set("Access-Control-Allow-Origin", "*"); newResponse.headers.set("Access-Control-Allow-Methods", "GET, OPTIONS"); newResponse.headers.set("Access-Control-Allow-Headers", "Content-Type"); return newResponse; }; // Wrap OAuth Provider to restrict CORS headers on public metadata endpoints // OAuth Provider v0.0.12 adds overly permissive CORS (allows all methods/headers). // We override with secure headers for .well-known endpoints and add CORS to robots.txt/llms.txt. const wrappedOAuthProvider = { fetch: async (request: Request, env: Env, ctx: ExecutionContext) => { const url = new URL(request.url); // Handle CORS preflight for public metadata endpoints if (request.method === "OPTIONS") { if (isPublicMetadataEndpoint(url.pathname)) { return addCorsHeaders(new Response(null, { status: 204 })); } } // Apply rate limiting to MCP and OAuth routes // This protects against abuse at the earliest possible point if (url.pathname.startsWith("/mcp") || url.pathname.startsWith("/oauth")) { const clientIP = getClientIp(request); // In local development or when IP can't be extracted, skip rate limiting // Rate limiter is optional and primarily for production abuse prevention if (clientIP) { const rateLimitResult = await checkRateLimit( clientIP, env.MCP_RATE_LIMITER, { keyPrefix: "mcp", errorMessage: "Rate limit exceeded. Please wait before trying again.", }, ); if (!rateLimitResult.allowed) { return new Response(rateLimitResult.errorMessage, { status: 429 }); } } // If no clientIP, allow the request (likely local dev) } const oAuthProvider = new OAuthProvider({ apiRoute: "/mcp", // @ts-expect-error - OAuthProvider types don't support specific Env types apiHandler: sentryMcpHandler, // @ts-expect-error - OAuthProvider types don't support specific Env types defaultHandler: app, // must match the routes registered in `app.ts` authorizeEndpoint: "/oauth/authorize", tokenEndpoint: "/oauth/token", clientRegistrationEndpoint: "/oauth/register", tokenExchangeCallback: (options) => tokenExchangeCallback(options, env), scopesSupported: Object.keys(SCOPES), }); const response = await oAuthProvider.fetch(request, env, ctx); // Flush buffered logs before Worker terminates ctx.waitUntil(Sentry.flush(2000)); // Add CORS headers to public metadata endpoints if (isPublicMetadataEndpoint(url.pathname)) { return addCorsHeaders(response); } return response; }, }; export default Sentry.withSentry( getSentryConfig, wrappedOAuthProvider, ) satisfies ExportedHandler<Env>;

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