@cardor/heimdall-mcp 0.1.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 +391 -0
- package/bin/heimdall-mcp.js +2 -0
- package/dist/MySqlStore-VOXUWDAJ.js +111 -0
- package/dist/MySqlStore-VOXUWDAJ.js.map +1 -0
- package/dist/PostgresStore-QUSLDMPH.js +111 -0
- package/dist/PostgresStore-QUSLDMPH.js.map +1 -0
- package/dist/SqliteStore-HMGNDB3O.js +109 -0
- package/dist/SqliteStore-HMGNDB3O.js.map +1 -0
- package/dist/chunk-VOBMDW4J.js +660 -0
- package/dist/chunk-VOBMDW4J.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proxy/McpProxy.ts","../src/interceptor/ForwardInterceptor.ts","../src/interceptor/InterceptorPipeline.ts","../src/interceptor/TelemetryInterceptor.ts","../src/telemetry/LogEmitter.ts","../src/telemetry/McpSpanBuilder.ts","../src/telemetry/MetricsRecorder.ts","../src/telemetry/TelemetryCollector.ts","../src/proxy/ProxyBuilder.ts","../src/store/StoreResolver.ts","../src/transport/HttpInbound.ts","../src/transport/HttpOutbound.ts","../src/transport/SseInbound.ts","../src/transport/SseOutbound.ts","../src/transport/StdioInbound.ts","../src/transport/StdioOutbound.ts","../src/transport/TransportFactory.ts"],"sourcesContent":["import { EventEmitter } from 'node:events'\n\nimport { ForwardInterceptor } from '@/interceptor/ForwardInterceptor'\nimport { InterceptorPipeline } from '@/interceptor/InterceptorPipeline'\nimport { TelemetryInterceptor } from '@/interceptor/TelemetryInterceptor'\nimport { TelemetryCollector } from '@/telemetry/TelemetryCollector'\n\nimport type { TraceStore } from '@/store/TraceStore'\nimport type { HttpOutbound } from '@/transport/HttpOutbound'\nimport type { McpTransport } from '@/transport/McpTransport'\nimport type { SseOutbound } from '@/transport/SseOutbound'\nimport type { StdioOutbound } from '@/transport/StdioOutbound'\n\nexport class McpProxy extends EventEmitter {\n private pipeline: InterceptorPipeline\n\n constructor(\n private inbound: McpTransport,\n private outbound: StdioOutbound | HttpOutbound | SseOutbound,\n store: TraceStore,\n ) {\n super()\n const collector = new TelemetryCollector(store)\n this.pipeline = new InterceptorPipeline()\n this.pipeline.use(new TelemetryInterceptor(collector))\n this.pipeline.use(new ForwardInterceptor(outbound))\n }\n\n addInterceptor(interceptor: Parameters<InterceptorPipeline['use']>[0]): this {\n // insert before ForwardInterceptor (last item) — not possible post-construction\n // so this is a pre-build concern; McpProxy accepts a custom pipeline instead\n this.pipeline.use(interceptor)\n return this\n }\n\n async start(): Promise<void> {\n this.inbound.onMessage(async (msg) => {\n try {\n return await this.pipeline.run(msg)\n } catch (err) {\n this.emit('error', err)\n return {\n jsonrpc: '2.0' as const,\n id: msg.id,\n error: { code: -32603, message: String(err) },\n }\n }\n })\n }\n\n async stop(): Promise<void> {\n await this.inbound.close()\n await this.outbound.close()\n }\n}\n","import type { Interceptor, InterceptorContext } from './Interceptor'\nimport type { HttpOutbound } from '@/transport/HttpOutbound'\nimport type { SseOutbound } from '@/transport/SseOutbound'\nimport type { StdioOutbound } from '@/transport/StdioOutbound'\nimport type { JsonRpcMessage } from '@/types'\n\ntype ForwardableOutbound = StdioOutbound | HttpOutbound | SseOutbound\n\nexport class ForwardInterceptor implements Interceptor {\n readonly name = 'ForwardInterceptor'\n\n constructor(private outbound: ForwardableOutbound) {}\n\n async intercept(\n request: JsonRpcMessage,\n _context: InterceptorContext,\n _next: () => Promise<JsonRpcMessage>\n ): Promise<JsonRpcMessage> {\n return this.outbound.sendAndWait(request)\n }\n}\n","import { randomBytes } from 'node:crypto'\n\nimport type { Interceptor, InterceptorContext } from './Interceptor'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class InterceptorPipeline {\n private interceptors: Interceptor[] = []\n\n use(interceptor: Interceptor): this {\n this.interceptors.push(interceptor)\n return this\n }\n\n async run(request: JsonRpcMessage): Promise<JsonRpcMessage> {\n const context: InterceptorContext = {\n startedAt: new Date(),\n traceId: randomBytes(16).toString('hex'),\n spanId: randomBytes(8).toString('hex'),\n metadata: {},\n }\n\n let index = 0\n const next = async (): Promise<JsonRpcMessage> => {\n const interceptor = this.interceptors[index++]\n if (!interceptor) throw new Error('Pipeline ended without ForwardInterceptor')\n return interceptor.intercept(request, context, next)\n }\n\n return next()\n }\n}\n","import type { Interceptor, InterceptorContext } from './Interceptor'\nimport type { TelemetryCollector } from '@/telemetry/TelemetryCollector'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class TelemetryInterceptor implements Interceptor {\n readonly name = 'TelemetryInterceptor'\n\n constructor(private collector: TelemetryCollector) {}\n\n async intercept(\n request: JsonRpcMessage,\n context: InterceptorContext,\n next: () => Promise<JsonRpcMessage>\n ): Promise<JsonRpcMessage> {\n context.startedAt = new Date()\n\n let response: JsonRpcMessage\n let status: 'ok' | 'error' = 'ok'\n\n try {\n response = await next()\n if (response.error) status = 'error'\n } catch (err) {\n status = 'error'\n response = {\n jsonrpc: '2.0',\n id: request.id,\n error: { code: -32603, message: String(err) },\n }\n }\n\n const endedAt = new Date()\n const durationMs = endedAt.getTime() - context.startedAt.getTime()\n\n await this.collector.record({\n traceId: context.traceId,\n spanId: context.spanId,\n request,\n response,\n status,\n startedAt: context.startedAt,\n endedAt,\n durationMs,\n })\n\n return response\n }\n}\n","import pc from 'picocolors'\n\ntype LogLevel = 'info' | 'warn' | 'error' | 'debug'\n\nexport class LogEmitter {\n private enabled: boolean\n\n constructor(enabled = true) {\n this.enabled = enabled\n }\n\n info(message: string, meta?: Record<string, unknown>): void {\n this.emit('info', message, meta)\n }\n\n warn(message: string, meta?: Record<string, unknown>): void {\n this.emit('warn', message, meta)\n }\n\n error(message: string, meta?: Record<string, unknown>): void {\n this.emit('error', message, meta)\n }\n\n debug(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', message, meta)\n }\n\n private emit(level: LogLevel, message: string, meta?: Record<string, unknown>): void {\n if (!this.enabled) return\n const ts = new Date().toISOString()\n const prefix = {\n info: pc.blue('[info]'),\n warn: pc.yellow('[warn]'),\n error: pc.red('[error]'),\n debug: pc.gray('[debug]'),\n }[level]\n const metaStr = meta ? ' ' + JSON.stringify(meta) : ''\n process.stderr.write(`${ts} ${prefix} ${message}${metaStr}\\n`)\n }\n}\n","import { randomBytes } from 'node:crypto'\n\nimport type { JsonRpcMessage, McpSpan, SpanStatus } from '@/types'\n\nexport interface SpanInput {\n traceId: string\n spanId: string\n request: JsonRpcMessage\n response: JsonRpcMessage\n status: SpanStatus\n startedAt: Date\n endedAt: Date\n durationMs: number\n}\n\nconst METHOD_TO_SPAN_NAME: Record<string, string> = {\n initialize: 'mcp.initialize',\n 'tools/list': 'mcp.tools.list',\n 'tools/call': 'mcp.tool.call',\n 'resources/read': 'mcp.resource.read',\n 'resources/list': 'mcp.resources.list',\n 'prompts/get': 'mcp.prompt.get',\n 'prompts/list': 'mcp.prompts.list',\n shutdown: 'mcp.shutdown',\n}\n\nexport class McpSpanBuilder {\n build(input: SpanInput): McpSpan {\n const method = input.request.method ?? 'unknown'\n const spanName = METHOD_TO_SPAN_NAME[method] ?? `mcp.${method}`\n const params = input.request.params as Record<string, unknown> | undefined\n const result = input.response.result as Record<string, unknown> | undefined\n\n const attributes = this.buildAttributes(method, params, result, input)\n const events = this.buildEvents(method, params, result)\n\n return {\n id: `${input.traceId}-${input.spanId}`,\n traceId: input.traceId,\n spanId: input.spanId,\n name: spanName,\n status: input.status,\n startedAt: input.startedAt,\n endedAt: input.endedAt,\n durationMs: input.durationMs,\n attributes,\n events,\n }\n }\n\n private buildAttributes(\n method: string,\n params: Record<string, unknown> | undefined,\n result: Record<string, unknown> | undefined,\n input: SpanInput\n ): Record<string, unknown> {\n const base: Record<string, unknown> = {\n 'gen_ai.operation.name': method,\n 'mcp.duration_ms': input.durationMs,\n }\n\n if (method === 'tools/call') {\n const name = (params as { name?: string } | undefined)?.name\n base['gen_ai.tool.name'] = name\n base['gen_ai.tool.call.id'] = randomBytes(8).toString('hex')\n }\n\n if (method === 'tools/list') {\n const tools = (result as { tools?: unknown[] } | undefined)?.tools\n base['mcp.tools_count'] = Array.isArray(tools) ? tools.length : 0\n }\n\n if (method === 'resources/read') {\n base['url.full'] = (params as { uri?: string } | undefined)?.uri\n }\n\n if (method === 'prompts/get') {\n base['mcp.prompt_name'] = (params as { name?: string } | undefined)?.name\n }\n\n if (method === 'initialize') {\n const p = params as { clientInfo?: { version?: string }; capabilities?: unknown } | undefined\n const r = result as { serverInfo?: { version?: string }; capabilities?: unknown } | undefined\n base['mcp.client_version'] = p?.clientInfo?.version\n base['mcp.server_version'] = r?.serverInfo?.version\n base['mcp.client_capabilities'] = JSON.stringify(p?.capabilities ?? {})\n base['mcp.server_capabilities'] = JSON.stringify(r?.capabilities ?? {})\n }\n\n return base\n }\n\n private buildEvents(\n method: string,\n params: Record<string, unknown> | undefined,\n result: Record<string, unknown> | undefined\n ) {\n const events = []\n\n if (method === 'tools/call') {\n events.push({ name: 'tool.input', timestamp: new Date(), attributes: { body: JSON.stringify(params) } })\n if (result) events.push({ name: 'tool.output', timestamp: new Date(), attributes: { body: JSON.stringify(result) } })\n }\n\n if (method === 'tools/list' && result) {\n events.push({ name: 'tools.list', timestamp: new Date(), attributes: { tools: JSON.stringify((result as { tools?: unknown }).tools ?? []) } })\n }\n\n if (method === 'prompts/get' && result) {\n events.push({ name: 'prompt.rendered', timestamp: new Date(), attributes: { body: JSON.stringify(result) } })\n }\n\n return events\n }\n}\n","import type { SpanStatus } from '@/types'\n\nexport interface ToolMetrics {\n callCount: number\n errorCount: number\n totalDurationMs: number\n}\n\nexport class MetricsRecorder {\n private metrics = new Map<string, ToolMetrics>()\n\n record(toolName: string, status: SpanStatus, durationMs: number): void {\n const existing = this.metrics.get(toolName) ?? { callCount: 0, errorCount: 0, totalDurationMs: 0 }\n existing.callCount++\n if (status === 'error') existing.errorCount++\n existing.totalDurationMs += durationMs\n this.metrics.set(toolName, existing)\n }\n\n getAll(): Map<string, ToolMetrics & { avgDurationMs: number }> {\n const result = new Map<string, ToolMetrics & { avgDurationMs: number }>()\n for (const [name, m] of this.metrics) {\n result.set(name, {\n ...m,\n avgDurationMs: m.callCount > 0 ? m.totalDurationMs / m.callCount : 0,\n })\n }\n return result\n }\n}\n","import { LogEmitter } from './LogEmitter'\nimport { McpSpanBuilder } from './McpSpanBuilder'\nimport { MetricsRecorder } from './MetricsRecorder'\n\nimport type { SpanInput } from './McpSpanBuilder'\nimport type { TraceStore } from '@/store/TraceStore'\n\nexport type { SpanInput }\n\nexport class TelemetryCollector {\n private spanBuilder = new McpSpanBuilder()\n private metrics = new MetricsRecorder()\n private log = new LogEmitter()\n\n constructor(private store: TraceStore) {}\n\n async record(input: SpanInput): Promise<void> {\n const span = this.spanBuilder.build(input)\n\n const toolName = (input.request.params as { name?: string } | undefined)?.name\n if (input.request.method === 'tools/call' && toolName) {\n this.metrics.record(toolName, input.status, input.durationMs)\n }\n\n this.log.debug(`${span.name} [${input.status}] ${input.durationMs}ms`, {\n traceId: span.traceId,\n spanId: span.spanId,\n })\n\n try {\n await this.store.save(span)\n } catch (err) {\n this.log.error('Failed to save span', { err: String(err) })\n }\n }\n\n getMetrics() {\n return this.metrics.getAll()\n }\n}\n","import * as v from 'valibot'\n\nimport { StoreResolver } from '@/store/StoreResolver'\nimport { TransportFactory } from '@/transport/TransportFactory'\n\nimport { McpProxy } from './McpProxy'\n\nimport type { InboundConfig, OutboundConfig } from '@/types'\n\nconst InboundSchema = v.object({\n transport: v.picklist(['stdio', 'http', 'sse']),\n port: v.optional(v.number()),\n host: v.optional(v.string()),\n})\n\nconst OutboundSchema = v.object({\n transport: v.picklist(['stdio', 'http', 'sse']),\n url: v.optional(v.string()),\n command: v.optional(v.string()),\n args: v.optional(v.array(v.string())),\n})\n\nexport class ProxyBuilder {\n private _inbound?: InboundConfig\n private _outbound?: OutboundConfig\n private _store?: string\n\n static create(): ProxyBuilder {\n return new ProxyBuilder()\n }\n\n inbound(config: InboundConfig): this {\n this._inbound = v.parse(InboundSchema, config)\n return this\n }\n\n outbound(config: OutboundConfig): this {\n this._outbound = v.parse(OutboundSchema, config)\n return this\n }\n\n store(connectionString: string): this {\n this._store = connectionString\n return this\n }\n\n async build(): Promise<McpProxy> {\n if (!this._inbound) throw new Error('ProxyBuilder: inbound config is required')\n if (!this._outbound) throw new Error('ProxyBuilder: outbound config is required')\n if (!this._store) throw new Error('ProxyBuilder: store connection string is required')\n\n const inbound = TransportFactory.createInbound(this._inbound)\n const outbound = TransportFactory.createOutbound(this._outbound)\n const store = await StoreResolver.resolve(this._store)\n\n return new McpProxy(\n inbound,\n outbound as ConstructorParameters<typeof McpProxy>[1],\n store,\n )\n }\n}\n","import { homedir } from 'node:os'\n\nimport type { TraceStore } from './TraceStore'\n\ninterface InitableStore extends TraceStore {\n init(): Promise<void>\n}\n\nfunction toLibsqlUrl(connectionString: string): string {\n // Already a file: or :memory: URL — pass through\n if (connectionString.startsWith('file:') || connectionString === ':memory:') {\n return connectionString\n }\n // Strip sqlite:// scheme and expand ~ so @libsql/client gets a valid file: URL\n const raw = connectionString.replace(/^sqlite:\\/\\//, '')\n const expanded = raw.startsWith('~/') ? homedir() + raw.slice(1) : raw\n return `file:${expanded}`\n}\n\nexport class StoreResolver {\n static async resolve(connectionString: string): Promise<TraceStore> {\n let store: InitableStore\n\n if (connectionString.startsWith('sqlite://') || connectionString.startsWith('file:') || connectionString === ':memory:') {\n const { SqliteStore } = await import('./SqliteStore')\n store = new SqliteStore(toLibsqlUrl(connectionString))\n } else if (connectionString.startsWith('postgres://') || connectionString.startsWith('postgresql://')) {\n const { PostgresStore } = await import('./PostgresStore')\n store = new PostgresStore(connectionString)\n } else if (connectionString.startsWith('mysql://')) {\n const { MySqlStore } = await import('./MySqlStore')\n store = new MySqlStore(connectionString)\n } else {\n throw new Error(`Unsupported store connection string: \"${connectionString}\". Expected sqlite://, postgres://, or mysql://`)\n }\n\n await store.init()\n return store\n }\n}\n","import { createServer } from 'node:http'\n\nimport type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class HttpInbound implements McpTransport {\n private handler?: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>\n private server\n\n constructor(private port: number, private host = '0.0.0.0') {\n this.server = createServer(async (req, res) => {\n if (req.method !== 'POST') {\n res.writeHead(405).end()\n return\n }\n const chunks: Buffer[] = []\n for await (const chunk of req) chunks.push(chunk as Buffer)\n const body = Buffer.concat(chunks).toString()\n\n try {\n const msg = JSON.parse(body) as JsonRpcMessage\n const response = this.handler ? await this.handler(msg) : { jsonrpc: '2.0' as const, id: msg.id, result: null }\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(response))\n } catch {\n res.writeHead(400).end()\n }\n })\n }\n\n async send(_message: JsonRpcMessage): Promise<void> {\n // HTTP inbound is request/response — responses go through the handler return value\n }\n\n onMessage(handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {\n this.handler = handler\n this.server.listen(this.port, this.host)\n }\n\n async close(): Promise<void> {\n await new Promise<void>((resolve, reject) =>\n this.server.close((err) => (err ? reject(err) : resolve()))\n )\n }\n}\n","import type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class HttpOutbound implements McpTransport {\n constructor(private url: string) {}\n\n async send(message: JsonRpcMessage): Promise<void> {\n await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(message),\n })\n }\n\n async sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage> {\n const res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(message),\n })\n return res.json() as Promise<JsonRpcMessage>\n }\n\n onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {}\n\n async close(): Promise<void> {}\n}\n","import { createServer } from 'node:http'\n\nimport type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\nimport type { ServerResponse } from 'node:http'\n\nexport class SseInbound implements McpTransport {\n private handler?: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>\n private server\n private clients = new Set<ServerResponse>()\n\n constructor(private port: number, private host = '0.0.0.0') {\n this.server = createServer(async (req, res) => {\n if (req.url === '/sse' && req.method === 'GET') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n this.clients.add(res)\n req.on('close', () => this.clients.delete(res))\n return\n }\n\n if (req.method === 'POST') {\n const chunks: Buffer[] = []\n for await (const chunk of req) chunks.push(chunk as Buffer)\n try {\n const msg = JSON.parse(Buffer.concat(chunks).toString()) as JsonRpcMessage\n const response = this.handler ? await this.handler(msg) : { jsonrpc: '2.0' as const, id: msg.id, result: null }\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(response))\n } catch {\n res.writeHead(400).end()\n }\n return\n }\n\n res.writeHead(404).end()\n })\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n const data = `data: ${JSON.stringify(message)}\\n\\n`\n for (const client of this.clients) client.write(data)\n }\n\n onMessage(handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {\n this.handler = handler\n this.server.listen(this.port, this.host)\n }\n\n async close(): Promise<void> {\n for (const client of this.clients) client.end()\n await new Promise<void>((resolve, reject) =>\n this.server.close((err) => (err ? reject(err) : resolve()))\n )\n }\n}\n","import type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class SseOutbound implements McpTransport {\n private eventSource?: EventSource\n private pending = new Map<string | number, (msg: JsonRpcMessage) => void>()\n\n constructor(private url: string) {}\n\n private ensureConnected() {\n if (this.eventSource) return\n // Node 22 has built-in EventSource\n this.eventSource = new EventSource(`${this.url}/sse`)\n this.eventSource.onmessage = (e) => {\n try {\n const msg = JSON.parse(e.data) as JsonRpcMessage\n const id = msg.id\n if (id != null) {\n const resolve = this.pending.get(id)\n if (resolve) {\n this.pending.delete(id)\n resolve(msg)\n }\n }\n } catch {\n // ignore\n }\n }\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(message),\n })\n }\n\n async sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage> {\n this.ensureConnected()\n return new Promise((resolve, reject) => {\n const id = message.id\n if (id == null) {\n this.send(message).catch(reject)\n resolve({ jsonrpc: '2.0', id: null })\n return\n }\n this.pending.set(id, resolve)\n this.send(message).catch((err) => {\n this.pending.delete(id)\n reject(err)\n })\n })\n }\n\n onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {}\n\n async close(): Promise<void> {\n this.eventSource?.close()\n }\n}\n","import { createInterface } from 'node:readline'\n\nimport type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class StdioInbound implements McpTransport {\n private handler?: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>\n\n async send(message: JsonRpcMessage): Promise<void> {\n process.stdout.write(JSON.stringify(message) + '\\n')\n }\n\n onMessage(handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {\n this.handler = handler\n const rl = createInterface({ input: process.stdin, terminal: false })\n\n rl.on('line', async (line) => {\n const trimmed = line.trim()\n if (!trimmed) return\n try {\n const msg = JSON.parse(trimmed) as JsonRpcMessage\n if (this.handler) {\n const response = await this.handler(msg)\n await this.send(response)\n }\n } catch {\n // malformed JSON — ignore\n }\n })\n }\n\n async close(): Promise<void> {\n process.stdin.destroy()\n }\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\n\nimport type { McpTransport } from './McpTransport'\nimport type { JsonRpcMessage } from '@/types'\n\nexport class StdioOutbound implements McpTransport {\n private proc\n private pending = new Map<string | number, (msg: JsonRpcMessage) => void>()\n\n constructor(command: string, args: string[] = []) {\n this.proc = spawn(command, args, {\n stdio: ['pipe', 'pipe', 'inherit'],\n })\n\n const rl = createInterface({ input: this.proc.stdout!, terminal: false })\n rl.on('line', (line) => {\n const trimmed = line.trim()\n if (!trimmed) return\n try {\n const msg = JSON.parse(trimmed) as JsonRpcMessage\n const id = msg.id\n if (id != null) {\n const resolve = this.pending.get(id)\n if (resolve) {\n this.pending.delete(id)\n resolve(msg)\n }\n }\n } catch {\n // ignore\n }\n })\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n this.proc.stdin!.write(JSON.stringify(message) + '\\n', (err) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n async sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage> {\n return new Promise((resolve, reject) => {\n const id = message.id\n if (id == null) {\n this.send(message).catch(reject)\n // notifications have no response — return empty ack\n resolve({ jsonrpc: '2.0', id: null })\n return\n }\n this.pending.set(id, resolve)\n this.send(message).catch((err) => {\n this.pending.delete(id)\n reject(err)\n })\n })\n }\n\n // McpTransport.onMessage is not used for outbound — responses come via sendAndWait\n onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void {}\n\n async close(): Promise<void> {\n this.proc.stdin!.end()\n await new Promise<void>((resolve) => this.proc.on('close', resolve))\n }\n}\n","import { HttpInbound } from './HttpInbound'\nimport { HttpOutbound } from './HttpOutbound'\nimport { SseInbound } from './SseInbound'\nimport { SseOutbound } from './SseOutbound'\nimport { StdioInbound } from './StdioInbound'\nimport { StdioOutbound } from './StdioOutbound'\n\nimport type { McpTransport } from './McpTransport'\nimport type { InboundConfig, OutboundConfig } from '@/types'\n\nexport class TransportFactory {\n static createInbound(config: InboundConfig): McpTransport {\n switch (config.transport) {\n case 'stdio':\n return new StdioInbound()\n case 'http':\n return new HttpInbound(config.port ?? 3000, config.host)\n case 'sse':\n return new SseInbound(config.port ?? 3000, config.host)\n }\n }\n\n static createOutbound(config: OutboundConfig): McpTransport {\n switch (config.transport) {\n case 'stdio':\n if (!config.command) throw new Error('outbound stdio requires a command')\n return new StdioOutbound(config.command, config.args)\n case 'http':\n if (!config.url) throw new Error('outbound http requires a url')\n return new HttpOutbound(config.url)\n case 'sse':\n if (!config.url) throw new Error('outbound sse requires a url')\n return new SseOutbound(config.url)\n }\n }\n}\n"],"mappings":"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","names":["randomBytes","createServer","createInterface"]}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProxyBuilder
|
|
3
|
+
} from "./chunk-VOBMDW4J.js";
|
|
4
|
+
|
|
5
|
+
// src/cli.ts
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
var program = new Command();
|
|
9
|
+
program.name("heimdall-mcp").description("Transparent MCP proxy with tracing and configurable storage").version("0.1.0");
|
|
10
|
+
program.command("start", { isDefault: true }).description("Start the proxy (default command)").option("--store <url>", "Storage connection string (sqlite://, postgres://, mysql://)").option("--target <url>", "Target server URL for http/sse outbound").option("--in <transport>", "Inbound transport: stdio | http | sse", "stdio").option("--in-port <port>", "Port for inbound http/sse", parseInt).option("--out <transport>", "Outbound transport: stdio | http | sse", "stdio").option("--out-port <port>", "Port for outbound http/sse", parseInt).allowUnknownOption(true).action(async (opts, cmd) => {
|
|
11
|
+
const rawArgs = cmd.args;
|
|
12
|
+
if (!opts.store) {
|
|
13
|
+
console.error(pc.red("Error: --store is required"));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const inTransport = opts.in;
|
|
17
|
+
const outTransport = opts.out;
|
|
18
|
+
const separatorIdx = rawArgs.indexOf("--");
|
|
19
|
+
const subArgs = separatorIdx >= 0 ? rawArgs.slice(separatorIdx + 1) : [];
|
|
20
|
+
const [subCommand, ...subCommandArgs] = subArgs;
|
|
21
|
+
const builder = ProxyBuilder.create().inbound({ transport: inTransport, port: opts.inPort }).store(opts.store);
|
|
22
|
+
if (outTransport === "stdio") {
|
|
23
|
+
if (!subCommand) {
|
|
24
|
+
console.error(pc.red("Error: stdio outbound requires a command after --"));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
builder.outbound({ transport: "stdio", command: subCommand, args: subCommandArgs });
|
|
28
|
+
} else {
|
|
29
|
+
if (!opts.target) {
|
|
30
|
+
console.error(pc.red("Error: http/sse outbound requires --target <url>"));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
builder.outbound({ transport: outTransport, url: opts.target });
|
|
34
|
+
}
|
|
35
|
+
const proxy = await builder.build();
|
|
36
|
+
process.on("SIGINT", () => proxy.stop().then(() => process.exit(0)));
|
|
37
|
+
process.on("SIGTERM", () => proxy.stop().then(() => process.exit(0)));
|
|
38
|
+
proxy.on("error", (err) => console.error(pc.red("[proxy error]"), err));
|
|
39
|
+
await proxy.start();
|
|
40
|
+
});
|
|
41
|
+
program.parse();
|
|
42
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\n\nimport { ProxyBuilder } from '@/proxy/ProxyBuilder'\n\nconst program = new Command()\n\nprogram\n .name('heimdall-mcp')\n .description('Transparent MCP proxy with tracing and configurable storage')\n .version('0.1.0')\n\nprogram\n .command('start', { isDefault: true })\n .description('Start the proxy (default command)')\n .option('--store <url>', 'Storage connection string (sqlite://, postgres://, mysql://)')\n .option('--target <url>', 'Target server URL for http/sse outbound')\n .option('--in <transport>', 'Inbound transport: stdio | http | sse', 'stdio')\n .option('--in-port <port>', 'Port for inbound http/sse', parseInt)\n .option('--out <transport>', 'Outbound transport: stdio | http | sse', 'stdio')\n .option('--out-port <port>', 'Port for outbound http/sse', parseInt)\n .allowUnknownOption(true)\n .action(async (opts, cmd) => {\n const rawArgs = cmd.args\n\n if (!opts.store) {\n console.error(pc.red('Error: --store is required'))\n process.exit(1)\n }\n\n const inTransport = opts.in as 'stdio' | 'http' | 'sse'\n const outTransport = opts.out as 'stdio' | 'http' | 'sse'\n\n // remaining args after '--' are the subprocess command\n const separatorIdx = rawArgs.indexOf('--')\n const subArgs = separatorIdx >= 0 ? rawArgs.slice(separatorIdx + 1) : []\n const [subCommand, ...subCommandArgs] = subArgs\n\n const builder = ProxyBuilder.create()\n .inbound({ transport: inTransport, port: opts.inPort })\n .store(opts.store)\n\n if (outTransport === 'stdio') {\n if (!subCommand) {\n console.error(pc.red('Error: stdio outbound requires a command after --'))\n process.exit(1)\n }\n builder.outbound({ transport: 'stdio', command: subCommand, args: subCommandArgs })\n } else {\n if (!opts.target) {\n console.error(pc.red('Error: http/sse outbound requires --target <url>'))\n process.exit(1)\n }\n builder.outbound({ transport: outTransport, url: opts.target })\n }\n\n const proxy = await builder.build()\n\n process.on('SIGINT', () => proxy.stop().then(() => process.exit(0)))\n process.on('SIGTERM', () => proxy.stop().then(() => process.exit(0)))\n proxy.on('error', (err) => console.error(pc.red('[proxy error]'), err))\n\n await proxy.start()\n })\n\nprogram.parse()\n"],"mappings":";;;;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AAIf,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,6DAA6D,EACzE,QAAQ,OAAO;AAElB,QACG,QAAQ,SAAS,EAAE,WAAW,KAAK,CAAC,EACpC,YAAY,mCAAmC,EAC/C,OAAO,iBAAiB,8DAA8D,EACtF,OAAO,kBAAkB,yCAAyC,EAClE,OAAO,oBAAoB,yCAAyC,OAAO,EAC3E,OAAO,oBAAoB,6BAA6B,QAAQ,EAChE,OAAO,qBAAqB,0CAA0C,OAAO,EAC7E,OAAO,qBAAqB,8BAA8B,QAAQ,EAClE,mBAAmB,IAAI,EACvB,OAAO,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,IAAI;AAEpB,MAAI,CAAC,KAAK,OAAO;AACf,YAAQ,MAAM,GAAG,IAAI,4BAA4B,CAAC;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,KAAK;AACzB,QAAM,eAAe,KAAK;AAG1B,QAAM,eAAe,QAAQ,QAAQ,IAAI;AACzC,QAAM,UAAU,gBAAgB,IAAI,QAAQ,MAAM,eAAe,CAAC,IAAI,CAAC;AACvE,QAAM,CAAC,YAAY,GAAG,cAAc,IAAI;AAExC,QAAM,UAAU,aAAa,OAAO,EACjC,QAAQ,EAAE,WAAW,aAAa,MAAM,KAAK,OAAO,CAAC,EACrD,MAAM,KAAK,KAAK;AAEnB,MAAI,iBAAiB,SAAS;AAC5B,QAAI,CAAC,YAAY;AACf,cAAQ,MAAM,GAAG,IAAI,mDAAmD,CAAC;AACzE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,SAAS,SAAS,YAAY,MAAM,eAAe,CAAC;AAAA,EACpF,OAAO;AACL,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,MAAM,GAAG,IAAI,kDAAkD,CAAC;AACxE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,SAAS,EAAE,WAAW,cAAc,KAAK,KAAK,OAAO,CAAC;AAAA,EAChE;AAEA,QAAM,QAAQ,MAAM,QAAQ,MAAM;AAElC,UAAQ,GAAG,UAAW,MAAM,MAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACpE,UAAQ,GAAG,WAAW,MAAM,MAAM,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACpE,QAAM,GAAG,SAAS,CAAC,QAAQ,QAAQ,MAAM,GAAG,IAAI,eAAe,GAAG,GAAG,CAAC;AAEtE,QAAM,MAAM,MAAM;AACpB,CAAC;AAEH,QAAQ,MAAM;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
|
|
3
|
+
type TransportType = 'stdio' | 'http' | 'sse';
|
|
4
|
+
type StoreType = 'sqlite' | 'postgres' | 'mysql';
|
|
5
|
+
type SpanStatus = 'ok' | 'error';
|
|
6
|
+
interface JsonRpcMessage {
|
|
7
|
+
jsonrpc: '2.0';
|
|
8
|
+
id?: string | number | null;
|
|
9
|
+
method?: string;
|
|
10
|
+
params?: unknown;
|
|
11
|
+
result?: unknown;
|
|
12
|
+
error?: {
|
|
13
|
+
code: number;
|
|
14
|
+
message: string;
|
|
15
|
+
data?: unknown;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface InboundConfig {
|
|
19
|
+
transport: TransportType;
|
|
20
|
+
port?: number;
|
|
21
|
+
host?: string;
|
|
22
|
+
}
|
|
23
|
+
interface OutboundConfig {
|
|
24
|
+
transport: TransportType;
|
|
25
|
+
url?: string;
|
|
26
|
+
command?: string;
|
|
27
|
+
args?: string[];
|
|
28
|
+
}
|
|
29
|
+
interface StoreConfig {
|
|
30
|
+
connectionString: string;
|
|
31
|
+
}
|
|
32
|
+
interface McpProxyConfig {
|
|
33
|
+
inbound: InboundConfig;
|
|
34
|
+
outbound: OutboundConfig;
|
|
35
|
+
store: StoreConfig;
|
|
36
|
+
interceptors?: unknown[];
|
|
37
|
+
}
|
|
38
|
+
interface McpSpan {
|
|
39
|
+
id: string;
|
|
40
|
+
traceId: string;
|
|
41
|
+
spanId: string;
|
|
42
|
+
parentId?: string;
|
|
43
|
+
name: string;
|
|
44
|
+
status: SpanStatus;
|
|
45
|
+
startedAt: Date;
|
|
46
|
+
endedAt: Date;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
attributes?: Record<string, unknown>;
|
|
49
|
+
events?: McpSpanEvent[];
|
|
50
|
+
}
|
|
51
|
+
interface McpSpanEvent {
|
|
52
|
+
name: string;
|
|
53
|
+
timestamp: Date;
|
|
54
|
+
attributes?: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
interface SpanFilters {
|
|
57
|
+
traceId?: string;
|
|
58
|
+
spanId?: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
status?: SpanStatus;
|
|
61
|
+
from?: Date;
|
|
62
|
+
to?: Date;
|
|
63
|
+
limit?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface InterceptorContext {
|
|
67
|
+
startedAt: Date;
|
|
68
|
+
traceId: string;
|
|
69
|
+
spanId: string;
|
|
70
|
+
metadata: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
interface Interceptor {
|
|
73
|
+
name: string;
|
|
74
|
+
intercept(request: JsonRpcMessage, context: InterceptorContext, next: () => Promise<JsonRpcMessage>): Promise<JsonRpcMessage>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
declare class InterceptorPipeline {
|
|
78
|
+
private interceptors;
|
|
79
|
+
use(interceptor: Interceptor): this;
|
|
80
|
+
run(request: JsonRpcMessage): Promise<JsonRpcMessage>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface TraceStore {
|
|
84
|
+
save(span: McpSpan): Promise<void>;
|
|
85
|
+
query(filters: SpanFilters): Promise<McpSpan[]>;
|
|
86
|
+
close(): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface McpTransport {
|
|
90
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
91
|
+
onMessage(handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void;
|
|
92
|
+
close(): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
declare class HttpOutbound implements McpTransport {
|
|
96
|
+
private url;
|
|
97
|
+
constructor(url: string);
|
|
98
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
99
|
+
sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage>;
|
|
100
|
+
onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void;
|
|
101
|
+
close(): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
declare class SseOutbound implements McpTransport {
|
|
105
|
+
private url;
|
|
106
|
+
private eventSource?;
|
|
107
|
+
private pending;
|
|
108
|
+
constructor(url: string);
|
|
109
|
+
private ensureConnected;
|
|
110
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
111
|
+
sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage>;
|
|
112
|
+
onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void;
|
|
113
|
+
close(): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
declare class StdioOutbound implements McpTransport {
|
|
117
|
+
private proc;
|
|
118
|
+
private pending;
|
|
119
|
+
constructor(command: string, args?: string[]);
|
|
120
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
121
|
+
sendAndWait(message: JsonRpcMessage): Promise<JsonRpcMessage>;
|
|
122
|
+
onMessage(_handler: (msg: JsonRpcMessage) => Promise<JsonRpcMessage>): void;
|
|
123
|
+
close(): Promise<void>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
declare class McpProxy extends EventEmitter {
|
|
127
|
+
private inbound;
|
|
128
|
+
private outbound;
|
|
129
|
+
private pipeline;
|
|
130
|
+
constructor(inbound: McpTransport, outbound: StdioOutbound | HttpOutbound | SseOutbound, store: TraceStore);
|
|
131
|
+
addInterceptor(interceptor: Parameters<InterceptorPipeline['use']>[0]): this;
|
|
132
|
+
start(): Promise<void>;
|
|
133
|
+
stop(): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
declare class ProxyBuilder {
|
|
137
|
+
private _inbound?;
|
|
138
|
+
private _outbound?;
|
|
139
|
+
private _store?;
|
|
140
|
+
static create(): ProxyBuilder;
|
|
141
|
+
inbound(config: InboundConfig): this;
|
|
142
|
+
outbound(config: OutboundConfig): this;
|
|
143
|
+
store(connectionString: string): this;
|
|
144
|
+
build(): Promise<McpProxy>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export { type InboundConfig, type Interceptor, type InterceptorContext, McpProxy, type McpProxyConfig, type McpSpan, type McpTransport, type OutboundConfig, ProxyBuilder, type SpanFilters, type SpanStatus, type StoreConfig, type StoreType, type TraceStore, type TransportType };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cardor/heimdall-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Transparent MCP proxy with OpenTelemetry tracing and configurable storage",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"heimdall-mcp": "./bin/heimdall-mcp.js"
|
|
16
|
+
},
|
|
17
|
+
"files": ["bin", "dist"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"test": "node --test --import tsx/esm src/tests/*.test.ts",
|
|
22
|
+
"lint": "eslint . --fix",
|
|
23
|
+
"prepublishOnly": "npm run build && npm test",
|
|
24
|
+
"prepare": "husky"
|
|
25
|
+
},
|
|
26
|
+
"keywords": ["mcp", "proxy", "opentelemetry", "tracing", "observability"],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.10.2",
|
|
31
|
+
"drizzle-orm": "^0.45.2",
|
|
32
|
+
"@libsql/client": "^0.14.0",
|
|
33
|
+
"postgres": "^3.4.9",
|
|
34
|
+
"mysql2": "^3.22.3",
|
|
35
|
+
"commander": "^12.1.0",
|
|
36
|
+
"valibot": "^1.4.0",
|
|
37
|
+
"picocolors": "^1.1.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@commitlint/cli": "^20.5.3",
|
|
41
|
+
"@commitlint/config-conventional": "^20.5.3",
|
|
42
|
+
"@types/node": "^22.19.17",
|
|
43
|
+
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
44
|
+
"husky": "^9.1.7",
|
|
45
|
+
"tsup": "^8.5.1",
|
|
46
|
+
"tsx": "^4.21.0",
|
|
47
|
+
"typescript": "^5.5.0",
|
|
48
|
+
"typescript-eslint": "^8.59.1"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22.5.0"
|
|
52
|
+
}
|
|
53
|
+
}
|