Skip to main content
Glama

Sentry MCP

Official
by getsentry
config.ts5.19 kB
import { promises as fs } from "node:fs"; import { join } from "node:path"; import { homedir } from "node:os"; export interface OAuthClientConfig { clientId: string; mcpHost: string; registeredAt: string; accessToken?: string; tokenExpiresAt?: string; } export interface ClientConfigFile { oauthClients: Record<string, OAuthClientConfig>; } export class ConfigManager { private configDir: string; private configFile: string; constructor() { this.configDir = join(homedir(), ".config", "sentry-mcp"); this.configFile = join(this.configDir, "config.json"); } /** * Ensure config directory exists */ private async ensureConfigDir(): Promise<void> { try { await fs.mkdir(this.configDir, { recursive: true }); } catch (error) { // Directory might already exist, ignore EEXIST errors if ((error as any).code !== "EEXIST") { throw error; } } } /** * Load config file */ private async loadConfig(): Promise<ClientConfigFile> { try { const content = await fs.readFile(this.configFile, "utf-8"); return JSON.parse(content); } catch (error) { // Config file doesn't exist or is invalid, return empty config return { oauthClients: {} }; } } /** * Save config file */ private async saveConfig(config: ClientConfigFile): Promise<void> { await this.ensureConfigDir(); await fs.writeFile( this.configFile, JSON.stringify(config, null, 2), "utf-8", ); } /** * Get OAuth client ID for a specific MCP host */ async getOAuthClientId(mcpHost: string): Promise<string | null> { const config = await this.loadConfig(); const clientConfig = config.oauthClients[mcpHost]; return clientConfig?.clientId || null; } /** * Store OAuth client ID for a specific MCP host */ async setOAuthClientId(mcpHost: string, clientId: string): Promise<void> { const config = await this.loadConfig(); // Preserve existing access token if present const existing = config.oauthClients[mcpHost]; config.oauthClients[mcpHost] = { clientId, mcpHost, registeredAt: new Date().toISOString(), accessToken: existing?.accessToken, tokenExpiresAt: existing?.tokenExpiresAt, }; await this.saveConfig(config); } /** * Remove OAuth client configuration for a specific MCP host */ async removeOAuthClientId(mcpHost: string): Promise<void> { const config = await this.loadConfig(); delete config.oauthClients[mcpHost]; await this.saveConfig(config); } /** * Get cached access token for a specific MCP host */ async getAccessToken(mcpHost: string): Promise<string | null> { const config = await this.loadConfig(); const clientConfig = config.oauthClients[mcpHost]; if (!clientConfig?.accessToken) { return null; } // Check if token is expired if (clientConfig.tokenExpiresAt) { const expiresAt = new Date(clientConfig.tokenExpiresAt); const now = new Date(); // Add 5 minute buffer before expiration const bufferTime = 5 * 60 * 1000; if (now.getTime() + bufferTime >= expiresAt.getTime()) { // Token is expired or will expire soon await this.removeAccessToken(mcpHost); return null; } } return clientConfig.accessToken; } /** * Store access token for a specific MCP host */ async setAccessToken( mcpHost: string, accessToken: string, expiresIn?: number, ): Promise<void> { const config = await this.loadConfig(); const existing = config.oauthClients[mcpHost]; if (!existing) { throw new Error(`No OAuth client configuration found for ${mcpHost}`); } let tokenExpiresAt: string | undefined; if (expiresIn) { // expiresIn is in seconds, convert to milliseconds const expiresAtMs = Date.now() + expiresIn * 1000; tokenExpiresAt = new Date(expiresAtMs).toISOString(); } config.oauthClients[mcpHost] = { ...existing, accessToken, tokenExpiresAt, }; await this.saveConfig(config); } /** * Remove cached access token for a specific MCP host */ async removeAccessToken(mcpHost: string): Promise<void> { const config = await this.loadConfig(); const existing = config.oauthClients[mcpHost]; if (existing) { config.oauthClients[mcpHost] = { ...existing, accessToken: undefined, tokenExpiresAt: undefined, }; await this.saveConfig(config); } } /** * Clear all cached tokens (useful for logout) */ async clearAllTokens(): Promise<void> { const config = await this.loadConfig(); for (const [host, clientConfig] of Object.entries(config.oauthClients)) { config.oauthClients[host] = { ...clientConfig, accessToken: undefined, tokenExpiresAt: undefined, }; } await this.saveConfig(config); } /** * List all registered OAuth clients */ async listOAuthClients(): Promise<OAuthClientConfig[]> { const config = await this.loadConfig(); return Object.values(config.oauthClients); } }

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