UNPKG

mcp-server-kubernetes

Version:

MCP server for interacting with Kubernetes clusters via kubectl

156 lines (155 loc) 5.73 kB
import { NodeSDK, resources } from "@opentelemetry/sdk-node"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc"; import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions"; import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; import { serverConfig } from "./server-config.js"; /** * Parse OpenTelemetry sampling configuration from environment variables */ function parseSamplerConfig() { const samplerType = process.env.OTEL_TRACES_SAMPLER; const samplerArg = process.env.OTEL_TRACES_SAMPLER_ARG; if (!samplerType) { return undefined; } const config = { type: samplerType, }; if (samplerArg && (samplerType === "traceidratio" || samplerType.includes("traceidratio"))) { const arg = parseFloat(samplerArg); if (!isNaN(arg) && arg >= 0 && arg <= 1) { config.arg = arg; } } return config; } /** * Parse resource attributes from environment variable * Format: "key1=value1,key2=value2" */ function parseResourceAttributes() { const attrs = {}; const envAttrs = process.env.OTEL_RESOURCE_ATTRIBUTES; if (envAttrs) { const pairs = envAttrs.split(","); for (const pair of pairs) { const [key, value] = pair.split("=").map((s) => s.trim()); if (key && value) { attrs[key] = value; } } } return attrs; } /** * Get telemetry configuration from environment variables */ export function getTelemetryConfig() { // Check if telemetry is explicitly enabled (opt-in) const enableFlag = process.env.ENABLE_TELEMETRY; const isExplicitlyEnabled = enableFlag === "true" || enableFlag === "1"; const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT; // Telemetry is enabled only if: // 1. ENABLE_TELEMETRY=true is set, AND // 2. OTEL_EXPORTER_OTLP_ENDPOINT is configured const enabled = isExplicitlyEnabled && !!endpoint; // Check if response metadata capture is enabled (default: true) const captureResponseEnv = process.env.OTEL_CAPTURE_RESPONSE_METADATA; const captureResponseMetadata = captureResponseEnv !== "false" && captureResponseEnv !== "0"; return { enabled, endpoint, serviceName: process.env.OTEL_SERVICE_NAME || serverConfig.name, serviceVersion: process.env.OTEL_SERVICE_VERSION || serverConfig.version, resourceAttributes: parseResourceAttributes(), sampler: parseSamplerConfig(), captureResponseMetadata, // Enabled by default, can be disabled with OTEL_CAPTURE_RESPONSE_METADATA=false }; } /** * Initialize OpenTelemetry SDK with configuration * Call this before starting the MCP server */ export function initializeTelemetry() { const config = getTelemetryConfig(); if (!config.enabled) { const enableFlag = process.env.ENABLE_TELEMETRY; const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT; if (!enableFlag || enableFlag === "false" || enableFlag === "0") { // Observability is disabled by default return null; } else if (!endpoint) { console.error("OpenTelemetry: ENABLE_TELEMETRY=true but OTEL_EXPORTER_OTLP_ENDPOINT not set"); return null; } return null; } console.error(`Initializing OpenTelemetry: endpoint=${config.endpoint}, service=${config.serviceName}`); // Create OTLP trace exporter const traceExporter = new OTLPTraceExporter({ url: config.endpoint, }); // Create resource with service metadata const defaultRes = resources.defaultResource(); const customRes = resources.resourceFromAttributes({ [SEMRESATTRS_SERVICE_NAME]: config.serviceName, [SEMRESATTRS_SERVICE_VERSION]: config.serviceVersion, ...config.resourceAttributes, }); const resource = defaultRes.merge(customRes); // Initialize Node SDK const sdk = new NodeSDK({ resource, traceExporter, instrumentations: [ getNodeAutoInstrumentations({ // Disable some instrumentations that may be too verbose "@opentelemetry/instrumentation-fs": { enabled: false, }, }), ], }); try { sdk.start(); console.error("OpenTelemetry SDK initialized successfully"); // Graceful shutdown on process termination process.on("SIGTERM", async () => { try { await sdk.shutdown(); console.error("OpenTelemetry SDK shut down successfully"); } catch (error) { console.error("Error shutting down OpenTelemetry SDK:", error); } }); return sdk; } catch (error) { console.error("Failed to initialize OpenTelemetry SDK:", error); return null; } } /** * Get telemetry configuration summary for logging */ export function getTelemetryConfigSummary() { const config = getTelemetryConfig(); if (!config.enabled) { return "Telemetry: Disabled"; } const parts = [ `Telemetry: Enabled`, `Endpoint: ${config.endpoint}`, `Service: ${config.serviceName}@${config.serviceVersion}`, ]; if (config.sampler) { parts.push(`Sampler: ${config.sampler.type}${config.sampler.arg !== undefined ? `(${config.sampler.arg})` : ""}`); } const attrCount = Object.keys(config.resourceAttributes).length; if (attrCount > 0) { parts.push(`Resource Attributes: ${attrCount}`); } return parts.join(", "); }