@tournesol-tag/mcp-bridge 0.1.0 → 0.1.1
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/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { realpathSync } from "fs";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
3
7
|
// src/bridge.ts
|
|
4
8
|
import WebSocket from "ws";
|
|
5
9
|
|
|
@@ -467,7 +471,15 @@ async function main() {
|
|
|
467
471
|
process.on("SIGTERM", shutdown);
|
|
468
472
|
await bridge.start();
|
|
469
473
|
}
|
|
470
|
-
|
|
474
|
+
function entryMatchesModule(entryArg, moduleUrl) {
|
|
475
|
+
if (!entryArg) return false;
|
|
476
|
+
try {
|
|
477
|
+
return realpathSync(entryArg) === realpathSync(fileURLToPath(moduleUrl));
|
|
478
|
+
} catch {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
var isMain = entryMatchesModule(process.argv[1], import.meta.url);
|
|
471
483
|
if (isMain) {
|
|
472
484
|
main().catch((err) => {
|
|
473
485
|
console.error("[tag-mcp-bridge] fatal:", err);
|
|
@@ -475,6 +487,7 @@ if (isMain) {
|
|
|
475
487
|
});
|
|
476
488
|
}
|
|
477
489
|
export {
|
|
490
|
+
entryMatchesModule,
|
|
478
491
|
parseArgs
|
|
479
492
|
};
|
|
480
493
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bridge.ts","../src/backoff.ts","../src/protocol.ts","../src/mcp-stdio.ts","../src/cli.ts"],"sourcesContent":["// Bridge runtime: maintains a WebSocket to the relay, spawns the dev's\n// MCP child once, and translates relay RPC envelopes ↔ MCP JSON-RPC.\n//\n// Single-process design:\n// * one MCP child for the lifetime of the bridge (re-using its session\n// across relay reconnects — devs configure tools that may carry state).\n// * one WS connection to the relay at a time. On error/close we reconnect\n// with exponential backoff.\n// * each inbound rpc.request becomes one MCP request (`tools/list` or\n// `tools/call`). We synthesize a clean response envelope and forward.\n\nimport WebSocket from 'ws';\nimport { Backoff } from './backoff.js';\nimport { decode, encode, type Frame, type RpcRequestFrame } from './protocol.js';\nimport { McpStdioClient, type McpMessage } from './mcp-stdio.js';\n\nexport interface BridgeOptions {\n /** Relay base URL (without /dev/...). Trailing slash optional. */\n relayUrl: string;\n /** Bridge JWT — `dev:<id>` subject + scope=[\"bridge\"]. */\n token: string;\n /** Dev id; must match the JWT subject. */\n devId: string;\n /** Command to spawn for the MCP child. */\n mcpCommand: string;\n mcpArgs?: string[];\n mcpEnv?: Record<string, string>;\n /** Override for tests. */\n websocketCtor?: typeof WebSocket;\n /** Override backoff knobs. */\n backoff?: { base?: number; cap?: number; random?: () => number };\n /** Hook for tests + observability. */\n onConnect?: () => void;\n onDisconnect?: (info: { code: number; reason: string }) => void;\n onError?: (err: Error) => void;\n logger?: { info: (...a: unknown[]) => void; warn: (...a: unknown[]) => void; error: (...a: unknown[]) => void };\n}\n\ninterface ToolDescriptor {\n name: string;\n description?: string;\n inputSchema?: unknown;\n}\n\nexport class Bridge {\n private ws: WebSocket | null = null;\n private backoff: Backoff;\n private mcp: McpStdioClient | null = null;\n private toolCatalog: ToolDescriptor[] | null = null;\n private stopped = false;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private log: NonNullable<BridgeOptions['logger']>;\n\n constructor(private readonly opts: BridgeOptions) {\n this.backoff = new Backoff(opts.backoff);\n this.log = opts.logger ?? {\n info: (...a) => console.log('[bridge]', ...a),\n warn: (...a) => console.warn('[bridge]', ...a),\n error: (...a) => console.error('[bridge]', ...a),\n };\n }\n\n /** Start the MCP child + connect to relay. Returns once initial connect resolves OR backoff begins. */\n async start(): Promise<void> {\n if (this.mcp) throw new Error('already started');\n this.mcp = new McpStdioClient({\n command: this.opts.mcpCommand,\n args: this.opts.mcpArgs,\n env: this.opts.mcpEnv,\n });\n this.mcp.on('stderr', (s) => this.log.warn('mcp child stderr:', s.trim()));\n this.mcp.on('exit', (info) => this.log.error('mcp child exited', info));\n this.mcp.on('parseError', (line) => this.log.warn('mcp parse error', line));\n this.mcp.start();\n\n // Snapshot the tool catalog once so we can publish it on the `hello` frame.\n try {\n const resp = await this.mcp.request('tools/list');\n if (resp.result && typeof resp.result === 'object' && 'tools' in (resp.result as Record<string, unknown>)) {\n this.toolCatalog = ((resp.result as { tools: ToolDescriptor[] }).tools) ?? [];\n } else {\n this.toolCatalog = [];\n }\n } catch (err) {\n // Non-fatal: relay will fall back to live /list calls on demand.\n this.log.warn('initial tools/list failed; relay will use live RPC', (err as Error).message);\n }\n\n this.connect();\n }\n\n /** Stop the bridge: close WS, kill child, cancel reconnect. */\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }\n if (this.ws) {\n try { this.ws.close(1000, 'bridge-stop'); } catch { /* ignore */ }\n this.ws = null;\n }\n if (this.mcp) await this.mcp.stop();\n }\n\n private wsUrl(): string {\n const base = this.opts.relayUrl.replace(/\\/$/, '').replace(/^http/, 'ws');\n return `${base}/dev/${this.opts.devId}/connect`;\n }\n\n private connect(): void {\n if (this.stopped) return;\n const WS = this.opts.websocketCtor ?? WebSocket;\n const ws = new WS(this.wsUrl(), {\n headers: { Authorization: `Bearer ${this.opts.token}` },\n });\n this.ws = ws;\n\n ws.on('open', () => {\n this.backoff.reset();\n this.log.info('connected to relay');\n const helloTools = this.toolCatalog ?? [];\n try {\n ws.send(encode({ kind: 'hello', devId: this.opts.devId, version: '0.1.0', tools: helloTools }));\n } catch (err) {\n this.log.warn('failed to send hello', (err as Error).message);\n }\n this.opts.onConnect?.();\n });\n\n ws.on('message', (data) => {\n let frame: Frame;\n try { frame = decode(data as Buffer); }\n catch (err) { this.log.warn('malformed frame from relay', (err as Error).message); return; }\n if (frame.kind === 'rpc.request') void this.handleRpc(ws, frame);\n });\n\n ws.on('close', (code, reason) => {\n const r = reason?.toString() ?? '';\n this.log.warn(`relay disconnected code=${code} reason=${r}`);\n this.opts.onDisconnect?.({ code, reason: r });\n this.ws = null;\n // Code 4001 = relay says we were kicked because a fresher bridge took\n // over our slot. In that case it's almost always wrong for us to\n // reconnect — we'd just kick the new one. Surface and stop.\n if (code === 4001) {\n this.log.error('kicked by newer connection — exiting reconnect loop');\n return;\n }\n this.scheduleReconnect();\n });\n\n ws.on('error', (err) => {\n this.opts.onError?.(err);\n // close event fires after error; reconnect handling lives there.\n });\n }\n\n private scheduleReconnect(): void {\n if (this.stopped) return;\n const delay = this.backoff.next();\n this.log.info(`reconnect in ${delay}ms (attempt ${this.backoff.currentAttempt})`);\n this.reconnectTimer = setTimeout(() => this.connect(), delay);\n }\n\n private async handleRpc(ws: WebSocket, frame: RpcRequestFrame): Promise<void> {\n if (!this.mcp) {\n this.respond(ws, frame.id, 502, { error: 'mcp child not running' });\n return;\n }\n try {\n let mcpResp: McpMessage;\n if (frame.method === 'list') {\n mcpResp = await this.mcp.request('tools/list');\n } else if (frame.method === 'call') {\n mcpResp = await this.mcp.request('tools/call', {\n name: frame.path,\n arguments: (frame.body as Record<string, unknown>) ?? {},\n });\n } else {\n this.respond(ws, frame.id, 400, { error: `unsupported method ${(frame as { method: string }).method}` });\n return;\n }\n if (mcpResp.error) {\n this.respond(ws, frame.id, 500, { error: mcpResp.error.message, code: mcpResp.error.code, data: mcpResp.error.data });\n } else {\n this.respond(ws, frame.id, 200, mcpResp.result ?? null);\n }\n } catch (err) {\n this.respond(ws, frame.id, 500, { error: (err as Error).message });\n }\n }\n\n private respond(ws: WebSocket, id: string, status: number, body: unknown): void {\n if (ws.readyState !== WebSocket.OPEN) return;\n try {\n ws.send(encode({ kind: 'rpc.response', id, status, body }));\n } catch (err) {\n this.log.warn('failed to send rpc.response', (err as Error).message);\n }\n }\n}\n","// Exponential backoff with full jitter, capped.\n//\n// Reconnect schedule for transient WS errors:\n// attempt 0 → 500ms\n// attempt 1 → 1000ms\n// attempt 2 → 2000ms\n// ...\n// capped at 30_000ms (per spec).\n//\n// Full jitter (sleep ∈ [0, exp_delay]) avoids reconnect storms when the\n// relay reboots and N bridges all wake at once.\n\nexport interface BackoffOpts {\n base?: number;\n cap?: number;\n /** Override RNG for deterministic tests. */\n random?: () => number;\n}\n\nexport class Backoff {\n private attempt = 0;\n private base: number;\n private cap: number;\n private random: () => number;\n\n constructor(opts: BackoffOpts = {}) {\n this.base = opts.base ?? 500;\n this.cap = opts.cap ?? 30_000;\n this.random = opts.random ?? Math.random;\n }\n\n reset(): void { this.attempt = 0; }\n\n /** Return the next delay (ms) and advance attempt counter. */\n next(): number {\n const exp = Math.min(this.cap, this.base * 2 ** this.attempt);\n this.attempt += 1;\n return Math.floor(this.random() * exp);\n }\n\n /** Test introspection: current attempt count (0 after reset). */\n get currentAttempt(): number { return this.attempt; }\n}\n","// Wire protocol shared with apps/mcp-relay. Duplicated locally (not imported\n// from the relay package) so this CLI can be `npm install -g`'d on a dev\n// laptop without pulling the relay's runtime deps. The schema is stable —\n// any change to the protocol must land in both files in the same PR.\n//\n// See apps/mcp-relay/src/protocol.ts for the canonical doc-comments.\n\nexport type RpcRequestFrame = {\n kind: 'rpc.request';\n id: string;\n method: 'list' | 'call';\n path: string;\n body?: unknown;\n};\n\nexport type RpcResponseFrame = {\n kind: 'rpc.response';\n id: string;\n status: number;\n body?: unknown;\n};\n\nexport type HelloFrame = {\n kind: 'hello';\n devId: string;\n version: string;\n tools?: unknown;\n};\n\nexport type Frame =\n | RpcRequestFrame\n | RpcResponseFrame\n | HelloFrame\n | { kind: 'ping' }\n | { kind: 'pong' };\n\nexport function encode(frame: Frame): string {\n return JSON.stringify(frame);\n}\n\nexport function decode(raw: string | Buffer): Frame {\n const text = typeof raw === 'string' ? raw : raw.toString('utf-8');\n const parsed = JSON.parse(text) as Frame;\n if (!parsed || typeof parsed !== 'object' || !('kind' in parsed)) {\n throw new Error('invalid frame: missing kind');\n }\n return parsed;\n}\n","// MCP-over-stdio client: spawns the dev's MCP server (e.g. `tariff-tools\n// serve` or `python -m tariff_tools.server`) and speaks line-delimited\n// JSON-RPC 2.0 to it.\n//\n// MCP's standard stdio transport uses LSP-style \"Content-Length\" framing.\n// We support BOTH framings:\n// * newline-delimited JSON (one JSON object per line) — common for\n// hand-rolled servers + simpler to test.\n// * Content-Length-prefixed JSON — what the MCP reference server emits.\n//\n// The bridge auto-detects which mode the child is using by sniffing the\n// first 16 bytes of output. If they start with \"Content-Length:\" we use\n// LSP framing; otherwise we use newline-delimited.\n\nimport { spawn, type ChildProcessWithoutNullStreams } from 'child_process';\nimport { EventEmitter } from 'events';\n\nexport type McpMessage = {\n jsonrpc: '2.0';\n id?: string | number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n};\n\ntype Framing = 'newline' | 'lsp' | 'unknown';\n\nexport interface McpStdioOptions {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n}\n\nexport class McpStdioClient extends EventEmitter {\n private proc: ChildProcessWithoutNullStreams | null = null;\n private framing: Framing = 'unknown';\n private buf = Buffer.alloc(0);\n private nextId = 1;\n private pending = new Map<string | number, (msg: McpMessage) => void>();\n private closed = false;\n\n constructor(private readonly opts: McpStdioOptions) {\n super();\n }\n\n start(): void {\n if (this.proc) throw new Error('already started');\n const proc = spawn(this.opts.command, this.opts.args ?? [], {\n env: { ...process.env, ...this.opts.env },\n cwd: this.opts.cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n this.proc = proc;\n proc.stdout.on('data', (chunk: Buffer) => this.onStdout(chunk));\n proc.stderr.on('data', (chunk: Buffer) => {\n this.emit('stderr', chunk.toString('utf-8'));\n });\n proc.on('exit', (code, signal) => {\n this.closed = true;\n this.emit('exit', { code, signal });\n // Surface a synthetic error to any pending requests.\n for (const [, resolve] of this.pending) {\n resolve({\n jsonrpc: '2.0',\n error: { code: -32000, message: `mcp child exited code=${code} signal=${signal}` },\n });\n }\n this.pending.clear();\n });\n proc.on('error', (err) => {\n this.emit('error', err);\n });\n }\n\n async stop(signal: NodeJS.Signals = 'SIGTERM'): Promise<void> {\n if (!this.proc || this.closed) return;\n return new Promise((resolve) => {\n this.proc!.once('exit', () => resolve());\n try { this.proc!.kill(signal); } catch { /* ignore */ }\n // hard-kill after 2s if it ignores SIGTERM\n setTimeout(() => { try { this.proc?.kill('SIGKILL'); } catch { /* ignore */ } }, 2_000).unref();\n });\n }\n\n /**\n * Send a JSON-RPC request to the MCP child and await its response.\n * Auto-generates an id if the caller doesn't supply one.\n */\n request(method: string, params?: unknown): Promise<McpMessage> {\n if (!this.proc) throw new Error('not started');\n const id = this.nextId++;\n const msg: McpMessage = { jsonrpc: '2.0', id, method, params };\n return new Promise((resolve) => {\n this.pending.set(id, resolve);\n this.write(msg);\n });\n }\n\n /** Send an MCP notification (no response expected). */\n notify(method: string, params?: unknown): void {\n if (!this.proc) throw new Error('not started');\n this.write({ jsonrpc: '2.0', method, params });\n }\n\n private write(msg: McpMessage): void {\n const json = JSON.stringify(msg);\n if (this.framing === 'lsp') {\n const body = Buffer.from(json, 'utf-8');\n this.proc!.stdin.write(`Content-Length: ${body.length}\\r\\n\\r\\n`);\n this.proc!.stdin.write(body);\n } else {\n // newline framing (default for unknown until we observe LSP from child)\n this.proc!.stdin.write(json + '\\n');\n }\n }\n\n private onStdout(chunk: Buffer): void {\n this.buf = Buffer.concat([this.buf, chunk]);\n\n if (this.framing === 'unknown') {\n // Sniff: LSP framing always starts with \"Content-Length:\" header.\n const peek = this.buf.subarray(0, Math.min(16, this.buf.length)).toString('utf-8');\n if (peek.startsWith('Content-Length:')) this.framing = 'lsp';\n else if (this.buf.includes(0x0a)) this.framing = 'newline';\n }\n\n if (this.framing === 'lsp') this.drainLsp();\n else if (this.framing === 'newline') this.drainNewline();\n }\n\n private drainNewline(): void {\n while (true) {\n const nl = this.buf.indexOf(0x0a);\n if (nl < 0) return;\n const line = this.buf.subarray(0, nl).toString('utf-8').trim();\n this.buf = this.buf.subarray(nl + 1);\n if (!line) continue;\n this.dispatchLine(line);\n }\n }\n\n private drainLsp(): void {\n while (true) {\n const headerEnd = this.buf.indexOf('\\r\\n\\r\\n');\n if (headerEnd < 0) return;\n const headers = this.buf.subarray(0, headerEnd).toString('utf-8');\n const m = /Content-Length:\\s*(\\d+)/i.exec(headers);\n if (!m) {\n // garbled headers — skip this chunk\n this.buf = this.buf.subarray(headerEnd + 4);\n continue;\n }\n const len = Number(m[1]);\n const start = headerEnd + 4;\n if (this.buf.length < start + len) return; // wait for more\n const body = this.buf.subarray(start, start + len).toString('utf-8');\n this.buf = this.buf.subarray(start + len);\n this.dispatchLine(body);\n }\n }\n\n private dispatchLine(line: string): void {\n let msg: McpMessage;\n try { msg = JSON.parse(line); }\n catch {\n this.emit('parseError', line);\n return;\n }\n if (msg.id !== undefined && this.pending.has(msg.id)) {\n const cb = this.pending.get(msg.id)!;\n this.pending.delete(msg.id);\n cb(msg);\n } else {\n // server-initiated notification or unmatched response\n this.emit('message', msg);\n }\n }\n}\n","// tag-mcp-bridge CLI entrypoint.\n//\n// Example:\n// tag-mcp-bridge \\\n// --relay https://tag-mcp-relay.example.com \\\n// --token \"$TAG_BRIDGE_JWT\" \\\n// --dev-id alice \\\n// --mcp-cmd \"python -m tariff_tools.server\"\n//\n// The dev-id is decoded from the JWT subject (`dev:<id>`) if not passed.\n\nimport { Bridge } from './bridge.js';\n\ninterface CliArgs {\n relayUrl: string;\n token: string;\n devId?: string;\n mcpCommand: string;\n mcpArgs: string[];\n mcpEnv: Record<string, string>;\n}\n\nfunction usage(): never {\n // eslint-disable-next-line no-console\n console.error(`tag-mcp-bridge — proxy a local MCP server to TAG via reverse-WS\n\nUsage:\n tag-mcp-bridge --relay <url> --token <jwt> --mcp-cmd <cmd> [--dev-id <id>] [-- <mcp-args>]\n\nFlags:\n --relay <url> Relay base URL, e.g. https://tag-mcp-relay.example.com\n Env: TAG_RELAY_URL\n --token <jwt> Bridge JWT (sub=dev:<id>, scope=[\"bridge\"])\n Env: TAG_BRIDGE_TOKEN\n --dev-id <id> Optional; derived from JWT subject if omitted.\n --mcp-cmd <cmd> Shell-style command to spawn the MCP server.\n Quoted form: --mcp-cmd \"python -m tariff_tools.server\"\n Env: TAG_MCP_CMD\n --mcp-env <k=v> Extra env var for the MCP child. Repeatable.\n -- Everything after this is appended to the MCP child argv.\n\nReconnect: exponential backoff up to 30s. SIGINT to stop cleanly.\n`);\n process.exit(1);\n}\n\nfunction parseArgs(argv: string[]): CliArgs {\n const out: Partial<CliArgs> & { mcpArgs: string[]; mcpEnv: Record<string, string> } = {\n mcpArgs: [],\n mcpEnv: {},\n };\n let mcpCmdLine = '';\n let sawSep = false;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (sawSep) { out.mcpArgs.push(a); continue; }\n if (a === '--') { sawSep = true; continue; }\n if (a === '--help' || a === '-h') usage();\n if (!a.startsWith('--')) {\n // eslint-disable-next-line no-console\n console.error(`unexpected positional arg: ${a}`);\n usage();\n }\n const eq = a.indexOf('=');\n let key: string, value: string;\n if (eq >= 0) {\n key = a.slice(2, eq);\n value = a.slice(eq + 1);\n } else {\n key = a.slice(2);\n value = argv[++i] ?? '';\n }\n switch (key) {\n case 'relay': out.relayUrl = value; break;\n case 'token': out.token = value; break;\n case 'dev-id': out.devId = value; break;\n case 'mcp-cmd': mcpCmdLine = value; break;\n case 'mcp-env': {\n const [k, ...rest] = value.split('=');\n if (k) out.mcpEnv[k] = rest.join('=');\n break;\n }\n default:\n // eslint-disable-next-line no-console\n console.error(`unknown flag: --${key}`);\n usage();\n }\n }\n\n out.relayUrl = out.relayUrl ?? process.env.TAG_RELAY_URL;\n out.token = out.token ?? process.env.TAG_BRIDGE_TOKEN;\n mcpCmdLine = mcpCmdLine || process.env.TAG_MCP_CMD || '';\n\n if (!out.relayUrl || !out.token || !mcpCmdLine) {\n // eslint-disable-next-line no-console\n console.error('missing required: --relay / --token / --mcp-cmd');\n usage();\n }\n\n if (!out.devId) {\n out.devId = decodeDevIdFromToken(out.token);\n }\n\n // Split mcp-cmd on whitespace (simple — no shell quoting). Anything\n // requiring quotes should use `-- arg arg` after the rest of the flags.\n const parts = mcpCmdLine.trim().split(/\\s+/);\n const command = parts[0]!;\n const args = parts.slice(1).concat(out.mcpArgs);\n\n return {\n relayUrl: out.relayUrl,\n token: out.token,\n devId: out.devId,\n mcpCommand: command,\n mcpArgs: args,\n mcpEnv: out.mcpEnv,\n };\n}\n\n/** Decode the `sub` claim of a JWT without verifying — we just want devId. */\nfunction decodeDevIdFromToken(token: string): string {\n const parts = token.split('.');\n if (parts.length < 2) throw new Error('invalid token: cannot derive dev id');\n try {\n const payload = JSON.parse(Buffer.from(parts[1]!, 'base64url').toString('utf-8')) as { sub?: string };\n if (!payload.sub?.startsWith('dev:')) throw new Error('token sub missing dev: prefix');\n return payload.sub.slice('dev:'.length);\n } catch (err) {\n throw new Error(`cannot derive dev id from token: ${(err as Error).message}`);\n }\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv.slice(2));\n // parseArgs guarantees these are set (calls usage()→never otherwise),\n // but the inferred return type lets `token`/`devId` widen to\n // `string | undefined` because the underlying `out` is `Partial<CliArgs>`.\n // Guard explicitly here so the Bridge constructor's strict types hold.\n if (!args.token) throw new Error('--token required (or set TAG_BRIDGE_TOKEN)');\n if (!args.devId) throw new Error('--dev-id required (or derivable from --token)');\n // eslint-disable-next-line no-console\n console.log(`[tag-mcp-bridge] dev=${args.devId} relay=${args.relayUrl} mcp=\"${args.mcpCommand} ${args.mcpArgs.join(' ')}\"`);\n const bridge = new Bridge({\n relayUrl: args.relayUrl,\n token: args.token,\n devId: args.devId,\n mcpCommand: args.mcpCommand,\n mcpArgs: args.mcpArgs,\n mcpEnv: args.mcpEnv,\n });\n const shutdown = async (sig: NodeJS.Signals) => {\n // eslint-disable-next-line no-console\n console.log(`[tag-mcp-bridge] received ${sig}, shutting down`);\n await bridge.stop();\n process.exit(0);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n await bridge.start();\n}\n\n// Run only when invoked directly (not when imported by tests).\nconst isMain = import.meta.url === `file://${process.argv[1]}`;\nif (isMain) {\n main().catch((err) => {\n // eslint-disable-next-line no-console\n console.error('[tag-mcp-bridge] fatal:', err);\n process.exit(1);\n });\n}\n\nexport { parseArgs };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/bridge.ts","../src/backoff.ts","../src/protocol.ts","../src/mcp-stdio.ts"],"sourcesContent":["// tag-mcp-bridge CLI entrypoint.\n//\n// Example:\n// tag-mcp-bridge \\\n// --relay https://tag-mcp-relay.example.com \\\n// --token \"$TAG_BRIDGE_JWT\" \\\n// --dev-id alice \\\n// --mcp-cmd \"python -m tariff_tools.server\"\n//\n// The dev-id is decoded from the JWT subject (`dev:<id>`) if not passed.\n\nimport { realpathSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { Bridge } from './bridge.js';\n\ninterface CliArgs {\n relayUrl: string;\n token: string;\n devId?: string;\n mcpCommand: string;\n mcpArgs: string[];\n mcpEnv: Record<string, string>;\n}\n\nfunction usage(): never {\n // eslint-disable-next-line no-console\n console.error(`tag-mcp-bridge — proxy a local MCP server to TAG via reverse-WS\n\nUsage:\n tag-mcp-bridge --relay <url> --token <jwt> --mcp-cmd <cmd> [--dev-id <id>] [-- <mcp-args>]\n\nFlags:\n --relay <url> Relay base URL, e.g. https://tag-mcp-relay.example.com\n Env: TAG_RELAY_URL\n --token <jwt> Bridge JWT (sub=dev:<id>, scope=[\"bridge\"])\n Env: TAG_BRIDGE_TOKEN\n --dev-id <id> Optional; derived from JWT subject if omitted.\n --mcp-cmd <cmd> Shell-style command to spawn the MCP server.\n Quoted form: --mcp-cmd \"python -m tariff_tools.server\"\n Env: TAG_MCP_CMD\n --mcp-env <k=v> Extra env var for the MCP child. Repeatable.\n -- Everything after this is appended to the MCP child argv.\n\nReconnect: exponential backoff up to 30s. SIGINT to stop cleanly.\n`);\n process.exit(1);\n}\n\nfunction parseArgs(argv: string[]): CliArgs {\n const out: Partial<CliArgs> & { mcpArgs: string[]; mcpEnv: Record<string, string> } = {\n mcpArgs: [],\n mcpEnv: {},\n };\n let mcpCmdLine = '';\n let sawSep = false;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (sawSep) { out.mcpArgs.push(a); continue; }\n if (a === '--') { sawSep = true; continue; }\n if (a === '--help' || a === '-h') usage();\n if (!a.startsWith('--')) {\n // eslint-disable-next-line no-console\n console.error(`unexpected positional arg: ${a}`);\n usage();\n }\n const eq = a.indexOf('=');\n let key: string, value: string;\n if (eq >= 0) {\n key = a.slice(2, eq);\n value = a.slice(eq + 1);\n } else {\n key = a.slice(2);\n value = argv[++i] ?? '';\n }\n switch (key) {\n case 'relay': out.relayUrl = value; break;\n case 'token': out.token = value; break;\n case 'dev-id': out.devId = value; break;\n case 'mcp-cmd': mcpCmdLine = value; break;\n case 'mcp-env': {\n const [k, ...rest] = value.split('=');\n if (k) out.mcpEnv[k] = rest.join('=');\n break;\n }\n default:\n // eslint-disable-next-line no-console\n console.error(`unknown flag: --${key}`);\n usage();\n }\n }\n\n out.relayUrl = out.relayUrl ?? process.env.TAG_RELAY_URL;\n out.token = out.token ?? process.env.TAG_BRIDGE_TOKEN;\n mcpCmdLine = mcpCmdLine || process.env.TAG_MCP_CMD || '';\n\n if (!out.relayUrl || !out.token || !mcpCmdLine) {\n // eslint-disable-next-line no-console\n console.error('missing required: --relay / --token / --mcp-cmd');\n usage();\n }\n\n if (!out.devId) {\n out.devId = decodeDevIdFromToken(out.token);\n }\n\n // Split mcp-cmd on whitespace (simple — no shell quoting). Anything\n // requiring quotes should use `-- arg arg` after the rest of the flags.\n const parts = mcpCmdLine.trim().split(/\\s+/);\n const command = parts[0]!;\n const args = parts.slice(1).concat(out.mcpArgs);\n\n return {\n relayUrl: out.relayUrl,\n token: out.token,\n devId: out.devId,\n mcpCommand: command,\n mcpArgs: args,\n mcpEnv: out.mcpEnv,\n };\n}\n\n/** Decode the `sub` claim of a JWT without verifying — we just want devId. */\nfunction decodeDevIdFromToken(token: string): string {\n const parts = token.split('.');\n if (parts.length < 2) throw new Error('invalid token: cannot derive dev id');\n try {\n const payload = JSON.parse(Buffer.from(parts[1]!, 'base64url').toString('utf-8')) as { sub?: string };\n if (!payload.sub?.startsWith('dev:')) throw new Error('token sub missing dev: prefix');\n return payload.sub.slice('dev:'.length);\n } catch (err) {\n throw new Error(`cannot derive dev id from token: ${(err as Error).message}`);\n }\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv.slice(2));\n // parseArgs guarantees these are set (calls usage()→never otherwise),\n // but the inferred return type lets `token`/`devId` widen to\n // `string | undefined` because the underlying `out` is `Partial<CliArgs>`.\n // Guard explicitly here so the Bridge constructor's strict types hold.\n if (!args.token) throw new Error('--token required (or set TAG_BRIDGE_TOKEN)');\n if (!args.devId) throw new Error('--dev-id required (or derivable from --token)');\n // eslint-disable-next-line no-console\n console.log(`[tag-mcp-bridge] dev=${args.devId} relay=${args.relayUrl} mcp=\"${args.mcpCommand} ${args.mcpArgs.join(' ')}\"`);\n const bridge = new Bridge({\n relayUrl: args.relayUrl,\n token: args.token,\n devId: args.devId,\n mcpCommand: args.mcpCommand,\n mcpArgs: args.mcpArgs,\n mcpEnv: args.mcpEnv,\n });\n const shutdown = async (sig: NodeJS.Signals) => {\n // eslint-disable-next-line no-console\n console.log(`[tag-mcp-bridge] received ${sig}, shutting down`);\n await bridge.stop();\n process.exit(0);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n await bridge.start();\n}\n\n// Run only when invoked directly (not when imported by tests).\n//\n// A naive `import.meta.url === \\`file://${process.argv[1]}\\`` check breaks\n// for globally-installed bins: npm symlinks the bin (e.g.\n// /usr/local/bin/tag-mcp-bridge -> .../dist/cli.js), so `process.argv[1]`\n// is the SYMLINK path while `import.meta.url` is the REAL path — they never\n// match, `main()` never runs, and the CLI silently exits 0. Resolve both\n// to real paths before comparing.\n//\n// Exported so the symlink-resilience can be unit-tested without spawning.\nexport function entryMatchesModule(\n entryArg: string | undefined,\n moduleUrl: string,\n): boolean {\n if (!entryArg) return false;\n try {\n return realpathSync(entryArg) === realpathSync(fileURLToPath(moduleUrl));\n } catch {\n return false;\n }\n}\nconst isMain = entryMatchesModule(process.argv[1], import.meta.url);\nif (isMain) {\n main().catch((err) => {\n // eslint-disable-next-line no-console\n console.error('[tag-mcp-bridge] fatal:', err);\n process.exit(1);\n });\n}\n\nexport { parseArgs };\n","// Bridge runtime: maintains a WebSocket to the relay, spawns the dev's\n// MCP child once, and translates relay RPC envelopes ↔ MCP JSON-RPC.\n//\n// Single-process design:\n// * one MCP child for the lifetime of the bridge (re-using its session\n// across relay reconnects — devs configure tools that may carry state).\n// * one WS connection to the relay at a time. On error/close we reconnect\n// with exponential backoff.\n// * each inbound rpc.request becomes one MCP request (`tools/list` or\n// `tools/call`). We synthesize a clean response envelope and forward.\n\nimport WebSocket from 'ws';\nimport { Backoff } from './backoff.js';\nimport { decode, encode, type Frame, type RpcRequestFrame } from './protocol.js';\nimport { McpStdioClient, type McpMessage } from './mcp-stdio.js';\n\nexport interface BridgeOptions {\n /** Relay base URL (without /dev/...). Trailing slash optional. */\n relayUrl: string;\n /** Bridge JWT — `dev:<id>` subject + scope=[\"bridge\"]. */\n token: string;\n /** Dev id; must match the JWT subject. */\n devId: string;\n /** Command to spawn for the MCP child. */\n mcpCommand: string;\n mcpArgs?: string[];\n mcpEnv?: Record<string, string>;\n /** Override for tests. */\n websocketCtor?: typeof WebSocket;\n /** Override backoff knobs. */\n backoff?: { base?: number; cap?: number; random?: () => number };\n /** Hook for tests + observability. */\n onConnect?: () => void;\n onDisconnect?: (info: { code: number; reason: string }) => void;\n onError?: (err: Error) => void;\n logger?: { info: (...a: unknown[]) => void; warn: (...a: unknown[]) => void; error: (...a: unknown[]) => void };\n}\n\ninterface ToolDescriptor {\n name: string;\n description?: string;\n inputSchema?: unknown;\n}\n\nexport class Bridge {\n private ws: WebSocket | null = null;\n private backoff: Backoff;\n private mcp: McpStdioClient | null = null;\n private toolCatalog: ToolDescriptor[] | null = null;\n private stopped = false;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private log: NonNullable<BridgeOptions['logger']>;\n\n constructor(private readonly opts: BridgeOptions) {\n this.backoff = new Backoff(opts.backoff);\n this.log = opts.logger ?? {\n info: (...a) => console.log('[bridge]', ...a),\n warn: (...a) => console.warn('[bridge]', ...a),\n error: (...a) => console.error('[bridge]', ...a),\n };\n }\n\n /** Start the MCP child + connect to relay. Returns once initial connect resolves OR backoff begins. */\n async start(): Promise<void> {\n if (this.mcp) throw new Error('already started');\n this.mcp = new McpStdioClient({\n command: this.opts.mcpCommand,\n args: this.opts.mcpArgs,\n env: this.opts.mcpEnv,\n });\n this.mcp.on('stderr', (s) => this.log.warn('mcp child stderr:', s.trim()));\n this.mcp.on('exit', (info) => this.log.error('mcp child exited', info));\n this.mcp.on('parseError', (line) => this.log.warn('mcp parse error', line));\n this.mcp.start();\n\n // Snapshot the tool catalog once so we can publish it on the `hello` frame.\n try {\n const resp = await this.mcp.request('tools/list');\n if (resp.result && typeof resp.result === 'object' && 'tools' in (resp.result as Record<string, unknown>)) {\n this.toolCatalog = ((resp.result as { tools: ToolDescriptor[] }).tools) ?? [];\n } else {\n this.toolCatalog = [];\n }\n } catch (err) {\n // Non-fatal: relay will fall back to live /list calls on demand.\n this.log.warn('initial tools/list failed; relay will use live RPC', (err as Error).message);\n }\n\n this.connect();\n }\n\n /** Stop the bridge: close WS, kill child, cancel reconnect. */\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }\n if (this.ws) {\n try { this.ws.close(1000, 'bridge-stop'); } catch { /* ignore */ }\n this.ws = null;\n }\n if (this.mcp) await this.mcp.stop();\n }\n\n private wsUrl(): string {\n const base = this.opts.relayUrl.replace(/\\/$/, '').replace(/^http/, 'ws');\n return `${base}/dev/${this.opts.devId}/connect`;\n }\n\n private connect(): void {\n if (this.stopped) return;\n const WS = this.opts.websocketCtor ?? WebSocket;\n const ws = new WS(this.wsUrl(), {\n headers: { Authorization: `Bearer ${this.opts.token}` },\n });\n this.ws = ws;\n\n ws.on('open', () => {\n this.backoff.reset();\n this.log.info('connected to relay');\n const helloTools = this.toolCatalog ?? [];\n try {\n ws.send(encode({ kind: 'hello', devId: this.opts.devId, version: '0.1.0', tools: helloTools }));\n } catch (err) {\n this.log.warn('failed to send hello', (err as Error).message);\n }\n this.opts.onConnect?.();\n });\n\n ws.on('message', (data) => {\n let frame: Frame;\n try { frame = decode(data as Buffer); }\n catch (err) { this.log.warn('malformed frame from relay', (err as Error).message); return; }\n if (frame.kind === 'rpc.request') void this.handleRpc(ws, frame);\n });\n\n ws.on('close', (code, reason) => {\n const r = reason?.toString() ?? '';\n this.log.warn(`relay disconnected code=${code} reason=${r}`);\n this.opts.onDisconnect?.({ code, reason: r });\n this.ws = null;\n // Code 4001 = relay says we were kicked because a fresher bridge took\n // over our slot. In that case it's almost always wrong for us to\n // reconnect — we'd just kick the new one. Surface and stop.\n if (code === 4001) {\n this.log.error('kicked by newer connection — exiting reconnect loop');\n return;\n }\n this.scheduleReconnect();\n });\n\n ws.on('error', (err) => {\n this.opts.onError?.(err);\n // close event fires after error; reconnect handling lives there.\n });\n }\n\n private scheduleReconnect(): void {\n if (this.stopped) return;\n const delay = this.backoff.next();\n this.log.info(`reconnect in ${delay}ms (attempt ${this.backoff.currentAttempt})`);\n this.reconnectTimer = setTimeout(() => this.connect(), delay);\n }\n\n private async handleRpc(ws: WebSocket, frame: RpcRequestFrame): Promise<void> {\n if (!this.mcp) {\n this.respond(ws, frame.id, 502, { error: 'mcp child not running' });\n return;\n }\n try {\n let mcpResp: McpMessage;\n if (frame.method === 'list') {\n mcpResp = await this.mcp.request('tools/list');\n } else if (frame.method === 'call') {\n mcpResp = await this.mcp.request('tools/call', {\n name: frame.path,\n arguments: (frame.body as Record<string, unknown>) ?? {},\n });\n } else {\n this.respond(ws, frame.id, 400, { error: `unsupported method ${(frame as { method: string }).method}` });\n return;\n }\n if (mcpResp.error) {\n this.respond(ws, frame.id, 500, { error: mcpResp.error.message, code: mcpResp.error.code, data: mcpResp.error.data });\n } else {\n this.respond(ws, frame.id, 200, mcpResp.result ?? null);\n }\n } catch (err) {\n this.respond(ws, frame.id, 500, { error: (err as Error).message });\n }\n }\n\n private respond(ws: WebSocket, id: string, status: number, body: unknown): void {\n if (ws.readyState !== WebSocket.OPEN) return;\n try {\n ws.send(encode({ kind: 'rpc.response', id, status, body }));\n } catch (err) {\n this.log.warn('failed to send rpc.response', (err as Error).message);\n }\n }\n}\n","// Exponential backoff with full jitter, capped.\n//\n// Reconnect schedule for transient WS errors:\n// attempt 0 → 500ms\n// attempt 1 → 1000ms\n// attempt 2 → 2000ms\n// ...\n// capped at 30_000ms (per spec).\n//\n// Full jitter (sleep ∈ [0, exp_delay]) avoids reconnect storms when the\n// relay reboots and N bridges all wake at once.\n\nexport interface BackoffOpts {\n base?: number;\n cap?: number;\n /** Override RNG for deterministic tests. */\n random?: () => number;\n}\n\nexport class Backoff {\n private attempt = 0;\n private base: number;\n private cap: number;\n private random: () => number;\n\n constructor(opts: BackoffOpts = {}) {\n this.base = opts.base ?? 500;\n this.cap = opts.cap ?? 30_000;\n this.random = opts.random ?? Math.random;\n }\n\n reset(): void { this.attempt = 0; }\n\n /** Return the next delay (ms) and advance attempt counter. */\n next(): number {\n const exp = Math.min(this.cap, this.base * 2 ** this.attempt);\n this.attempt += 1;\n return Math.floor(this.random() * exp);\n }\n\n /** Test introspection: current attempt count (0 after reset). */\n get currentAttempt(): number { return this.attempt; }\n}\n","// Wire protocol shared with apps/mcp-relay. Duplicated locally (not imported\n// from the relay package) so this CLI can be `npm install -g`'d on a dev\n// laptop without pulling the relay's runtime deps. The schema is stable —\n// any change to the protocol must land in both files in the same PR.\n//\n// See apps/mcp-relay/src/protocol.ts for the canonical doc-comments.\n\nexport type RpcRequestFrame = {\n kind: 'rpc.request';\n id: string;\n method: 'list' | 'call';\n path: string;\n body?: unknown;\n};\n\nexport type RpcResponseFrame = {\n kind: 'rpc.response';\n id: string;\n status: number;\n body?: unknown;\n};\n\nexport type HelloFrame = {\n kind: 'hello';\n devId: string;\n version: string;\n tools?: unknown;\n};\n\nexport type Frame =\n | RpcRequestFrame\n | RpcResponseFrame\n | HelloFrame\n | { kind: 'ping' }\n | { kind: 'pong' };\n\nexport function encode(frame: Frame): string {\n return JSON.stringify(frame);\n}\n\nexport function decode(raw: string | Buffer): Frame {\n const text = typeof raw === 'string' ? raw : raw.toString('utf-8');\n const parsed = JSON.parse(text) as Frame;\n if (!parsed || typeof parsed !== 'object' || !('kind' in parsed)) {\n throw new Error('invalid frame: missing kind');\n }\n return parsed;\n}\n","// MCP-over-stdio client: spawns the dev's MCP server (e.g. `tariff-tools\n// serve` or `python -m tariff_tools.server`) and speaks line-delimited\n// JSON-RPC 2.0 to it.\n//\n// MCP's standard stdio transport uses LSP-style \"Content-Length\" framing.\n// We support BOTH framings:\n// * newline-delimited JSON (one JSON object per line) — common for\n// hand-rolled servers + simpler to test.\n// * Content-Length-prefixed JSON — what the MCP reference server emits.\n//\n// The bridge auto-detects which mode the child is using by sniffing the\n// first 16 bytes of output. If they start with \"Content-Length:\" we use\n// LSP framing; otherwise we use newline-delimited.\n\nimport { spawn, type ChildProcessWithoutNullStreams } from 'child_process';\nimport { EventEmitter } from 'events';\n\nexport type McpMessage = {\n jsonrpc: '2.0';\n id?: string | number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n};\n\ntype Framing = 'newline' | 'lsp' | 'unknown';\n\nexport interface McpStdioOptions {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n}\n\nexport class McpStdioClient extends EventEmitter {\n private proc: ChildProcessWithoutNullStreams | null = null;\n private framing: Framing = 'unknown';\n private buf = Buffer.alloc(0);\n private nextId = 1;\n private pending = new Map<string | number, (msg: McpMessage) => void>();\n private closed = false;\n\n constructor(private readonly opts: McpStdioOptions) {\n super();\n }\n\n start(): void {\n if (this.proc) throw new Error('already started');\n const proc = spawn(this.opts.command, this.opts.args ?? [], {\n env: { ...process.env, ...this.opts.env },\n cwd: this.opts.cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n this.proc = proc;\n proc.stdout.on('data', (chunk: Buffer) => this.onStdout(chunk));\n proc.stderr.on('data', (chunk: Buffer) => {\n this.emit('stderr', chunk.toString('utf-8'));\n });\n proc.on('exit', (code, signal) => {\n this.closed = true;\n this.emit('exit', { code, signal });\n // Surface a synthetic error to any pending requests.\n for (const [, resolve] of this.pending) {\n resolve({\n jsonrpc: '2.0',\n error: { code: -32000, message: `mcp child exited code=${code} signal=${signal}` },\n });\n }\n this.pending.clear();\n });\n proc.on('error', (err) => {\n this.emit('error', err);\n });\n }\n\n async stop(signal: NodeJS.Signals = 'SIGTERM'): Promise<void> {\n if (!this.proc || this.closed) return;\n return new Promise((resolve) => {\n this.proc!.once('exit', () => resolve());\n try { this.proc!.kill(signal); } catch { /* ignore */ }\n // hard-kill after 2s if it ignores SIGTERM\n setTimeout(() => { try { this.proc?.kill('SIGKILL'); } catch { /* ignore */ } }, 2_000).unref();\n });\n }\n\n /**\n * Send a JSON-RPC request to the MCP child and await its response.\n * Auto-generates an id if the caller doesn't supply one.\n */\n request(method: string, params?: unknown): Promise<McpMessage> {\n if (!this.proc) throw new Error('not started');\n const id = this.nextId++;\n const msg: McpMessage = { jsonrpc: '2.0', id, method, params };\n return new Promise((resolve) => {\n this.pending.set(id, resolve);\n this.write(msg);\n });\n }\n\n /** Send an MCP notification (no response expected). */\n notify(method: string, params?: unknown): void {\n if (!this.proc) throw new Error('not started');\n this.write({ jsonrpc: '2.0', method, params });\n }\n\n private write(msg: McpMessage): void {\n const json = JSON.stringify(msg);\n if (this.framing === 'lsp') {\n const body = Buffer.from(json, 'utf-8');\n this.proc!.stdin.write(`Content-Length: ${body.length}\\r\\n\\r\\n`);\n this.proc!.stdin.write(body);\n } else {\n // newline framing (default for unknown until we observe LSP from child)\n this.proc!.stdin.write(json + '\\n');\n }\n }\n\n private onStdout(chunk: Buffer): void {\n this.buf = Buffer.concat([this.buf, chunk]);\n\n if (this.framing === 'unknown') {\n // Sniff: LSP framing always starts with \"Content-Length:\" header.\n const peek = this.buf.subarray(0, Math.min(16, this.buf.length)).toString('utf-8');\n if (peek.startsWith('Content-Length:')) this.framing = 'lsp';\n else if (this.buf.includes(0x0a)) this.framing = 'newline';\n }\n\n if (this.framing === 'lsp') this.drainLsp();\n else if (this.framing === 'newline') this.drainNewline();\n }\n\n private drainNewline(): void {\n while (true) {\n const nl = this.buf.indexOf(0x0a);\n if (nl < 0) return;\n const line = this.buf.subarray(0, nl).toString('utf-8').trim();\n this.buf = this.buf.subarray(nl + 1);\n if (!line) continue;\n this.dispatchLine(line);\n }\n }\n\n private drainLsp(): void {\n while (true) {\n const headerEnd = this.buf.indexOf('\\r\\n\\r\\n');\n if (headerEnd < 0) return;\n const headers = this.buf.subarray(0, headerEnd).toString('utf-8');\n const m = /Content-Length:\\s*(\\d+)/i.exec(headers);\n if (!m) {\n // garbled headers — skip this chunk\n this.buf = this.buf.subarray(headerEnd + 4);\n continue;\n }\n const len = Number(m[1]);\n const start = headerEnd + 4;\n if (this.buf.length < start + len) return; // wait for more\n const body = this.buf.subarray(start, start + len).toString('utf-8');\n this.buf = this.buf.subarray(start + len);\n this.dispatchLine(body);\n }\n }\n\n private dispatchLine(line: string): void {\n let msg: McpMessage;\n try { msg = JSON.parse(line); }\n catch {\n this.emit('parseError', line);\n return;\n }\n if (msg.id !== undefined && this.pending.has(msg.id)) {\n const cb = this.pending.get(msg.id)!;\n this.pending.delete(msg.id);\n cb(msg);\n } else {\n // server-initiated notification or unmatched response\n this.emit('message', msg);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tournesol-tag/mcp-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "TAG MCP bridge — runs your local MCP server and proxies stdio ↔ TAG relay WebSocket so hosted TAG / engine can call your tools (J1, ADR-033).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tag",
|