mcp-wiretap 0.1.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/dist/index.js ADDED
@@ -0,0 +1,1426 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/proxy.ts
27
+ var import_node_child_process = require("child_process");
28
+
29
+ // src/parser.ts
30
+ function isRequest(msg) {
31
+ return "method" in msg && msg.method !== void 0;
32
+ }
33
+ function isResponse(msg) {
34
+ return !("method" in msg) && ("result" in msg || "error" in msg);
35
+ }
36
+ function extractToolName(msg) {
37
+ if (isRequest(msg) && msg.method === "tools/call" && msg.params) {
38
+ return msg.params.name || null;
39
+ }
40
+ return null;
41
+ }
42
+ function tryParseJsonRpc(raw) {
43
+ try {
44
+ const parsed = JSON.parse(raw);
45
+ if (parsed && typeof parsed === "object" && parsed.jsonrpc === "2.0") {
46
+ return parsed;
47
+ }
48
+ return null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ var RequestCorrelator = class {
54
+ pending = /* @__PURE__ */ new Map();
55
+ /**
56
+ * Record an outgoing request so we can match its response later.
57
+ */
58
+ trackRequest(id, method, toolName) {
59
+ this.pending.set(id, {
60
+ method,
61
+ toolName,
62
+ timestamp: performance.now()
63
+ });
64
+ }
65
+ /**
66
+ * Match a response to a tracked request.
67
+ * Returns { method, toolName, latencyMs } or null if no match.
68
+ */
69
+ matchResponse(id) {
70
+ const req = this.pending.get(id);
71
+ if (!req) return null;
72
+ this.pending.delete(id);
73
+ return {
74
+ method: req.method,
75
+ toolName: req.toolName,
76
+ latencyMs: Math.round(performance.now() - req.timestamp)
77
+ };
78
+ }
79
+ /**
80
+ * Number of in-flight requests
81
+ */
82
+ get size() {
83
+ return this.pending.size;
84
+ }
85
+ };
86
+ var MessageBuffer = class {
87
+ buffer = "";
88
+ /**
89
+ * Feed raw data into the buffer. Returns an array of complete
90
+ * raw JSON strings ready for parsing.
91
+ */
92
+ feed(chunk) {
93
+ this.buffer += chunk;
94
+ const messages = [];
95
+ let newlineIdx;
96
+ while ((newlineIdx = this.buffer.indexOf("\n")) !== -1) {
97
+ const line = this.buffer.slice(0, newlineIdx).trim();
98
+ this.buffer = this.buffer.slice(newlineIdx + 1);
99
+ if (line.length > 0) {
100
+ messages.push(line);
101
+ }
102
+ }
103
+ return messages;
104
+ }
105
+ /**
106
+ * Flush any remaining content in the buffer (for shutdown).
107
+ */
108
+ flush() {
109
+ const remaining = this.buffer.trim();
110
+ this.buffer = "";
111
+ return remaining.length > 0 ? remaining : null;
112
+ }
113
+ };
114
+
115
+ // src/logger.ts
116
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"));
117
+ var import_node_path = __toESM(require("path"));
118
+ var import_node_fs = __toESM(require("fs"));
119
+ var import_node_os = __toESM(require("os"));
120
+ var import_node_crypto = __toESM(require("crypto"));
121
+ var DEFAULT_DB_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".mcp-wiretap");
122
+ var DEFAULT_DB_PATH = import_node_path.default.join(DEFAULT_DB_DIR, "wiretap.db");
123
+ var WiretapLogger = class {
124
+ db;
125
+ insertStmt;
126
+ sessionId;
127
+ constructor(dbPath) {
128
+ const resolvedPath = dbPath || DEFAULT_DB_PATH;
129
+ const dir = import_node_path.default.dirname(resolvedPath);
130
+ if (!import_node_fs.default.existsSync(dir)) {
131
+ import_node_fs.default.mkdirSync(dir, { recursive: true });
132
+ }
133
+ this.db = new import_better_sqlite3.default(resolvedPath);
134
+ this.sessionId = import_node_crypto.default.randomUUID();
135
+ this.db.pragma("journal_mode = WAL");
136
+ this.db.exec(`
137
+ CREATE TABLE IF NOT EXISTS events (
138
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
139
+ timestamp TEXT NOT NULL,
140
+ direction TEXT NOT NULL,
141
+ jsonrpc_id TEXT,
142
+ method TEXT,
143
+ tool_name TEXT,
144
+ params TEXT,
145
+ latency_ms INTEGER,
146
+ payload_bytes INTEGER NOT NULL,
147
+ session_id TEXT NOT NULL
148
+ );
149
+
150
+ CREATE TABLE IF NOT EXISTS sessions (
151
+ session_id TEXT PRIMARY KEY,
152
+ started_at TEXT NOT NULL,
153
+ server_command TEXT NOT NULL
154
+ );
155
+
156
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
157
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
158
+ CREATE INDEX IF NOT EXISTS idx_events_method ON events(method);
159
+ CREATE INDEX IF NOT EXISTS idx_events_tool_name ON events(tool_name);
160
+ `);
161
+ this.insertStmt = this.db.prepare(`
162
+ INSERT INTO events (timestamp, direction, jsonrpc_id, method, tool_name, params, latency_ms, payload_bytes, session_id)
163
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
164
+ `);
165
+ }
166
+ /**
167
+ * Record this session in the sessions table
168
+ */
169
+ startSession(serverCommand) {
170
+ this.db.prepare("INSERT INTO sessions (session_id, started_at, server_command) VALUES (?, ?, ?)").run(this.sessionId, (/* @__PURE__ */ new Date()).toISOString(), serverCommand);
171
+ }
172
+ /**
173
+ * Log a correlated event to SQLite
174
+ */
175
+ logEvent(event) {
176
+ this.insertStmt.run(
177
+ event.timestamp,
178
+ event.direction,
179
+ event.jsonrpcId != null ? String(event.jsonrpcId) : null,
180
+ event.method,
181
+ event.toolName,
182
+ event.params,
183
+ event.latencyMs,
184
+ event.payloadBytes,
185
+ event.sessionId
186
+ );
187
+ }
188
+ /**
189
+ * Get all events for a session
190
+ */
191
+ getSessionEvents(sessionId) {
192
+ const sid = sessionId || this.sessionId;
193
+ return this.db.prepare("SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC").all(sid);
194
+ }
195
+ /**
196
+ * Get all events across all sessions
197
+ */
198
+ getAllEvents() {
199
+ return this.db.prepare("SELECT * FROM events ORDER BY timestamp ASC").all();
200
+ }
201
+ /**
202
+ * List all recorded sessions
203
+ */
204
+ listSessions() {
205
+ return this.db.prepare(
206
+ `SELECT s.session_id as sessionId, s.started_at as startedAt, s.server_command as serverCommand,
207
+ COUNT(e.id) as eventCount
208
+ FROM sessions s
209
+ LEFT JOIN events e ON s.session_id = e.session_id
210
+ GROUP BY s.session_id
211
+ ORDER BY s.started_at DESC`
212
+ ).all();
213
+ }
214
+ /**
215
+ * Search events by method or tool name across all sessions
216
+ */
217
+ searchEvents(query) {
218
+ return this.db.prepare(
219
+ `SELECT * FROM events
220
+ WHERE method LIKE ? OR tool_name LIKE ? OR params LIKE ?
221
+ ORDER BY timestamp ASC`
222
+ ).all(`%${query}%`, `%${query}%`, `%${query}%`);
223
+ }
224
+ /**
225
+ * Close the database connection
226
+ */
227
+ close() {
228
+ this.db.close();
229
+ }
230
+ /**
231
+ * Get the path to the database
232
+ */
233
+ static get defaultDbPath() {
234
+ return DEFAULT_DB_PATH;
235
+ }
236
+ };
237
+
238
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
239
+ var ANSI_BACKGROUND_OFFSET = 10;
240
+ var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
241
+ var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
242
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
243
+ var styles = {
244
+ modifier: {
245
+ reset: [0, 0],
246
+ // 21 isn't widely supported and 22 does the same thing
247
+ bold: [1, 22],
248
+ dim: [2, 22],
249
+ italic: [3, 23],
250
+ underline: [4, 24],
251
+ overline: [53, 55],
252
+ inverse: [7, 27],
253
+ hidden: [8, 28],
254
+ strikethrough: [9, 29]
255
+ },
256
+ color: {
257
+ black: [30, 39],
258
+ red: [31, 39],
259
+ green: [32, 39],
260
+ yellow: [33, 39],
261
+ blue: [34, 39],
262
+ magenta: [35, 39],
263
+ cyan: [36, 39],
264
+ white: [37, 39],
265
+ // Bright color
266
+ blackBright: [90, 39],
267
+ gray: [90, 39],
268
+ // Alias of `blackBright`
269
+ grey: [90, 39],
270
+ // Alias of `blackBright`
271
+ redBright: [91, 39],
272
+ greenBright: [92, 39],
273
+ yellowBright: [93, 39],
274
+ blueBright: [94, 39],
275
+ magentaBright: [95, 39],
276
+ cyanBright: [96, 39],
277
+ whiteBright: [97, 39]
278
+ },
279
+ bgColor: {
280
+ bgBlack: [40, 49],
281
+ bgRed: [41, 49],
282
+ bgGreen: [42, 49],
283
+ bgYellow: [43, 49],
284
+ bgBlue: [44, 49],
285
+ bgMagenta: [45, 49],
286
+ bgCyan: [46, 49],
287
+ bgWhite: [47, 49],
288
+ // Bright color
289
+ bgBlackBright: [100, 49],
290
+ bgGray: [100, 49],
291
+ // Alias of `bgBlackBright`
292
+ bgGrey: [100, 49],
293
+ // Alias of `bgBlackBright`
294
+ bgRedBright: [101, 49],
295
+ bgGreenBright: [102, 49],
296
+ bgYellowBright: [103, 49],
297
+ bgBlueBright: [104, 49],
298
+ bgMagentaBright: [105, 49],
299
+ bgCyanBright: [106, 49],
300
+ bgWhiteBright: [107, 49]
301
+ }
302
+ };
303
+ var modifierNames = Object.keys(styles.modifier);
304
+ var foregroundColorNames = Object.keys(styles.color);
305
+ var backgroundColorNames = Object.keys(styles.bgColor);
306
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
307
+ function assembleStyles() {
308
+ const codes = /* @__PURE__ */ new Map();
309
+ for (const [groupName, group] of Object.entries(styles)) {
310
+ for (const [styleName, style] of Object.entries(group)) {
311
+ styles[styleName] = {
312
+ open: `\x1B[${style[0]}m`,
313
+ close: `\x1B[${style[1]}m`
314
+ };
315
+ group[styleName] = styles[styleName];
316
+ codes.set(style[0], style[1]);
317
+ }
318
+ Object.defineProperty(styles, groupName, {
319
+ value: group,
320
+ enumerable: false
321
+ });
322
+ }
323
+ Object.defineProperty(styles, "codes", {
324
+ value: codes,
325
+ enumerable: false
326
+ });
327
+ styles.color.close = "\x1B[39m";
328
+ styles.bgColor.close = "\x1B[49m";
329
+ styles.color.ansi = wrapAnsi16();
330
+ styles.color.ansi256 = wrapAnsi256();
331
+ styles.color.ansi16m = wrapAnsi16m();
332
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
333
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
334
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
335
+ Object.defineProperties(styles, {
336
+ rgbToAnsi256: {
337
+ value(red, green, blue) {
338
+ if (red === green && green === blue) {
339
+ if (red < 8) {
340
+ return 16;
341
+ }
342
+ if (red > 248) {
343
+ return 231;
344
+ }
345
+ return Math.round((red - 8) / 247 * 24) + 232;
346
+ }
347
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
348
+ },
349
+ enumerable: false
350
+ },
351
+ hexToRgb: {
352
+ value(hex) {
353
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
354
+ if (!matches) {
355
+ return [0, 0, 0];
356
+ }
357
+ let [colorString] = matches;
358
+ if (colorString.length === 3) {
359
+ colorString = [...colorString].map((character) => character + character).join("");
360
+ }
361
+ const integer = Number.parseInt(colorString, 16);
362
+ return [
363
+ /* eslint-disable no-bitwise */
364
+ integer >> 16 & 255,
365
+ integer >> 8 & 255,
366
+ integer & 255
367
+ /* eslint-enable no-bitwise */
368
+ ];
369
+ },
370
+ enumerable: false
371
+ },
372
+ hexToAnsi256: {
373
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
374
+ enumerable: false
375
+ },
376
+ ansi256ToAnsi: {
377
+ value(code) {
378
+ if (code < 8) {
379
+ return 30 + code;
380
+ }
381
+ if (code < 16) {
382
+ return 90 + (code - 8);
383
+ }
384
+ let red;
385
+ let green;
386
+ let blue;
387
+ if (code >= 232) {
388
+ red = ((code - 232) * 10 + 8) / 255;
389
+ green = red;
390
+ blue = red;
391
+ } else {
392
+ code -= 16;
393
+ const remainder = code % 36;
394
+ red = Math.floor(code / 36) / 5;
395
+ green = Math.floor(remainder / 6) / 5;
396
+ blue = remainder % 6 / 5;
397
+ }
398
+ const value = Math.max(red, green, blue) * 2;
399
+ if (value === 0) {
400
+ return 30;
401
+ }
402
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
403
+ if (value === 2) {
404
+ result += 60;
405
+ }
406
+ return result;
407
+ },
408
+ enumerable: false
409
+ },
410
+ rgbToAnsi: {
411
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
412
+ enumerable: false
413
+ },
414
+ hexToAnsi: {
415
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
416
+ enumerable: false
417
+ }
418
+ });
419
+ return styles;
420
+ }
421
+ var ansiStyles = assembleStyles();
422
+ var ansi_styles_default = ansiStyles;
423
+
424
+ // node_modules/chalk/source/vendor/supports-color/index.js
425
+ var import_node_process = __toESM(require("process"), 1);
426
+ var import_node_os2 = __toESM(require("os"), 1);
427
+ var import_node_tty = __toESM(require("tty"), 1);
428
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) {
429
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
430
+ const position = argv.indexOf(prefix + flag);
431
+ const terminatorPosition = argv.indexOf("--");
432
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
433
+ }
434
+ var { env } = import_node_process.default;
435
+ var flagForceColor;
436
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
437
+ flagForceColor = 0;
438
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
439
+ flagForceColor = 1;
440
+ }
441
+ function envForceColor() {
442
+ if ("FORCE_COLOR" in env) {
443
+ if (env.FORCE_COLOR === "true") {
444
+ return 1;
445
+ }
446
+ if (env.FORCE_COLOR === "false") {
447
+ return 0;
448
+ }
449
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
450
+ }
451
+ }
452
+ function translateLevel(level) {
453
+ if (level === 0) {
454
+ return false;
455
+ }
456
+ return {
457
+ level,
458
+ hasBasic: true,
459
+ has256: level >= 2,
460
+ has16m: level >= 3
461
+ };
462
+ }
463
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
464
+ const noFlagForceColor = envForceColor();
465
+ if (noFlagForceColor !== void 0) {
466
+ flagForceColor = noFlagForceColor;
467
+ }
468
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
469
+ if (forceColor === 0) {
470
+ return 0;
471
+ }
472
+ if (sniffFlags) {
473
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
474
+ return 3;
475
+ }
476
+ if (hasFlag("color=256")) {
477
+ return 2;
478
+ }
479
+ }
480
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
481
+ return 1;
482
+ }
483
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
484
+ return 0;
485
+ }
486
+ const min = forceColor || 0;
487
+ if (env.TERM === "dumb") {
488
+ return min;
489
+ }
490
+ if (import_node_process.default.platform === "win32") {
491
+ const osRelease = import_node_os2.default.release().split(".");
492
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
493
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
494
+ }
495
+ return 1;
496
+ }
497
+ if ("CI" in env) {
498
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
499
+ return 3;
500
+ }
501
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
502
+ return 1;
503
+ }
504
+ return min;
505
+ }
506
+ if ("TEAMCITY_VERSION" in env) {
507
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
508
+ }
509
+ if (env.COLORTERM === "truecolor") {
510
+ return 3;
511
+ }
512
+ if (env.TERM === "xterm-kitty") {
513
+ return 3;
514
+ }
515
+ if (env.TERM === "xterm-ghostty") {
516
+ return 3;
517
+ }
518
+ if (env.TERM === "wezterm") {
519
+ return 3;
520
+ }
521
+ if ("TERM_PROGRAM" in env) {
522
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
523
+ switch (env.TERM_PROGRAM) {
524
+ case "iTerm.app": {
525
+ return version >= 3 ? 3 : 2;
526
+ }
527
+ case "Apple_Terminal": {
528
+ return 2;
529
+ }
530
+ }
531
+ }
532
+ if (/-256(color)?$/i.test(env.TERM)) {
533
+ return 2;
534
+ }
535
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
536
+ return 1;
537
+ }
538
+ if ("COLORTERM" in env) {
539
+ return 1;
540
+ }
541
+ return min;
542
+ }
543
+ function createSupportsColor(stream, options = {}) {
544
+ const level = _supportsColor(stream, {
545
+ streamIsTTY: stream && stream.isTTY,
546
+ ...options
547
+ });
548
+ return translateLevel(level);
549
+ }
550
+ var supportsColor = {
551
+ stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }),
552
+ stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) })
553
+ };
554
+ var supports_color_default = supportsColor;
555
+
556
+ // node_modules/chalk/source/utilities.js
557
+ function stringReplaceAll(string, substring, replacer) {
558
+ let index = string.indexOf(substring);
559
+ if (index === -1) {
560
+ return string;
561
+ }
562
+ const substringLength = substring.length;
563
+ let endIndex = 0;
564
+ let returnValue = "";
565
+ do {
566
+ returnValue += string.slice(endIndex, index) + substring + replacer;
567
+ endIndex = index + substringLength;
568
+ index = string.indexOf(substring, endIndex);
569
+ } while (index !== -1);
570
+ returnValue += string.slice(endIndex);
571
+ return returnValue;
572
+ }
573
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
574
+ let endIndex = 0;
575
+ let returnValue = "";
576
+ do {
577
+ const gotCR = string[index - 1] === "\r";
578
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
579
+ endIndex = index + 1;
580
+ index = string.indexOf("\n", endIndex);
581
+ } while (index !== -1);
582
+ returnValue += string.slice(endIndex);
583
+ return returnValue;
584
+ }
585
+
586
+ // node_modules/chalk/source/index.js
587
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
588
+ var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
589
+ var STYLER = /* @__PURE__ */ Symbol("STYLER");
590
+ var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
591
+ var levelMapping = [
592
+ "ansi",
593
+ "ansi",
594
+ "ansi256",
595
+ "ansi16m"
596
+ ];
597
+ var styles2 = /* @__PURE__ */ Object.create(null);
598
+ var applyOptions = (object, options = {}) => {
599
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
600
+ throw new Error("The `level` option should be an integer from 0 to 3");
601
+ }
602
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
603
+ object.level = options.level === void 0 ? colorLevel : options.level;
604
+ };
605
+ var chalkFactory = (options) => {
606
+ const chalk2 = (...strings) => strings.join(" ");
607
+ applyOptions(chalk2, options);
608
+ Object.setPrototypeOf(chalk2, createChalk.prototype);
609
+ return chalk2;
610
+ };
611
+ function createChalk(options) {
612
+ return chalkFactory(options);
613
+ }
614
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
615
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
616
+ styles2[styleName] = {
617
+ get() {
618
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
619
+ Object.defineProperty(this, styleName, { value: builder });
620
+ return builder;
621
+ }
622
+ };
623
+ }
624
+ styles2.visible = {
625
+ get() {
626
+ const builder = createBuilder(this, this[STYLER], true);
627
+ Object.defineProperty(this, "visible", { value: builder });
628
+ return builder;
629
+ }
630
+ };
631
+ var getModelAnsi = (model, level, type, ...arguments_) => {
632
+ if (model === "rgb") {
633
+ if (level === "ansi16m") {
634
+ return ansi_styles_default[type].ansi16m(...arguments_);
635
+ }
636
+ if (level === "ansi256") {
637
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
638
+ }
639
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
640
+ }
641
+ if (model === "hex") {
642
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
643
+ }
644
+ return ansi_styles_default[type][model](...arguments_);
645
+ };
646
+ var usedModels = ["rgb", "hex", "ansi256"];
647
+ for (const model of usedModels) {
648
+ styles2[model] = {
649
+ get() {
650
+ const { level } = this;
651
+ return function(...arguments_) {
652
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
653
+ return createBuilder(this, styler, this[IS_EMPTY]);
654
+ };
655
+ }
656
+ };
657
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
658
+ styles2[bgModel] = {
659
+ get() {
660
+ const { level } = this;
661
+ return function(...arguments_) {
662
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
663
+ return createBuilder(this, styler, this[IS_EMPTY]);
664
+ };
665
+ }
666
+ };
667
+ }
668
+ var proto = Object.defineProperties(() => {
669
+ }, {
670
+ ...styles2,
671
+ level: {
672
+ enumerable: true,
673
+ get() {
674
+ return this[GENERATOR].level;
675
+ },
676
+ set(level) {
677
+ this[GENERATOR].level = level;
678
+ }
679
+ }
680
+ });
681
+ var createStyler = (open, close, parent) => {
682
+ let openAll;
683
+ let closeAll;
684
+ if (parent === void 0) {
685
+ openAll = open;
686
+ closeAll = close;
687
+ } else {
688
+ openAll = parent.openAll + open;
689
+ closeAll = close + parent.closeAll;
690
+ }
691
+ return {
692
+ open,
693
+ close,
694
+ openAll,
695
+ closeAll,
696
+ parent
697
+ };
698
+ };
699
+ var createBuilder = (self, _styler, _isEmpty) => {
700
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
701
+ Object.setPrototypeOf(builder, proto);
702
+ builder[GENERATOR] = self;
703
+ builder[STYLER] = _styler;
704
+ builder[IS_EMPTY] = _isEmpty;
705
+ return builder;
706
+ };
707
+ var applyStyle = (self, string) => {
708
+ if (self.level <= 0 || !string) {
709
+ return self[IS_EMPTY] ? "" : string;
710
+ }
711
+ let styler = self[STYLER];
712
+ if (styler === void 0) {
713
+ return string;
714
+ }
715
+ const { openAll, closeAll } = styler;
716
+ if (string.includes("\x1B")) {
717
+ while (styler !== void 0) {
718
+ string = stringReplaceAll(string, styler.close, styler.open);
719
+ styler = styler.parent;
720
+ }
721
+ }
722
+ const lfIndex = string.indexOf("\n");
723
+ if (lfIndex !== -1) {
724
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
725
+ }
726
+ return openAll + string + closeAll;
727
+ };
728
+ Object.defineProperties(createChalk.prototype, styles2);
729
+ var chalk = createChalk();
730
+ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
731
+ var source_default = chalk;
732
+
733
+ // src/display.ts
734
+ var DEFAULT_OPTIONS = {
735
+ verbose: false,
736
+ quiet: false,
737
+ noColor: false,
738
+ maxPayload: null,
739
+ filter: null
740
+ };
741
+ function formatTime(isoTimestamp) {
742
+ const d = new Date(isoTimestamp);
743
+ const h = String(d.getHours()).padStart(2, "0");
744
+ const m = String(d.getMinutes()).padStart(2, "0");
745
+ const s = String(d.getSeconds()).padStart(2, "0");
746
+ const ms = String(d.getMilliseconds()).padStart(3, "0");
747
+ return `${h}:${m}:${s}.${ms}`;
748
+ }
749
+ function truncate(str, maxLen) {
750
+ if (str.length <= maxLen) return str;
751
+ return str.slice(0, maxLen - 3) + "...";
752
+ }
753
+ function formatBytes(bytes) {
754
+ if (bytes < 1024) return `${bytes} bytes`;
755
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
756
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
757
+ }
758
+ function displayCompact(event, opts) {
759
+ const time = formatTime(event.timestamp);
760
+ const isRequest2 = event.direction === "client\u2192server";
761
+ let arrow;
762
+ let dirLabel;
763
+ if (isRequest2) {
764
+ arrow = opts.noColor ? "\u2500\u2500\u25B6" : source_default.green("\u2500\u2500\u25B6");
765
+ dirLabel = "";
766
+ } else {
767
+ arrow = opts.noColor ? "\u25C0\u2500\u2500" : source_default.blue("\u25C0\u2500\u2500");
768
+ dirLabel = "";
769
+ }
770
+ const method = event.method || "";
771
+ const tool = event.toolName ? ` ${event.toolName}` : "";
772
+ const latency = event.latencyMs != null ? ` ${event.latencyMs}ms` : "";
773
+ let payload = "";
774
+ if (!isRequest2 && event.params) {
775
+ try {
776
+ const parsed = JSON.parse(event.params);
777
+ if (parsed.result) {
778
+ if (typeof parsed.result === "object" && parsed.result.content) {
779
+ payload = ` (${formatBytes(event.payloadBytes)})`;
780
+ } else if (typeof parsed.result === "object") {
781
+ const keys = Object.keys(parsed.result);
782
+ if (parsed.result.tools) {
783
+ payload = ` (${parsed.result.tools.length} tools)`;
784
+ } else {
785
+ payload = ` (${formatBytes(event.payloadBytes)})`;
786
+ }
787
+ } else {
788
+ payload = ` (ok)`;
789
+ }
790
+ } else if (parsed.error) {
791
+ const errMsg = parsed.error.message || "error";
792
+ payload = opts.noColor ? ` ERROR: ${errMsg}` : ` ${source_default.red("ERROR:")} ${errMsg}`;
793
+ }
794
+ } catch {
795
+ payload = ` (${formatBytes(event.payloadBytes)})`;
796
+ }
797
+ }
798
+ if (isRequest2 && event.params && event.method === "tools/call") {
799
+ try {
800
+ const parsed = JSON.parse(event.params);
801
+ if (parsed.params?.arguments) {
802
+ const argsStr = JSON.stringify(parsed.params.arguments);
803
+ payload = ` ${truncate(argsStr, 60)}`;
804
+ }
805
+ } catch {
806
+ }
807
+ }
808
+ const methodDisplay = opts.noColor ? method : isRequest2 ? source_default.green(method) : source_default.blue(method);
809
+ const toolDisplay = opts.noColor ? tool : source_default.yellow(tool);
810
+ const latencyDisplay = opts.noColor ? latency : source_default.gray(latency);
811
+ const timeDisplay = opts.noColor ? time : source_default.gray(time);
812
+ process.stderr.write(`${timeDisplay} ${arrow} ${methodDisplay}${toolDisplay}${payload}${latencyDisplay}
813
+ `);
814
+ }
815
+ function displayVerbose(event, opts) {
816
+ const time = formatTime(event.timestamp);
817
+ const isRequest2 = event.direction === "client\u2192server";
818
+ let arrow;
819
+ if (isRequest2) {
820
+ arrow = opts.noColor ? "\u2500\u2500\u25B6" : source_default.green("\u2500\u2500\u25B6");
821
+ } else {
822
+ arrow = opts.noColor ? "\u25C0\u2500\u2500" : source_default.blue("\u25C0\u2500\u2500");
823
+ }
824
+ const method = event.method || "(response)";
825
+ const methodDisplay = opts.noColor ? method : isRequest2 ? source_default.green(method) : source_default.blue(method);
826
+ const timeDisplay = opts.noColor ? time : source_default.gray(time);
827
+ process.stderr.write(`${timeDisplay} ${arrow} ${methodDisplay}
828
+ `);
829
+ if (event.toolName) {
830
+ process.stderr.write(` tool: ${event.toolName}
831
+ `);
832
+ }
833
+ if (event.latencyMs != null) {
834
+ process.stderr.write(` latency: ${event.latencyMs}ms
835
+ `);
836
+ }
837
+ if (event.params) {
838
+ try {
839
+ const parsed = JSON.parse(event.params);
840
+ let displayPayload;
841
+ if (isRequest2 && parsed.params) {
842
+ displayPayload = JSON.stringify(parsed.params, null, 2);
843
+ } else if (!isRequest2 && (parsed.result || parsed.error)) {
844
+ displayPayload = JSON.stringify(parsed.result || parsed.error, null, 2);
845
+ } else {
846
+ displayPayload = JSON.stringify(parsed, null, 2);
847
+ }
848
+ if (opts.maxPayload && displayPayload.length > opts.maxPayload) {
849
+ displayPayload = truncate(displayPayload, opts.maxPayload);
850
+ }
851
+ const indented = displayPayload.split("\n").map((line) => ` ${line}`).join("\n");
852
+ process.stderr.write(`${indented}
853
+ `);
854
+ } catch {
855
+ process.stderr.write(` ${event.params}
856
+ `);
857
+ }
858
+ }
859
+ process.stderr.write("\n");
860
+ }
861
+ function displayEvent(event, opts = {}) {
862
+ const options = { ...DEFAULT_OPTIONS, ...opts };
863
+ if (options.quiet) return;
864
+ if (options.filter && event.method && !event.method.includes(options.filter)) {
865
+ return;
866
+ }
867
+ if (options.verbose) {
868
+ displayVerbose(event, options);
869
+ } else {
870
+ displayCompact(event, options);
871
+ }
872
+ }
873
+ function displayBanner(serverCommand, sessionId, dbPath) {
874
+ const header = source_default.bold("mcp-wiretap");
875
+ const dim = source_default.gray;
876
+ process.stderr.write(`
877
+ ${header} ${dim("v0.1.0")}
878
+ `);
879
+ process.stderr.write(` ${dim("session:")} ${sessionId.slice(0, 8)}
880
+ `);
881
+ process.stderr.write(` ${dim("server:")} ${serverCommand}
882
+ `);
883
+ process.stderr.write(` ${dim("db:")} ${dbPath}
884
+ `);
885
+ process.stderr.write(` ${dim("\u2500".repeat(50))}
886
+
887
+ `);
888
+ }
889
+ function displayShutdown(eventCount, sessionId) {
890
+ const dim = source_default.gray;
891
+ process.stderr.write(`
892
+ ${dim("\u2500".repeat(50))}
893
+ `);
894
+ process.stderr.write(` ${dim("session")} ${sessionId.slice(0, 8)} ${dim("ended \u2014")} ${eventCount} ${dim("events logged")}
895
+
896
+ `);
897
+ }
898
+
899
+ // src/proxy.ts
900
+ var WiretapProxy = class {
901
+ child = null;
902
+ logger;
903
+ correlator = new RequestCorrelator();
904
+ clientBuffer = new MessageBuffer();
905
+ serverBuffer = new MessageBuffer();
906
+ eventCount = 0;
907
+ displayOptions;
908
+ serverCommand;
909
+ constructor(options) {
910
+ this.logger = new WiretapLogger(options.dbPath);
911
+ this.displayOptions = options.displayOptions || {};
912
+ this.serverCommand = [options.command, ...options.args].join(" ");
913
+ }
914
+ /**
915
+ * Start the proxy. Spawns the child MCP server and begins intercepting.
916
+ */
917
+ start(command, args) {
918
+ this.logger.startSession(this.serverCommand);
919
+ displayBanner(
920
+ this.serverCommand,
921
+ this.logger.sessionId,
922
+ WiretapLogger.defaultDbPath
923
+ );
924
+ this.child = (0, import_node_child_process.spawn)(command, args, {
925
+ stdio: ["pipe", "pipe", "pipe"],
926
+ env: process.env
927
+ });
928
+ process.stdin.on("data", (chunk) => {
929
+ const data = chunk.toString("utf-8");
930
+ if (this.child?.stdin?.writable) {
931
+ this.child.stdin.write(chunk);
932
+ }
933
+ const messages = this.clientBuffer.feed(data);
934
+ for (const raw of messages) {
935
+ this.processMessage(raw, "client\u2192server");
936
+ }
937
+ });
938
+ process.stdin.on("end", () => {
939
+ this.child?.stdin?.end();
940
+ });
941
+ this.child.stdout?.on("data", (chunk) => {
942
+ const data = chunk.toString("utf-8");
943
+ process.stdout.write(chunk);
944
+ const messages = this.serverBuffer.feed(data);
945
+ for (const raw of messages) {
946
+ this.processMessage(raw, "server\u2192client");
947
+ }
948
+ });
949
+ this.child.stderr?.on("data", (chunk) => {
950
+ process.stderr.write(chunk);
951
+ });
952
+ this.child.on("error", (err) => {
953
+ process.stderr.write(`
954
+ mcp-wiretap: failed to start server: ${err.message}
955
+ `);
956
+ this.shutdown(1);
957
+ });
958
+ this.child.on("exit", (code, signal) => {
959
+ const exitReason = signal ? `signal ${signal}` : `code ${code}`;
960
+ process.stderr.write(`
961
+ mcp-wiretap: server exited (${exitReason})
962
+ `);
963
+ this.shutdown(code ?? 1);
964
+ });
965
+ process.on("SIGINT", () => this.shutdown(0));
966
+ process.on("SIGTERM", () => this.shutdown(0));
967
+ }
968
+ /**
969
+ * Process a single parsed message line
970
+ */
971
+ processMessage(raw, direction) {
972
+ const msg = tryParseJsonRpc(raw);
973
+ if (!msg) return;
974
+ const now = (/* @__PURE__ */ new Date()).toISOString();
975
+ const payloadBytes = Buffer.byteLength(raw, "utf-8");
976
+ if (isRequest(msg)) {
977
+ const toolName = extractToolName(msg);
978
+ if (msg.id !== void 0) {
979
+ this.correlator.trackRequest(msg.id, msg.method, toolName);
980
+ }
981
+ const event = {
982
+ direction,
983
+ timestamp: now,
984
+ jsonrpcId: msg.id ?? null,
985
+ method: msg.method,
986
+ toolName,
987
+ params: raw,
988
+ payloadBytes,
989
+ latencyMs: null,
990
+ sessionId: this.logger.sessionId
991
+ };
992
+ this.logger.logEvent(event);
993
+ displayEvent(event, this.displayOptions);
994
+ this.eventCount++;
995
+ } else if (isResponse(msg)) {
996
+ let method = null;
997
+ let toolName = null;
998
+ let latencyMs = null;
999
+ if (msg.id !== void 0) {
1000
+ const match = this.correlator.matchResponse(msg.id);
1001
+ if (match) {
1002
+ method = match.method;
1003
+ toolName = match.toolName;
1004
+ latencyMs = match.latencyMs;
1005
+ }
1006
+ }
1007
+ const event = {
1008
+ direction,
1009
+ timestamp: now,
1010
+ jsonrpcId: msg.id ?? null,
1011
+ method,
1012
+ toolName,
1013
+ params: raw,
1014
+ payloadBytes,
1015
+ latencyMs,
1016
+ sessionId: this.logger.sessionId
1017
+ };
1018
+ this.logger.logEvent(event);
1019
+ displayEvent(event, this.displayOptions);
1020
+ this.eventCount++;
1021
+ } else {
1022
+ const event = {
1023
+ direction,
1024
+ timestamp: now,
1025
+ jsonrpcId: null,
1026
+ method: msg.method || null,
1027
+ toolName: null,
1028
+ params: raw,
1029
+ payloadBytes,
1030
+ latencyMs: null,
1031
+ sessionId: this.logger.sessionId
1032
+ };
1033
+ this.logger.logEvent(event);
1034
+ displayEvent(event, this.displayOptions);
1035
+ this.eventCount++;
1036
+ }
1037
+ }
1038
+ /**
1039
+ * Clean shutdown
1040
+ */
1041
+ shutdown(exitCode) {
1042
+ const clientRemaining = this.clientBuffer.flush();
1043
+ if (clientRemaining) this.processMessage(clientRemaining, "client\u2192server");
1044
+ const serverRemaining = this.serverBuffer.flush();
1045
+ if (serverRemaining) this.processMessage(serverRemaining, "server\u2192client");
1046
+ displayShutdown(this.eventCount, this.logger.sessionId);
1047
+ if (this.child && !this.child.killed) {
1048
+ this.child.kill("SIGTERM");
1049
+ }
1050
+ this.logger.close();
1051
+ process.exit(exitCode);
1052
+ }
1053
+ };
1054
+
1055
+ // src/export.ts
1056
+ var import_node_fs2 = __toESM(require("fs"));
1057
+ function eventsToCSV(events) {
1058
+ const headers = [
1059
+ "id",
1060
+ "timestamp",
1061
+ "direction",
1062
+ "jsonrpc_id",
1063
+ "method",
1064
+ "tool_name",
1065
+ "params",
1066
+ "latency_ms",
1067
+ "payload_bytes",
1068
+ "session_id"
1069
+ ];
1070
+ const rows = events.map((e) => {
1071
+ return [
1072
+ e.id,
1073
+ e.timestamp,
1074
+ `"${e.direction}"`,
1075
+ e.jsonrpc_id ?? "",
1076
+ e.method ?? "",
1077
+ e.tool_name ?? "",
1078
+ `"${(e.params ?? "").replace(/"/g, '""')}"`,
1079
+ e.latency_ms ?? "",
1080
+ e.payload_bytes,
1081
+ e.session_id
1082
+ ].join(",");
1083
+ });
1084
+ return [headers.join(","), ...rows].join("\n") + "\n";
1085
+ }
1086
+ function exportEvents(options) {
1087
+ const logger = new WiretapLogger(options.dbPath);
1088
+ let events;
1089
+ if (options.all) {
1090
+ events = logger.getAllEvents();
1091
+ } else if (options.sessionId) {
1092
+ events = logger.getSessionEvents(options.sessionId);
1093
+ } else {
1094
+ const sessions = logger.listSessions();
1095
+ if (sessions.length === 0) {
1096
+ process.stderr.write("No sessions found.\n");
1097
+ logger.close();
1098
+ process.exit(1);
1099
+ }
1100
+ events = logger.getSessionEvents(sessions[0].sessionId);
1101
+ }
1102
+ let output;
1103
+ if (options.format === "csv") {
1104
+ output = eventsToCSV(events);
1105
+ } else {
1106
+ output = JSON.stringify(events, null, 2) + "\n";
1107
+ }
1108
+ if (options.output) {
1109
+ import_node_fs2.default.writeFileSync(options.output, output, "utf-8");
1110
+ process.stderr.write(`Exported ${events.length} events to ${options.output}
1111
+ `);
1112
+ } else {
1113
+ process.stdout.write(output);
1114
+ }
1115
+ logger.close();
1116
+ }
1117
+
1118
+ // src/sessions.ts
1119
+ function listSessions(dbPath) {
1120
+ const logger = new WiretapLogger(dbPath);
1121
+ const sessions = logger.listSessions();
1122
+ if (sessions.length === 0) {
1123
+ process.stderr.write("No sessions recorded yet.\n");
1124
+ logger.close();
1125
+ return;
1126
+ }
1127
+ process.stderr.write(`
1128
+ ${source_default.bold("Recorded Sessions")}
1129
+ `);
1130
+ process.stderr.write(` ${source_default.gray("\u2500".repeat(70))}
1131
+
1132
+ `);
1133
+ for (const session of sessions) {
1134
+ const id = session.sessionId.slice(0, 8);
1135
+ const date = new Date(session.startedAt).toLocaleString();
1136
+ const count = session.eventCount ?? 0;
1137
+ process.stderr.write(
1138
+ ` ${source_default.yellow(id)} ${source_default.gray(date)} ${count} events ${source_default.gray(session.serverCommand)}
1139
+ `
1140
+ );
1141
+ }
1142
+ process.stderr.write("\n");
1143
+ logger.close();
1144
+ }
1145
+ function replaySession(sessionId, dbPath, verbose = false) {
1146
+ const logger = new WiretapLogger(dbPath);
1147
+ const sessions = logger.listSessions();
1148
+ const match = sessions.find(
1149
+ (s) => s.sessionId === sessionId || s.sessionId.startsWith(sessionId)
1150
+ );
1151
+ if (!match) {
1152
+ process.stderr.write(`Session "${sessionId}" not found.
1153
+ `);
1154
+ logger.close();
1155
+ process.exit(1);
1156
+ }
1157
+ const events = logger.getSessionEvents(match.sessionId);
1158
+ process.stderr.write(`
1159
+ ${source_default.bold("Replaying session")} ${source_default.yellow(match.sessionId.slice(0, 8))}
1160
+ `);
1161
+ process.stderr.write(` ${source_default.gray("server:")} ${match.serverCommand}
1162
+ `);
1163
+ process.stderr.write(` ${source_default.gray("started:")} ${new Date(match.startedAt).toLocaleString()}
1164
+ `);
1165
+ process.stderr.write(` ${source_default.gray("events:")} ${events.length}
1166
+ `);
1167
+ process.stderr.write(` ${source_default.gray("\u2500".repeat(50))}
1168
+
1169
+ `);
1170
+ for (const event of events) {
1171
+ displayEvent(
1172
+ {
1173
+ direction: event.direction,
1174
+ timestamp: event.timestamp,
1175
+ jsonrpcId: event.jsonrpc_id ? isNaN(Number(event.jsonrpc_id)) ? event.jsonrpc_id : Number(event.jsonrpc_id) : null,
1176
+ method: event.method,
1177
+ toolName: event.tool_name,
1178
+ params: event.params || "",
1179
+ payloadBytes: event.payload_bytes,
1180
+ latencyMs: event.latency_ms,
1181
+ sessionId: event.session_id
1182
+ },
1183
+ { verbose }
1184
+ );
1185
+ }
1186
+ process.stderr.write(`
1187
+ ${source_default.gray("\u2500".repeat(50))}
1188
+ `);
1189
+ process.stderr.write(` ${source_default.gray("replay complete \u2014")} ${events.length} ${source_default.gray("events")}
1190
+
1191
+ `);
1192
+ logger.close();
1193
+ }
1194
+ function searchEvents(query, dbPath, verbose = false) {
1195
+ const logger = new WiretapLogger(dbPath);
1196
+ const events = logger.searchEvents(query);
1197
+ if (events.length === 0) {
1198
+ process.stderr.write(`No events matching "${query}".
1199
+ `);
1200
+ logger.close();
1201
+ return;
1202
+ }
1203
+ process.stderr.write(`
1204
+ ${source_default.bold("Search results for")} "${query}" ${source_default.gray(`(${events.length} matches)`)}
1205
+ `);
1206
+ process.stderr.write(` ${source_default.gray("\u2500".repeat(50))}
1207
+
1208
+ `);
1209
+ for (const event of events) {
1210
+ displayEvent(
1211
+ {
1212
+ direction: event.direction,
1213
+ timestamp: event.timestamp,
1214
+ jsonrpcId: event.jsonrpc_id ? isNaN(Number(event.jsonrpc_id)) ? event.jsonrpc_id : Number(event.jsonrpc_id) : null,
1215
+ method: event.method,
1216
+ toolName: event.tool_name,
1217
+ params: event.params || "",
1218
+ payloadBytes: event.payload_bytes,
1219
+ latencyMs: event.latency_ms,
1220
+ sessionId: event.session_id
1221
+ },
1222
+ { verbose }
1223
+ );
1224
+ }
1225
+ process.stderr.write("\n");
1226
+ logger.close();
1227
+ }
1228
+
1229
+ // src/index.ts
1230
+ var VERSION = "0.1.0";
1231
+ function printUsage() {
1232
+ process.stderr.write(`
1233
+ mcp-wiretap v${VERSION}
1234
+ See everything your AI agents do through MCP.
1235
+
1236
+ USAGE
1237
+
1238
+ Wrap an MCP server:
1239
+ npx mcp-wiretap [options] -- <server-command> [server-args...]
1240
+
1241
+ Export logs:
1242
+ npx mcp-wiretap export [--format json|csv] [--output file] [--all]
1243
+
1244
+ Browse sessions:
1245
+ npx mcp-wiretap sessions
1246
+ npx mcp-wiretap replay <session-id>
1247
+ npx mcp-wiretap search <query>
1248
+
1249
+ OPTIONS
1250
+
1251
+ --db <path> SQLite database location (default: ~/.mcp-wiretap/wiretap.db)
1252
+ --quiet Suppress terminal output, log only
1253
+ --verbose Show full payloads in terminal
1254
+ --filter <method> Only log specific methods (e.g., tools/call)
1255
+ --max-payload <bytes> Truncate logged payloads in display
1256
+ --no-color Disable color output
1257
+ --version Show version
1258
+ --help Show this help
1259
+
1260
+ EXAMPLES
1261
+
1262
+ # Wrap a filesystem MCP server
1263
+ npx mcp-wiretap -- npx -y @modelcontextprotocol/server-filesystem ~/Code
1264
+
1265
+ # Verbose output with method filter
1266
+ npx mcp-wiretap --verbose --filter tools/call -- npx -y @modelcontextprotocol/server-filesystem ~/Code
1267
+
1268
+ # Export last session to JSON
1269
+ npx mcp-wiretap export --format json --output audit.json
1270
+
1271
+ # Search across all sessions
1272
+ npx mcp-wiretap search "read_file"
1273
+
1274
+ `);
1275
+ }
1276
+ function parseArgs(argv) {
1277
+ const flags = {
1278
+ quiet: false,
1279
+ verbose: false,
1280
+ noColor: false,
1281
+ help: false,
1282
+ version: false,
1283
+ all: false
1284
+ };
1285
+ const positionalArgs = [];
1286
+ let serverCommand = null;
1287
+ let serverArgs = [];
1288
+ let subcommand = null;
1289
+ const dashDashIdx = argv.indexOf("--");
1290
+ let cliArgs;
1291
+ if (dashDashIdx !== -1) {
1292
+ cliArgs = argv.slice(0, dashDashIdx);
1293
+ const serverParts = argv.slice(dashDashIdx + 1);
1294
+ if (serverParts.length > 0) {
1295
+ serverCommand = serverParts[0];
1296
+ serverArgs = serverParts.slice(1);
1297
+ }
1298
+ } else {
1299
+ cliArgs = argv;
1300
+ }
1301
+ let i = 0;
1302
+ while (i < cliArgs.length) {
1303
+ const arg = cliArgs[i];
1304
+ switch (arg) {
1305
+ case "--db":
1306
+ flags.db = cliArgs[++i];
1307
+ break;
1308
+ case "--quiet":
1309
+ case "-q":
1310
+ flags.quiet = true;
1311
+ break;
1312
+ case "--verbose":
1313
+ case "-v":
1314
+ flags.verbose = true;
1315
+ break;
1316
+ case "--filter":
1317
+ flags.filter = cliArgs[++i];
1318
+ break;
1319
+ case "--max-payload":
1320
+ flags.maxPayload = parseInt(cliArgs[++i], 10);
1321
+ break;
1322
+ case "--no-color":
1323
+ flags.noColor = true;
1324
+ break;
1325
+ case "--help":
1326
+ case "-h":
1327
+ flags.help = true;
1328
+ break;
1329
+ case "--version":
1330
+ flags.version = true;
1331
+ break;
1332
+ case "--format":
1333
+ case "-f":
1334
+ flags.format = cliArgs[++i];
1335
+ break;
1336
+ case "--output":
1337
+ case "-o":
1338
+ flags.output = cliArgs[++i];
1339
+ break;
1340
+ case "--all":
1341
+ flags.all = true;
1342
+ break;
1343
+ default:
1344
+ if (!arg.startsWith("-")) {
1345
+ positionalArgs.push(arg);
1346
+ }
1347
+ break;
1348
+ }
1349
+ i++;
1350
+ }
1351
+ if (positionalArgs.length > 0) {
1352
+ const first = positionalArgs[0];
1353
+ if (["export", "sessions", "replay", "search"].includes(first)) {
1354
+ subcommand = first;
1355
+ }
1356
+ }
1357
+ return { subcommand, serverCommand, serverArgs, flags, positionalArgs };
1358
+ }
1359
+ function main() {
1360
+ const argv = process.argv.slice(2);
1361
+ const parsed = parseArgs(argv);
1362
+ if (parsed.flags.version) {
1363
+ process.stderr.write(`mcp-wiretap v${VERSION}
1364
+ `);
1365
+ process.exit(0);
1366
+ }
1367
+ if (parsed.flags.help) {
1368
+ printUsage();
1369
+ process.exit(0);
1370
+ }
1371
+ switch (parsed.subcommand) {
1372
+ case "export": {
1373
+ exportEvents({
1374
+ format: parsed.flags.format || "json",
1375
+ output: parsed.flags.output,
1376
+ all: parsed.flags.all,
1377
+ dbPath: parsed.flags.db
1378
+ });
1379
+ return;
1380
+ }
1381
+ case "sessions": {
1382
+ listSessions(parsed.flags.db);
1383
+ return;
1384
+ }
1385
+ case "replay": {
1386
+ const sessionId = parsed.positionalArgs[1];
1387
+ if (!sessionId) {
1388
+ process.stderr.write("Usage: mcp-wiretap replay <session-id>\n");
1389
+ process.exit(1);
1390
+ }
1391
+ replaySession(sessionId, parsed.flags.db, parsed.flags.verbose);
1392
+ return;
1393
+ }
1394
+ case "search": {
1395
+ const query = parsed.positionalArgs[1];
1396
+ if (!query) {
1397
+ process.stderr.write("Usage: mcp-wiretap search <query>\n");
1398
+ process.exit(1);
1399
+ }
1400
+ searchEvents(query, parsed.flags.db, parsed.flags.verbose);
1401
+ return;
1402
+ }
1403
+ }
1404
+ if (!parsed.serverCommand) {
1405
+ process.stderr.write("Error: No server command provided.\n\n");
1406
+ process.stderr.write("Usage: npx mcp-wiretap [options] -- <server-command> [server-args...]\n\n");
1407
+ process.stderr.write("Example: npx mcp-wiretap -- npx -y @modelcontextprotocol/server-filesystem ~/Code\n\n");
1408
+ process.stderr.write("Run 'npx mcp-wiretap --help' for full usage.\n");
1409
+ process.exit(1);
1410
+ }
1411
+ const proxy = new WiretapProxy({
1412
+ command: parsed.serverCommand,
1413
+ args: parsed.serverArgs,
1414
+ dbPath: parsed.flags.db,
1415
+ displayOptions: {
1416
+ verbose: parsed.flags.verbose,
1417
+ quiet: parsed.flags.quiet,
1418
+ noColor: parsed.flags.noColor,
1419
+ maxPayload: parsed.flags.maxPayload || null,
1420
+ filter: parsed.flags.filter || null
1421
+ }
1422
+ });
1423
+ proxy.start(parsed.serverCommand, parsed.serverArgs);
1424
+ }
1425
+ main();
1426
+ //# sourceMappingURL=index.js.map