run-mcp 1.3.0 → 1.3.2

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 +6 -28
  2. package/dist/index.js +130 -272
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,17 +2,14 @@
2
2
 
3
3
  A smart proxy, interactive REPL, and live test harness for [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers.
4
4
 
5
- `run-mcp` wraps any MCP server and operates in three modes:
5
+ `run-mcp` operates in two modes:
6
6
 
7
- | Mode | Audience | Purpose |
8
- |------|----------|---------|
9
- | **`repl`** | Humans / developers | Interactive CLI for testing and exploring MCP servers with shorthand commands |
10
- | **`proxy`** | AI agents (transparent) | Transparent MCP proxy that intercepts responses to save images to disk, enforce timeouts, and truncate massive payloads |
11
- | **`server`** | AI agents (explicit) | MCP server that lets agents dynamically connect to, inspect, and test local MCP servers |
7
+ 1. **Interactive REPL** (`run-mcp repl`) A headless CLI for human developers to manually test and explore MCP servers using short, memorable commands (`tools/call`, `status`, etc.).
8
+ 2. **Server Mode** (`run-mcp server`) — An MCP server that exposes tools (`connect_to_mcp`, `call_mcp_tool`) so AI agents can dynamically connect to and test local MCP projects without hardcoding them in configuration files.
12
9
 
13
- ## Why?
10
+ ### Interception Rules (Server Mode & REPL)
14
11
 
15
- MCP servers often return large base64-encoded images (screenshots, charts) or massive JSON payloads that can blow up an AI agent's context window. `run-mcp` sits between the agent and the server, transparently:
12
+ To protect the CLI and parent agents from large payloads, `run-mcp` automatically applies the following rules:
16
13
 
17
14
  - **Saving images to disk** instead of passing multi-MB base64 strings through
18
15
  - **Enforcing timeouts** so a hung tool call doesn't block forever
@@ -56,14 +53,6 @@ You'll see an interactive prompt:
56
53
  >
57
54
  ```
58
55
 
59
- ### Proxy Mode — Protect your agent's context
60
-
61
- ```bash
62
- run-mcp proxy node path/to/my-mcp-server.js --out-dir ./captured-images
63
- ```
64
-
65
- Then point your AI agent at `run-mcp` as the MCP server command. It transparently forwards all tools while sanitizing responses.
66
-
67
56
  ## Usage
68
57
 
69
58
  ```
@@ -71,7 +60,7 @@ run-mcp <command> [options]
71
60
 
72
61
  Commands:
73
62
  repl <target_command...> Start an interactive REPL session
74
- proxy <target_command...> Start as a transparent MCP proxy
63
+ server Start as an MCP server for agents
75
64
 
76
65
  Options:
77
66
  -V, --version Show version number
@@ -88,17 +77,6 @@ Options:
88
77
  -o, --out-dir <path> Directory to save intercepted images (default: $TMPDIR/run-mcp)
89
78
  ```
90
79
 
91
- ### Proxy Command
92
-
93
- ```
94
- run-mcp proxy <target_command...> [options]
95
-
96
- Options:
97
- -o, --out-dir <path> Directory to save intercepted images and audio (default: $TMPDIR/run-mcp)
98
- -t, --timeout <ms> Default tool call timeout in milliseconds (default: 60000)
99
- --max-text <chars> Max text response length before truncation (default: 50000)
100
- ```
101
-
102
80
  ### Server Command
103
81
 
104
82
  ```
package/dist/index.js CHANGED
@@ -3,33 +3,17 @@
3
3
  // src/index.ts
4
4
  import { program } from "commander";
5
5
 
6
- // src/proxy.ts
7
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.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";
6
+ // src/repl.ts
7
+ import { readFile } from "fs/promises";
8
+ import { createInterface } from "readline";
9
+ import pc from "picocolors";
26
10
 
27
11
  // src/interceptor.ts
28
12
  import { mkdir, writeFile } from "fs/promises";
29
13
  import { tmpdir } from "os";
30
14
  import { join } from "path";
31
15
  var BASE64_PATTERN = /^[A-Za-z0-9+/]{1000,}={0,2}$/;
32
- var DEFAULT_TIMEOUT_MS = 6e4;
16
+ var DEFAULT_TIMEOUT_MS = 3e5;
33
17
  var DEFAULT_MAX_TEXT_LENGTH = 5e4;
34
18
  var ResponseInterceptor = class {
35
19
  outDir;
@@ -49,7 +33,10 @@ var ResponseInterceptor = class {
49
33
  */
50
34
  async callTool(target, name, args = {}, timeoutMs) {
51
35
  const timeout = timeoutMs ?? this.defaultTimeoutMs;
52
- const result = await Promise.race([target.callTool(name, args), this._timeout(timeout, name)]);
36
+ const targetCall = target.callTool(name, args);
37
+ targetCall.catch(() => {
38
+ });
39
+ const result = await Promise.race([targetCall, this._timeout(timeout, name)]);
53
40
  const content = result.content;
54
41
  if (Array.isArray(content)) {
55
42
  for (let i = 0; i < content.length; i++) {
@@ -146,6 +133,67 @@ var ResponseInterceptor = class {
146
133
  }
147
134
  };
148
135
 
136
+ // src/parsing.ts
137
+ function parseCommandLine(input) {
138
+ const spaceIdx = input.indexOf(" ");
139
+ if (spaceIdx === -1) {
140
+ return { cmd: input.toLowerCase(), rest: "" };
141
+ }
142
+ return {
143
+ cmd: input.slice(0, spaceIdx).toLowerCase(),
144
+ rest: input.slice(spaceIdx + 1)
145
+ };
146
+ }
147
+ function parseCallArgs(rest) {
148
+ const trimmed = rest.trim();
149
+ if (!trimmed) return { toolName: "", jsonArgs: "" };
150
+ const spaceIdx = trimmed.indexOf(" ");
151
+ if (spaceIdx === -1) {
152
+ return { toolName: trimmed, jsonArgs: "" };
153
+ }
154
+ const toolName = trimmed.slice(0, spaceIdx);
155
+ let remainder = trimmed.slice(spaceIdx + 1).trim();
156
+ let timeoutMs;
157
+ const timeoutMatch = remainder.match(/\s--timeout\s+(\d+)\s*$/);
158
+ if (timeoutMatch) {
159
+ timeoutMs = parseInt(timeoutMatch[1], 10);
160
+ remainder = remainder.slice(0, timeoutMatch.index).trim();
161
+ }
162
+ return { toolName, jsonArgs: remainder, timeoutMs };
163
+ }
164
+ function formatJson(obj, indent = 2) {
165
+ const json = JSON.stringify(obj, null, indent);
166
+ return json.split("\n").map((line) => " ".repeat(indent) + line).join("\n");
167
+ }
168
+ function levenshtein(a, b) {
169
+ const m = a.length;
170
+ const n = b.length;
171
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
172
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
173
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
174
+ for (let i = 1; i <= m; i++) {
175
+ for (let j = 1; j <= n; j++) {
176
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
177
+ }
178
+ }
179
+ return dp[m][n];
180
+ }
181
+ function suggestCommand(input, commands, threshold = 0.4) {
182
+ let best = null;
183
+ let bestDist = Infinity;
184
+ for (const cmd of commands) {
185
+ const dist = levenshtein(input, cmd);
186
+ if (dist < bestDist) {
187
+ bestDist = dist;
188
+ best = cmd;
189
+ }
190
+ }
191
+ if (best && bestDist <= Math.ceil(input.length * threshold)) {
192
+ return best;
193
+ }
194
+ return null;
195
+ }
196
+
149
197
  // src/target-manager.ts
150
198
  import { EventEmitter } from "events";
151
199
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -204,7 +252,7 @@ var TargetManager = class _TargetManager extends EventEmitter {
204
252
  this.emit("stderr", text);
205
253
  }
206
254
  });
207
- this.client = new Client({ name: "run-mcp", version: "1.3.0" }, { capabilities: {} });
255
+ this.client = new Client({ name: "run-mcp", version: "1.3.1" }, { capabilities: {} });
208
256
  this.client.onclose = () => {
209
257
  this._connected = false;
210
258
  this._clearStableTimer();
@@ -262,10 +310,15 @@ var TargetManager = class _TargetManager extends EventEmitter {
262
310
  }
263
311
  /**
264
312
  * Call a tool on the target MCP server.
313
+ * We apply a massive SDK-level timeout (e.g. 10 hours) because we want to handle
314
+ * timeouts in the interceptor via Promise.race, and we DO NOT want to send
315
+ * protocol-level cancellation requests to the target server if the agent gives up.
316
+ * This allows long-running builds (like mobile app compiling) to finish in the background.
265
317
  */
266
- async callTool(name, args = {}) {
318
+ async callTool(name, args = {}, _timeoutMs) {
267
319
  this._assertConnected();
268
- const result = await this.client.callTool({ name, arguments: args });
320
+ const requestOptions = { timeout: 36e5 * 10 };
321
+ const result = await this.client.callTool({ name, arguments: args }, void 0, requestOptions);
269
322
  this.recordResponse();
270
323
  return result;
271
324
  }
@@ -511,217 +564,6 @@ var TargetManager = class _TargetManager extends EventEmitter {
511
564
  }
512
565
  };
513
566
 
514
- // src/proxy.ts
515
- async function startProxy(targetCommand, opts) {
516
- const [command, ...args] = targetCommand;
517
- const target = new TargetManager(command, args);
518
- const interceptor = new ResponseInterceptor({
519
- outDir: opts.outDir,
520
- defaultTimeoutMs: opts.timeoutMs,
521
- maxTextLength: opts.maxTextLength
522
- });
523
- target.on("stderr", (text) => {
524
- process.stderr.write(`[target] ${text}
525
- `);
526
- });
527
- process.stderr.write("[proxy] Connecting to target MCP server...\n");
528
- try {
529
- await target.connect();
530
- } catch (err) {
531
- process.stderr.write(`[proxy] Failed to connect to target: ${err.message}
532
- `);
533
- process.exit(1);
534
- }
535
- const status = target.getStatus();
536
- process.stderr.write(`[proxy] Connected to target (PID: ${status.pid})
537
- `);
538
- const targetCaps = target.getServerCapabilities() ?? {};
539
- const proxyCaps = {};
540
- proxyCaps.tools = targetCaps.tools ?? {};
541
- if (targetCaps.resources) proxyCaps.resources = targetCaps.resources;
542
- if (targetCaps.prompts) proxyCaps.prompts = targetCaps.prompts;
543
- if (targetCaps.logging) proxyCaps.logging = targetCaps.logging;
544
- if (targetCaps.completions) proxyCaps.completions = targetCaps.completions;
545
- process.stderr.write(`[proxy] Mirroring capabilities: ${Object.keys(proxyCaps).join(", ")}
546
- `);
547
- const instructions = target.getInstructions();
548
- if (instructions) {
549
- process.stderr.write(
550
- `[proxy] Target instructions: ${instructions.slice(0, 200)}${instructions.length > 200 ? "..." : ""}
551
- `
552
- );
553
- }
554
- const mcpServer = new McpServer(
555
- {
556
- name: "run-mcp-proxy",
557
- version: "1.3.0"
558
- },
559
- { capabilities: proxyCaps }
560
- );
561
- const server = mcpServer.server;
562
- server.setRequestHandler(ListToolsRequestSchema, async (request) => {
563
- const result = await target.listTools(request.params);
564
- return result;
565
- });
566
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
567
- const { name, arguments: toolArgs } = request.params;
568
- try {
569
- const result = await interceptor.callTool(
570
- target,
571
- name,
572
- toolArgs ?? {}
573
- );
574
- return result;
575
- } catch (err) {
576
- return {
577
- content: [{ type: "text", text: `Error: ${err.message}` }],
578
- isError: true
579
- };
580
- }
581
- });
582
- if (targetCaps.resources) {
583
- server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
584
- return await target.listResources(request.params);
585
- });
586
- server.setRequestHandler(ListResourceTemplatesRequestSchema, async (request) => {
587
- return await target.listResourceTemplates(request.params);
588
- });
589
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
590
- return await target.readResource(request.params);
591
- });
592
- if (targetCaps.resources.subscribe) {
593
- server.setRequestHandler(SubscribeRequestSchema, async (request) => {
594
- return await target.subscribeResource(request.params);
595
- });
596
- server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
597
- return await target.unsubscribeResource(request.params);
598
- });
599
- }
600
- }
601
- if (targetCaps.prompts) {
602
- server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
603
- return await target.listPrompts(request.params);
604
- });
605
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
606
- return await target.getPrompt(request.params);
607
- });
608
- }
609
- if (targetCaps.logging) {
610
- server.setRequestHandler(SetLevelRequestSchema, async (request) => {
611
- return await target.setLoggingLevel(request.params.level);
612
- });
613
- }
614
- if (targetCaps.completions) {
615
- server.setRequestHandler(CompleteRequestSchema, async (request) => {
616
- return await target.complete(request.params);
617
- });
618
- }
619
- const rawClient = target.getRawClient();
620
- if (rawClient) {
621
- if (targetCaps.tools && targetCaps.tools.listChanged) {
622
- rawClient.setNotificationHandler(ToolListChangedNotificationSchema, () => {
623
- server.notification({ method: "notifications/tools/list_changed" });
624
- });
625
- }
626
- if (targetCaps.resources && targetCaps.resources.listChanged) {
627
- rawClient.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
628
- server.notification({ method: "notifications/resources/list_changed" });
629
- });
630
- }
631
- if (targetCaps.prompts && targetCaps.prompts.listChanged) {
632
- rawClient.setNotificationHandler(PromptListChangedNotificationSchema, () => {
633
- server.notification({ method: "notifications/prompts/list_changed" });
634
- });
635
- }
636
- if (targetCaps.logging) {
637
- rawClient.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
638
- server.notification({
639
- method: "notifications/message",
640
- params: notification.params
641
- });
642
- });
643
- }
644
- }
645
- const transport = new StdioServerTransport();
646
- server.onclose = async () => {
647
- process.stderr.write("[proxy] Parent disconnected, shutting down...\n");
648
- await target.close();
649
- process.exit(0);
650
- };
651
- await mcpServer.connect(transport);
652
- process.stderr.write("[proxy] Proxy server running on stdio.\n");
653
- target.on("disconnected", () => {
654
- process.stderr.write("[proxy] Target server disconnected.\n");
655
- process.exit(1);
656
- });
657
- }
658
-
659
- // src/repl.ts
660
- import { readFile } from "fs/promises";
661
- import { createInterface } from "readline";
662
- import pc from "picocolors";
663
-
664
- // src/parsing.ts
665
- function parseCommandLine(input) {
666
- const spaceIdx = input.indexOf(" ");
667
- if (spaceIdx === -1) {
668
- return { cmd: input.toLowerCase(), rest: "" };
669
- }
670
- return {
671
- cmd: input.slice(0, spaceIdx).toLowerCase(),
672
- rest: input.slice(spaceIdx + 1)
673
- };
674
- }
675
- function parseCallArgs(rest) {
676
- const trimmed = rest.trim();
677
- if (!trimmed) return { toolName: "", jsonArgs: "" };
678
- const spaceIdx = trimmed.indexOf(" ");
679
- if (spaceIdx === -1) {
680
- return { toolName: trimmed, jsonArgs: "" };
681
- }
682
- const toolName = trimmed.slice(0, spaceIdx);
683
- let remainder = trimmed.slice(spaceIdx + 1).trim();
684
- let timeoutMs;
685
- const timeoutMatch = remainder.match(/\s--timeout\s+(\d+)\s*$/);
686
- if (timeoutMatch) {
687
- timeoutMs = parseInt(timeoutMatch[1], 10);
688
- remainder = remainder.slice(0, timeoutMatch.index).trim();
689
- }
690
- return { toolName, jsonArgs: remainder, timeoutMs };
691
- }
692
- function formatJson(obj, indent = 2) {
693
- const json = JSON.stringify(obj, null, indent);
694
- return json.split("\n").map((line) => " ".repeat(indent) + line).join("\n");
695
- }
696
- function levenshtein(a, b) {
697
- const m = a.length;
698
- const n = b.length;
699
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
700
- for (let i = 0; i <= m; i++) dp[i][0] = i;
701
- for (let j = 0; j <= n; j++) dp[0][j] = j;
702
- for (let i = 1; i <= m; i++) {
703
- for (let j = 1; j <= n; j++) {
704
- dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
705
- }
706
- }
707
- return dp[m][n];
708
- }
709
- function suggestCommand(input, commands, threshold = 0.4) {
710
- let best = null;
711
- let bestDist = Infinity;
712
- for (const cmd of commands) {
713
- const dist = levenshtein(input, cmd);
714
- if (dist < bestDist) {
715
- bestDist = dist;
716
- best = cmd;
717
- }
718
- }
719
- if (best && bestDist <= Math.ceil(input.length * threshold)) {
720
- return best;
721
- }
722
- return null;
723
- }
724
-
725
567
  // src/repl.ts
726
568
  var KNOWN_COMMANDS = [
727
569
  "tools/list",
@@ -989,8 +831,8 @@ async function readScriptLines(filepath) {
989
831
  }
990
832
 
991
833
  // src/server.ts
992
- import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
993
- import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
834
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
835
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
994
836
  import { z } from "zod";
995
837
  async function startServer(opts) {
996
838
  let target = null;
@@ -999,8 +841,8 @@ async function startServer(opts) {
999
841
  defaultTimeoutMs: opts.timeoutMs,
1000
842
  maxTextLength: opts.maxTextLength
1001
843
  });
1002
- const mcpServer = new McpServer2(
1003
- { name: "run-mcp", version: "1.3.0" },
844
+ const mcpServer = new McpServer(
845
+ { name: "run-mcp", version: "1.3.1" },
1004
846
  {
1005
847
  capabilities: {
1006
848
  tools: {}
@@ -1174,6 +1016,50 @@ Check that the command is correct and the server starts without errors. You can
1174
1016
  }
1175
1017
  }
1176
1018
  );
1019
+ mcpServer.registerTool(
1020
+ "describe_mcp_tool",
1021
+ {
1022
+ title: "Describe MCP Tool",
1023
+ description: "Get the description and input schema for a specific tool on the connected server.",
1024
+ inputSchema: {
1025
+ name: z.string().describe("Name of the tool to describe")
1026
+ }
1027
+ },
1028
+ async ({ name }) => {
1029
+ if (!target?.connected) {
1030
+ return {
1031
+ content: [
1032
+ { type: "text", text: "No target server connected. Use connect_to_mcp first." }
1033
+ ],
1034
+ isError: true
1035
+ };
1036
+ }
1037
+ try {
1038
+ const result = await target.listTools();
1039
+ const tool = result.tools.find((t) => t.name === name);
1040
+ if (!tool) {
1041
+ const available = result.tools.map((t) => t.name).join(", ");
1042
+ return {
1043
+ content: [
1044
+ { type: "text", text: `Tool "${name}" not found.
1045
+ Available tools: ${available}` }
1046
+ ],
1047
+ isError: true
1048
+ };
1049
+ }
1050
+ return {
1051
+ content: [
1052
+ { type: "text", text: JSON.stringify(tool, null, 2) }
1053
+ ]
1054
+ };
1055
+ } catch (err) {
1056
+ return {
1057
+ content: [{ type: "text", text: `Error describing tool: ${err.message}` }],
1058
+ isError: true
1059
+ };
1060
+ }
1061
+ }
1062
+ );
1177
1063
  mcpServer.registerTool(
1178
1064
  "call_mcp_tool",
1179
1065
  {
@@ -1378,7 +1264,7 @@ Check that the command is correct and the server starts without errors. You can
1378
1264
  };
1379
1265
  }
1380
1266
  );
1381
- const transport = new StdioServerTransport2();
1267
+ const transport = new StdioServerTransport();
1382
1268
  mcpServer.server.onclose = async () => {
1383
1269
  if (target) {
1384
1270
  await target.close();
@@ -1392,14 +1278,13 @@ Check that the command is correct and the server starts without errors. You can
1392
1278
 
1393
1279
  // src/index.ts
1394
1280
  program.name("run-mcp").enablePositionalOptions().description(
1395
- "A smart proxy, interactive REPL, and live test harness for MCP servers.\n\nOperates in three 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\n server - MCP server that lets AI agents dynamically test local MCP servers"
1396
- ).version("1.3.0").addHelpText(
1281
+ "A smart interactive REPL and live test harness for MCP servers.\n\nOperates in two modes:\n repl - Human-friendly CLI for testing MCP servers interactively\n server - MCP server that lets AI agents dynamically test local MCP servers"
1282
+ ).version("1.3.1").addHelpText(
1397
1283
  "after",
1398
1284
  `
1399
1285
  Examples:
1400
1286
  $ run-mcp repl node my-server.js # Interactive testing (human)
1401
1287
  $ run-mcp repl node my-server.js -s test.txt # Run a script
1402
- $ run-mcp proxy node my-server.js # Transparent proxy (agent)
1403
1288
  $ run-mcp server # Test harness (agent)
1404
1289
  $ run-mcp repl npx -y some-mcp-server # Test an npx server
1405
1290
 
@@ -1426,34 +1311,7 @@ REPL Commands (once connected):
1426
1311
  ).action(async (targetCommand, opts) => {
1427
1312
  await startRepl(targetCommand, opts);
1428
1313
  });
1429
- 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(
1430
- "after",
1431
- `
1432
- Examples:
1433
- $ run-mcp proxy node my-server.js
1434
- $ run-mcp proxy node my-server.js --out-dir ./images
1435
- $ run-mcp proxy node my-server.js --timeout 120000
1436
- $ run-mcp proxy node my-server.js --max-text 100000
1437
-
1438
- Use this in your MCP client configuration to wrap any MCP server:
1439
- {
1440
- "mcpServers": {
1441
- "my-server": {
1442
- "command": "run-mcp",
1443
- "args": ["proxy", "node", "my-server.js"]
1444
- }
1445
- }
1446
- }`
1447
- ).action(
1448
- async (targetCommand, opts) => {
1449
- await startProxy(targetCommand, {
1450
- outDir: opts.outDir,
1451
- timeoutMs: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
1452
- maxTextLength: opts.maxText ? Number.parseInt(opts.maxText, 10) : void 0
1453
- });
1454
- }
1455
- );
1456
- program.command("server").description("Start as an MCP server that lets AI agents dynamically test local MCP servers").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(
1314
+ program.command("server").description("Start as an MCP server that lets AI agents dynamically test local MCP servers").option("-o, --out-dir <path>", "Directory to save intercepted images and audio").option("-t, --timeout <ms>", "Default tool call timeout in milliseconds (default: 300000)").option("--max-text <chars>", "Max text response length before truncation (default: 50000)").addHelpText(
1457
1315
  "after",
1458
1316
  `
1459
1317
  Examples:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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": {