@turingspark/mcp-debug 0.1.3 → 0.1.4

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:
package/dist/index.js CHANGED
@@ -315,9 +315,10 @@ var Proxy = class {
315
315
  this.wsServer = new WsServer(options.port);
316
316
  }
317
317
  async start() {
318
+ const serverCmd = this.options.cmdArgs ? this.options.cmdArgs.join(" ") : this.options.cmd;
318
319
  this.store.createSession({
319
320
  id: this.sessionId,
320
- serverCmd: this.options.cmd,
321
+ serverCmd,
321
322
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
322
323
  endedAt: null
323
324
  });
@@ -329,7 +330,7 @@ var Proxy = class {
329
330
  this.logToStderr(msg);
330
331
  }
331
332
  });
332
- const [command, ...args] = this.parseCommand(this.options.cmd);
333
+ const [command, ...args] = this.options.cmdArgs ?? this.parseCommand(this.options.cmd);
333
334
  this.child = spawn(command, args, {
334
335
  stdio: ["pipe", "pipe", "pipe"],
335
336
  env: { ...process.env }
@@ -365,7 +366,8 @@ var Proxy = class {
365
366
  this.shutdown();
366
367
  });
367
368
  if (!this.options.quiet) {
368
- process.stderr.write(`[mcp-debug] Proxying: ${this.options.cmd}
369
+ const displayCmd = this.options.cmdArgs ? this.options.cmdArgs.join(" ") : this.options.cmd;
370
+ process.stderr.write(`[mcp-debug] Proxying: ${displayCmd}
369
371
  `);
370
372
  process.stderr.write(`[mcp-debug] Dashboard: http://localhost:${this.options.port}
371
373
  `);
@@ -433,9 +435,21 @@ var Proxy = class {
433
435
  // src/index.ts
434
436
  var DEFAULT_DB_PATH = join2(homedir(), ".mcp-debug", "data.db");
435
437
  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) => {
438
+ var doubleDashIdx = process.argv.indexOf("--");
439
+ var rawServerArgs = doubleDashIdx !== -1 ? process.argv.slice(doubleDashIdx + 1) : null;
440
+ if (doubleDashIdx !== -1) {
441
+ process.argv.splice(doubleDashIdx);
442
+ }
443
+ 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) => {
444
+ if (!opts.cmd && (!rawServerArgs || rawServerArgs.length === 0)) {
445
+ process.stderr.write(
446
+ '[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'
447
+ );
448
+ process.exit(1);
449
+ }
437
450
  const proxy = new Proxy({
438
451
  cmd: opts.cmd,
452
+ cmdArgs: rawServerArgs ?? void 0,
439
453
  dbPath: opts.db,
440
454
  port: parseInt(opts.port, 10),
441
455
  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 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":"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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.3",
3
+ "version": "0.1.4",
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",