@turingspark/mcp-debug 0.1.3 → 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
@@ -74,10 +74,10 @@ The dashboard connects via WebSocket and replays message history, so you can ope
74
74
  ## CLI Options
75
75
 
76
76
  ```
77
- Usage: mcp-debug [options]
77
+ Usage: mcp-debug [options] [-- <server command> [args...]]
78
78
 
79
79
  Options:
80
- --cmd <command> The MCP server command to proxy (required)
80
+ --cmd <command> The MCP server command to proxy (quoted string)
81
81
  --port <number> Dashboard port (default: 8100)
82
82
  --db <path> SQLite database path (default: ~/.mcp-debug/data.db)
83
83
  --quiet Suppress stderr output (default: false)
@@ -86,6 +86,33 @@ Options:
86
86
  -h, --help Display help
87
87
  ```
88
88
 
89
+ ### Two ways to specify the server command
90
+
91
+ **`--cmd` (quoted string)** — simple, works for most cases:
92
+ ```bash
93
+ npx @turingspark/mcp-debug --cmd "node server.js"
94
+ npx @turingspark/mcp-debug --cmd "python3 server.py --port 9000"
95
+ ```
96
+
97
+ **`--` separator** — recommended for paths with spaces or complex args:
98
+ ```bash
99
+ npx @turingspark/mcp-debug -- node server.js
100
+ npx @turingspark/mcp-debug -- python3 "/path/with spaces/server.py"
101
+ npx @turingspark/mcp-debug --verbose -- mcp-server-filesystem /my/project
102
+ ```
103
+
104
+ Use `--` in Claude Desktop config to keep args clean:
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "my-server": {
109
+ "command": "npx",
110
+ "args": ["@turingspark/mcp-debug", "--open", "--", "node", "my-mcp-server.js"]
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
89
116
  ## CLI-Only Debugging with --verbose
90
117
 
91
118
  Use `--verbose` to print full JSON-RPC messages to stderr — no browser needed. Useful for CI, SSH sessions, or quick debugging:
@@ -142,6 +169,10 @@ MCP Server (your server)
142
169
 
143
170
  The proxy is fully transparent — it passes all messages through without modification. Your MCP host and server don't know it's there.
144
171
 
172
+ ## Documentation
173
+
174
+ Please visit [https://turingspark.com/tools/mcp-debugger](https://turingspark.com/tools/mcp-debugger) for more examples.
175
+
145
176
  ## License
146
177
 
147
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) => {
@@ -315,12 +328,18 @@ var Proxy = class {
315
328
  this.wsServer = new WsServer(options.port);
316
329
  }
317
330
  async start() {
331
+ const serverCmd = this.options.cmdArgs ? this.options.cmdArgs.join(" ") : this.options.cmd;
318
332
  this.store.createSession({
319
333
  id: this.sessionId,
320
- serverCmd: this.options.cmd,
334
+ serverCmd,
321
335
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
322
336
  endedAt: null
323
337
  });
338
+ this.wsServer.onReplay((rawJson) => {
339
+ if (this.child?.stdin?.writable) {
340
+ this.child.stdin.write(rawJson + "\n");
341
+ }
342
+ });
324
343
  await this.wsServer.start();
325
344
  this.interceptor.on("message", (msg) => {
326
345
  this.store.insertMessage(msg);
@@ -329,7 +348,7 @@ var Proxy = class {
329
348
  this.logToStderr(msg);
330
349
  }
331
350
  });
332
- const [command, ...args] = this.parseCommand(this.options.cmd);
351
+ const [command, ...args] = this.options.cmdArgs ?? this.parseCommand(this.options.cmd);
333
352
  this.child = spawn(command, args, {
334
353
  stdio: ["pipe", "pipe", "pipe"],
335
354
  env: { ...process.env }
@@ -365,7 +384,8 @@ var Proxy = class {
365
384
  this.shutdown();
366
385
  });
367
386
  if (!this.options.quiet) {
368
- process.stderr.write(`[mcp-debug] Proxying: ${this.options.cmd}
387
+ const displayCmd = this.options.cmdArgs ? this.options.cmdArgs.join(" ") : this.options.cmd;
388
+ process.stderr.write(`[mcp-debug] Proxying: ${displayCmd}
369
389
  `);
370
390
  process.stderr.write(`[mcp-debug] Dashboard: http://localhost:${this.options.port}
371
391
  `);
@@ -406,7 +426,8 @@ var Proxy = class {
406
426
  const parsed = JSON.parse(msg.rawJson);
407
427
  const isError = parsed.result?.isError || parsed.error != null;
408
428
  const status = isError ? " ERROR" : "";
409
- 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}
410
431
  `);
411
432
  process.stderr.write(` ${JSON.stringify(parsed, null, 2).split("\n").join("\n ")}
412
433
  `);
@@ -433,9 +454,21 @@ var Proxy = class {
433
454
  // src/index.ts
434
455
  var DEFAULT_DB_PATH = join2(homedir(), ".mcp-debug", "data.db");
435
456
  var DEFAULT_PORT = 8100;
436
- var program = new Command().name("mcp-debug").description("MCP Inspector & Debugger \u2014 transparent proxy for MCP stdio traffic").requiredOption("--cmd <command>", 'The MCP server command to proxy (e.g. "node server.js")').option("--port <number>", "Dashboard port", String(DEFAULT_PORT)).option("--db <path>", "SQLite database path", DEFAULT_DB_PATH).option("--quiet", "Suppress stderr output", false).option("--verbose", "Print full JSON-RPC messages to stderr", false).option("--open", "Auto-open dashboard in browser", false).action(async (opts) => {
457
+ var doubleDashIdx = process.argv.indexOf("--");
458
+ var rawServerArgs = doubleDashIdx !== -1 ? process.argv.slice(doubleDashIdx + 1) : null;
459
+ if (doubleDashIdx !== -1) {
460
+ process.argv.splice(doubleDashIdx);
461
+ }
462
+ var program = new Command().name("mcp-debug").description("MCP Inspector & Debugger \u2014 transparent proxy for MCP stdio traffic").option("--cmd <command>", 'The MCP server command to proxy (e.g. "node server.js")').option("--port <number>", "Dashboard port", String(DEFAULT_PORT)).option("--db <path>", "SQLite database path", DEFAULT_DB_PATH).option("--quiet", "Suppress stderr output", false).option("--verbose", "Print full JSON-RPC messages to stderr", false).option("--open", "Auto-open dashboard in browser", false).action(async (opts) => {
463
+ if (!opts.cmd && (!rawServerArgs || rawServerArgs.length === 0)) {
464
+ process.stderr.write(
465
+ '[mcp-debug] Error: specify a server command via --cmd "..." or using -- <command> [args]\n Examples:\n mcp-debug --cmd "node server.js"\n mcp-debug -- node server.js\n mcp-debug -- python3 "/path/with spaces/server.py"\n'
466
+ );
467
+ process.exit(1);
468
+ }
437
469
  const proxy = new Proxy({
438
470
  cmd: opts.cmd,
471
+ cmdArgs: rawServerArgs ?? void 0,
439
472
  dbPath: opts.db,
440
473
  port: parseInt(opts.port, 10),
441
474
  quiet: opts.quiet,
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\nconst program = new Command()\n .name('mcp-debug')\n .description('MCP Inspector & Debugger — transparent proxy for MCP stdio traffic')\n .requiredOption('--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 const proxy = new Proxy({\n cmd: opts.cmd,\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 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 this.store.createSession({\n id: this.sessionId,\n serverCmd: this.options.cmd,\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 const [command, ...args] = 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 process.stderr.write(`[mcp-debug] Proxying: ${this.options.cmd}\\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":"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","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":";;;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,EAC5B,gBAAoD;AAAA,EAE5D,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,WAAW,CAAC,SAAS;AACzB,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,cAAI,IAAI,SAAS,YAAY,OAAO,IAAI,YAAY,YAAY,KAAK,eAAe;AAClF,iBAAK,cAAc,IAAI,OAAO;AAAA,UAChC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AACD,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,SAAS,SAA0C;AACjD,SAAK,gBAAgB;AAAA,EACvB;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;;;AH3HO,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;AAID,SAAK,SAAS,SAAS,CAAC,YAAoB;AAC1C,UAAI,KAAK,OAAO,OAAO,UAAU;AAC/B,aAAK,MAAM,MAAM,MAAM,UAAU,IAAI;AAAA,MACvC;AAAA,IACF,CAAC;AACD,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,YAAM,SAAS,KAAK,KAAK,IAAI,QAAQ,SAAS,CAAC;AAC/C,cAAQ,OAAO,MAAM,eAAe,KAAK,IAAI,MAAM,OAAO,IAAI,SAAS,GAAG,GAAG,OAAO,KAAK,MAAM,UAAU,MAAM;AAAA,CAAI;AACnH,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;;;ADrLA,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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turingspark/mcp-debug",
3
- "version": "0.1.3",
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",