UNPKG

@debugg-ai/debugg-ai-mcp

Version:

Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.

138 lines (137 loc) 5.09 kB
/** * Centralized configuration management for DebuggAI MCP Server */ import { z } from 'zod'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { currentApiKey } from '../utils/requestContext.js'; function findPackageVersion() { const __dir = dirname(fileURLToPath(import.meta.url)); let dir = __dir; while (true) { try { const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8')); if (pkg.name === '@debugg-ai/debugg-ai-mcp') return pkg.version; } catch { /* keep walking */ } const parent = dirname(dir); if (parent === dir) return 'unknown'; dir = parent; } } const _version = findPackageVersion(); /** * Public PostHog project key (write-only). Embedded so every MCP install * sends telemetry by default — lets the team observe cache hit rates, * poll cadence, tunnel reliability, etc. across the whole install base * without requiring users to configure anything. * * Safe to embed: this is a `phc_*` project key (PostHog's client-side * convention), not a personal API key. It can only write events, not * read them. * * Override with POSTHOG_API_KEY (e.g. for a private fork pointing at a * different PostHog project). Disable with DEBUGGAI_TELEMETRY_DISABLED=1. */ const DEBUGGAI_DEFAULT_POSTHOG_KEY = 'phc_4h2Yov2P0Vc9UMqfKf3dYKSQ6THOs7N6LZR0VKYopZN'; function isTelemetryDisabled() { const v = (process.env.DEBUGGAI_TELEMETRY_DISABLED || '').toLowerCase(); return v === '1' || v === 'true' || v === 'yes' || v === 'on'; } function isDevMode() { const v = (process.env.DEBUGGAI_DEV_MODE || '').toLowerCase(); return v === '1' || v === 'true' || v === 'yes' || v === 'on'; } function resolvePosthogKey() { if (isTelemetryDisabled()) return undefined; return process.env.POSTHOG_API_KEY || DEBUGGAI_DEFAULT_POSTHOG_KEY; } const configSchema = z.object({ server: z.object({ name: z.string().default('DebuggAI MCP Server'), version: z.string(), }), devMode: z.boolean().default(false), api: z.object({ // key is validated at tool-call time (not at boot) so MCP clients can surface // a proper error message instead of seeing the subprocess die → "Failed to // reconnect". See bead cma + flow 25. key: z.string(), tokenType: z.enum(['token', 'bearer']).default('token'), baseUrl: z.string().url().default('https://api.debugg.ai'), }), defaults: z.object({}), logging: z.object({ level: z.enum(['error', 'warn', 'info', 'debug']).default('info'), format: z.enum(['json', 'simple']).default('simple'), }), telemetry: z.object({ posthogApiKey: z.string().optional(), posthogHost: z.string().optional(), }), }); export function loadConfig() { const rawConfig = { server: { name: 'DebuggAI MCP Server', version: _version, }, devMode: isDevMode(), api: { // Priority: DEBUGGAI_API_TOKEN → DEBUGGAI_JWT_TOKEN → DEBUGGAI_API_KEY key: process.env.DEBUGGAI_API_TOKEN || process.env.DEBUGGAI_JWT_TOKEN || process.env.DEBUGGAI_API_KEY || '', tokenType: process.env.DEBUGGAI_TOKEN_TYPE || 'token', baseUrl: process.env.DEBUGGAI_API_URL || (isDevMode() ? 'http://localhost:8012' : 'https://api.debugg.ai'), }, defaults: {}, logging: { level: process.env.LOG_LEVEL || 'info', format: process.env.LOG_FORMAT || 'simple', }, telemetry: { posthogApiKey: resolvePosthogKey(), posthogHost: process.env.POSTHOG_HOST || undefined, }, }; try { return configSchema.parse(rawConfig); } catch (error) { if (error instanceof z.ZodError) { const missingFields = error.errors .map(err => `${err.path.join('.')}: ${err.message}`) .join(', '); throw new Error(`Configuration validation failed: ${missingFields}`); } throw error; } } let _config; export const config = { get server() { return getConfig().server; }, get devMode() { return getConfig().devMode; }, // api.key is request-scoped under the HTTP transport: if a per-request token // is set (AsyncLocalStorage), it overrides the env key for that request only. // stdio / tests have no request store, so the env key is returned unchanged. get api() { const api = getConfig().api; const requestKey = currentApiKey(); return requestKey ? { ...api, key: requestKey } : api; }, get defaults() { return getConfig().defaults; }, get logging() { return getConfig().logging; }, get telemetry() { return getConfig().telemetry; }, }; function getConfig() { if (!_config) { _config = loadConfig(); } return _config; } export function _resetConfigForTest() { _config = undefined; }