run-mcp 1.1.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +27 -7
  2. package/dist/index.js +275 -35
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -93,7 +93,9 @@ Options:
93
93
  run-mcp proxy <target_command...> [options]
94
94
 
95
95
  Options:
96
- -o, --out-dir <path> Directory to save intercepted images (default: $TMPDIR/run-mcp)
96
+ -o, --out-dir <path> Directory to save intercepted images and audio (default: $TMPDIR/run-mcp)
97
+ -t, --timeout <ms> Default tool call timeout in milliseconds (default: 60000)
98
+ --max-text <chars> Max text response length before truncation (default: 50000)
97
99
  ```
98
100
 
99
101
  ## REPL Commands
@@ -161,14 +163,32 @@ In proxy mode, `run-mcp` acts as an MCP server itself. Configure it as the comma
161
163
  }
162
164
  ```
163
165
 
166
+ ### What the proxy forwards
167
+
168
+ The proxy dynamically mirrors the target server's capabilities. All MCP primitives that the target supports are forwarded transparently:
169
+
170
+ | Primitive | Forwarded? |
171
+ |-----------|------------|
172
+ | **Tools** (`tools/list`, `tools/call`) | ✅ Always (with interception) |
173
+ | **Resources** (`resources/list`, `resources/read`, `resources/templates/list`) | ✅ If target supports |
174
+ | **Prompts** (`prompts/list`, `prompts/get`) | ✅ If target supports |
175
+ | **Logging** (`logging/setLevel`) | ✅ If target supports |
176
+ | **Completion** (`completion/complete`) | ✅ If target supports |
177
+ | **Notifications** (list changes, logging) | ✅ Forwarded from target to agent |
178
+ | **Tool annotations** (`readOnlyHint`, `destructiveHint`, etc.) | ✅ Preserved as-is |
179
+ | **Pagination** (`nextCursor` / `cursor`) | ✅ Passed through |
180
+
164
181
  ### What the proxy intercepts
165
182
 
183
+ Tool call responses are processed through the interceptor pipeline. All other primitives pass through untouched.
184
+
166
185
  | Feature | Behavior |
167
186
  |---------|----------|
168
- | **Image extraction** | `type: "image"` responses with base64 data are saved to disk. The response is replaced with `[Image saved to /path/to/img.png (24KB)]` |
187
+ | **Image extraction** | `type: "image"` responses with base64 data are saved to disk. Replaced with `[Image saved to /path/to/img.png (24KB)]` |
188
+ | **Audio extraction** | `type: "audio"` responses with base64 data are saved to disk. Replaced with `[Audio saved to /path/to/audio.wav (12KB)]` |
169
189
  | **Base64 detection** | Text responses that are entirely base64-encoded (1000+ chars) are also saved as images |
170
- | **Timeouts** | Tool calls are wrapped in a 60-second timeout (prevents hung calls from blocking the agent) |
171
- | **Truncation** | Text responses exceeding 50,000 characters are truncated with a `... (truncated, N chars total)` message |
190
+ | **Timeouts** | Tool calls are wrapped in a configurable timeout (default 60s, use `--timeout` to change) |
191
+ | **Truncation** | Text responses exceeding the limit (default 50K chars, use `--max-text` to change) are truncated |
172
192
 
173
193
  ## Architecture
174
194
 
@@ -200,10 +220,10 @@ In proxy mode, `run-mcp` acts as an MCP server itself. Configure it as the comma
200
220
 
201
221
  | Module | File | Responsibility |
202
222
  |--------|------|----------------|
203
- | **TargetManager** | `src/target-manager.ts` | Spawns the target MCP server, manages the MCP Client connection, captures stderr, tracks lifecycle |
204
- | **ResponseInterceptor** | `src/interceptor.ts` | Wraps tool calls with timeouts, extracts base64 images to disk, truncates oversized text |
223
+ | **TargetManager** | `src/target-manager.ts` | Spawns the target MCP server, manages the MCP Client connection, forwards all MCP primitives (tools, resources, prompts, logging), captures stderr, tracks lifecycle |
224
+ | **ResponseInterceptor** | `src/interceptor.ts` | Wraps tool calls with timeouts, extracts base64 images and audio to disk, truncates oversized text |
205
225
  | **REPLMode** | `src/repl.ts` | Interactive readline REPL with shorthand command parsing and script mode |
206
- | **ProxyMode** | `src/proxy.ts` | MCP Server that bridges `tools/list` and `tools/call` through the interceptor to the target |
226
+ | **ProxyMode** | `src/proxy.ts` | MCP Server that transparently forwards all MCP primitives to the target, with tool responses running through the interceptor |
207
227
 
208
228
  ## Development
209
229
 
package/dist/index.js CHANGED
@@ -6,7 +6,23 @@ import { program } from "commander";
6
6
  // src/proxy.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
+ import {
10
+ CallToolRequestSchema,
11
+ CompleteRequestSchema,
12
+ GetPromptRequestSchema,
13
+ ListPromptsRequestSchema,
14
+ ListResourcesRequestSchema,
15
+ ListResourceTemplatesRequestSchema,
16
+ ListToolsRequestSchema,
17
+ LoggingMessageNotificationSchema,
18
+ PromptListChangedNotificationSchema,
19
+ ReadResourceRequestSchema,
20
+ ResourceListChangedNotificationSchema,
21
+ SetLevelRequestSchema,
22
+ SubscribeRequestSchema,
23
+ ToolListChangedNotificationSchema,
24
+ UnsubscribeRequestSchema
25
+ } from "@modelcontextprotocol/sdk/types.js";
10
26
 
11
27
  // src/interceptor.ts
12
28
  import { mkdir, writeFile } from "fs/promises";
@@ -14,17 +30,22 @@ import { tmpdir } from "os";
14
30
  import { join } from "path";
15
31
  var BASE64_PATTERN = /^[A-Za-z0-9+/]{1000,}={0,2}$/;
16
32
  var DEFAULT_TIMEOUT_MS = 6e4;
17
- var MAX_TEXT_LENGTH = 5e4;
33
+ var DEFAULT_MAX_TEXT_LENGTH = 5e4;
18
34
  var ResponseInterceptor = class {
19
35
  outDir;
20
36
  defaultTimeoutMs;
37
+ maxTextLength;
21
38
  fileCounter = 0;
22
39
  constructor(opts = {}) {
23
40
  this.outDir = opts.outDir ?? join(tmpdir(), "run-mcp");
24
41
  this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
42
+ this.maxTextLength = opts.maxTextLength ?? DEFAULT_MAX_TEXT_LENGTH;
25
43
  }
26
44
  /**
27
- * Call a tool on the target, applying timeout, image extraction, and truncation.
45
+ * Call a tool on the target, applying timeout, media extraction, and truncation.
46
+ *
47
+ * Returns the full result object as-is (including structuredContent, isError, _meta)
48
+ * with only the content array items modified when interception is needed.
28
49
  */
29
50
  async callTool(target, name, args = {}, timeoutMs) {
30
51
  const timeout = timeoutMs ?? this.defaultTimeoutMs;
@@ -38,20 +59,25 @@ var ResponseInterceptor = class {
38
59
  return result;
39
60
  }
40
61
  /**
41
- * Process a single content item — extract images, truncate text.
62
+ * Process a single content item — extract media, truncate text.
63
+ * Preserves all item properties not related to the intercepted data
64
+ * (e.g., annotations, _meta).
42
65
  */
43
66
  async _processItem(item) {
44
67
  if (item.type === "image" && item.data) {
45
- return this._saveImage(item.data, item.mimeType ?? "image/png");
68
+ return this._saveMedia(item.data, item.mimeType ?? "image/png", "image");
69
+ }
70
+ if (item.type === "audio" && item.data) {
71
+ return this._saveMedia(item.data, item.mimeType ?? "audio/wav", "audio");
46
72
  }
47
73
  if (item.type === "text" && item.text && BASE64_PATTERN.test(item.text.trim())) {
48
- return this._saveImage(item.text.trim(), "image/png");
74
+ return this._saveMedia(item.text.trim(), "image/png", "image");
49
75
  }
50
- if (item.type === "text" && item.text && item.text.length > MAX_TEXT_LENGTH) {
76
+ if (item.type === "text" && item.text && item.text.length > this.maxTextLength) {
51
77
  const totalLength = item.text.length;
52
78
  return {
53
- type: "text",
54
- text: item.text.slice(0, MAX_TEXT_LENGTH) + `
79
+ ...item,
80
+ text: item.text.slice(0, this.maxTextLength) + `
55
81
  ... (truncated, ${totalLength.toLocaleString()} chars total)`
56
82
  };
57
83
  }
@@ -59,20 +85,23 @@ var ResponseInterceptor = class {
59
85
  }
60
86
  /**
61
87
  * Decode base64, write to disk, return a text item with the file path.
88
+ * Works for both images and audio.
62
89
  */
63
- async _saveImage(base64Data, mimeType) {
90
+ async _saveMedia(base64Data, mimeType, mediaType) {
64
91
  await mkdir(this.outDir, { recursive: true });
65
92
  const ext = this._extensionFromMime(mimeType);
66
93
  const timestamp = Date.now();
67
94
  const counter = this.fileCounter++;
68
- const filename = `img_${timestamp}_${counter}${ext}`;
95
+ const prefix = mediaType === "audio" ? "audio" : "img";
96
+ const filename = `${prefix}_${timestamp}_${counter}${ext}`;
69
97
  const filepath = join(this.outDir, filename);
70
98
  const buffer = Buffer.from(base64Data, "base64");
71
99
  await writeFile(filepath, buffer);
72
100
  const sizeKB = (buffer.length / 1024).toFixed(1);
101
+ const label = mediaType === "audio" ? "Audio" : "Image";
73
102
  return {
74
103
  type: "text",
75
- text: `[Image saved to ${filepath} (${sizeKB}KB)]`
104
+ text: `[${label} saved to ${filepath} (${sizeKB}KB)]`
76
105
  };
77
106
  }
78
107
  /**
@@ -92,17 +121,28 @@ var ResponseInterceptor = class {
92
121
  }
93
122
  /**
94
123
  * Map MIME type to file extension.
124
+ * Covers image and audio types.
95
125
  */
96
126
  _extensionFromMime(mimeType) {
97
127
  const map = {
128
+ // Images
98
129
  "image/png": ".png",
99
130
  "image/jpeg": ".jpg",
100
131
  "image/gif": ".gif",
101
132
  "image/webp": ".webp",
102
133
  "image/svg+xml": ".svg",
103
- "image/bmp": ".bmp"
134
+ "image/bmp": ".bmp",
135
+ // Audio
136
+ "audio/wav": ".wav",
137
+ "audio/mpeg": ".mp3",
138
+ "audio/mp3": ".mp3",
139
+ "audio/ogg": ".ogg",
140
+ "audio/flac": ".flac",
141
+ "audio/aac": ".aac",
142
+ "audio/webm": ".webm",
143
+ "audio/mp4": ".m4a"
104
144
  };
105
- return map[mimeType] ?? ".png";
145
+ return map[mimeType] ?? (mimeType.startsWith("audio/") ? ".wav" : ".png");
106
146
  }
107
147
  };
108
148
 
@@ -157,7 +197,7 @@ var TargetManager = class _TargetManager extends EventEmitter {
157
197
  this.emit("stderr", text);
158
198
  }
159
199
  });
160
- this.client = new Client({ name: "run-mcp", version: "1.1.0" }, { capabilities: {} });
200
+ this.client = new Client({ name: "run-mcp", version: "1.2.0" }, { capabilities: {} });
161
201
  this.client.onclose = () => {
162
202
  this._connected = false;
163
203
  this._clearStableTimer();
@@ -187,12 +227,29 @@ var TargetManager = class _TargetManager extends EventEmitter {
187
227
  recordResponse() {
188
228
  this._lastResponseTime = Date.now();
189
229
  }
230
+ // ─── Server introspection ───────────────────────────────────────────────────
231
+ /**
232
+ * Returns the target server's advertised capabilities.
233
+ * Available after connect() completes.
234
+ */
235
+ getServerCapabilities() {
236
+ return this.client?.getServerCapabilities();
237
+ }
238
+ /**
239
+ * Returns the target server's instructions string (if any).
240
+ * Agents may use this for system prompts or behavioral hints.
241
+ */
242
+ getInstructions() {
243
+ return this.client?.getInstructions();
244
+ }
245
+ // ─── Tools ──────────────────────────────────────────────────────────────────
190
246
  /**
191
247
  * List all tools exposed by the target MCP server.
248
+ * Supports cursor-based pagination via params.
192
249
  */
193
- async listTools() {
250
+ async listTools(params) {
194
251
  this._assertConnected();
195
- const result = await this.client.listTools();
252
+ const result = await this.client.listTools(params);
196
253
  this.recordResponse();
197
254
  return result;
198
255
  }
@@ -205,6 +262,104 @@ var TargetManager = class _TargetManager extends EventEmitter {
205
262
  this.recordResponse();
206
263
  return result;
207
264
  }
265
+ // ─── Resources ──────────────────────────────────────────────────────────────
266
+ /**
267
+ * List resources exposed by the target MCP server.
268
+ * Supports cursor-based pagination.
269
+ */
270
+ async listResources(params) {
271
+ this._assertConnected();
272
+ const result = await this.client.listResources(params);
273
+ this.recordResponse();
274
+ return result;
275
+ }
276
+ /**
277
+ * List resource templates exposed by the target MCP server.
278
+ * Supports cursor-based pagination.
279
+ */
280
+ async listResourceTemplates(params) {
281
+ this._assertConnected();
282
+ const result = await this.client.listResourceTemplates(params);
283
+ this.recordResponse();
284
+ return result;
285
+ }
286
+ /**
287
+ * Read a specific resource by URI from the target MCP server.
288
+ */
289
+ async readResource(params) {
290
+ this._assertConnected();
291
+ const result = await this.client.readResource(params);
292
+ this.recordResponse();
293
+ return result;
294
+ }
295
+ /**
296
+ * Subscribe to resource updates on the target MCP server.
297
+ */
298
+ async subscribeResource(params) {
299
+ this._assertConnected();
300
+ const result = await this.client.subscribeResource(params);
301
+ this.recordResponse();
302
+ return result;
303
+ }
304
+ /**
305
+ * Unsubscribe from resource updates on the target MCP server.
306
+ */
307
+ async unsubscribeResource(params) {
308
+ this._assertConnected();
309
+ const result = await this.client.unsubscribeResource(params);
310
+ this.recordResponse();
311
+ return result;
312
+ }
313
+ // ─── Prompts ────────────────────────────────────────────────────────────────
314
+ /**
315
+ * List prompts exposed by the target MCP server.
316
+ * Supports cursor-based pagination.
317
+ */
318
+ async listPrompts(params) {
319
+ this._assertConnected();
320
+ const result = await this.client.listPrompts(params);
321
+ this.recordResponse();
322
+ return result;
323
+ }
324
+ /**
325
+ * Get a specific prompt by name from the target MCP server.
326
+ */
327
+ async getPrompt(params) {
328
+ this._assertConnected();
329
+ const result = await this.client.getPrompt(params);
330
+ this.recordResponse();
331
+ return result;
332
+ }
333
+ // ─── Logging ────────────────────────────────────────────────────────────────
334
+ /**
335
+ * Set the logging level on the target MCP server.
336
+ */
337
+ async setLoggingLevel(level) {
338
+ this._assertConnected();
339
+ const result = await this.client.setLoggingLevel(level);
340
+ this.recordResponse();
341
+ return result;
342
+ }
343
+ // ─── Completion ─────────────────────────────────────────────────────────────
344
+ /**
345
+ * Request completion from the target MCP server (for autocomplete UX).
346
+ */
347
+ async complete(params) {
348
+ this._assertConnected();
349
+ const result = await this.client.complete(params);
350
+ this.recordResponse();
351
+ return result;
352
+ }
353
+ // ─── Notification forwarding ────────────────────────────────────────────────
354
+ /**
355
+ * Access the underlying MCP client for advanced use cases like
356
+ * subscribing to notifications with proper SDK schemas.
357
+ * Prefer the typed methods above when possible.
358
+ */
359
+ getRawClient() {
360
+ return this.client;
361
+ }
362
+ // ─── Status & lifecycle ─────────────────────────────────────────────────────
208
363
  /**
209
364
  * Returns current connection status, PID, uptime, and diagnostics.
210
365
  */
@@ -345,7 +500,11 @@ var TargetManager = class _TargetManager extends EventEmitter {
345
500
  async function startProxy(targetCommand, opts) {
346
501
  const [command, ...args] = targetCommand;
347
502
  const target = new TargetManager(command, args);
348
- const interceptor = new ResponseInterceptor({ outDir: opts.outDir });
503
+ const interceptor = new ResponseInterceptor({
504
+ outDir: opts.outDir,
505
+ defaultTimeoutMs: opts.timeoutMs,
506
+ maxTextLength: opts.maxTextLength
507
+ });
349
508
  target.on("stderr", (text) => {
350
509
  process.stderr.write(`[target] ${text}
351
510
  `);
@@ -361,17 +520,33 @@ async function startProxy(targetCommand, opts) {
361
520
  const status = target.getStatus();
362
521
  process.stderr.write(`[proxy] Connected to target (PID: ${status.pid})
363
522
  `);
523
+ const targetCaps = target.getServerCapabilities() ?? {};
524
+ const proxyCaps = {};
525
+ proxyCaps.tools = targetCaps.tools ?? {};
526
+ if (targetCaps.resources) proxyCaps.resources = targetCaps.resources;
527
+ if (targetCaps.prompts) proxyCaps.prompts = targetCaps.prompts;
528
+ if (targetCaps.logging) proxyCaps.logging = targetCaps.logging;
529
+ if (targetCaps.completions) proxyCaps.completions = targetCaps.completions;
530
+ process.stderr.write(`[proxy] Mirroring capabilities: ${Object.keys(proxyCaps).join(", ")}
531
+ `);
532
+ const instructions = target.getInstructions();
533
+ if (instructions) {
534
+ process.stderr.write(
535
+ `[proxy] Target instructions: ${instructions.slice(0, 200)}${instructions.length > 200 ? "..." : ""}
536
+ `
537
+ );
538
+ }
364
539
  const mcpServer = new McpServer(
365
540
  {
366
541
  name: "run-mcp-proxy",
367
- version: "1.1.0"
542
+ version: "1.2.0"
368
543
  },
369
- { capabilities: { tools: {} } }
544
+ { capabilities: proxyCaps }
370
545
  );
371
546
  const server = mcpServer.server;
372
- server.setRequestHandler(ListToolsRequestSchema, async () => {
373
- const result = await target.listTools();
374
- return { tools: result.tools };
547
+ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
548
+ const result = await target.listTools(request.params);
549
+ return result;
375
550
  });
376
551
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
377
552
  const { name, arguments: toolArgs } = request.params;
@@ -381,13 +556,7 @@ async function startProxy(targetCommand, opts) {
381
556
  name,
382
557
  toolArgs ?? {}
383
558
  );
384
- const content = (result.content ?? []).map((item) => {
385
- if (item.type === "image") {
386
- return { type: "image", data: item.data, mimeType: item.mimeType };
387
- }
388
- return { type: "text", text: String(item.text ?? "") };
389
- });
390
- return { content };
559
+ return result;
391
560
  } catch (err) {
392
561
  return {
393
562
  content: [{ type: "text", text: `Error: ${err.message}` }],
@@ -395,6 +564,69 @@ async function startProxy(targetCommand, opts) {
395
564
  };
396
565
  }
397
566
  });
567
+ if (targetCaps.resources) {
568
+ server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
569
+ return await target.listResources(request.params);
570
+ });
571
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async (request) => {
572
+ return await target.listResourceTemplates(request.params);
573
+ });
574
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
575
+ return await target.readResource(request.params);
576
+ });
577
+ if (targetCaps.resources.subscribe) {
578
+ server.setRequestHandler(SubscribeRequestSchema, async (request) => {
579
+ return await target.subscribeResource(request.params);
580
+ });
581
+ server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
582
+ return await target.unsubscribeResource(request.params);
583
+ });
584
+ }
585
+ }
586
+ if (targetCaps.prompts) {
587
+ server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
588
+ return await target.listPrompts(request.params);
589
+ });
590
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
591
+ return await target.getPrompt(request.params);
592
+ });
593
+ }
594
+ if (targetCaps.logging) {
595
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
596
+ return await target.setLoggingLevel(request.params.level);
597
+ });
598
+ }
599
+ if (targetCaps.completions) {
600
+ server.setRequestHandler(CompleteRequestSchema, async (request) => {
601
+ return await target.complete(request.params);
602
+ });
603
+ }
604
+ const rawClient = target.getRawClient();
605
+ if (rawClient) {
606
+ if (targetCaps.tools && targetCaps.tools.listChanged) {
607
+ rawClient.setNotificationHandler(ToolListChangedNotificationSchema, () => {
608
+ server.notification({ method: "notifications/tools/list_changed" });
609
+ });
610
+ }
611
+ if (targetCaps.resources && targetCaps.resources.listChanged) {
612
+ rawClient.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
613
+ server.notification({ method: "notifications/resources/list_changed" });
614
+ });
615
+ }
616
+ if (targetCaps.prompts && targetCaps.prompts.listChanged) {
617
+ rawClient.setNotificationHandler(PromptListChangedNotificationSchema, () => {
618
+ server.notification({ method: "notifications/prompts/list_changed" });
619
+ });
620
+ }
621
+ if (targetCaps.logging) {
622
+ rawClient.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
623
+ server.notification({
624
+ method: "notifications/message",
625
+ params: notification.params
626
+ });
627
+ });
628
+ }
629
+ }
398
630
  const transport = new StdioServerTransport();
399
631
  server.onclose = async () => {
400
632
  process.stderr.write("[proxy] Parent disconnected, shutting down...\n");
@@ -744,7 +976,7 @@ async function readScriptLines(filepath) {
744
976
  // src/index.ts
745
977
  program.name("run-mcp").enablePositionalOptions().description(
746
978
  "A smart proxy and interactive REPL for Model Context Protocol (MCP) servers.\n\nOperates in two modes:\n repl - Human-friendly CLI for testing MCP servers interactively\n proxy - Transparent MCP proxy that intercepts images, enforces timeouts,\n and truncates large payloads to protect an AI agent's context window"
747
- ).version("1.1.0").addHelpText(
979
+ ).version("1.2.0").addHelpText(
748
980
  "after",
749
981
  `
750
982
  Examples:
@@ -776,12 +1008,14 @@ REPL Commands (once connected):
776
1008
  ).action(async (targetCommand, opts) => {
777
1009
  await startRepl(targetCommand, opts);
778
1010
  });
779
- program.command("proxy").description("Start as a transparent MCP proxy between an AI agent and a target server").passThroughOptions().allowUnknownOption().argument("<target_command...>", "Command to spawn the target MCP server").option("-o, --out-dir <path>", "Directory to save intercepted images").addHelpText(
1011
+ program.command("proxy").description("Start as a transparent MCP proxy between an AI agent and a target server").passThroughOptions().allowUnknownOption().argument("<target_command...>", "Command to spawn the target MCP server").option("-o, --out-dir <path>", "Directory to save intercepted images and audio").option("-t, --timeout <ms>", "Default tool call timeout in milliseconds (default: 60000)").option("--max-text <chars>", "Max text response length before truncation (default: 50000)").addHelpText(
780
1012
  "after",
781
1013
  `
782
1014
  Examples:
783
1015
  $ run-mcp proxy node my-server.js
784
1016
  $ run-mcp proxy node my-server.js --out-dir ./images
1017
+ $ run-mcp proxy node my-server.js --timeout 120000
1018
+ $ run-mcp proxy node my-server.js --max-text 100000
785
1019
 
786
1020
  Use this in your MCP client configuration to wrap any MCP server:
787
1021
  {
@@ -792,7 +1026,13 @@ Use this in your MCP client configuration to wrap any MCP server:
792
1026
  }
793
1027
  }
794
1028
  }`
795
- ).action(async (targetCommand, opts) => {
796
- await startProxy(targetCommand, opts);
797
- });
1029
+ ).action(
1030
+ async (targetCommand, opts) => {
1031
+ await startProxy(targetCommand, {
1032
+ outDir: opts.outDir,
1033
+ timeoutMs: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
1034
+ maxTextLength: opts.maxText ? Number.parseInt(opts.maxText, 10) : void 0
1035
+ });
1036
+ }
1037
+ );
798
1038
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A smart proxy and interactive REPL for Model Context Protocol (MCP) servers",
5
5
  "homepage": "https://github.com/funkyfunc/run-mcp#readme",
6
6
  "bugs": {