@openconductor/mcp-sdk 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -11
- package/dist/config/index.d.mts +70 -0
- package/dist/config/index.d.ts +70 -0
- package/dist/config/index.js +80 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +74 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/demo/index.d.mts +27 -0
- package/dist/demo/index.d.ts +27 -0
- package/dist/demo/index.js +136 -0
- package/dist/demo/index.js.map +1 -0
- package/dist/demo/index.mjs +129 -0
- package/dist/demo/index.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +288 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +278 -1
- package/dist/index.mjs.map +1 -1
- package/dist/payment/index.d.mts +3 -1
- package/dist/payment/index.d.ts +3 -1
- package/dist/payment/index.js +205 -0
- package/dist/payment/index.js.map +1 -1
- package/dist/payment/index.mjs +205 -0
- package/dist/payment/index.mjs.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/telemetry/index.d.mts +34 -6
- package/dist/telemetry/index.d.ts +34 -6
- package/dist/telemetry/index.js +143 -0
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/index.mjs +143 -1
- package/dist/telemetry/index.mjs.map +1 -1
- package/package.json +12 -2
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors/codes.ts","../../src/errors/index.ts","../../src/logger/index.ts","../../src/telemetry/index.ts","../../src/server/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * JSON-RPC 2.0 Standard Error Codes\n * https://www.jsonrpc.org/specification#error_object\n */\nexport const ErrorCodes = {\n // JSON-RPC 2.0 Standard Errors\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // MCP-Specific Errors (-32000 to -32099 reserved for implementation)\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n RESOURCE_NOT_FOUND: -32003,\n AUTHENTICATION_ERROR: -32004,\n AUTHORIZATION_ERROR: -32005,\n RATE_LIMIT_ERROR: -32006,\n TIMEOUT_ERROR: -32007,\n VALIDATION_ERROR: -32008,\n DEPENDENCY_ERROR: -32009,\n CONFIGURATION_ERROR: -32010,\n PAYMENT_REQUIRED: -32011,\n INSUFFICIENT_CREDITS: -32012,\n SUBSCRIPTION_REQUIRED: -32013,\n} as const\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]\n","import { ErrorCodes, type ErrorCode } from './codes'\n\nexport { ErrorCodes, type ErrorCode } from './codes'\n\n/**\n * Base error class for MCP servers\n * Formats errors according to JSON-RPC 2.0 specification\n */\nexport class MCPError extends Error {\n public readonly code: ErrorCode\n public readonly data?: Record<string, unknown>\n\n constructor(\n code: ErrorCode,\n message: string,\n data?: Record<string, unknown>\n ) {\n super(message)\n this.name = 'MCPError'\n this.code = code\n this.data = data\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Returns JSON-RPC 2.0 formatted error object\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n ...(this.data && { data: this.data }),\n }\n }\n\n /**\n * Create error response for JSON-RPC\n */\n toResponse(id: string | number | null = null) {\n return {\n jsonrpc: '2.0' as const,\n id,\n error: this.toJSON(),\n }\n }\n}\n\n/**\n * Thrown when tool input validation fails\n */\nexport class ValidationError extends MCPError {\n constructor(field: string, reason: string, value?: unknown) {\n super(ErrorCodes.INVALID_PARAMS, `Validation failed for '${field}': ${reason}`, {\n field,\n reason,\n ...(value !== undefined && { value }),\n })\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a requested tool doesn't exist\n */\nexport class ToolNotFoundError extends MCPError {\n constructor(toolName: string) {\n super(ErrorCodes.TOOL_NOT_FOUND, `Tool '${toolName}' not found`, {\n tool: toolName,\n })\n this.name = 'ToolNotFoundError'\n }\n}\n\n/**\n * Thrown when tool execution fails\n */\nexport class ToolExecutionError extends MCPError {\n constructor(toolName: string, reason: string, cause?: Error) {\n super(ErrorCodes.TOOL_EXECUTION_ERROR, `Tool '${toolName}' failed: ${reason}`, {\n tool: toolName,\n reason,\n ...(cause && { cause: cause.message }),\n })\n this.name = 'ToolExecutionError'\n }\n}\n\n/**\n * Thrown when a requested resource doesn't exist\n */\nexport class ResourceNotFoundError extends MCPError {\n constructor(resourceUri: string) {\n super(ErrorCodes.RESOURCE_NOT_FOUND, `Resource '${resourceUri}' not found`, {\n uri: resourceUri,\n })\n this.name = 'ResourceNotFoundError'\n }\n}\n\n/**\n * Thrown when authentication fails\n */\nexport class AuthenticationError extends MCPError {\n constructor(reason: string = 'Authentication required') {\n super(ErrorCodes.AUTHENTICATION_ERROR, reason)\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Thrown when authorization fails (authenticated but not permitted)\n */\nexport class AuthorizationError extends MCPError {\n constructor(action: string, resource?: string) {\n const msg = resource\n ? `Not authorized to ${action} on '${resource}'`\n : `Not authorized to ${action}`\n super(ErrorCodes.AUTHORIZATION_ERROR, msg, {\n action,\n ...(resource && { resource }),\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Thrown when rate limits are exceeded\n */\nexport class RateLimitError extends MCPError {\n constructor(retryAfterMs?: number) {\n super(ErrorCodes.RATE_LIMIT_ERROR, 'Rate limit exceeded', {\n ...(retryAfterMs && { retryAfterMs }),\n })\n this.name = 'RateLimitError'\n }\n}\n\n/**\n * Thrown when an operation times out\n */\nexport class TimeoutError extends MCPError {\n constructor(operation: string, timeoutMs: number) {\n super(ErrorCodes.TIMEOUT_ERROR, `Operation '${operation}' timed out after ${timeoutMs}ms`, {\n operation,\n timeoutMs,\n })\n this.name = 'TimeoutError'\n }\n}\n\n/**\n * Thrown when a required dependency is unavailable\n */\nexport class DependencyError extends MCPError {\n constructor(dependency: string, reason: string) {\n super(ErrorCodes.DEPENDENCY_ERROR, `Dependency '${dependency}' unavailable: ${reason}`, {\n dependency,\n reason,\n })\n this.name = 'DependencyError'\n }\n}\n\n/**\n * Thrown when server configuration is invalid\n */\nexport class ConfigurationError extends MCPError {\n constructor(setting: string, reason: string) {\n super(ErrorCodes.CONFIGURATION_ERROR, `Invalid configuration '${setting}': ${reason}`, {\n setting,\n reason,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Thrown when payment is required to access a tool\n */\nexport class PaymentRequiredError extends MCPError {\n constructor(toolName: string, options?: { upgradeUrl?: string; priceId?: string }) {\n super(ErrorCodes.PAYMENT_REQUIRED, `Payment required to use '${toolName}'`, {\n tool: toolName,\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n ...(options?.priceId && { priceId: options.priceId }),\n })\n this.name = 'PaymentRequiredError'\n }\n}\n\n/**\n * Thrown when user doesn't have enough credits\n */\nexport class InsufficientCreditsError extends MCPError {\n constructor(required: number, available: number, options?: { purchaseUrl?: string }) {\n super(ErrorCodes.INSUFFICIENT_CREDITS, `Insufficient credits: need ${required}, have ${available}`, {\n required,\n available,\n ...(options?.purchaseUrl && { purchaseUrl: options.purchaseUrl }),\n })\n this.name = 'InsufficientCreditsError'\n }\n}\n\n/**\n * Thrown when a subscription tier is required\n */\nexport class SubscriptionRequiredError extends MCPError {\n constructor(requiredTier: string, currentTier?: string, options?: { upgradeUrl?: string }) {\n const msg = currentTier \n ? `Subscription '${requiredTier}' required (current: '${currentTier}')`\n : `Subscription '${requiredTier}' required`\n super(ErrorCodes.SUBSCRIPTION_REQUIRED, msg, {\n requiredTier,\n ...(currentTier && { currentTier }),\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n })\n this.name = 'SubscriptionRequiredError'\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogEntry {\n timestamp?: string\n level: LogLevel\n service: string\n message: string\n [key: string]: unknown\n}\n\nexport interface LoggerOptions {\n /** Minimum log level to output (default: 'info') */\n level?: LogLevel\n /** Custom output function (default: console methods) */\n output?: (entry: LogEntry) => void\n /** Include timestamps (default: true) */\n timestamps?: boolean\n /** Pretty print JSON (default: false, use true for local dev) */\n pretty?: boolean\n}\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Creates a structured logger for MCP servers\n */\nexport function createLogger(service: string, options: LoggerOptions = {}) {\n const {\n level: minLevel = 'info',\n timestamps = true,\n pretty = false,\n } = options\n\n const shouldLog = (level: LogLevel): boolean => {\n return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[minLevel]\n }\n\n const formatEntry = (entry: LogEntry): string => {\n return pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry)\n }\n\n const log = (level: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (!shouldLog(level)) return\n\n const entry: LogEntry = {\n ...(timestamps && { timestamp: new Date().toISOString() }),\n level,\n service,\n message,\n ...data,\n }\n\n if (options.output) {\n options.output(entry)\n } else {\n const formatted = formatEntry(entry)\n switch (level) {\n case 'debug':\n console.debug(formatted)\n break\n case 'info':\n console.info(formatted)\n break\n case 'warn':\n console.warn(formatted)\n break\n case 'error':\n console.error(formatted)\n break\n }\n }\n\n return entry\n }\n\n return {\n debug: (message: string, data?: Record<string, unknown>) => log('debug', message, data),\n info: (message: string, data?: Record<string, unknown>) => log('info', message, data),\n warn: (message: string, data?: Record<string, unknown>) => log('warn', message, data),\n error: (message: string, data?: Record<string, unknown>) => log('error', message, data),\n \n /**\n * Create a child logger with additional context\n */\n child: (context: Record<string, unknown>) => {\n return createLogger(service, {\n ...options,\n output: (entry) => {\n const merged = { ...entry, ...context }\n if (options.output) {\n options.output(merged)\n } else {\n const formatted = pretty ? JSON.stringify(merged, null, 2) : JSON.stringify(merged)\n console[entry.level](formatted)\n }\n },\n })\n },\n }\n}\n\nexport type Logger = ReturnType<typeof createLogger>\n","export interface TelemetryConfig {\n /** Your OpenConductor API key */\n apiKey: string\n /** Server name for identification */\n serverName: string\n /** Server version */\n serverVersion?: string\n /** Custom endpoint (default: OpenConductor production) */\n endpoint?: string\n /** Batch size before flushing (default: 10) */\n batchSize?: number\n /** Flush interval in ms (default: 30000) */\n flushInterval?: number\n /** Enable debug logging (default: false) */\n debug?: boolean\n}\n\nexport interface ToolMetric {\n tool: string\n duration: number\n success: boolean\n error?: string\n timestamp: string\n}\n\nexport interface TelemetryBatch {\n serverName: string\n serverVersion?: string\n metrics: ToolMetric[]\n meta: {\n sdkVersion: string\n nodeVersion: string\n platform: string\n }\n}\n\nconst SDK_VERSION = '1.0.0'\nconst DEFAULT_ENDPOINT = 'https://api.openconductor.ai/functions/v1/telemetry'\n\nlet globalTelemetry: Telemetry | null = null\n\n/**\n * Initialize telemetry for your MCP server\n * Call this once at startup with your OpenConductor API key\n */\nexport function initTelemetry(config: TelemetryConfig): Telemetry {\n globalTelemetry = new Telemetry(config)\n return globalTelemetry\n}\n\n/**\n * Get the global telemetry instance (if initialized)\n */\nexport function getTelemetry(): Telemetry | null {\n return globalTelemetry\n}\n\n\nexport class Telemetry {\n private config: Required<Omit<TelemetryConfig, 'serverVersion'>> & Pick<TelemetryConfig, 'serverVersion'>\n private buffer: ToolMetric[] = []\n private flushTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(config: TelemetryConfig) {\n this.config = {\n apiKey: config.apiKey,\n serverName: config.serverName,\n serverVersion: config.serverVersion,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n batchSize: config.batchSize ?? 10,\n flushInterval: config.flushInterval ?? 30000,\n debug: config.debug ?? false,\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => {\n this.flush().catch(this.handleError.bind(this))\n }, this.config.flushInterval)\n\n // Flush on process exit\n if (typeof process !== 'undefined') {\n process.on('beforeExit', () => this.flush())\n process.on('SIGINT', () => {\n this.flush().finally(() => process.exit(0))\n })\n process.on('SIGTERM', () => {\n this.flush().finally(() => process.exit(0))\n })\n }\n\n this.log('Telemetry initialized', { serverName: config.serverName })\n }\n\n /**\n * Track a tool invocation\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n this.log('Metric tracked', { ...metric })\n\n // Auto-flush if buffer is full\n if (this.buffer.length >= this.config.batchSize) {\n this.flush().catch(this.handleError.bind(this))\n }\n }\n\n /**\n * Flush buffered metrics to OpenConductor\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const metrics = [...this.buffer]\n this.buffer = []\n\n const batch: TelemetryBatch = {\n serverName: this.config.serverName,\n serverVersion: this.config.serverVersion,\n metrics,\n meta: {\n sdkVersion: SDK_VERSION,\n nodeVersion: typeof process !== 'undefined' ? process.version : 'unknown',\n platform: typeof process !== 'undefined' ? process.platform : 'unknown',\n },\n }\n\n this.log('Flushing metrics', { count: metrics.length })\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'X-OpenConductor-SDK': SDK_VERSION,\n },\n body: JSON.stringify(batch),\n })\n\n if (!response.ok) {\n throw new Error(`Telemetry flush failed: ${response.status} ${response.statusText}`)\n }\n\n this.log('Metrics flushed successfully', { count: metrics.length })\n } catch (error) {\n // Put metrics back in buffer on failure\n this.buffer.unshift(...metrics)\n throw error\n }\n }\n\n /**\n * Stop telemetry collection\n */\n shutdown(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n this.flush().catch(this.handleError.bind(this))\n this.log('Telemetry shutdown')\n }\n\n private log(message: string, data?: Record<string, unknown>): void {\n if (this.config.debug) {\n console.debug(JSON.stringify({\n timestamp: new Date().toISOString(),\n level: 'debug',\n service: 'openconductor-telemetry',\n message,\n ...data,\n }))\n }\n }\n\n private handleError(error: unknown): void {\n if (this.config.debug) {\n console.error('[OpenConductor Telemetry Error]', error)\n }\n }\n}\n","import { MCPError, ToolExecutionError, TimeoutError } from '../errors'\nimport { createLogger, type Logger } from '../logger'\nimport { getTelemetry } from '../telemetry'\n\nexport interface HealthCheckInfo {\n name: string\n version: string\n description?: string\n uptime?: () => number\n checks?: Record<string, () => Promise<boolean> | boolean>\n}\n\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy'\n name: string\n version: string\n description?: string\n uptime?: number\n timestamp: string\n checks?: Record<string, boolean>\n}\n\n/**\n * Creates a standard health check response for MCP servers\n */\nexport function createHealthCheck(info: HealthCheckInfo) {\n return async (): Promise<HealthCheckResponse> => {\n const checks: Record<string, boolean> = {}\n let allHealthy = true\n\n if (info.checks) {\n for (const [name, check] of Object.entries(info.checks)) {\n try {\n checks[name] = await check()\n if (!checks[name]) allHealthy = false\n } catch {\n checks[name] = false\n allHealthy = false\n }\n }\n }\n\n return {\n status: allHealthy ? 'healthy' : 'degraded',\n name: info.name,\n version: info.version,\n ...(info.description && { description: info.description }),\n ...(info.uptime && { uptime: info.uptime() }),\n timestamp: new Date().toISOString(),\n ...(Object.keys(checks).length > 0 && { checks }),\n }\n }\n}\n\n\nexport interface WrapToolOptions {\n /** Tool name for logging and telemetry */\n name: string\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number\n /** Custom logger instance */\n logger?: Logger\n /** Enable telemetry reporting (default: true if telemetry initialized) */\n telemetry?: boolean\n}\n\nexport interface ToolContext {\n /** Unique ID for this tool call */\n callId: string\n /** Tool name */\n name: string\n /** Start time of execution */\n startTime: number\n /** Logger scoped to this call */\n log: Logger\n}\n\n/**\n * Wraps a tool handler with automatic error handling, logging, timeouts, and telemetry\n */\nexport function wrapTool<TInput, TOutput>(\n handler: (input: TInput, ctx: ToolContext) => TOutput | Promise<TOutput>,\n options: WrapToolOptions\n): (input: TInput) => Promise<TOutput> {\n const {\n name,\n timeout = 30000,\n telemetry: enableTelemetry = true,\n } = options\n\n const baseLogger = options.logger ?? createLogger(name)\n\n return async (input: TInput): Promise<TOutput> => {\n const callId = generateCallId()\n const startTime = Date.now()\n const log = baseLogger.child({ callId })\n\n const ctx: ToolContext = { callId, name, startTime, log }\n\n log.info('Tool invoked', { tool: name, input: sanitizeInput(input) })\n\n try {\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new TimeoutError(name, timeout))\n }, timeout)\n })\n\n // Race handler against timeout\n const result = await Promise.race([\n Promise.resolve(handler(input, ctx)),\n timeoutPromise,\n ])\n\n const duration = Date.now() - startTime\n log.info('Tool completed', { tool: name, duration })\n\n // Report success to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, true)\n }\n\n return result\n } catch (error) {\n const duration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n log.error('Tool failed', { \n tool: name, \n duration, \n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n })\n\n // Report failure to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, false, errorMessage)\n }\n\n // Re-throw MCPErrors as-is, wrap others\n if (error instanceof MCPError) {\n throw error\n }\n\n throw new ToolExecutionError(\n name,\n errorMessage,\n error instanceof Error ? error : undefined\n )\n }\n }\n}\n\n/**\n * Generates a unique call ID\n */\nfunction generateCallId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * Sanitizes input for logging (removes sensitive fields)\n */\nfunction sanitizeInput(input: unknown): unknown {\n if (typeof input !== 'object' || input === null) return input\n\n const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth', 'credential']\n const sanitized: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {\n sanitized[key] = '[REDACTED]'\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors/codes.ts","../../src/errors/index.ts","../../src/logger/index.ts","../../src/telemetry/index.ts","../../src/server/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * JSON-RPC 2.0 Standard Error Codes\n * https://www.jsonrpc.org/specification#error_object\n */\nexport const ErrorCodes = {\n // JSON-RPC 2.0 Standard Errors\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // MCP-Specific Errors (-32000 to -32099 reserved for implementation)\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n RESOURCE_NOT_FOUND: -32003,\n AUTHENTICATION_ERROR: -32004,\n AUTHORIZATION_ERROR: -32005,\n RATE_LIMIT_ERROR: -32006,\n TIMEOUT_ERROR: -32007,\n VALIDATION_ERROR: -32008,\n DEPENDENCY_ERROR: -32009,\n CONFIGURATION_ERROR: -32010,\n PAYMENT_REQUIRED: -32011,\n INSUFFICIENT_CREDITS: -32012,\n SUBSCRIPTION_REQUIRED: -32013,\n} as const\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]\n","import { ErrorCodes, type ErrorCode } from './codes'\n\nexport { ErrorCodes, type ErrorCode } from './codes'\n\n/**\n * Base error class for MCP servers\n * Formats errors according to JSON-RPC 2.0 specification\n */\nexport class MCPError extends Error {\n public readonly code: ErrorCode\n public readonly data?: Record<string, unknown>\n\n constructor(\n code: ErrorCode,\n message: string,\n data?: Record<string, unknown>\n ) {\n super(message)\n this.name = 'MCPError'\n this.code = code\n this.data = data\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Returns JSON-RPC 2.0 formatted error object\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n ...(this.data && { data: this.data }),\n }\n }\n\n /**\n * Create error response for JSON-RPC\n */\n toResponse(id: string | number | null = null) {\n return {\n jsonrpc: '2.0' as const,\n id,\n error: this.toJSON(),\n }\n }\n}\n\n/**\n * Thrown when tool input validation fails\n */\nexport class ValidationError extends MCPError {\n constructor(field: string, reason: string, value?: unknown) {\n super(ErrorCodes.INVALID_PARAMS, `Validation failed for '${field}': ${reason}`, {\n field,\n reason,\n ...(value !== undefined && { value }),\n })\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a requested tool doesn't exist\n */\nexport class ToolNotFoundError extends MCPError {\n constructor(toolName: string) {\n super(ErrorCodes.TOOL_NOT_FOUND, `Tool '${toolName}' not found`, {\n tool: toolName,\n })\n this.name = 'ToolNotFoundError'\n }\n}\n\n/**\n * Thrown when tool execution fails\n */\nexport class ToolExecutionError extends MCPError {\n constructor(toolName: string, reason: string, cause?: Error) {\n super(ErrorCodes.TOOL_EXECUTION_ERROR, `Tool '${toolName}' failed: ${reason}`, {\n tool: toolName,\n reason,\n ...(cause && { cause: cause.message }),\n })\n this.name = 'ToolExecutionError'\n }\n}\n\n/**\n * Thrown when a requested resource doesn't exist\n */\nexport class ResourceNotFoundError extends MCPError {\n constructor(resourceUri: string) {\n super(ErrorCodes.RESOURCE_NOT_FOUND, `Resource '${resourceUri}' not found`, {\n uri: resourceUri,\n })\n this.name = 'ResourceNotFoundError'\n }\n}\n\n/**\n * Thrown when authentication fails\n */\nexport class AuthenticationError extends MCPError {\n constructor(reason: string = 'Authentication required') {\n super(ErrorCodes.AUTHENTICATION_ERROR, reason)\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Thrown when authorization fails (authenticated but not permitted)\n */\nexport class AuthorizationError extends MCPError {\n constructor(action: string, resource?: string) {\n const msg = resource\n ? `Not authorized to ${action} on '${resource}'`\n : `Not authorized to ${action}`\n super(ErrorCodes.AUTHORIZATION_ERROR, msg, {\n action,\n ...(resource && { resource }),\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Thrown when rate limits are exceeded\n */\nexport class RateLimitError extends MCPError {\n constructor(retryAfterMs?: number) {\n super(ErrorCodes.RATE_LIMIT_ERROR, 'Rate limit exceeded', {\n ...(retryAfterMs && { retryAfterMs }),\n })\n this.name = 'RateLimitError'\n }\n}\n\n/**\n * Thrown when an operation times out\n */\nexport class TimeoutError extends MCPError {\n constructor(operation: string, timeoutMs: number) {\n super(ErrorCodes.TIMEOUT_ERROR, `Operation '${operation}' timed out after ${timeoutMs}ms`, {\n operation,\n timeoutMs,\n })\n this.name = 'TimeoutError'\n }\n}\n\n/**\n * Thrown when a required dependency is unavailable\n */\nexport class DependencyError extends MCPError {\n constructor(dependency: string, reason: string) {\n super(ErrorCodes.DEPENDENCY_ERROR, `Dependency '${dependency}' unavailable: ${reason}`, {\n dependency,\n reason,\n })\n this.name = 'DependencyError'\n }\n}\n\n/**\n * Thrown when server configuration is invalid\n */\nexport class ConfigurationError extends MCPError {\n constructor(setting: string, reason: string) {\n super(ErrorCodes.CONFIGURATION_ERROR, `Invalid configuration '${setting}': ${reason}`, {\n setting,\n reason,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Thrown when payment is required to access a tool\n */\nexport class PaymentRequiredError extends MCPError {\n constructor(toolName: string, options?: { upgradeUrl?: string; priceId?: string }) {\n super(ErrorCodes.PAYMENT_REQUIRED, `Payment required to use '${toolName}'`, {\n tool: toolName,\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n ...(options?.priceId && { priceId: options.priceId }),\n })\n this.name = 'PaymentRequiredError'\n }\n}\n\n/**\n * Thrown when user doesn't have enough credits\n */\nexport class InsufficientCreditsError extends MCPError {\n constructor(required: number, available: number, options?: { purchaseUrl?: string }) {\n super(ErrorCodes.INSUFFICIENT_CREDITS, `Insufficient credits: need ${required}, have ${available}`, {\n required,\n available,\n ...(options?.purchaseUrl && { purchaseUrl: options.purchaseUrl }),\n })\n this.name = 'InsufficientCreditsError'\n }\n}\n\n/**\n * Thrown when a subscription tier is required\n */\nexport class SubscriptionRequiredError extends MCPError {\n constructor(requiredTier: string, currentTier?: string, options?: { upgradeUrl?: string }) {\n const msg = currentTier \n ? `Subscription '${requiredTier}' required (current: '${currentTier}')`\n : `Subscription '${requiredTier}' required`\n super(ErrorCodes.SUBSCRIPTION_REQUIRED, msg, {\n requiredTier,\n ...(currentTier && { currentTier }),\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n })\n this.name = 'SubscriptionRequiredError'\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogEntry {\n timestamp?: string\n level: LogLevel\n service: string\n message: string\n [key: string]: unknown\n}\n\nexport interface LoggerOptions {\n /** Minimum log level to output (default: 'info') */\n level?: LogLevel\n /** Custom output function (default: console methods) */\n output?: (entry: LogEntry) => void\n /** Include timestamps (default: true) */\n timestamps?: boolean\n /** Pretty print JSON (default: false, use true for local dev) */\n pretty?: boolean\n}\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Creates a structured logger for MCP servers\n */\nexport function createLogger(service: string, options: LoggerOptions = {}) {\n const {\n level: minLevel = 'info',\n timestamps = true,\n pretty = false,\n } = options\n\n const shouldLog = (level: LogLevel): boolean => {\n return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[minLevel]\n }\n\n const formatEntry = (entry: LogEntry): string => {\n return pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry)\n }\n\n const log = (level: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (!shouldLog(level)) return\n\n const entry: LogEntry = {\n ...(timestamps && { timestamp: new Date().toISOString() }),\n level,\n service,\n message,\n ...data,\n }\n\n if (options.output) {\n options.output(entry)\n } else {\n const formatted = formatEntry(entry)\n switch (level) {\n case 'debug':\n console.debug(formatted)\n break\n case 'info':\n console.info(formatted)\n break\n case 'warn':\n console.warn(formatted)\n break\n case 'error':\n console.error(formatted)\n break\n }\n }\n\n return entry\n }\n\n return {\n debug: (message: string, data?: Record<string, unknown>) => log('debug', message, data),\n info: (message: string, data?: Record<string, unknown>) => log('info', message, data),\n warn: (message: string, data?: Record<string, unknown>) => log('warn', message, data),\n error: (message: string, data?: Record<string, unknown>) => log('error', message, data),\n \n /**\n * Create a child logger with additional context\n */\n child: (context: Record<string, unknown>) => {\n return createLogger(service, {\n ...options,\n output: (entry) => {\n const merged = { ...entry, ...context }\n if (options.output) {\n options.output(merged)\n } else {\n const formatted = pretty ? JSON.stringify(merged, null, 2) : JSON.stringify(merged)\n console[entry.level](formatted)\n }\n },\n })\n },\n }\n}\n\nexport type Logger = ReturnType<typeof createLogger>\n","import { getConfig, isDemoMode } from '../config'\nimport { demoLogger } from '../demo'\n\nexport interface TelemetryConfig {\n /** Your OpenConductor API key */\n apiKey: string\n /** Server name for identification */\n serverName: string\n /** Server version */\n serverVersion?: string\n /** Custom endpoint (default: OpenConductor production) */\n endpoint?: string\n /** Batch size before flushing (default: 10) */\n batchSize?: number\n /** Flush interval in ms (default: 30000) */\n flushInterval?: number\n /** Enable debug logging (default: false) */\n debug?: boolean\n}\n\nexport interface ToolMetric {\n tool: string\n duration: number\n success: boolean\n error?: string\n timestamp: string\n}\n\nexport interface TelemetryBatch {\n serverName: string\n serverVersion?: string\n metrics: ToolMetric[]\n meta: {\n sdkVersion: string\n nodeVersion: string\n platform: string\n }\n}\n\nconst SDK_VERSION = '1.0.0'\nconst DEFAULT_ENDPOINT = 'https://api.openconductor.ai/functions/v1/telemetry'\n\nlet globalTelemetry: Telemetry | DemoTelemetry | null = null\n\n/**\n * Initialize telemetry for your MCP server\n * \n * In demo mode, this is optional - telemetry will log to console instead of API.\n * Call this once at startup with your OpenConductor API key for production.\n */\nexport function initTelemetry(config?: TelemetryConfig): Telemetry | DemoTelemetry {\n const sdkConfig = getConfig()\n \n // In demo mode, create a demo telemetry instance\n if (isDemoMode()) {\n globalTelemetry = new DemoTelemetry(\n config?.serverName ?? sdkConfig.serverName,\n config?.serverVersion ?? sdkConfig.serverVersion\n )\n return globalTelemetry\n }\n \n // Production mode - require config\n if (!config?.apiKey) {\n throw new Error('Telemetry requires apiKey in production mode. Set OPENCONDUCTOR_API_KEY or pass apiKey to initTelemetry().')\n }\n \n globalTelemetry = new Telemetry(config)\n return globalTelemetry\n}\n\n/**\n * Get the global telemetry instance (if initialized)\n */\nexport function getTelemetry(): Telemetry | DemoTelemetry | null {\n return globalTelemetry\n}\n\n\nexport class Telemetry {\n private config: Required<Omit<TelemetryConfig, 'serverVersion'>> & Pick<TelemetryConfig, 'serverVersion'>\n protected buffer: ToolMetric[] = []\n private flushTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(config: TelemetryConfig) {\n this.config = {\n apiKey: config.apiKey,\n serverName: config.serverName,\n serverVersion: config.serverVersion,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n batchSize: config.batchSize ?? 10,\n flushInterval: config.flushInterval ?? 30000,\n debug: config.debug ?? false,\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => {\n this.flush().catch(this.handleError.bind(this))\n }, this.config.flushInterval)\n\n // Flush on process exit\n if (typeof process !== 'undefined') {\n process.on('beforeExit', () => this.flush())\n process.on('SIGINT', () => {\n this.flush().finally(() => process.exit(0))\n })\n process.on('SIGTERM', () => {\n this.flush().finally(() => process.exit(0))\n })\n }\n\n this.log('Telemetry initialized', { serverName: config.serverName })\n }\n\n /**\n * Track a tool invocation\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n this.log('Metric tracked', { ...metric })\n\n // Auto-flush if buffer is full\n if (this.buffer.length >= this.config.batchSize) {\n this.flush().catch(this.handleError.bind(this))\n }\n }\n\n /**\n * Flush buffered metrics to OpenConductor\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const metrics = [...this.buffer]\n this.buffer = []\n\n const batch: TelemetryBatch = {\n serverName: this.config.serverName,\n serverVersion: this.config.serverVersion,\n metrics,\n meta: {\n sdkVersion: SDK_VERSION,\n nodeVersion: typeof process !== 'undefined' ? process.version : 'unknown',\n platform: typeof process !== 'undefined' ? process.platform : 'unknown',\n },\n }\n\n this.log('Flushing metrics', { count: metrics.length })\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'X-OpenConductor-SDK': SDK_VERSION,\n },\n body: JSON.stringify(batch),\n })\n\n if (!response.ok) {\n throw new Error(`Telemetry flush failed: ${response.status} ${response.statusText}`)\n }\n\n this.log('Metrics flushed successfully', { count: metrics.length })\n } catch (error) {\n // Put metrics back in buffer on failure\n this.buffer.unshift(...metrics)\n throw error\n }\n }\n\n /**\n * Stop telemetry collection\n */\n shutdown(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n this.flush().catch(this.handleError.bind(this))\n this.log('Telemetry shutdown')\n }\n\n protected log(message: string, data?: Record<string, unknown>): void {\n if (this.config.debug) {\n console.debug(JSON.stringify({\n timestamp: new Date().toISOString(),\n level: 'debug',\n service: 'openconductor-telemetry',\n message,\n ...data,\n }))\n }\n }\n\n private handleError(error: unknown): void {\n if (this.config.debug) {\n console.error('[OpenConductor Telemetry Error]', error)\n }\n }\n}\n\n\n// ============================================================================\n// Demo Telemetry\n// ============================================================================\n\nconst DEMO_PREFIX = '[🎮 DEMO]'\n\n/**\n * Demo telemetry that logs to console instead of sending to API\n * Automatically used in demo mode\n */\nexport class DemoTelemetry {\n private serverName: string\n private serverVersion: string\n private buffer: ToolMetric[] = []\n\n constructor(serverName: string, serverVersion?: string) {\n this.serverName = serverName\n this.serverVersion = serverVersion ?? '0.0.0'\n console.log(`${DEMO_PREFIX} Telemetry initialized (console mode) for ${serverName}`)\n }\n\n /**\n * Track a tool invocation - logs to console in demo mode\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n\n // Log to console in demo mode\n demoLogger.telemetry('track', {\n tool,\n duration: `${duration}ms`,\n success,\n ...(error && { error }),\n })\n }\n\n /**\n * Flush - in demo mode, just logs a summary\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const count = this.buffer.length\n\n // Log summary instead of sending\n demoLogger.telemetry('flush', {\n message: `Would send ${count} metrics to OpenConductor`,\n metrics: this.buffer.slice(0, 3),\n ...(count > 3 && { more: `...and ${count - 3} more` }),\n })\n\n this.buffer = []\n }\n\n /**\n * Shutdown - no-op in demo mode\n */\n shutdown(): void {\n console.log(`${DEMO_PREFIX} Telemetry shutdown`)\n }\n\n /**\n * Get buffered metrics (useful for testing/inspection)\n */\n getBuffer(): ToolMetric[] {\n return [...this.buffer]\n }\n}\n","import { MCPError, ToolExecutionError, TimeoutError } from '../errors'\nimport { createLogger, type Logger } from '../logger'\nimport { getTelemetry } from '../telemetry'\n\nexport interface HealthCheckInfo {\n name: string\n version: string\n description?: string\n uptime?: () => number\n checks?: Record<string, () => Promise<boolean> | boolean>\n}\n\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy'\n name: string\n version: string\n description?: string\n uptime?: number\n timestamp: string\n checks?: Record<string, boolean>\n}\n\n/**\n * Creates a standard health check response for MCP servers\n */\nexport function createHealthCheck(info: HealthCheckInfo) {\n return async (): Promise<HealthCheckResponse> => {\n const checks: Record<string, boolean> = {}\n let allHealthy = true\n\n if (info.checks) {\n for (const [name, check] of Object.entries(info.checks)) {\n try {\n checks[name] = await check()\n if (!checks[name]) allHealthy = false\n } catch {\n checks[name] = false\n allHealthy = false\n }\n }\n }\n\n return {\n status: allHealthy ? 'healthy' : 'degraded',\n name: info.name,\n version: info.version,\n ...(info.description && { description: info.description }),\n ...(info.uptime && { uptime: info.uptime() }),\n timestamp: new Date().toISOString(),\n ...(Object.keys(checks).length > 0 && { checks }),\n }\n }\n}\n\n\nexport interface WrapToolOptions {\n /** Tool name for logging and telemetry */\n name: string\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number\n /** Custom logger instance */\n logger?: Logger\n /** Enable telemetry reporting (default: true if telemetry initialized) */\n telemetry?: boolean\n}\n\nexport interface ToolContext {\n /** Unique ID for this tool call */\n callId: string\n /** Tool name */\n name: string\n /** Start time of execution */\n startTime: number\n /** Logger scoped to this call */\n log: Logger\n}\n\n/**\n * Wraps a tool handler with automatic error handling, logging, timeouts, and telemetry\n */\nexport function wrapTool<TInput, TOutput>(\n handler: (input: TInput, ctx: ToolContext) => TOutput | Promise<TOutput>,\n options: WrapToolOptions\n): (input: TInput) => Promise<TOutput> {\n const {\n name,\n timeout = 30000,\n telemetry: enableTelemetry = true,\n } = options\n\n const baseLogger = options.logger ?? createLogger(name)\n\n return async (input: TInput): Promise<TOutput> => {\n const callId = generateCallId()\n const startTime = Date.now()\n const log = baseLogger.child({ callId })\n\n const ctx: ToolContext = { callId, name, startTime, log }\n\n log.info('Tool invoked', { tool: name, input: sanitizeInput(input) })\n\n try {\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new TimeoutError(name, timeout))\n }, timeout)\n })\n\n // Race handler against timeout\n const result = await Promise.race([\n Promise.resolve(handler(input, ctx)),\n timeoutPromise,\n ])\n\n const duration = Date.now() - startTime\n log.info('Tool completed', { tool: name, duration })\n\n // Report success to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, true)\n }\n\n return result\n } catch (error) {\n const duration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n log.error('Tool failed', { \n tool: name, \n duration, \n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n })\n\n // Report failure to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, false, errorMessage)\n }\n\n // Re-throw MCPErrors as-is, wrap others\n if (error instanceof MCPError) {\n throw error\n }\n\n throw new ToolExecutionError(\n name,\n errorMessage,\n error instanceof Error ? error : undefined\n )\n }\n }\n}\n\n/**\n * Generates a unique call ID\n */\nfunction generateCallId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * Sanitizes input for logging (removes sensitive fields)\n */\nfunction sanitizeInput(input: unknown): unknown {\n if (typeof input !== 'object' || input === null) return input\n\n const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth', 'credential']\n const sanitized: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {\n sanitized[key] = '[REDACTED]'\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors/codes.ts","../../src/errors/index.ts","../../src/logger/index.ts","../../src/telemetry/index.ts","../../src/server/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * JSON-RPC 2.0 Standard Error Codes\n * https://www.jsonrpc.org/specification#error_object\n */\nexport const ErrorCodes = {\n // JSON-RPC 2.0 Standard Errors\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // MCP-Specific Errors (-32000 to -32099 reserved for implementation)\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n RESOURCE_NOT_FOUND: -32003,\n AUTHENTICATION_ERROR: -32004,\n AUTHORIZATION_ERROR: -32005,\n RATE_LIMIT_ERROR: -32006,\n TIMEOUT_ERROR: -32007,\n VALIDATION_ERROR: -32008,\n DEPENDENCY_ERROR: -32009,\n CONFIGURATION_ERROR: -32010,\n PAYMENT_REQUIRED: -32011,\n INSUFFICIENT_CREDITS: -32012,\n SUBSCRIPTION_REQUIRED: -32013,\n} as const\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]\n","import { ErrorCodes, type ErrorCode } from './codes'\n\nexport { ErrorCodes, type ErrorCode } from './codes'\n\n/**\n * Base error class for MCP servers\n * Formats errors according to JSON-RPC 2.0 specification\n */\nexport class MCPError extends Error {\n public readonly code: ErrorCode\n public readonly data?: Record<string, unknown>\n\n constructor(\n code: ErrorCode,\n message: string,\n data?: Record<string, unknown>\n ) {\n super(message)\n this.name = 'MCPError'\n this.code = code\n this.data = data\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Returns JSON-RPC 2.0 formatted error object\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n ...(this.data && { data: this.data }),\n }\n }\n\n /**\n * Create error response for JSON-RPC\n */\n toResponse(id: string | number | null = null) {\n return {\n jsonrpc: '2.0' as const,\n id,\n error: this.toJSON(),\n }\n }\n}\n\n/**\n * Thrown when tool input validation fails\n */\nexport class ValidationError extends MCPError {\n constructor(field: string, reason: string, value?: unknown) {\n super(ErrorCodes.INVALID_PARAMS, `Validation failed for '${field}': ${reason}`, {\n field,\n reason,\n ...(value !== undefined && { value }),\n })\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a requested tool doesn't exist\n */\nexport class ToolNotFoundError extends MCPError {\n constructor(toolName: string) {\n super(ErrorCodes.TOOL_NOT_FOUND, `Tool '${toolName}' not found`, {\n tool: toolName,\n })\n this.name = 'ToolNotFoundError'\n }\n}\n\n/**\n * Thrown when tool execution fails\n */\nexport class ToolExecutionError extends MCPError {\n constructor(toolName: string, reason: string, cause?: Error) {\n super(ErrorCodes.TOOL_EXECUTION_ERROR, `Tool '${toolName}' failed: ${reason}`, {\n tool: toolName,\n reason,\n ...(cause && { cause: cause.message }),\n })\n this.name = 'ToolExecutionError'\n }\n}\n\n/**\n * Thrown when a requested resource doesn't exist\n */\nexport class ResourceNotFoundError extends MCPError {\n constructor(resourceUri: string) {\n super(ErrorCodes.RESOURCE_NOT_FOUND, `Resource '${resourceUri}' not found`, {\n uri: resourceUri,\n })\n this.name = 'ResourceNotFoundError'\n }\n}\n\n/**\n * Thrown when authentication fails\n */\nexport class AuthenticationError extends MCPError {\n constructor(reason: string = 'Authentication required') {\n super(ErrorCodes.AUTHENTICATION_ERROR, reason)\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Thrown when authorization fails (authenticated but not permitted)\n */\nexport class AuthorizationError extends MCPError {\n constructor(action: string, resource?: string) {\n const msg = resource\n ? `Not authorized to ${action} on '${resource}'`\n : `Not authorized to ${action}`\n super(ErrorCodes.AUTHORIZATION_ERROR, msg, {\n action,\n ...(resource && { resource }),\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Thrown when rate limits are exceeded\n */\nexport class RateLimitError extends MCPError {\n constructor(retryAfterMs?: number) {\n super(ErrorCodes.RATE_LIMIT_ERROR, 'Rate limit exceeded', {\n ...(retryAfterMs && { retryAfterMs }),\n })\n this.name = 'RateLimitError'\n }\n}\n\n/**\n * Thrown when an operation times out\n */\nexport class TimeoutError extends MCPError {\n constructor(operation: string, timeoutMs: number) {\n super(ErrorCodes.TIMEOUT_ERROR, `Operation '${operation}' timed out after ${timeoutMs}ms`, {\n operation,\n timeoutMs,\n })\n this.name = 'TimeoutError'\n }\n}\n\n/**\n * Thrown when a required dependency is unavailable\n */\nexport class DependencyError extends MCPError {\n constructor(dependency: string, reason: string) {\n super(ErrorCodes.DEPENDENCY_ERROR, `Dependency '${dependency}' unavailable: ${reason}`, {\n dependency,\n reason,\n })\n this.name = 'DependencyError'\n }\n}\n\n/**\n * Thrown when server configuration is invalid\n */\nexport class ConfigurationError extends MCPError {\n constructor(setting: string, reason: string) {\n super(ErrorCodes.CONFIGURATION_ERROR, `Invalid configuration '${setting}': ${reason}`, {\n setting,\n reason,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Thrown when payment is required to access a tool\n */\nexport class PaymentRequiredError extends MCPError {\n constructor(toolName: string, options?: { upgradeUrl?: string; priceId?: string }) {\n super(ErrorCodes.PAYMENT_REQUIRED, `Payment required to use '${toolName}'`, {\n tool: toolName,\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n ...(options?.priceId && { priceId: options.priceId }),\n })\n this.name = 'PaymentRequiredError'\n }\n}\n\n/**\n * Thrown when user doesn't have enough credits\n */\nexport class InsufficientCreditsError extends MCPError {\n constructor(required: number, available: number, options?: { purchaseUrl?: string }) {\n super(ErrorCodes.INSUFFICIENT_CREDITS, `Insufficient credits: need ${required}, have ${available}`, {\n required,\n available,\n ...(options?.purchaseUrl && { purchaseUrl: options.purchaseUrl }),\n })\n this.name = 'InsufficientCreditsError'\n }\n}\n\n/**\n * Thrown when a subscription tier is required\n */\nexport class SubscriptionRequiredError extends MCPError {\n constructor(requiredTier: string, currentTier?: string, options?: { upgradeUrl?: string }) {\n const msg = currentTier \n ? `Subscription '${requiredTier}' required (current: '${currentTier}')`\n : `Subscription '${requiredTier}' required`\n super(ErrorCodes.SUBSCRIPTION_REQUIRED, msg, {\n requiredTier,\n ...(currentTier && { currentTier }),\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n })\n this.name = 'SubscriptionRequiredError'\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogEntry {\n timestamp?: string\n level: LogLevel\n service: string\n message: string\n [key: string]: unknown\n}\n\nexport interface LoggerOptions {\n /** Minimum log level to output (default: 'info') */\n level?: LogLevel\n /** Custom output function (default: console methods) */\n output?: (entry: LogEntry) => void\n /** Include timestamps (default: true) */\n timestamps?: boolean\n /** Pretty print JSON (default: false, use true for local dev) */\n pretty?: boolean\n}\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Creates a structured logger for MCP servers\n */\nexport function createLogger(service: string, options: LoggerOptions = {}) {\n const {\n level: minLevel = 'info',\n timestamps = true,\n pretty = false,\n } = options\n\n const shouldLog = (level: LogLevel): boolean => {\n return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[minLevel]\n }\n\n const formatEntry = (entry: LogEntry): string => {\n return pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry)\n }\n\n const log = (level: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (!shouldLog(level)) return\n\n const entry: LogEntry = {\n ...(timestamps && { timestamp: new Date().toISOString() }),\n level,\n service,\n message,\n ...data,\n }\n\n if (options.output) {\n options.output(entry)\n } else {\n const formatted = formatEntry(entry)\n switch (level) {\n case 'debug':\n console.debug(formatted)\n break\n case 'info':\n console.info(formatted)\n break\n case 'warn':\n console.warn(formatted)\n break\n case 'error':\n console.error(formatted)\n break\n }\n }\n\n return entry\n }\n\n return {\n debug: (message: string, data?: Record<string, unknown>) => log('debug', message, data),\n info: (message: string, data?: Record<string, unknown>) => log('info', message, data),\n warn: (message: string, data?: Record<string, unknown>) => log('warn', message, data),\n error: (message: string, data?: Record<string, unknown>) => log('error', message, data),\n \n /**\n * Create a child logger with additional context\n */\n child: (context: Record<string, unknown>) => {\n return createLogger(service, {\n ...options,\n output: (entry) => {\n const merged = { ...entry, ...context }\n if (options.output) {\n options.output(merged)\n } else {\n const formatted = pretty ? JSON.stringify(merged, null, 2) : JSON.stringify(merged)\n console[entry.level](formatted)\n }\n },\n })\n },\n }\n}\n\nexport type Logger = ReturnType<typeof createLogger>\n","export interface TelemetryConfig {\n /** Your OpenConductor API key */\n apiKey: string\n /** Server name for identification */\n serverName: string\n /** Server version */\n serverVersion?: string\n /** Custom endpoint (default: OpenConductor production) */\n endpoint?: string\n /** Batch size before flushing (default: 10) */\n batchSize?: number\n /** Flush interval in ms (default: 30000) */\n flushInterval?: number\n /** Enable debug logging (default: false) */\n debug?: boolean\n}\n\nexport interface ToolMetric {\n tool: string\n duration: number\n success: boolean\n error?: string\n timestamp: string\n}\n\nexport interface TelemetryBatch {\n serverName: string\n serverVersion?: string\n metrics: ToolMetric[]\n meta: {\n sdkVersion: string\n nodeVersion: string\n platform: string\n }\n}\n\nconst SDK_VERSION = '1.0.0'\nconst DEFAULT_ENDPOINT = 'https://api.openconductor.ai/functions/v1/telemetry'\n\nlet globalTelemetry: Telemetry | null = null\n\n/**\n * Initialize telemetry for your MCP server\n * Call this once at startup with your OpenConductor API key\n */\nexport function initTelemetry(config: TelemetryConfig): Telemetry {\n globalTelemetry = new Telemetry(config)\n return globalTelemetry\n}\n\n/**\n * Get the global telemetry instance (if initialized)\n */\nexport function getTelemetry(): Telemetry | null {\n return globalTelemetry\n}\n\n\nexport class Telemetry {\n private config: Required<Omit<TelemetryConfig, 'serverVersion'>> & Pick<TelemetryConfig, 'serverVersion'>\n private buffer: ToolMetric[] = []\n private flushTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(config: TelemetryConfig) {\n this.config = {\n apiKey: config.apiKey,\n serverName: config.serverName,\n serverVersion: config.serverVersion,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n batchSize: config.batchSize ?? 10,\n flushInterval: config.flushInterval ?? 30000,\n debug: config.debug ?? false,\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => {\n this.flush().catch(this.handleError.bind(this))\n }, this.config.flushInterval)\n\n // Flush on process exit\n if (typeof process !== 'undefined') {\n process.on('beforeExit', () => this.flush())\n process.on('SIGINT', () => {\n this.flush().finally(() => process.exit(0))\n })\n process.on('SIGTERM', () => {\n this.flush().finally(() => process.exit(0))\n })\n }\n\n this.log('Telemetry initialized', { serverName: config.serverName })\n }\n\n /**\n * Track a tool invocation\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n this.log('Metric tracked', { ...metric })\n\n // Auto-flush if buffer is full\n if (this.buffer.length >= this.config.batchSize) {\n this.flush().catch(this.handleError.bind(this))\n }\n }\n\n /**\n * Flush buffered metrics to OpenConductor\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const metrics = [...this.buffer]\n this.buffer = []\n\n const batch: TelemetryBatch = {\n serverName: this.config.serverName,\n serverVersion: this.config.serverVersion,\n metrics,\n meta: {\n sdkVersion: SDK_VERSION,\n nodeVersion: typeof process !== 'undefined' ? process.version : 'unknown',\n platform: typeof process !== 'undefined' ? process.platform : 'unknown',\n },\n }\n\n this.log('Flushing metrics', { count: metrics.length })\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'X-OpenConductor-SDK': SDK_VERSION,\n },\n body: JSON.stringify(batch),\n })\n\n if (!response.ok) {\n throw new Error(`Telemetry flush failed: ${response.status} ${response.statusText}`)\n }\n\n this.log('Metrics flushed successfully', { count: metrics.length })\n } catch (error) {\n // Put metrics back in buffer on failure\n this.buffer.unshift(...metrics)\n throw error\n }\n }\n\n /**\n * Stop telemetry collection\n */\n shutdown(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n this.flush().catch(this.handleError.bind(this))\n this.log('Telemetry shutdown')\n }\n\n private log(message: string, data?: Record<string, unknown>): void {\n if (this.config.debug) {\n console.debug(JSON.stringify({\n timestamp: new Date().toISOString(),\n level: 'debug',\n service: 'openconductor-telemetry',\n message,\n ...data,\n }))\n }\n }\n\n private handleError(error: unknown): void {\n if (this.config.debug) {\n console.error('[OpenConductor Telemetry Error]', error)\n }\n }\n}\n","import { MCPError, ToolExecutionError, TimeoutError } from '../errors'\nimport { createLogger, type Logger } from '../logger'\nimport { getTelemetry } from '../telemetry'\n\nexport interface HealthCheckInfo {\n name: string\n version: string\n description?: string\n uptime?: () => number\n checks?: Record<string, () => Promise<boolean> | boolean>\n}\n\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy'\n name: string\n version: string\n description?: string\n uptime?: number\n timestamp: string\n checks?: Record<string, boolean>\n}\n\n/**\n * Creates a standard health check response for MCP servers\n */\nexport function createHealthCheck(info: HealthCheckInfo) {\n return async (): Promise<HealthCheckResponse> => {\n const checks: Record<string, boolean> = {}\n let allHealthy = true\n\n if (info.checks) {\n for (const [name, check] of Object.entries(info.checks)) {\n try {\n checks[name] = await check()\n if (!checks[name]) allHealthy = false\n } catch {\n checks[name] = false\n allHealthy = false\n }\n }\n }\n\n return {\n status: allHealthy ? 'healthy' : 'degraded',\n name: info.name,\n version: info.version,\n ...(info.description && { description: info.description }),\n ...(info.uptime && { uptime: info.uptime() }),\n timestamp: new Date().toISOString(),\n ...(Object.keys(checks).length > 0 && { checks }),\n }\n }\n}\n\n\nexport interface WrapToolOptions {\n /** Tool name for logging and telemetry */\n name: string\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number\n /** Custom logger instance */\n logger?: Logger\n /** Enable telemetry reporting (default: true if telemetry initialized) */\n telemetry?: boolean\n}\n\nexport interface ToolContext {\n /** Unique ID for this tool call */\n callId: string\n /** Tool name */\n name: string\n /** Start time of execution */\n startTime: number\n /** Logger scoped to this call */\n log: Logger\n}\n\n/**\n * Wraps a tool handler with automatic error handling, logging, timeouts, and telemetry\n */\nexport function wrapTool<TInput, TOutput>(\n handler: (input: TInput, ctx: ToolContext) => TOutput | Promise<TOutput>,\n options: WrapToolOptions\n): (input: TInput) => Promise<TOutput> {\n const {\n name,\n timeout = 30000,\n telemetry: enableTelemetry = true,\n } = options\n\n const baseLogger = options.logger ?? createLogger(name)\n\n return async (input: TInput): Promise<TOutput> => {\n const callId = generateCallId()\n const startTime = Date.now()\n const log = baseLogger.child({ callId })\n\n const ctx: ToolContext = { callId, name, startTime, log }\n\n log.info('Tool invoked', { tool: name, input: sanitizeInput(input) })\n\n try {\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new TimeoutError(name, timeout))\n }, timeout)\n })\n\n // Race handler against timeout\n const result = await Promise.race([\n Promise.resolve(handler(input, ctx)),\n timeoutPromise,\n ])\n\n const duration = Date.now() - startTime\n log.info('Tool completed', { tool: name, duration })\n\n // Report success to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, true)\n }\n\n return result\n } catch (error) {\n const duration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n log.error('Tool failed', { \n tool: name, \n duration, \n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n })\n\n // Report failure to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, false, errorMessage)\n }\n\n // Re-throw MCPErrors as-is, wrap others\n if (error instanceof MCPError) {\n throw error\n }\n\n throw new ToolExecutionError(\n name,\n errorMessage,\n error instanceof Error ? error : undefined\n )\n }\n }\n}\n\n/**\n * Generates a unique call ID\n */\nfunction generateCallId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * Sanitizes input for logging (removes sensitive fields)\n */\nfunction sanitizeInput(input: unknown): unknown {\n if (typeof input !== 'object' || input === null) return input\n\n const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth', 'credential']\n const sanitized: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {\n sanitized[key] = '[REDACTED]'\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors/codes.ts","../../src/errors/index.ts","../../src/logger/index.ts","../../src/telemetry/index.ts","../../src/server/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * JSON-RPC 2.0 Standard Error Codes\n * https://www.jsonrpc.org/specification#error_object\n */\nexport const ErrorCodes = {\n // JSON-RPC 2.0 Standard Errors\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // MCP-Specific Errors (-32000 to -32099 reserved for implementation)\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n RESOURCE_NOT_FOUND: -32003,\n AUTHENTICATION_ERROR: -32004,\n AUTHORIZATION_ERROR: -32005,\n RATE_LIMIT_ERROR: -32006,\n TIMEOUT_ERROR: -32007,\n VALIDATION_ERROR: -32008,\n DEPENDENCY_ERROR: -32009,\n CONFIGURATION_ERROR: -32010,\n PAYMENT_REQUIRED: -32011,\n INSUFFICIENT_CREDITS: -32012,\n SUBSCRIPTION_REQUIRED: -32013,\n} as const\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]\n","import { ErrorCodes, type ErrorCode } from './codes'\n\nexport { ErrorCodes, type ErrorCode } from './codes'\n\n/**\n * Base error class for MCP servers\n * Formats errors according to JSON-RPC 2.0 specification\n */\nexport class MCPError extends Error {\n public readonly code: ErrorCode\n public readonly data?: Record<string, unknown>\n\n constructor(\n code: ErrorCode,\n message: string,\n data?: Record<string, unknown>\n ) {\n super(message)\n this.name = 'MCPError'\n this.code = code\n this.data = data\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Returns JSON-RPC 2.0 formatted error object\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n ...(this.data && { data: this.data }),\n }\n }\n\n /**\n * Create error response for JSON-RPC\n */\n toResponse(id: string | number | null = null) {\n return {\n jsonrpc: '2.0' as const,\n id,\n error: this.toJSON(),\n }\n }\n}\n\n/**\n * Thrown when tool input validation fails\n */\nexport class ValidationError extends MCPError {\n constructor(field: string, reason: string, value?: unknown) {\n super(ErrorCodes.INVALID_PARAMS, `Validation failed for '${field}': ${reason}`, {\n field,\n reason,\n ...(value !== undefined && { value }),\n })\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a requested tool doesn't exist\n */\nexport class ToolNotFoundError extends MCPError {\n constructor(toolName: string) {\n super(ErrorCodes.TOOL_NOT_FOUND, `Tool '${toolName}' not found`, {\n tool: toolName,\n })\n this.name = 'ToolNotFoundError'\n }\n}\n\n/**\n * Thrown when tool execution fails\n */\nexport class ToolExecutionError extends MCPError {\n constructor(toolName: string, reason: string, cause?: Error) {\n super(ErrorCodes.TOOL_EXECUTION_ERROR, `Tool '${toolName}' failed: ${reason}`, {\n tool: toolName,\n reason,\n ...(cause && { cause: cause.message }),\n })\n this.name = 'ToolExecutionError'\n }\n}\n\n/**\n * Thrown when a requested resource doesn't exist\n */\nexport class ResourceNotFoundError extends MCPError {\n constructor(resourceUri: string) {\n super(ErrorCodes.RESOURCE_NOT_FOUND, `Resource '${resourceUri}' not found`, {\n uri: resourceUri,\n })\n this.name = 'ResourceNotFoundError'\n }\n}\n\n/**\n * Thrown when authentication fails\n */\nexport class AuthenticationError extends MCPError {\n constructor(reason: string = 'Authentication required') {\n super(ErrorCodes.AUTHENTICATION_ERROR, reason)\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Thrown when authorization fails (authenticated but not permitted)\n */\nexport class AuthorizationError extends MCPError {\n constructor(action: string, resource?: string) {\n const msg = resource\n ? `Not authorized to ${action} on '${resource}'`\n : `Not authorized to ${action}`\n super(ErrorCodes.AUTHORIZATION_ERROR, msg, {\n action,\n ...(resource && { resource }),\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Thrown when rate limits are exceeded\n */\nexport class RateLimitError extends MCPError {\n constructor(retryAfterMs?: number) {\n super(ErrorCodes.RATE_LIMIT_ERROR, 'Rate limit exceeded', {\n ...(retryAfterMs && { retryAfterMs }),\n })\n this.name = 'RateLimitError'\n }\n}\n\n/**\n * Thrown when an operation times out\n */\nexport class TimeoutError extends MCPError {\n constructor(operation: string, timeoutMs: number) {\n super(ErrorCodes.TIMEOUT_ERROR, `Operation '${operation}' timed out after ${timeoutMs}ms`, {\n operation,\n timeoutMs,\n })\n this.name = 'TimeoutError'\n }\n}\n\n/**\n * Thrown when a required dependency is unavailable\n */\nexport class DependencyError extends MCPError {\n constructor(dependency: string, reason: string) {\n super(ErrorCodes.DEPENDENCY_ERROR, `Dependency '${dependency}' unavailable: ${reason}`, {\n dependency,\n reason,\n })\n this.name = 'DependencyError'\n }\n}\n\n/**\n * Thrown when server configuration is invalid\n */\nexport class ConfigurationError extends MCPError {\n constructor(setting: string, reason: string) {\n super(ErrorCodes.CONFIGURATION_ERROR, `Invalid configuration '${setting}': ${reason}`, {\n setting,\n reason,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Thrown when payment is required to access a tool\n */\nexport class PaymentRequiredError extends MCPError {\n constructor(toolName: string, options?: { upgradeUrl?: string; priceId?: string }) {\n super(ErrorCodes.PAYMENT_REQUIRED, `Payment required to use '${toolName}'`, {\n tool: toolName,\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n ...(options?.priceId && { priceId: options.priceId }),\n })\n this.name = 'PaymentRequiredError'\n }\n}\n\n/**\n * Thrown when user doesn't have enough credits\n */\nexport class InsufficientCreditsError extends MCPError {\n constructor(required: number, available: number, options?: { purchaseUrl?: string }) {\n super(ErrorCodes.INSUFFICIENT_CREDITS, `Insufficient credits: need ${required}, have ${available}`, {\n required,\n available,\n ...(options?.purchaseUrl && { purchaseUrl: options.purchaseUrl }),\n })\n this.name = 'InsufficientCreditsError'\n }\n}\n\n/**\n * Thrown when a subscription tier is required\n */\nexport class SubscriptionRequiredError extends MCPError {\n constructor(requiredTier: string, currentTier?: string, options?: { upgradeUrl?: string }) {\n const msg = currentTier \n ? `Subscription '${requiredTier}' required (current: '${currentTier}')`\n : `Subscription '${requiredTier}' required`\n super(ErrorCodes.SUBSCRIPTION_REQUIRED, msg, {\n requiredTier,\n ...(currentTier && { currentTier }),\n ...(options?.upgradeUrl && { upgradeUrl: options.upgradeUrl }),\n })\n this.name = 'SubscriptionRequiredError'\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogEntry {\n timestamp?: string\n level: LogLevel\n service: string\n message: string\n [key: string]: unknown\n}\n\nexport interface LoggerOptions {\n /** Minimum log level to output (default: 'info') */\n level?: LogLevel\n /** Custom output function (default: console methods) */\n output?: (entry: LogEntry) => void\n /** Include timestamps (default: true) */\n timestamps?: boolean\n /** Pretty print JSON (default: false, use true for local dev) */\n pretty?: boolean\n}\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Creates a structured logger for MCP servers\n */\nexport function createLogger(service: string, options: LoggerOptions = {}) {\n const {\n level: minLevel = 'info',\n timestamps = true,\n pretty = false,\n } = options\n\n const shouldLog = (level: LogLevel): boolean => {\n return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[minLevel]\n }\n\n const formatEntry = (entry: LogEntry): string => {\n return pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry)\n }\n\n const log = (level: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (!shouldLog(level)) return\n\n const entry: LogEntry = {\n ...(timestamps && { timestamp: new Date().toISOString() }),\n level,\n service,\n message,\n ...data,\n }\n\n if (options.output) {\n options.output(entry)\n } else {\n const formatted = formatEntry(entry)\n switch (level) {\n case 'debug':\n console.debug(formatted)\n break\n case 'info':\n console.info(formatted)\n break\n case 'warn':\n console.warn(formatted)\n break\n case 'error':\n console.error(formatted)\n break\n }\n }\n\n return entry\n }\n\n return {\n debug: (message: string, data?: Record<string, unknown>) => log('debug', message, data),\n info: (message: string, data?: Record<string, unknown>) => log('info', message, data),\n warn: (message: string, data?: Record<string, unknown>) => log('warn', message, data),\n error: (message: string, data?: Record<string, unknown>) => log('error', message, data),\n \n /**\n * Create a child logger with additional context\n */\n child: (context: Record<string, unknown>) => {\n return createLogger(service, {\n ...options,\n output: (entry) => {\n const merged = { ...entry, ...context }\n if (options.output) {\n options.output(merged)\n } else {\n const formatted = pretty ? JSON.stringify(merged, null, 2) : JSON.stringify(merged)\n console[entry.level](formatted)\n }\n },\n })\n },\n }\n}\n\nexport type Logger = ReturnType<typeof createLogger>\n","import { getConfig, isDemoMode } from '../config'\nimport { demoLogger } from '../demo'\n\nexport interface TelemetryConfig {\n /** Your OpenConductor API key */\n apiKey: string\n /** Server name for identification */\n serverName: string\n /** Server version */\n serverVersion?: string\n /** Custom endpoint (default: OpenConductor production) */\n endpoint?: string\n /** Batch size before flushing (default: 10) */\n batchSize?: number\n /** Flush interval in ms (default: 30000) */\n flushInterval?: number\n /** Enable debug logging (default: false) */\n debug?: boolean\n}\n\nexport interface ToolMetric {\n tool: string\n duration: number\n success: boolean\n error?: string\n timestamp: string\n}\n\nexport interface TelemetryBatch {\n serverName: string\n serverVersion?: string\n metrics: ToolMetric[]\n meta: {\n sdkVersion: string\n nodeVersion: string\n platform: string\n }\n}\n\nconst SDK_VERSION = '1.0.0'\nconst DEFAULT_ENDPOINT = 'https://api.openconductor.ai/functions/v1/telemetry'\n\nlet globalTelemetry: Telemetry | DemoTelemetry | null = null\n\n/**\n * Initialize telemetry for your MCP server\n * \n * In demo mode, this is optional - telemetry will log to console instead of API.\n * Call this once at startup with your OpenConductor API key for production.\n */\nexport function initTelemetry(config?: TelemetryConfig): Telemetry | DemoTelemetry {\n const sdkConfig = getConfig()\n \n // In demo mode, create a demo telemetry instance\n if (isDemoMode()) {\n globalTelemetry = new DemoTelemetry(\n config?.serverName ?? sdkConfig.serverName,\n config?.serverVersion ?? sdkConfig.serverVersion\n )\n return globalTelemetry\n }\n \n // Production mode - require config\n if (!config?.apiKey) {\n throw new Error('Telemetry requires apiKey in production mode. Set OPENCONDUCTOR_API_KEY or pass apiKey to initTelemetry().')\n }\n \n globalTelemetry = new Telemetry(config)\n return globalTelemetry\n}\n\n/**\n * Get the global telemetry instance (if initialized)\n */\nexport function getTelemetry(): Telemetry | DemoTelemetry | null {\n return globalTelemetry\n}\n\n\nexport class Telemetry {\n private config: Required<Omit<TelemetryConfig, 'serverVersion'>> & Pick<TelemetryConfig, 'serverVersion'>\n protected buffer: ToolMetric[] = []\n private flushTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(config: TelemetryConfig) {\n this.config = {\n apiKey: config.apiKey,\n serverName: config.serverName,\n serverVersion: config.serverVersion,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n batchSize: config.batchSize ?? 10,\n flushInterval: config.flushInterval ?? 30000,\n debug: config.debug ?? false,\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => {\n this.flush().catch(this.handleError.bind(this))\n }, this.config.flushInterval)\n\n // Flush on process exit\n if (typeof process !== 'undefined') {\n process.on('beforeExit', () => this.flush())\n process.on('SIGINT', () => {\n this.flush().finally(() => process.exit(0))\n })\n process.on('SIGTERM', () => {\n this.flush().finally(() => process.exit(0))\n })\n }\n\n this.log('Telemetry initialized', { serverName: config.serverName })\n }\n\n /**\n * Track a tool invocation\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n this.log('Metric tracked', { ...metric })\n\n // Auto-flush if buffer is full\n if (this.buffer.length >= this.config.batchSize) {\n this.flush().catch(this.handleError.bind(this))\n }\n }\n\n /**\n * Flush buffered metrics to OpenConductor\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const metrics = [...this.buffer]\n this.buffer = []\n\n const batch: TelemetryBatch = {\n serverName: this.config.serverName,\n serverVersion: this.config.serverVersion,\n metrics,\n meta: {\n sdkVersion: SDK_VERSION,\n nodeVersion: typeof process !== 'undefined' ? process.version : 'unknown',\n platform: typeof process !== 'undefined' ? process.platform : 'unknown',\n },\n }\n\n this.log('Flushing metrics', { count: metrics.length })\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'X-OpenConductor-SDK': SDK_VERSION,\n },\n body: JSON.stringify(batch),\n })\n\n if (!response.ok) {\n throw new Error(`Telemetry flush failed: ${response.status} ${response.statusText}`)\n }\n\n this.log('Metrics flushed successfully', { count: metrics.length })\n } catch (error) {\n // Put metrics back in buffer on failure\n this.buffer.unshift(...metrics)\n throw error\n }\n }\n\n /**\n * Stop telemetry collection\n */\n shutdown(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n this.flush().catch(this.handleError.bind(this))\n this.log('Telemetry shutdown')\n }\n\n protected log(message: string, data?: Record<string, unknown>): void {\n if (this.config.debug) {\n console.debug(JSON.stringify({\n timestamp: new Date().toISOString(),\n level: 'debug',\n service: 'openconductor-telemetry',\n message,\n ...data,\n }))\n }\n }\n\n private handleError(error: unknown): void {\n if (this.config.debug) {\n console.error('[OpenConductor Telemetry Error]', error)\n }\n }\n}\n\n\n// ============================================================================\n// Demo Telemetry\n// ============================================================================\n\nconst DEMO_PREFIX = '[🎮 DEMO]'\n\n/**\n * Demo telemetry that logs to console instead of sending to API\n * Automatically used in demo mode\n */\nexport class DemoTelemetry {\n private serverName: string\n private serverVersion: string\n private buffer: ToolMetric[] = []\n\n constructor(serverName: string, serverVersion?: string) {\n this.serverName = serverName\n this.serverVersion = serverVersion ?? '0.0.0'\n console.log(`${DEMO_PREFIX} Telemetry initialized (console mode) for ${serverName}`)\n }\n\n /**\n * Track a tool invocation - logs to console in demo mode\n */\n trackToolCall(\n tool: string,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n const metric: ToolMetric = {\n tool,\n duration,\n success,\n ...(error && { error }),\n timestamp: new Date().toISOString(),\n }\n\n this.buffer.push(metric)\n\n // Log to console in demo mode\n demoLogger.telemetry('track', {\n tool,\n duration: `${duration}ms`,\n success,\n ...(error && { error }),\n })\n }\n\n /**\n * Flush - in demo mode, just logs a summary\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return\n\n const count = this.buffer.length\n\n // Log summary instead of sending\n demoLogger.telemetry('flush', {\n message: `Would send ${count} metrics to OpenConductor`,\n metrics: this.buffer.slice(0, 3),\n ...(count > 3 && { more: `...and ${count - 3} more` }),\n })\n\n this.buffer = []\n }\n\n /**\n * Shutdown - no-op in demo mode\n */\n shutdown(): void {\n console.log(`${DEMO_PREFIX} Telemetry shutdown`)\n }\n\n /**\n * Get buffered metrics (useful for testing/inspection)\n */\n getBuffer(): ToolMetric[] {\n return [...this.buffer]\n }\n}\n","import { MCPError, ToolExecutionError, TimeoutError } from '../errors'\nimport { createLogger, type Logger } from '../logger'\nimport { getTelemetry } from '../telemetry'\n\nexport interface HealthCheckInfo {\n name: string\n version: string\n description?: string\n uptime?: () => number\n checks?: Record<string, () => Promise<boolean> | boolean>\n}\n\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy'\n name: string\n version: string\n description?: string\n uptime?: number\n timestamp: string\n checks?: Record<string, boolean>\n}\n\n/**\n * Creates a standard health check response for MCP servers\n */\nexport function createHealthCheck(info: HealthCheckInfo) {\n return async (): Promise<HealthCheckResponse> => {\n const checks: Record<string, boolean> = {}\n let allHealthy = true\n\n if (info.checks) {\n for (const [name, check] of Object.entries(info.checks)) {\n try {\n checks[name] = await check()\n if (!checks[name]) allHealthy = false\n } catch {\n checks[name] = false\n allHealthy = false\n }\n }\n }\n\n return {\n status: allHealthy ? 'healthy' : 'degraded',\n name: info.name,\n version: info.version,\n ...(info.description && { description: info.description }),\n ...(info.uptime && { uptime: info.uptime() }),\n timestamp: new Date().toISOString(),\n ...(Object.keys(checks).length > 0 && { checks }),\n }\n }\n}\n\n\nexport interface WrapToolOptions {\n /** Tool name for logging and telemetry */\n name: string\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number\n /** Custom logger instance */\n logger?: Logger\n /** Enable telemetry reporting (default: true if telemetry initialized) */\n telemetry?: boolean\n}\n\nexport interface ToolContext {\n /** Unique ID for this tool call */\n callId: string\n /** Tool name */\n name: string\n /** Start time of execution */\n startTime: number\n /** Logger scoped to this call */\n log: Logger\n}\n\n/**\n * Wraps a tool handler with automatic error handling, logging, timeouts, and telemetry\n */\nexport function wrapTool<TInput, TOutput>(\n handler: (input: TInput, ctx: ToolContext) => TOutput | Promise<TOutput>,\n options: WrapToolOptions\n): (input: TInput) => Promise<TOutput> {\n const {\n name,\n timeout = 30000,\n telemetry: enableTelemetry = true,\n } = options\n\n const baseLogger = options.logger ?? createLogger(name)\n\n return async (input: TInput): Promise<TOutput> => {\n const callId = generateCallId()\n const startTime = Date.now()\n const log = baseLogger.child({ callId })\n\n const ctx: ToolContext = { callId, name, startTime, log }\n\n log.info('Tool invoked', { tool: name, input: sanitizeInput(input) })\n\n try {\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new TimeoutError(name, timeout))\n }, timeout)\n })\n\n // Race handler against timeout\n const result = await Promise.race([\n Promise.resolve(handler(input, ctx)),\n timeoutPromise,\n ])\n\n const duration = Date.now() - startTime\n log.info('Tool completed', { tool: name, duration })\n\n // Report success to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, true)\n }\n\n return result\n } catch (error) {\n const duration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n log.error('Tool failed', { \n tool: name, \n duration, \n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n })\n\n // Report failure to telemetry\n if (enableTelemetry) {\n const tel = getTelemetry()\n tel?.trackToolCall(name, duration, false, errorMessage)\n }\n\n // Re-throw MCPErrors as-is, wrap others\n if (error instanceof MCPError) {\n throw error\n }\n\n throw new ToolExecutionError(\n name,\n errorMessage,\n error instanceof Error ? error : undefined\n )\n }\n }\n}\n\n/**\n * Generates a unique call ID\n */\nfunction generateCallId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * Sanitizes input for logging (removes sensitive fields)\n */\nfunction sanitizeInput(input: unknown): unknown {\n if (typeof input !== 'object' || input === null) return input\n\n const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth', 'credential']\n const sanitized: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {\n sanitized[key] = '[REDACTED]'\n } else {\n sanitized[key] = value\n }\n }\n\n return sanitized\n}\n"]}
|
|
@@ -33,16 +33,18 @@ interface TelemetryBatch {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Initialize telemetry for your MCP server
|
|
36
|
-
*
|
|
36
|
+
*
|
|
37
|
+
* In demo mode, this is optional - telemetry will log to console instead of API.
|
|
38
|
+
* Call this once at startup with your OpenConductor API key for production.
|
|
37
39
|
*/
|
|
38
|
-
declare function initTelemetry(config
|
|
40
|
+
declare function initTelemetry(config?: TelemetryConfig): Telemetry | DemoTelemetry;
|
|
39
41
|
/**
|
|
40
42
|
* Get the global telemetry instance (if initialized)
|
|
41
43
|
*/
|
|
42
|
-
declare function getTelemetry(): Telemetry | null;
|
|
44
|
+
declare function getTelemetry(): Telemetry | DemoTelemetry | null;
|
|
43
45
|
declare class Telemetry {
|
|
44
46
|
private config;
|
|
45
|
-
|
|
47
|
+
protected buffer: ToolMetric[];
|
|
46
48
|
private flushTimer;
|
|
47
49
|
constructor(config: TelemetryConfig);
|
|
48
50
|
/**
|
|
@@ -57,8 +59,34 @@ declare class Telemetry {
|
|
|
57
59
|
* Stop telemetry collection
|
|
58
60
|
*/
|
|
59
61
|
shutdown(): void;
|
|
60
|
-
|
|
62
|
+
protected log(message: string, data?: Record<string, unknown>): void;
|
|
61
63
|
private handleError;
|
|
62
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Demo telemetry that logs to console instead of sending to API
|
|
67
|
+
* Automatically used in demo mode
|
|
68
|
+
*/
|
|
69
|
+
declare class DemoTelemetry {
|
|
70
|
+
private serverName;
|
|
71
|
+
private serverVersion;
|
|
72
|
+
private buffer;
|
|
73
|
+
constructor(serverName: string, serverVersion?: string);
|
|
74
|
+
/**
|
|
75
|
+
* Track a tool invocation - logs to console in demo mode
|
|
76
|
+
*/
|
|
77
|
+
trackToolCall(tool: string, duration: number, success: boolean, error?: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Flush - in demo mode, just logs a summary
|
|
80
|
+
*/
|
|
81
|
+
flush(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Shutdown - no-op in demo mode
|
|
84
|
+
*/
|
|
85
|
+
shutdown(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Get buffered metrics (useful for testing/inspection)
|
|
88
|
+
*/
|
|
89
|
+
getBuffer(): ToolMetric[];
|
|
90
|
+
}
|
|
63
91
|
|
|
64
|
-
export { Telemetry, type TelemetryBatch, type TelemetryConfig, type ToolMetric, getTelemetry, initTelemetry };
|
|
92
|
+
export { DemoTelemetry, Telemetry, type TelemetryBatch, type TelemetryConfig, type ToolMetric, getTelemetry, initTelemetry };
|
|
@@ -33,16 +33,18 @@ interface TelemetryBatch {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Initialize telemetry for your MCP server
|
|
36
|
-
*
|
|
36
|
+
*
|
|
37
|
+
* In demo mode, this is optional - telemetry will log to console instead of API.
|
|
38
|
+
* Call this once at startup with your OpenConductor API key for production.
|
|
37
39
|
*/
|
|
38
|
-
declare function initTelemetry(config
|
|
40
|
+
declare function initTelemetry(config?: TelemetryConfig): Telemetry | DemoTelemetry;
|
|
39
41
|
/**
|
|
40
42
|
* Get the global telemetry instance (if initialized)
|
|
41
43
|
*/
|
|
42
|
-
declare function getTelemetry(): Telemetry | null;
|
|
44
|
+
declare function getTelemetry(): Telemetry | DemoTelemetry | null;
|
|
43
45
|
declare class Telemetry {
|
|
44
46
|
private config;
|
|
45
|
-
|
|
47
|
+
protected buffer: ToolMetric[];
|
|
46
48
|
private flushTimer;
|
|
47
49
|
constructor(config: TelemetryConfig);
|
|
48
50
|
/**
|
|
@@ -57,8 +59,34 @@ declare class Telemetry {
|
|
|
57
59
|
* Stop telemetry collection
|
|
58
60
|
*/
|
|
59
61
|
shutdown(): void;
|
|
60
|
-
|
|
62
|
+
protected log(message: string, data?: Record<string, unknown>): void;
|
|
61
63
|
private handleError;
|
|
62
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Demo telemetry that logs to console instead of sending to API
|
|
67
|
+
* Automatically used in demo mode
|
|
68
|
+
*/
|
|
69
|
+
declare class DemoTelemetry {
|
|
70
|
+
private serverName;
|
|
71
|
+
private serverVersion;
|
|
72
|
+
private buffer;
|
|
73
|
+
constructor(serverName: string, serverVersion?: string);
|
|
74
|
+
/**
|
|
75
|
+
* Track a tool invocation - logs to console in demo mode
|
|
76
|
+
*/
|
|
77
|
+
trackToolCall(tool: string, duration: number, success: boolean, error?: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Flush - in demo mode, just logs a summary
|
|
80
|
+
*/
|
|
81
|
+
flush(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Shutdown - no-op in demo mode
|
|
84
|
+
*/
|
|
85
|
+
shutdown(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Get buffered metrics (useful for testing/inspection)
|
|
88
|
+
*/
|
|
89
|
+
getBuffer(): ToolMetric[];
|
|
90
|
+
}
|
|
63
91
|
|
|
64
|
-
export { Telemetry, type TelemetryBatch, type TelemetryConfig, type ToolMetric, getTelemetry, initTelemetry };
|
|
92
|
+
export { DemoTelemetry, Telemetry, type TelemetryBatch, type TelemetryConfig, type ToolMetric, getTelemetry, initTelemetry };
|
package/dist/telemetry/index.js
CHANGED
|
@@ -1,10 +1,97 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/config/index.ts
|
|
4
|
+
var globalConfig = {
|
|
5
|
+
apiKey: null,
|
|
6
|
+
demoMode: false,
|
|
7
|
+
serverName: "mcp-server",
|
|
8
|
+
serverVersion: "0.0.0",
|
|
9
|
+
initialized: false
|
|
10
|
+
};
|
|
11
|
+
var DEMO_BANNER = `
|
|
12
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
13
|
+
\u2502 \u2502
|
|
14
|
+
\u2502 \u{1F3AE} DEMO MODE ACTIVE \u2502
|
|
15
|
+
\u2502 \u2502
|
|
16
|
+
\u2502 The SDK is running without an API key. \u2502
|
|
17
|
+
\u2502 \u2022 Payment: Mock billing (always allowed, 9999 credits) \u2502
|
|
18
|
+
\u2502 \u2022 Telemetry: Logging to console only \u2502
|
|
19
|
+
\u2502 \u2022 All features work - no data sent to OpenConductor \u2502
|
|
20
|
+
\u2502 \u2502
|
|
21
|
+
\u2502 To enable production mode: \u2502
|
|
22
|
+
\u2502 1. Get a free API key at https://openconductor.ai \u2502
|
|
23
|
+
\u2502 2. Set OPENCONDUCTOR_API_KEY environment variable \u2502
|
|
24
|
+
\u2502 or pass apiKey to initOpenConductor() \u2502
|
|
25
|
+
\u2502 \u2502
|
|
26
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
27
|
+
`;
|
|
28
|
+
var PRODUCTION_BANNER = `
|
|
29
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
30
|
+
\u2502 \u2705 OpenConductor SDK initialized (production mode) \u2502
|
|
31
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
32
|
+
`;
|
|
33
|
+
function initOpenConductor(config = {}) {
|
|
34
|
+
const apiKey = config.apiKey ?? process.env.OPENCONDUCTOR_API_KEY ?? null;
|
|
35
|
+
const demoMode = config.demoMode === true || !apiKey;
|
|
36
|
+
globalConfig = {
|
|
37
|
+
apiKey: demoMode ? null : apiKey,
|
|
38
|
+
demoMode,
|
|
39
|
+
serverName: config.serverName ?? process.env.OPENCONDUCTOR_SERVER_NAME ?? "mcp-server",
|
|
40
|
+
serverVersion: config.serverVersion ?? process.env.npm_package_version ?? "0.0.0",
|
|
41
|
+
initialized: true
|
|
42
|
+
};
|
|
43
|
+
if (!config.quiet) {
|
|
44
|
+
if (demoMode) {
|
|
45
|
+
console.log(DEMO_BANNER);
|
|
46
|
+
} else {
|
|
47
|
+
console.log(PRODUCTION_BANNER);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return globalConfig;
|
|
51
|
+
}
|
|
52
|
+
function getConfig() {
|
|
53
|
+
if (!globalConfig.initialized) {
|
|
54
|
+
return initOpenConductor({ quiet: true });
|
|
55
|
+
}
|
|
56
|
+
return globalConfig;
|
|
57
|
+
}
|
|
58
|
+
function isDemoMode() {
|
|
59
|
+
return getConfig().demoMode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/demo/index.ts
|
|
63
|
+
var DEMO_PREFIX = "[\u{1F3AE} DEMO]";
|
|
64
|
+
var demoLogger = {
|
|
65
|
+
telemetry: (action, data) => {
|
|
66
|
+
console.log(`${DEMO_PREFIX} Telemetry ${action}:`, JSON.stringify(data, null, 2));
|
|
67
|
+
},
|
|
68
|
+
payment: (action, data) => {
|
|
69
|
+
console.log(`${DEMO_PREFIX} Payment ${action}:`, JSON.stringify(data, null, 2));
|
|
70
|
+
},
|
|
71
|
+
billing: (userId, result) => {
|
|
72
|
+
console.log(`${DEMO_PREFIX} Billing check for ${userId}: ALLOWED (demo mode)`);
|
|
73
|
+
},
|
|
74
|
+
deduct: (userId, credits, tool) => {
|
|
75
|
+
console.log(`${DEMO_PREFIX} Would deduct ${credits} credits from ${userId} for ${tool} (skipped in demo)`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
3
79
|
// src/telemetry/index.ts
|
|
4
80
|
var SDK_VERSION = "1.0.0";
|
|
5
81
|
var DEFAULT_ENDPOINT = "https://api.openconductor.ai/functions/v1/telemetry";
|
|
6
82
|
var globalTelemetry = null;
|
|
7
83
|
function initTelemetry(config) {
|
|
84
|
+
const sdkConfig = getConfig();
|
|
85
|
+
if (isDemoMode()) {
|
|
86
|
+
globalTelemetry = new DemoTelemetry(
|
|
87
|
+
config?.serverName ?? sdkConfig.serverName,
|
|
88
|
+
config?.serverVersion ?? sdkConfig.serverVersion
|
|
89
|
+
);
|
|
90
|
+
return globalTelemetry;
|
|
91
|
+
}
|
|
92
|
+
if (!config?.apiKey) {
|
|
93
|
+
throw new Error("Telemetry requires apiKey in production mode. Set OPENCONDUCTOR_API_KEY or pass apiKey to initTelemetry().");
|
|
94
|
+
}
|
|
8
95
|
globalTelemetry = new Telemetry(config);
|
|
9
96
|
return globalTelemetry;
|
|
10
97
|
}
|
|
@@ -121,7 +208,63 @@ var Telemetry = class {
|
|
|
121
208
|
}
|
|
122
209
|
}
|
|
123
210
|
};
|
|
211
|
+
var DEMO_PREFIX2 = "[\u{1F3AE} DEMO]";
|
|
212
|
+
var DemoTelemetry = class {
|
|
213
|
+
serverName;
|
|
214
|
+
serverVersion;
|
|
215
|
+
buffer = [];
|
|
216
|
+
constructor(serverName, serverVersion) {
|
|
217
|
+
this.serverName = serverName;
|
|
218
|
+
this.serverVersion = serverVersion ?? "0.0.0";
|
|
219
|
+
console.log(`${DEMO_PREFIX2} Telemetry initialized (console mode) for ${serverName}`);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Track a tool invocation - logs to console in demo mode
|
|
223
|
+
*/
|
|
224
|
+
trackToolCall(tool, duration, success, error) {
|
|
225
|
+
const metric = {
|
|
226
|
+
tool,
|
|
227
|
+
duration,
|
|
228
|
+
success,
|
|
229
|
+
...error && { error },
|
|
230
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
231
|
+
};
|
|
232
|
+
this.buffer.push(metric);
|
|
233
|
+
demoLogger.telemetry("track", {
|
|
234
|
+
tool,
|
|
235
|
+
duration: `${duration}ms`,
|
|
236
|
+
success,
|
|
237
|
+
...error && { error }
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Flush - in demo mode, just logs a summary
|
|
242
|
+
*/
|
|
243
|
+
async flush() {
|
|
244
|
+
if (this.buffer.length === 0) return;
|
|
245
|
+
const count = this.buffer.length;
|
|
246
|
+
demoLogger.telemetry("flush", {
|
|
247
|
+
message: `Would send ${count} metrics to OpenConductor`,
|
|
248
|
+
metrics: this.buffer.slice(0, 3),
|
|
249
|
+
...count > 3 && { more: `...and ${count - 3} more` }
|
|
250
|
+
});
|
|
251
|
+
this.buffer = [];
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Shutdown - no-op in demo mode
|
|
255
|
+
*/
|
|
256
|
+
shutdown() {
|
|
257
|
+
console.log(`${DEMO_PREFIX2} Telemetry shutdown`);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get buffered metrics (useful for testing/inspection)
|
|
261
|
+
*/
|
|
262
|
+
getBuffer() {
|
|
263
|
+
return [...this.buffer];
|
|
264
|
+
}
|
|
265
|
+
};
|
|
124
266
|
|
|
267
|
+
exports.DemoTelemetry = DemoTelemetry;
|
|
125
268
|
exports.Telemetry = Telemetry;
|
|
126
269
|
exports.getTelemetry = getTelemetry;
|
|
127
270
|
exports.initTelemetry = initTelemetry;
|