@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 +33 -2
- package/dist/index.js +38 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web-dist/assets/index-Cw-f--ST.js +49 -0
- package/web-dist/assets/index-DmjSyXZI.css +1 -0
- package/web-dist/index.html +2 -2
- package/web-dist/assets/index-Dhx4EXuk.css +0 -1
- package/web-dist/assets/index-zMycMyhm.js +0 -49
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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":"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","names":["join","join"]}
|
package/package.json
CHANGED