@turingspark/mcp-debug 0.1.4 → 0.2.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 CHANGED
@@ -169,6 +169,10 @@ MCP Server (your server)
169
169
 
170
170
  The proxy is fully transparent — it passes all messages through without modification. Your MCP host and server don't know it's there.
171
171
 
172
+ ## Documentation
173
+
174
+ Please visit [https://turingspark.com/tools/mcp-debugger](https://turingspark.com/tools/mcp-debugger) for more examples.
175
+
172
176
  ## License
173
177
 
174
178
  MIT
package/dist/index.js CHANGED
@@ -208,6 +208,7 @@ var WsServer = class {
208
208
  port;
209
209
  clients = /* @__PURE__ */ new Set();
210
210
  messageHistory = [];
211
+ replayHandler = null;
211
212
  constructor(port) {
212
213
  this.port = port;
213
214
  this.httpServer = createServer((req, res) => {
@@ -246,6 +247,15 @@ var WsServer = class {
246
247
  for (const msg of this.messageHistory) {
247
248
  ws.send(msg);
248
249
  }
250
+ ws.on("message", (data) => {
251
+ try {
252
+ const msg = JSON.parse(data.toString());
253
+ if (msg.type === "replay" && typeof msg.rawJson === "string" && this.replayHandler) {
254
+ this.replayHandler(msg.rawJson);
255
+ }
256
+ } catch {
257
+ }
258
+ });
249
259
  ws.on("close", () => this.clients.delete(ws));
250
260
  ws.on("error", () => this.clients.delete(ws));
251
261
  });
@@ -267,6 +277,9 @@ var WsServer = class {
267
277
  }
268
278
  return null;
269
279
  }
280
+ onReplay(handler) {
281
+ this.replayHandler = handler;
282
+ }
270
283
  async start() {
271
284
  return new Promise((resolve, reject) => {
272
285
  this.httpServer.on("error", (err) => {
@@ -322,6 +335,11 @@ var Proxy = class {
322
335
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
323
336
  endedAt: null
324
337
  });
338
+ this.wsServer.onReplay((rawJson) => {
339
+ if (this.child?.stdin?.writable) {
340
+ this.child.stdin.write(rawJson + "\n");
341
+ }
342
+ });
325
343
  await this.wsServer.start();
326
344
  this.interceptor.on("message", (msg) => {
327
345
  this.store.insertMessage(msg);
@@ -408,7 +426,8 @@ var Proxy = class {
408
426
  const parsed = JSON.parse(msg.rawJson);
409
427
  const isError = parsed.result?.isError || parsed.error != null;
410
428
  const status = isError ? " ERROR" : "";
411
- process.stderr.write(`[mcp-debug] ${arrow} ${method} id=${msg.rpcId ?? "-"}${latency}${status}
429
+ const tokens = Math.ceil(msg.rawJson.length / 4);
430
+ process.stderr.write(`[mcp-debug] ${arrow} ${method} id=${msg.rpcId ?? "-"}${latency} ~${tokens} tokens${status}
412
431
  `);
413
432
  process.stderr.write(` ${JSON.stringify(parsed, null, 2).split("\n").join("\n ")}
414
433
  `);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/proxy.ts","../src/interceptor.ts","../src/store.ts","../src/ws-server.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { Proxy } from './proxy.js';\n\nconst DEFAULT_DB_PATH = join(homedir(), '.mcp-debug', 'data.db');\nconst DEFAULT_PORT = 8100;\n\n// Check for -- separator before commander parses.\n// Everything after -- is treated as the server command + args directly,\n// bypassing --cmd and the custom string parser (handles spaces in paths, etc.)\nconst doubleDashIdx = process.argv.indexOf('--');\nconst rawServerArgs = doubleDashIdx !== -1 ? process.argv.slice(doubleDashIdx + 1) : null;\nif (doubleDashIdx !== -1) {\n process.argv.splice(doubleDashIdx); // remove -- and everything after so commander ignores it\n}\n\nconst program = new Command()\n .name('mcp-debug')\n .description('MCP Inspector & Debugger — transparent proxy for MCP stdio traffic')\n .option('--cmd <command>', 'The MCP server command to proxy (e.g. \"node server.js\")')\n .option('--port <number>', 'Dashboard port', String(DEFAULT_PORT))\n .option('--db <path>', 'SQLite database path', DEFAULT_DB_PATH)\n .option('--quiet', 'Suppress stderr output', false)\n .option('--verbose', 'Print full JSON-RPC messages to stderr', false)\n .option('--open', 'Auto-open dashboard in browser', false)\n .action(async (opts) => {\n // Validate: must have either --cmd or -- <server args>\n if (!opts.cmd && (!rawServerArgs || rawServerArgs.length === 0)) {\n process.stderr.write(\n '[mcp-debug] Error: specify a server command via --cmd \"...\" or using -- <command> [args]\\n' +\n ' Examples:\\n' +\n ' mcp-debug --cmd \"node server.js\"\\n' +\n ' mcp-debug -- node server.js\\n' +\n ' mcp-debug -- python3 \"/path/with spaces/server.py\"\\n'\n );\n process.exit(1);\n }\n\n const proxy = new Proxy({\n cmd: opts.cmd,\n cmdArgs: rawServerArgs ?? undefined,\n dbPath: opts.db,\n port: parseInt(opts.port, 10),\n quiet: opts.quiet,\n verbose: opts.verbose,\n });\n\n await proxy.start();\n\n if (opts.open) {\n const open = (await import('open')).default;\n await open(`http://localhost:${opts.port}`);\n }\n });\n\nprogram.parse();\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { Interceptor } from './interceptor.js';\nimport { Store } from './store.js';\nimport { WsServer } from './ws-server.js';\nimport type { InterceptedMessage, StderrLog } from './types.js';\n\nexport interface ProxyOptions {\n cmd?: string;\n cmdArgs?: string[]; // raw argv from -- separator (takes precedence over cmd)\n dbPath: string;\n port: number;\n quiet: boolean;\n verbose: boolean;\n}\n\nexport class Proxy {\n private child: ChildProcess | null = null;\n private interceptor: Interceptor;\n private store: Store;\n private wsServer: WsServer;\n private sessionId: string;\n private options: ProxyOptions;\n\n constructor(options: ProxyOptions) {\n this.options = options;\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n this.store = new Store(options.dbPath);\n this.interceptor = new Interceptor(this.sessionId);\n this.wsServer = new WsServer(options.port);\n }\n\n async start(): Promise<void> {\n // Create session\n const serverCmd = this.options.cmdArgs\n ? this.options.cmdArgs.join(' ')\n : this.options.cmd!;\n this.store.createSession({\n id: this.sessionId,\n serverCmd,\n startedAt: new Date().toISOString(),\n endedAt: null,\n });\n\n // Start WebSocket server for dashboard\n await this.wsServer.start();\n\n // Listen for intercepted messages\n this.interceptor.on('message', (msg: InterceptedMessage) => {\n this.store.insertMessage(msg);\n this.wsServer.broadcast({ type: 'message', message: msg });\n\n if (!this.options.quiet) {\n this.logToStderr(msg);\n }\n });\n\n // Spawn the real MCP server\n // cmdArgs (from -- separator) takes precedence — no parsing needed, handles spaces in paths\n const [command, ...args] = this.options.cmdArgs ?? this.parseCommand(this.options.cmd!);\n this.child = spawn(command, args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: { ...process.env },\n });\n\n // Create transform streams for interception\n const hostToServer = this.interceptor.createTransform('host_to_server');\n const serverToHost = this.interceptor.createTransform('server_to_host');\n\n // Wire up pipes:\n // Host stdin → interceptor → Server stdin\n process.stdin.pipe(hostToServer).pipe(this.child.stdin!);\n\n // Server stdout → interceptor → Host stdout\n this.child.stdout!.pipe(serverToHost).pipe(process.stdout);\n\n // Server stderr → capture for logging\n this.child.stderr!.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().split('\\n').filter((l) => l.trim());\n for (const line of lines) {\n const log = this.store.insertStderrLog(this.sessionId, line);\n this.wsServer.broadcast({ type: 'stderr', log });\n }\n });\n\n // Handle child process exit\n this.child.on('exit', (code, signal) => {\n if (!this.options.quiet) {\n process.stderr.write(\n `[mcp-debug] Server exited with ${signal ? `signal ${signal}` : `code ${code}`}\\n`\n );\n }\n this.shutdown();\n });\n\n this.child.on('error', (err) => {\n process.stderr.write(`[mcp-debug] Failed to start server: ${err.message}\\n`);\n this.shutdown(1);\n });\n\n // Handle signals for clean shutdown\n process.on('SIGTERM', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n\n // Handle host stdin closing (host disconnected)\n process.stdin.on('end', () => {\n this.shutdown();\n });\n\n if (!this.options.quiet) {\n const displayCmd = this.options.cmdArgs\n ? this.options.cmdArgs.join(' ')\n : this.options.cmd!;\n process.stderr.write(`[mcp-debug] Proxying: ${displayCmd}\\n`);\n process.stderr.write(`[mcp-debug] Dashboard: http://localhost:${this.options.port}\\n`);\n process.stderr.write(`[mcp-debug] Session: ${this.sessionId}\\n`);\n }\n }\n\n private parseCommand(cmd: string): string[] {\n // Simple shell-like parsing: split on spaces, respecting quotes\n const parts: string[] = [];\n let current = '';\n let inQuote: string | null = null;\n\n for (const char of cmd) {\n if (inQuote) {\n if (char === inQuote) {\n inQuote = null;\n } else {\n current += char;\n }\n } else if (char === '\"' || char === \"'\") {\n inQuote = char;\n } else if (char === ' ') {\n if (current) {\n parts.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n if (current) parts.push(current);\n\n return parts;\n }\n\n private logToStderr(msg: InterceptedMessage): void {\n const arrow = msg.direction === 'host_to_server' ? '→' : '←';\n const method = msg.method ?? '(response)';\n const latency = msg.latencyMs != null ? ` [${msg.latencyMs}ms]` : '';\n\n if (this.options.verbose) {\n const parsed = JSON.parse(msg.rawJson);\n const isError = parsed.result?.isError || parsed.error != null;\n const status = isError ? ' ERROR' : '';\n process.stderr.write(`[mcp-debug] ${arrow} ${method} id=${msg.rpcId ?? '-'}${latency}${status}\\n`);\n process.stderr.write(` ${JSON.stringify(parsed, null, 2).split('\\n').join('\\n ')}\\n`);\n } else {\n process.stderr.write(`[mcp-debug] ${arrow} ${method}${latency}\\n`);\n }\n }\n\n private shuttingDown = false;\n private shutdown(exitCode = 0): void {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n\n this.store.endSession(this.sessionId);\n this.wsServer.broadcast({ type: 'session_end', sessionId: this.sessionId });\n\n if (this.child && !this.child.killed) {\n this.child.kill('SIGTERM');\n }\n\n this.wsServer.close();\n this.store.close();\n process.exit(exitCode);\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Transform, type TransformCallback } from 'node:stream';\nimport type { MessageDirection, JsonRpcMessage, InterceptedMessage } from './types.js';\n\ninterface InterceptorEvents {\n message: [InterceptedMessage];\n}\n\nexport class Interceptor extends EventEmitter<InterceptorEvents> {\n private sessionId: string;\n private pendingRequests = new Map<string | number, { method: string; timestamp: number }>();\n private messageCounter = 0;\n\n constructor(sessionId: string) {\n super();\n this.sessionId = sessionId;\n }\n\n /**\n * Creates a Transform stream that intercepts JSON-RPC messages\n * while passing the raw data through unmodified.\n */\n createTransform(direction: MessageDirection): Transform {\n let buffer = '';\n\n return new Transform({\n transform: (chunk: Buffer, _encoding: string, callback: TransformCallback) => {\n // Always pass the raw data through immediately\n callback(null, chunk);\n\n // Parse asynchronously so we don't block the pipe\n buffer += chunk.toString();\n const lines = buffer.split('\\n');\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n this.processLine(trimmed, direction);\n }\n },\n flush: (callback: TransformCallback) => {\n // Process any remaining data\n if (buffer.trim()) {\n this.processLine(buffer.trim(), direction);\n }\n callback();\n },\n });\n }\n\n private processLine(line: string, direction: MessageDirection): void {\n let parsed: JsonRpcMessage;\n try {\n parsed = JSON.parse(line);\n } catch {\n // Not valid JSON — skip (could be partial data or non-JSON stderr)\n return;\n }\n\n if (parsed.jsonrpc !== '2.0') return;\n\n const now = Date.now();\n const timestamp = new Date(now).toISOString();\n\n let rpcId: string | number | null = null;\n let method: string | null = null;\n let latencyMs: number | null = null;\n\n if ('method' in parsed) {\n // Request or notification\n method = parsed.method;\n if ('id' in parsed && parsed.id != null) {\n rpcId = parsed.id;\n this.pendingRequests.set(rpcId, { method, timestamp: now });\n }\n } else if ('id' in parsed) {\n // Response\n rpcId = parsed.id;\n const pending = this.pendingRequests.get(rpcId);\n if (pending) {\n latencyMs = now - pending.timestamp;\n method = pending.method;\n this.pendingRequests.delete(rpcId);\n }\n if ('error' in parsed && parsed.error) {\n method = method ? `${method} (error)` : '(error)';\n }\n }\n\n const message: InterceptedMessage = {\n id: ++this.messageCounter,\n sessionId: this.sessionId,\n direction,\n rpcId,\n method,\n rawJson: line,\n timestamp,\n latencyMs,\n };\n\n this.emit('message', message);\n }\n}\n","import Database from 'better-sqlite3';\nimport { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { InterceptedMessage, Session, StderrLog } from './types.js';\n\nexport class Store {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n mkdirSync(dirname(dbPath), { recursive: true });\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.migrate();\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n server_cmd TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n direction TEXT NOT NULL CHECK(direction IN ('host_to_server', 'server_to_host')),\n rpc_id TEXT,\n method TEXT,\n raw_json TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n latency_ms INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS stderr_logs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n line TEXT NOT NULL,\n timestamp TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);\n CREATE INDEX IF NOT EXISTS idx_messages_method ON messages(method);\n CREATE INDEX IF NOT EXISTS idx_stderr_session ON stderr_logs(session_id);\n `);\n }\n\n createSession(session: Session): void {\n this.db.prepare(\n 'INSERT INTO sessions (id, server_cmd, started_at, ended_at) VALUES (?, ?, ?, ?)'\n ).run(session.id, session.serverCmd, session.startedAt, session.endedAt);\n }\n\n endSession(sessionId: string): void {\n this.db.prepare(\n 'UPDATE sessions SET ended_at = ? WHERE id = ?'\n ).run(new Date().toISOString(), sessionId);\n }\n\n insertMessage(msg: InterceptedMessage): void {\n this.db.prepare(\n `INSERT INTO messages (session_id, direction, rpc_id, method, raw_json, timestamp, latency_ms)\n VALUES (?, ?, ?, ?, ?, ?, ?)`\n ).run(\n msg.sessionId,\n msg.direction,\n msg.rpcId != null ? String(msg.rpcId) : null,\n msg.method,\n msg.rawJson,\n msg.timestamp,\n msg.latencyMs,\n );\n }\n\n insertStderrLog(sessionId: string, line: string): StderrLog {\n const timestamp = new Date().toISOString();\n const result = this.db.prepare(\n 'INSERT INTO stderr_logs (session_id, line, timestamp) VALUES (?, ?, ?)'\n ).run(sessionId, line, timestamp);\n\n return {\n id: Number(result.lastInsertRowid),\n sessionId,\n line,\n timestamp,\n };\n }\n\n getRecentMessages(sessionId: string, limit = 100): InterceptedMessage[] {\n return this.db.prepare(\n `SELECT id, session_id as sessionId, direction, rpc_id as rpcId, method, raw_json as rawJson, timestamp, latency_ms as latencyMs\n FROM messages WHERE session_id = ? ORDER BY id DESC LIMIT ?`\n ).all(sessionId, limit) as InterceptedMessage[];\n }\n\n getSession(sessionId: string): Session | undefined {\n return this.db.prepare(\n 'SELECT id, server_cmd as serverCmd, started_at as startedAt, ended_at as endedAt FROM sessions WHERE id = ?'\n ).get(sessionId) as Session | undefined;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { createServer, type Server as HttpServer } from 'node:http';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport type { WsMessage } from './types.js';\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n};\n\nexport class WsServer {\n private httpServer: HttpServer;\n private wss: WebSocketServer;\n private port: number;\n private clients = new Set<WebSocket>();\n private messageHistory: string[] = [];\n\n constructor(port: number) {\n this.port = port;\n\n // Serve static dashboard files\n this.httpServer = createServer((req, res) => {\n const webDistDir = this.getWebDistDir();\n if (!webDistDir) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <!DOCTYPE html>\n <html>\n <body style=\"font-family: system-ui; padding: 2rem; background: #1a1a2e; color: #e0e0e0;\">\n <h1>MCP Debug Dashboard</h1>\n <p>Web dashboard not built yet. Run <code>npm run build</code> in the web package.</p>\n <p>WebSocket is active on this port — the dashboard will connect when built.</p>\n </body>\n </html>\n `);\n return;\n }\n\n let filePath = join(webDistDir, req.url === '/' ? 'index.html' : req.url!);\n if (!existsSync(filePath)) {\n // SPA fallback\n filePath = join(webDistDir, 'index.html');\n }\n\n try {\n const content = readFileSync(filePath);\n const ext = extname(filePath);\n res.writeHead(200, { 'Content-Type': MIME_TYPES[ext] || 'application/octet-stream' });\n res.end(content);\n } catch {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n this.wss = new WebSocketServer({ server: this.httpServer });\n\n this.wss.on('connection', (ws) => {\n this.clients.add(ws);\n\n // Replay message history to new clients\n for (const msg of this.messageHistory) {\n ws.send(msg);\n }\n\n ws.on('close', () => this.clients.delete(ws));\n ws.on('error', () => this.clients.delete(ws));\n });\n }\n\n private getWebDistDir(): string | null {\n const __dirname = fileURLToPath(new URL('.', import.meta.url));\n const candidates = [\n join(__dirname, '..', 'web-dist'), // npm: packages/cli/web-dist (bundled for distribution)\n join(__dirname, '..', '..', 'web', 'dist'), // dev: packages/cli/src -> packages/web/dist\n join(__dirname, '..', '..', '..', 'web', 'dist'), // built: packages/cli/dist -> packages/web/dist\n ];\n for (const dir of candidates) {\n if (existsSync(join(dir, 'index.html'))) {\n return dir;\n }\n }\n return null;\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n process.stderr.write(\n `[mcp-debug] Port ${this.port} is in use. Try --port <number>\\n`\n );\n }\n reject(err);\n });\n this.httpServer.listen(this.port, () => resolve());\n });\n }\n\n broadcast(msg: WsMessage): void {\n const data = JSON.stringify(msg);\n this.messageHistory.push(data);\n for (const client of this.clients) {\n if (client.readyState === client.OPEN) {\n client.send(data);\n }\n }\n }\n\n close(): void {\n for (const client of this.clients) {\n client.close();\n }\n this.wss.close();\n this.httpServer.close();\n }\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,QAAAA,aAAY;AACrB,SAAS,eAAe;;;ACFxB,SAAS,aAAgC;;;ACAzC,SAAS,oBAAoB;AAC7B,SAAS,iBAAyC;AAO3C,IAAM,cAAN,cAA0B,aAAgC;AAAA,EACvD;AAAA,EACA,kBAAkB,oBAAI,IAA4D;AAAA,EAClF,iBAAiB;AAAA,EAEzB,YAAY,WAAmB;AAC7B,UAAM;AACN,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,WAAwC;AACtD,QAAI,SAAS;AAEb,WAAO,IAAI,UAAU;AAAA,MACnB,WAAW,CAAC,OAAe,WAAmB,aAAgC;AAE5E,iBAAS,MAAM,KAAK;AAGpB,kBAAU,MAAM,SAAS;AACzB,cAAM,QAAQ,OAAO,MAAM,IAAI;AAE/B,iBAAS,MAAM,IAAI,KAAK;AAExB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,KAAK,KAAK;AAC1B,cAAI,CAAC,QAAS;AACd,eAAK,YAAY,SAAS,SAAS;AAAA,QACrC;AAAA,MACF;AAAA,MACA,OAAO,CAAC,aAAgC;AAEtC,YAAI,OAAO,KAAK,GAAG;AACjB,eAAK,YAAY,OAAO,KAAK,GAAG,SAAS;AAAA,QAC3C;AACA,iBAAS;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,MAAc,WAAmC;AACnE,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AAEN;AAAA,IACF;AAEA,QAAI,OAAO,YAAY,MAAO;AAE9B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,IAAI,KAAK,GAAG,EAAE,YAAY;AAE5C,QAAI,QAAgC;AACpC,QAAI,SAAwB;AAC5B,QAAI,YAA2B;AAE/B,QAAI,YAAY,QAAQ;AAEtB,eAAS,OAAO;AAChB,UAAI,QAAQ,UAAU,OAAO,MAAM,MAAM;AACvC,gBAAQ,OAAO;AACf,aAAK,gBAAgB,IAAI,OAAO,EAAE,QAAQ,WAAW,IAAI,CAAC;AAAA,MAC5D;AAAA,IACF,WAAW,QAAQ,QAAQ;AAEzB,cAAQ,OAAO;AACf,YAAM,UAAU,KAAK,gBAAgB,IAAI,KAAK;AAC9C,UAAI,SAAS;AACX,oBAAY,MAAM,QAAQ;AAC1B,iBAAS,QAAQ;AACjB,aAAK,gBAAgB,OAAO,KAAK;AAAA,MACnC;AACA,UAAI,WAAW,UAAU,OAAO,OAAO;AACrC,iBAAS,SAAS,GAAG,MAAM,aAAa;AAAA,MAC1C;AAAA,IACF;AAEA,UAAM,UAA8B;AAAA,MAClC,IAAI,EAAE,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAEA,SAAK,KAAK,WAAW,OAAO;AAAA,EAC9B;AACF;;;ACxGA,OAAO,cAAc;AACrB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AAGjB,IAAM,QAAN,MAAY;AAAA,EACT;AAAA,EAER,YAAY,QAAgB;AAC1B,cAAU,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,SAAK,KAAK,IAAI,SAAS,MAAM;AAC7B,SAAK,GAAG,OAAO,oBAAoB;AACnC,SAAK,GAAG,OAAO,sBAAsB;AACrC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEQ,UAAgB;AACtB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA6BZ;AAAA,EACH;AAAA,EAEA,cAAc,SAAwB;AACpC,SAAK,GAAG;AAAA,MACN;AAAA,IACF,EAAE,IAAI,QAAQ,IAAI,QAAQ,WAAW,QAAQ,WAAW,QAAQ,OAAO;AAAA,EACzE;AAAA,EAEA,WAAW,WAAyB;AAClC,SAAK,GAAG;AAAA,MACN;AAAA,IACF,EAAE,KAAI,oBAAI,KAAK,GAAE,YAAY,GAAG,SAAS;AAAA,EAC3C;AAAA,EAEA,cAAc,KAA+B;AAC3C,SAAK,GAAG;AAAA,MACN;AAAA;AAAA,IAEF,EAAE;AAAA,MACA,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,MACxC,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,IACN;AAAA,EACF;AAAA,EAEA,gBAAgB,WAAmB,MAAyB;AAC1D,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,UAAM,SAAS,KAAK,GAAG;AAAA,MACrB;AAAA,IACF,EAAE,IAAI,WAAW,MAAM,SAAS;AAEhC,WAAO;AAAA,MACL,IAAI,OAAO,OAAO,eAAe;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBAAkB,WAAmB,QAAQ,KAA2B;AACtE,WAAO,KAAK,GAAG;AAAA,MACb;AAAA;AAAA,IAEF,EAAE,IAAI,WAAW,KAAK;AAAA,EACxB;AAAA,EAEA,WAAW,WAAwC;AACjD,WAAO,KAAK,GAAG;AAAA,MACb;AAAA,IACF,EAAE,IAAI,SAAS;AAAA,EACjB;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;;;AC1GA,SAAS,oBAA+C;AACxD,SAAS,cAAc,kBAAkB;AACzC,SAAS,MAAM,eAAe;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,uBAAuC;AAGhD,IAAM,aAAqC;AAAA,EACzC,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACV;AAEO,IAAM,WAAN,MAAe;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU,oBAAI,IAAe;AAAA,EAC7B,iBAA2B,CAAC;AAAA,EAEpC,YAAY,MAAc;AACxB,SAAK,OAAO;AAGZ,SAAK,aAAa,aAAa,CAAC,KAAK,QAAQ;AAC3C,YAAM,aAAa,KAAK,cAAc;AACtC,UAAI,CAAC,YAAY;AACf,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SASP;AACD;AAAA,MACF;AAEA,UAAI,WAAW,KAAK,YAAY,IAAI,QAAQ,MAAM,eAAe,IAAI,GAAI;AACzE,UAAI,CAAC,WAAW,QAAQ,GAAG;AAEzB,mBAAW,KAAK,YAAY,YAAY;AAAA,MAC1C;AAEA,UAAI;AACF,cAAM,UAAU,aAAa,QAAQ;AACrC,cAAM,MAAM,QAAQ,QAAQ;AAC5B,YAAI,UAAU,KAAK,EAAE,gBAAgB,WAAW,GAAG,KAAK,2BAA2B,CAAC;AACpF,YAAI,IAAI,OAAO;AAAA,MACjB,QAAQ;AACN,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI,WAAW;AAAA,MACrB;AAAA,IACF,CAAC;AAED,SAAK,MAAM,IAAI,gBAAgB,EAAE,QAAQ,KAAK,WAAW,CAAC;AAE1D,SAAK,IAAI,GAAG,cAAc,CAAC,OAAO;AAChC,WAAK,QAAQ,IAAI,EAAE;AAGnB,iBAAW,OAAO,KAAK,gBAAgB;AACrC,WAAG,KAAK,GAAG;AAAA,MACb;AAEA,SAAG,GAAG,SAAS,MAAM,KAAK,QAAQ,OAAO,EAAE,CAAC;AAC5C,SAAG,GAAG,SAAS,MAAM,KAAK,QAAQ,OAAO,EAAE,CAAC;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAEQ,gBAA+B;AACrC,UAAM,YAAY,cAAc,IAAI,IAAI,KAAK,YAAY,GAAG,CAAC;AAC7D,UAAM,aAAa;AAAA,MACjB,KAAK,WAAW,MAAM,UAAU;AAAA;AAAA,MAChC,KAAK,WAAW,MAAM,MAAM,OAAO,MAAM;AAAA;AAAA,MACzC,KAAK,WAAW,MAAM,MAAM,MAAM,OAAO,MAAM;AAAA;AAAA,IACjD;AACA,eAAW,OAAO,YAAY;AAC5B,UAAI,WAAW,KAAK,KAAK,YAAY,CAAC,GAAG;AACvC,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAuB;AAC3B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAK,WAAW,GAAG,SAAS,CAAC,QAA+B;AAC1D,YAAI,IAAI,SAAS,cAAc;AAC7B,kBAAQ,OAAO;AAAA,YACb,oBAAoB,KAAK,IAAI;AAAA;AAAA,UAC/B;AAAA,QACF;AACA,eAAO,GAAG;AAAA,MACZ,CAAC;AACD,WAAK,WAAW,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAAA,IACnD,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,KAAsB;AAC9B,UAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,SAAK,eAAe,KAAK,IAAI;AAC7B,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,eAAe,OAAO,MAAM;AACrC,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,eAAW,UAAU,KAAK,SAAS;AACjC,aAAO,MAAM;AAAA,IACf;AACA,SAAK,IAAI,MAAM;AACf,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;;;AH5GO,IAAM,QAAN,MAAY;AAAA,EACT,QAA6B;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAuB;AACjC,SAAK,UAAU;AACf,SAAK,YAAY,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAChF,SAAK,QAAQ,IAAI,MAAM,QAAQ,MAAM;AACrC,SAAK,cAAc,IAAI,YAAY,KAAK,SAAS;AACjD,SAAK,WAAW,IAAI,SAAS,QAAQ,IAAI;AAAA,EAC3C;AAAA,EAEA,MAAM,QAAuB;AAE3B,UAAM,YAAY,KAAK,QAAQ,UAC3B,KAAK,QAAQ,QAAQ,KAAK,GAAG,IAC7B,KAAK,QAAQ;AACjB,SAAK,MAAM,cAAc;AAAA,MACvB,IAAI,KAAK;AAAA,MACT;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,SAAS;AAAA,IACX,CAAC;AAGD,UAAM,KAAK,SAAS,MAAM;AAG1B,SAAK,YAAY,GAAG,WAAW,CAAC,QAA4B;AAC1D,WAAK,MAAM,cAAc,GAAG;AAC5B,WAAK,SAAS,UAAU,EAAE,MAAM,WAAW,SAAS,IAAI,CAAC;AAEzD,UAAI,CAAC,KAAK,QAAQ,OAAO;AACvB,aAAK,YAAY,GAAG;AAAA,MACtB;AAAA,IACF,CAAC;AAID,UAAM,CAAC,SAAS,GAAG,IAAI,IAAI,KAAK,QAAQ,WAAW,KAAK,aAAa,KAAK,QAAQ,GAAI;AACtF,SAAK,QAAQ,MAAM,SAAS,MAAM;AAAA,MAChC,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAC9B,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,IACxB,CAAC;AAGD,UAAM,eAAe,KAAK,YAAY,gBAAgB,gBAAgB;AACtE,UAAM,eAAe,KAAK,YAAY,gBAAgB,gBAAgB;AAItE,YAAQ,MAAM,KAAK,YAAY,EAAE,KAAK,KAAK,MAAM,KAAM;AAGvD,SAAK,MAAM,OAAQ,KAAK,YAAY,EAAE,KAAK,QAAQ,MAAM;AAGzD,SAAK,MAAM,OAAQ,GAAG,QAAQ,CAAC,UAAkB;AAC/C,YAAM,QAAQ,MAAM,SAAS,EAAE,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AACjE,iBAAW,QAAQ,OAAO;AACxB,cAAM,MAAM,KAAK,MAAM,gBAAgB,KAAK,WAAW,IAAI;AAC3D,aAAK,SAAS,UAAU,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,MACjD;AAAA,IACF,CAAC;AAGD,SAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACtC,UAAI,CAAC,KAAK,QAAQ,OAAO;AACvB,gBAAQ,OAAO;AAAA,UACb,kCAAkC,SAAS,UAAU,MAAM,KAAK,QAAQ,IAAI,EAAE;AAAA;AAAA,QAChF;AAAA,MACF;AACA,WAAK,SAAS;AAAA,IAChB,CAAC;AAED,SAAK,MAAM,GAAG,SAAS,CAAC,QAAQ;AAC9B,cAAQ,OAAO,MAAM,uCAAuC,IAAI,OAAO;AAAA,CAAI;AAC3E,WAAK,SAAS,CAAC;AAAA,IACjB,CAAC;AAGD,YAAQ,GAAG,WAAW,MAAM,KAAK,SAAS,CAAC;AAC3C,YAAQ,GAAG,UAAU,MAAM,KAAK,SAAS,CAAC;AAG1C,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,WAAK,SAAS;AAAA,IAChB,CAAC;AAED,QAAI,CAAC,KAAK,QAAQ,OAAO;AACvB,YAAM,aAAa,KAAK,QAAQ,UAC5B,KAAK,QAAQ,QAAQ,KAAK,GAAG,IAC7B,KAAK,QAAQ;AACjB,cAAQ,OAAO,MAAM,yBAAyB,UAAU;AAAA,CAAI;AAC5D,cAAQ,OAAO,MAAM,2CAA2C,KAAK,QAAQ,IAAI;AAAA,CAAI;AACrF,cAAQ,OAAO,MAAM,wBAAwB,KAAK,SAAS;AAAA,CAAI;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,aAAa,KAAuB;AAE1C,UAAM,QAAkB,CAAC;AACzB,QAAI,UAAU;AACd,QAAI,UAAyB;AAE7B,eAAW,QAAQ,KAAK;AACtB,UAAI,SAAS;AACX,YAAI,SAAS,SAAS;AACpB,oBAAU;AAAA,QACZ,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,WAAW,SAAS,OAAO,SAAS,KAAK;AACvC,kBAAU;AAAA,MACZ,WAAW,SAAS,KAAK;AACvB,YAAI,SAAS;AACX,gBAAM,KAAK,OAAO;AAClB,oBAAU;AAAA,QACZ;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AACA,QAAI,QAAS,OAAM,KAAK,OAAO;AAE/B,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,KAA+B;AACjD,UAAM,QAAQ,IAAI,cAAc,mBAAmB,WAAM;AACzD,UAAM,SAAS,IAAI,UAAU;AAC7B,UAAM,UAAU,IAAI,aAAa,OAAO,KAAK,IAAI,SAAS,QAAQ;AAElE,QAAI,KAAK,QAAQ,SAAS;AACxB,YAAM,SAAS,KAAK,MAAM,IAAI,OAAO;AACrC,YAAM,UAAU,OAAO,QAAQ,WAAW,OAAO,SAAS;AAC1D,YAAM,SAAS,UAAU,WAAW;AACpC,cAAQ,OAAO,MAAM,eAAe,KAAK,IAAI,MAAM,OAAO,IAAI,SAAS,GAAG,GAAG,OAAO,GAAG,MAAM;AAAA,CAAI;AACjG,cAAQ,OAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,MAAM,CAAC;AAAA,CAAI;AAAA,IACxF,OAAO;AACL,cAAQ,OAAO,MAAM,eAAe,KAAK,IAAI,MAAM,GAAG,OAAO;AAAA,CAAI;AAAA,IACnE;AAAA,EACF;AAAA,EAEQ,eAAe;AAAA,EACf,SAAS,WAAW,GAAS;AACnC,QAAI,KAAK,aAAc;AACvB,SAAK,eAAe;AAEpB,SAAK,MAAM,WAAW,KAAK,SAAS;AACpC,SAAK,SAAS,UAAU,EAAE,MAAM,eAAe,WAAW,KAAK,UAAU,CAAC;AAE1E,QAAI,KAAK,SAAS,CAAC,KAAK,MAAM,QAAQ;AACpC,WAAK,MAAM,KAAK,SAAS;AAAA,IAC3B;AAEA,SAAK,SAAS,MAAM;AACpB,SAAK,MAAM,MAAM;AACjB,YAAQ,KAAK,QAAQ;AAAA,EACvB;AACF;;;AD9KA,IAAM,kBAAkBC,MAAK,QAAQ,GAAG,cAAc,SAAS;AAC/D,IAAM,eAAe;AAKrB,IAAM,gBAAgB,QAAQ,KAAK,QAAQ,IAAI;AAC/C,IAAM,gBAAgB,kBAAkB,KAAK,QAAQ,KAAK,MAAM,gBAAgB,CAAC,IAAI;AACrF,IAAI,kBAAkB,IAAI;AACxB,UAAQ,KAAK,OAAO,aAAa;AACnC;AAEA,IAAM,UAAU,IAAI,QAAQ,EACzB,KAAK,WAAW,EAChB,YAAY,yEAAoE,EAChF,OAAO,mBAAmB,yDAAyD,EACnF,OAAO,mBAAmB,kBAAkB,OAAO,YAAY,CAAC,EAChE,OAAO,eAAe,wBAAwB,eAAe,EAC7D,OAAO,WAAW,0BAA0B,KAAK,EACjD,OAAO,aAAa,0CAA0C,KAAK,EACnE,OAAO,UAAU,kCAAkC,KAAK,EACxD,OAAO,OAAO,SAAS;AAEtB,MAAI,CAAC,KAAK,QAAQ,CAAC,iBAAiB,cAAc,WAAW,IAAI;AAC/D,YAAQ,OAAO;AAAA,MACb;AAAA,IAKF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,IAAI,MAAM;AAAA,IACtB,KAAK,KAAK;AAAA,IACV,SAAS,iBAAiB;AAAA,IAC1B,QAAQ,KAAK;AAAA,IACb,MAAM,SAAS,KAAK,MAAM,EAAE;AAAA,IAC5B,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,EAChB,CAAC;AAED,QAAM,MAAM,MAAM;AAElB,MAAI,KAAK,MAAM;AACb,UAAM,QAAQ,MAAM,OAAO,MAAM,GAAG;AACpC,UAAM,KAAK,oBAAoB,KAAK,IAAI,EAAE;AAAA,EAC5C;AACF,CAAC;AAEH,QAAQ,MAAM;","names":["join","join"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/proxy.ts","../src/interceptor.ts","../src/store.ts","../src/ws-server.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { Proxy } from './proxy.js';\n\nconst DEFAULT_DB_PATH = join(homedir(), '.mcp-debug', 'data.db');\nconst DEFAULT_PORT = 8100;\n\n// Check for -- separator before commander parses.\n// Everything after -- is treated as the server command + args directly,\n// bypassing --cmd and the custom string parser (handles spaces in paths, etc.)\nconst doubleDashIdx = process.argv.indexOf('--');\nconst rawServerArgs = doubleDashIdx !== -1 ? process.argv.slice(doubleDashIdx + 1) : null;\nif (doubleDashIdx !== -1) {\n process.argv.splice(doubleDashIdx); // remove -- and everything after so commander ignores it\n}\n\nconst program = new Command()\n .name('mcp-debug')\n .description('MCP Inspector & Debugger — transparent proxy for MCP stdio traffic')\n .option('--cmd <command>', 'The MCP server command to proxy (e.g. \"node server.js\")')\n .option('--port <number>', 'Dashboard port', String(DEFAULT_PORT))\n .option('--db <path>', 'SQLite database path', DEFAULT_DB_PATH)\n .option('--quiet', 'Suppress stderr output', false)\n .option('--verbose', 'Print full JSON-RPC messages to stderr', false)\n .option('--open', 'Auto-open dashboard in browser', false)\n .action(async (opts) => {\n // Validate: must have either --cmd or -- <server args>\n if (!opts.cmd && (!rawServerArgs || rawServerArgs.length === 0)) {\n process.stderr.write(\n '[mcp-debug] Error: specify a server command via --cmd \"...\" or using -- <command> [args]\\n' +\n ' Examples:\\n' +\n ' mcp-debug --cmd \"node server.js\"\\n' +\n ' mcp-debug -- node server.js\\n' +\n ' mcp-debug -- python3 \"/path/with spaces/server.py\"\\n'\n );\n process.exit(1);\n }\n\n const proxy = new Proxy({\n cmd: opts.cmd,\n cmdArgs: rawServerArgs ?? undefined,\n dbPath: opts.db,\n port: parseInt(opts.port, 10),\n quiet: opts.quiet,\n verbose: opts.verbose,\n });\n\n await proxy.start();\n\n if (opts.open) {\n const open = (await import('open')).default;\n await open(`http://localhost:${opts.port}`);\n }\n });\n\nprogram.parse();\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { Interceptor } from './interceptor.js';\nimport { Store } from './store.js';\nimport { WsServer } from './ws-server.js';\nimport type { InterceptedMessage, StderrLog } from './types.js';\n\nexport interface ProxyOptions {\n cmd?: string;\n cmdArgs?: string[]; // raw argv from -- separator (takes precedence over cmd)\n dbPath: string;\n port: number;\n quiet: boolean;\n verbose: boolean;\n}\n\nexport class Proxy {\n private child: ChildProcess | null = null;\n private interceptor: Interceptor;\n private store: Store;\n private wsServer: WsServer;\n private sessionId: string;\n private options: ProxyOptions;\n\n constructor(options: ProxyOptions) {\n this.options = options;\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n this.store = new Store(options.dbPath);\n this.interceptor = new Interceptor(this.sessionId);\n this.wsServer = new WsServer(options.port);\n }\n\n async start(): Promise<void> {\n // Create session\n const serverCmd = this.options.cmdArgs\n ? this.options.cmdArgs.join(' ')\n : this.options.cmd!;\n this.store.createSession({\n id: this.sessionId,\n serverCmd,\n startedAt: new Date().toISOString(),\n endedAt: null,\n });\n\n // Start WebSocket server for dashboard\n // Register replay handler: when dashboard sends a replay request, write it to the MCP server's stdin\n this.wsServer.onReplay((rawJson: string) => {\n if (this.child?.stdin?.writable) {\n this.child.stdin.write(rawJson + '\\n');\n }\n });\n await this.wsServer.start();\n\n // Listen for intercepted messages\n this.interceptor.on('message', (msg: InterceptedMessage) => {\n this.store.insertMessage(msg);\n this.wsServer.broadcast({ type: 'message', message: msg });\n\n if (!this.options.quiet) {\n this.logToStderr(msg);\n }\n });\n\n // Spawn the real MCP server\n // cmdArgs (from -- separator) takes precedence — no parsing needed, handles spaces in paths\n const [command, ...args] = this.options.cmdArgs ?? this.parseCommand(this.options.cmd!);\n this.child = spawn(command, args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: { ...process.env },\n });\n\n // Create transform streams for interception\n const hostToServer = this.interceptor.createTransform('host_to_server');\n const serverToHost = this.interceptor.createTransform('server_to_host');\n\n // Wire up pipes:\n // Host stdin → interceptor → Server stdin\n process.stdin.pipe(hostToServer).pipe(this.child.stdin!);\n\n // Server stdout → interceptor → Host stdout\n this.child.stdout!.pipe(serverToHost).pipe(process.stdout);\n\n // Server stderr → capture for logging\n this.child.stderr!.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().split('\\n').filter((l) => l.trim());\n for (const line of lines) {\n const log = this.store.insertStderrLog(this.sessionId, line);\n this.wsServer.broadcast({ type: 'stderr', log });\n }\n });\n\n // Handle child process exit\n this.child.on('exit', (code, signal) => {\n if (!this.options.quiet) {\n process.stderr.write(\n `[mcp-debug] Server exited with ${signal ? `signal ${signal}` : `code ${code}`}\\n`\n );\n }\n this.shutdown();\n });\n\n this.child.on('error', (err) => {\n process.stderr.write(`[mcp-debug] Failed to start server: ${err.message}\\n`);\n this.shutdown(1);\n });\n\n // Handle signals for clean shutdown\n process.on('SIGTERM', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n\n // Handle host stdin closing (host disconnected)\n process.stdin.on('end', () => {\n this.shutdown();\n });\n\n if (!this.options.quiet) {\n const displayCmd = this.options.cmdArgs\n ? this.options.cmdArgs.join(' ')\n : this.options.cmd!;\n process.stderr.write(`[mcp-debug] Proxying: ${displayCmd}\\n`);\n process.stderr.write(`[mcp-debug] Dashboard: http://localhost:${this.options.port}\\n`);\n process.stderr.write(`[mcp-debug] Session: ${this.sessionId}\\n`);\n }\n }\n\n private parseCommand(cmd: string): string[] {\n // Simple shell-like parsing: split on spaces, respecting quotes\n const parts: string[] = [];\n let current = '';\n let inQuote: string | null = null;\n\n for (const char of cmd) {\n if (inQuote) {\n if (char === inQuote) {\n inQuote = null;\n } else {\n current += char;\n }\n } else if (char === '\"' || char === \"'\") {\n inQuote = char;\n } else if (char === ' ') {\n if (current) {\n parts.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n if (current) parts.push(current);\n\n return parts;\n }\n\n private logToStderr(msg: InterceptedMessage): void {\n const arrow = msg.direction === 'host_to_server' ? '→' : '←';\n const method = msg.method ?? '(response)';\n const latency = msg.latencyMs != null ? ` [${msg.latencyMs}ms]` : '';\n\n if (this.options.verbose) {\n const parsed = JSON.parse(msg.rawJson);\n const isError = parsed.result?.isError || parsed.error != null;\n const status = isError ? ' ERROR' : '';\n const tokens = Math.ceil(msg.rawJson.length / 4);\n process.stderr.write(`[mcp-debug] ${arrow} ${method} id=${msg.rpcId ?? '-'}${latency} ~${tokens} tokens${status}\\n`);\n process.stderr.write(` ${JSON.stringify(parsed, null, 2).split('\\n').join('\\n ')}\\n`);\n } else {\n process.stderr.write(`[mcp-debug] ${arrow} ${method}${latency}\\n`);\n }\n }\n\n private shuttingDown = false;\n private shutdown(exitCode = 0): void {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n\n this.store.endSession(this.sessionId);\n this.wsServer.broadcast({ type: 'session_end', sessionId: this.sessionId });\n\n if (this.child && !this.child.killed) {\n this.child.kill('SIGTERM');\n }\n\n this.wsServer.close();\n this.store.close();\n process.exit(exitCode);\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Transform, type TransformCallback } from 'node:stream';\nimport type { MessageDirection, JsonRpcMessage, InterceptedMessage } from './types.js';\n\ninterface InterceptorEvents {\n message: [InterceptedMessage];\n}\n\nexport class Interceptor extends EventEmitter<InterceptorEvents> {\n private sessionId: string;\n private pendingRequests = new Map<string | number, { method: string; timestamp: number }>();\n private messageCounter = 0;\n\n constructor(sessionId: string) {\n super();\n this.sessionId = sessionId;\n }\n\n /**\n * Creates a Transform stream that intercepts JSON-RPC messages\n * while passing the raw data through unmodified.\n */\n createTransform(direction: MessageDirection): Transform {\n let buffer = '';\n\n return new Transform({\n transform: (chunk: Buffer, _encoding: string, callback: TransformCallback) => {\n // Always pass the raw data through immediately\n callback(null, chunk);\n\n // Parse asynchronously so we don't block the pipe\n buffer += chunk.toString();\n const lines = buffer.split('\\n');\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n this.processLine(trimmed, direction);\n }\n },\n flush: (callback: TransformCallback) => {\n // Process any remaining data\n if (buffer.trim()) {\n this.processLine(buffer.trim(), direction);\n }\n callback();\n },\n });\n }\n\n private processLine(line: string, direction: MessageDirection): void {\n let parsed: JsonRpcMessage;\n try {\n parsed = JSON.parse(line);\n } catch {\n // Not valid JSON — skip (could be partial data or non-JSON stderr)\n return;\n }\n\n if (parsed.jsonrpc !== '2.0') return;\n\n const now = Date.now();\n const timestamp = new Date(now).toISOString();\n\n let rpcId: string | number | null = null;\n let method: string | null = null;\n let latencyMs: number | null = null;\n\n if ('method' in parsed) {\n // Request or notification\n method = parsed.method;\n if ('id' in parsed && parsed.id != null) {\n rpcId = parsed.id;\n this.pendingRequests.set(rpcId, { method, timestamp: now });\n }\n } else if ('id' in parsed) {\n // Response\n rpcId = parsed.id;\n const pending = this.pendingRequests.get(rpcId);\n if (pending) {\n latencyMs = now - pending.timestamp;\n method = pending.method;\n this.pendingRequests.delete(rpcId);\n }\n if ('error' in parsed && parsed.error) {\n method = method ? `${method} (error)` : '(error)';\n }\n }\n\n const message: InterceptedMessage = {\n id: ++this.messageCounter,\n sessionId: this.sessionId,\n direction,\n rpcId,\n method,\n rawJson: line,\n timestamp,\n latencyMs,\n };\n\n this.emit('message', message);\n }\n}\n","import Database from 'better-sqlite3';\nimport { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { InterceptedMessage, Session, StderrLog } from './types.js';\n\nexport class Store {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n mkdirSync(dirname(dbPath), { recursive: true });\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.migrate();\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n server_cmd TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n direction TEXT NOT NULL CHECK(direction IN ('host_to_server', 'server_to_host')),\n rpc_id TEXT,\n method TEXT,\n raw_json TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n latency_ms INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS stderr_logs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n line TEXT NOT NULL,\n timestamp TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);\n CREATE INDEX IF NOT EXISTS idx_messages_method ON messages(method);\n CREATE INDEX IF NOT EXISTS idx_stderr_session ON stderr_logs(session_id);\n `);\n }\n\n createSession(session: Session): void {\n this.db.prepare(\n 'INSERT INTO sessions (id, server_cmd, started_at, ended_at) VALUES (?, ?, ?, ?)'\n ).run(session.id, session.serverCmd, session.startedAt, session.endedAt);\n }\n\n endSession(sessionId: string): void {\n this.db.prepare(\n 'UPDATE sessions SET ended_at = ? WHERE id = ?'\n ).run(new Date().toISOString(), sessionId);\n }\n\n insertMessage(msg: InterceptedMessage): void {\n this.db.prepare(\n `INSERT INTO messages (session_id, direction, rpc_id, method, raw_json, timestamp, latency_ms)\n VALUES (?, ?, ?, ?, ?, ?, ?)`\n ).run(\n msg.sessionId,\n msg.direction,\n msg.rpcId != null ? String(msg.rpcId) : null,\n msg.method,\n msg.rawJson,\n msg.timestamp,\n msg.latencyMs,\n );\n }\n\n insertStderrLog(sessionId: string, line: string): StderrLog {\n const timestamp = new Date().toISOString();\n const result = this.db.prepare(\n 'INSERT INTO stderr_logs (session_id, line, timestamp) VALUES (?, ?, ?)'\n ).run(sessionId, line, timestamp);\n\n return {\n id: Number(result.lastInsertRowid),\n sessionId,\n line,\n timestamp,\n };\n }\n\n getRecentMessages(sessionId: string, limit = 100): InterceptedMessage[] {\n return this.db.prepare(\n `SELECT id, session_id as sessionId, direction, rpc_id as rpcId, method, raw_json as rawJson, timestamp, latency_ms as latencyMs\n FROM messages WHERE session_id = ? ORDER BY id DESC LIMIT ?`\n ).all(sessionId, limit) as InterceptedMessage[];\n }\n\n getSession(sessionId: string): Session | undefined {\n return this.db.prepare(\n 'SELECT id, server_cmd as serverCmd, started_at as startedAt, ended_at as endedAt FROM sessions WHERE id = ?'\n ).get(sessionId) as Session | undefined;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { createServer, type Server as HttpServer } from 'node:http';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport type { WsMessage } from './types.js';\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n};\n\nexport class WsServer {\n private httpServer: HttpServer;\n private wss: WebSocketServer;\n private port: number;\n private clients = new Set<WebSocket>();\n private messageHistory: string[] = [];\n private replayHandler: ((rawJson: string) => void) | null = null;\n\n constructor(port: number) {\n this.port = port;\n\n // Serve static dashboard files\n this.httpServer = createServer((req, res) => {\n const webDistDir = this.getWebDistDir();\n if (!webDistDir) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <!DOCTYPE html>\n <html>\n <body style=\"font-family: system-ui; padding: 2rem; background: #1a1a2e; color: #e0e0e0;\">\n <h1>MCP Debug Dashboard</h1>\n <p>Web dashboard not built yet. Run <code>npm run build</code> in the web package.</p>\n <p>WebSocket is active on this port — the dashboard will connect when built.</p>\n </body>\n </html>\n `);\n return;\n }\n\n let filePath = join(webDistDir, req.url === '/' ? 'index.html' : req.url!);\n if (!existsSync(filePath)) {\n // SPA fallback\n filePath = join(webDistDir, 'index.html');\n }\n\n try {\n const content = readFileSync(filePath);\n const ext = extname(filePath);\n res.writeHead(200, { 'Content-Type': MIME_TYPES[ext] || 'application/octet-stream' });\n res.end(content);\n } catch {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n this.wss = new WebSocketServer({ server: this.httpServer });\n\n this.wss.on('connection', (ws) => {\n this.clients.add(ws);\n\n // Replay message history to new clients\n for (const msg of this.messageHistory) {\n ws.send(msg);\n }\n\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'replay' && typeof msg.rawJson === 'string' && this.replayHandler) {\n this.replayHandler(msg.rawJson);\n }\n } catch {\n // Ignore invalid messages from clients\n }\n });\n ws.on('close', () => this.clients.delete(ws));\n ws.on('error', () => this.clients.delete(ws));\n });\n }\n\n private getWebDistDir(): string | null {\n const __dirname = fileURLToPath(new URL('.', import.meta.url));\n const candidates = [\n join(__dirname, '..', 'web-dist'), // npm: packages/cli/web-dist (bundled for distribution)\n join(__dirname, '..', '..', 'web', 'dist'), // dev: packages/cli/src -> packages/web/dist\n join(__dirname, '..', '..', '..', 'web', 'dist'), // built: packages/cli/dist -> packages/web/dist\n ];\n for (const dir of candidates) {\n if (existsSync(join(dir, 'index.html'))) {\n return dir;\n }\n }\n return null;\n }\n\n onReplay(handler: (rawJson: string) => void): void {\n this.replayHandler = handler;\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n process.stderr.write(\n `[mcp-debug] Port ${this.port} is in use. Try --port <number>\\n`\n );\n }\n reject(err);\n });\n this.httpServer.listen(this.port, () => resolve());\n });\n }\n\n broadcast(msg: WsMessage): void {\n const data = JSON.stringify(msg);\n this.messageHistory.push(data);\n for (const client of this.clients) {\n if (client.readyState === client.OPEN) {\n client.send(data);\n }\n }\n }\n\n close(): void {\n for (const client of this.clients) {\n client.close();\n }\n this.wss.close();\n this.httpServer.close();\n }\n}\n"],"mappings":"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","names":["join","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turingspark/mcp-debug",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "MCP Inspector & Debugger — transparent proxy with a live dashboard for MCP stdio traffic",
5
5
  "author": "TuringSpark <team@turingspark.com>",
6
6
  "license": "MIT",