Skip to content

Repository files navigation

run-mcp

A thin MCP client for humans and agents — test the server you're building without touching a config file.

CI npm license

Change your Model Context Protocol server's code and test it immediately — no editing mcp.json, no restarting your agent, no publishing to npm first. run-mcp spawns the server, calls its tools, shows you its stderr when it crashes, and restarts it on demand.

run-mcp provides three interfaces for interacting with MCP servers:

  1. Agent MCP Server (run-mcp) — An MCP server that exposes tools (connect_to_mcp, call_mcp_primitive, reconnect_to_mcp) so AI agents can dynamically connect to and test local MCP projects without hardcoding them in configuration files. This is the default mode when you run npx -y run-mcp.
  2. Interactive REPL (run-mcp -- node server.js) — A human-friendly CLI for developers to manually test and explore MCP servers using short, memorable commands (tools/call, status, etc.).
  3. Headless CLI (run-mcp call, run-mcp list-tools, etc.) — Subcommands that print clean JSON to stdout. This is the loop an agent uses from a shell when it can't (or doesn't want to) add run-mcp to its own MCP config: add --session <name> and the server stays up between commands, with reconnect, stderr, and validate available against the running instance. Also works one-shot for CI, shell scripts, and jq.

Interception Rules (Agent Server & REPL)

To protect the CLI and parent agents from large payloads, run-mcp automatically applies the following rules:

  • Saving images to disk instead of passing multi-MB base64 strings through
  • Enforcing timeouts so a hung tool call doesn't block forever
  • Spilling huge text to disk: oversized responses are saved in full, and the truncated reply carries a result id — page through the rest with the read_result tool (or just open the file)

For humans, the REPL mode provides a quick way to test any MCP server without writing client code.

Installation

npm install
npm run build

To install globally (makes run-mcp available system-wide):

npm install -g .

Quick Start

REPL Mode — Test an MCP server interactively

# Start a REPL session with any MCP server
run-mcp -- node path/to/my-mcp-server.js

# Or use npx without installing globally
npx . -- node path/to/my-mcp-server.js

# Or start it without arguments to run the Agent Server mode!
run-mcp

You'll see an interactive prompt:

⟳ Connecting to target MCP server...
  Command: node path/to/my-mcp-server.js
✓ Connected (PID: 12345)
  5 tool(s) available. Type help for commands.

>

Usage

run-mcp [options] [target_command...]

Option Description
-V, --version output the version number
-o, --out-dir <path> Directory to save intercepted images and audio
-t, --timeout <ms> Default tool call timeout in milliseconds (default: 300000) (Agent Mode only)
--max-text <chars> Max text response length before truncation (default: 50000) (Agent Mode only)
-m, --media-threshold <kb> Media size threshold in KB to save to disk (0 to always save, -1 to keep inline)
--mcp Force start Agent Server mode even if run interactively without arguments
-s, --script <file> Read commands from a file instead of stdin (REPL Mode only)
--color <mode> Color output mode: always, never, auto (default: auto)
--open-media Automatically open intercepted images and audio files using the host OS viewer
--scan Scan the current workspace and parent directories for any JSON files containing mcpServers
--transport <mode> Transport for http(s) targets: auto (default), http (Streamable HTTP), sse
-w, --watch Watch the current directory for file changes and auto-reconnect (REPL Mode only)
-h, --help display help for command

Examples: $ run-mcp # Test harness (agent mode) $ run-mcp -- node my-server.js # Interactive testing (human REPL mode) $ run-mcp -s test.txt -- node my-server.js # Run a script in REPL mode $ run-mcp -- npx -y some-mcp-server # Test an npx server $ run-mcp --out-dir ./test-output # Agent mode with options $ run-mcp --out-dir ./screenshots -- node srv.js # REPL mode with options

Watch Mode

When developing an MCP server, use --watch (or -w) to automatically reconnect whenever your source files change. This eliminates the manual reconnect step from your edit-test loop:

run-mcp -w -- node my-server.js

On each file change, run-mcp will:

  1. Detect the changed files (debounced to 500ms to batch rapid saves)
  2. Disconnect from the current server process
  3. Reconnect to a fresh instance
  4. Show a diff of what primitives changed (tools added/removed/modified, resources, prompts)

Common directories like node_modules, .git, dist, and build are automatically ignored.

Headless Mode (from a shell, one command at a time)

run-mcp exposes a suite of headless subcommands that print clean JSON to stdout and keep status messages on stderr. Without --session each command spawns the server fresh — fine for CI and jq one-liners. For a dev loop, use sessions: the server stays up, and reconnect/stderr/validate work against the running instance.

If you're an agent driving run-mcp through a shell tool that merges stdout and stderr: use --session. A sessioned call prints nothing but the JSON result, and the server's stderr is reachable as data (run-mcp stderr --session <name>, or the stderr field of call --raw) instead of interleaving with your output.

⚠️ Double-Dash -- Separator

To prevent argument parsing conflicts between run-mcp and the target server, you should separate the target command with a double-dash -- when the target command itself contains flags or options.

  • Required when the target command has options/flags:
    run-mcp list-tools -- node my-server.js --verbose
    (Must use -- so --verbose is passed to your server, not parsed as an option for run-mcp.)
  • Optional when the target command has no options/flags:
    run-mcp list-tools node my-server.js
    (Runs successfully without --.)

⚡ HTTPie-Style Shorthand Arguments

Instead of escaping complex JSON strings on the command line, you can provide arguments using simple key-value shorthand notation:

  • key=value -> evaluated as a string
  • key:=json_val -> parsed as a JSON primitive (boolean, number, array, object, null)

Example:

# Call a tool using shorthand arguments
run-mcp call greet name=Alice count:=5 -- node my-server.js

🔄 Persistent Sessions (the dev loop from a shell)

Without a session, every headless command spawns a fresh process of the target server — slow (a server that launches a browser pays that cost on every call) and stateless. Pass --session <name> and the first call spawns a background daemon that keeps the server running; every later command with the same name attaches to it, needs no target command, and prints nothing but the result:

# First call spawns the session (and, say, launches the browser)
run-mcp call browser_launch headless:=true --session main -- node browser-server.js

# Later calls reuse the running server — no cold start, no progress lines
run-mcp call browser_navigate url=https://google.com --session main
run-mcp list-tools --session main

# The server's stderr, as a JSON array of lines (everything since it started, or the last N)
run-mcp stderr --session main
run-mcp stderr 20 --session main

# Edit your server's code, then restart it and see what your edit changed
run-mcp reconnect --session main
#   { "reconnected": true, "pid": 4242, "command": "node browser-server.js",
#     "changes": ["Changes since last connection:", "  Tools: +1 added (browser_pdf)"] }
# If the new code fails to start, the result carries the crash output inline
# ({ "reconnected": false, "error": ..., "stderr": [...] }); the old process is
# gone, `stderr --session` still shows why, and `reconnect` again once it's fixed.

# Spec-compliance checks against the running instance
run-mcp validate --deep --session main

# Stop the server and the daemon
run-mcp close-session main

--show-stderr on a sessioned call replays the stderr the server wrote during that call (the daemon holds the pipe, so it can't stream live). --out-dir, --timeout, and --media-threshold apply per call, exactly as without a session. --transport is fixed when the session is created.

Keeping track of sessions. run-mcp sessions lists what's running — name, pid, command, working directory, uptime, idle timeout — as JSON. A session remembers the command and directory it was started from: if you pass a different command (or the same relative command from a different directory) with an existing session name, the call is refused with both commands shown, rather than quietly answered by the wrong server. Omit the command to attach, close-session to replace.

Nothing leaks. A session lives until you close-session it — which means a forgotten one keeps its server (and whatever the server holds, like a browser) alive until reboot. Pass --idle-timeout <minutes> on any sessioned call to have it close itself after that long without a command; the value shows up in sessions. If the server fails to start on the first sessioned call, the call exits 69 with the server's stderr, and no session is left behind.

🔎 Stderr as data

The server's stderr is the main evidence when something goes wrong, so headless mode makes it available without you having to untangle it from stdout:

  • run-mcp call <tool> --raw includes a stderr array in the result envelope: the lines written during that call (in one-shot mode: everything since the server was spawned, startup output included).
  • run-mcp stderr -- node server.js prints what a fresh spawn writes at startup.
  • A server that dies during connect has its stderr printed under --- Target server stderr ---, instead of just Connection closed.

Available Headless Subcommands

  • call [options] <tool> [json_args] [target_command...]
  • list-tools [options] [target_command...]
  • list-resources [options] [target_command...]
  • list-prompts [options] [target_command...]
  • read [options] <uri> [target_command...]
  • describe [options] <tool> [target_command...]
  • get-prompt [options] <name> [json_args] [target_command...]
  • stderr [options] [count] [target_command...]
  • reconnect [options] [target_command...]
  • daemon [options] <session_name> [target_command...]
  • sessions
  • close-session <session_name>
  • validate [options] [target_command...]

Use run-mcp <subcommand> --help for specific command options.

Agent Use Cases

Dynamic Testing

When an AI agent is actively developing an MCP server, it needs to test it. Standard MCP clients require updating a configuration file (mcp.json) and restarting the agent session entirely.

run-mcp solves this by giving the agent a suite of tools to dynamically spawn, inspect, and test local MCP servers on the fly.

How to use: Add run-mcp to your agent's MCP configuration using npx:

{
  "mcpServers": {
    "run-mcp": {
      "command": "npx",
      "args": ["-y", "run-mcp"]
    }
  }
}

Then use these tools from your agent:

Tool Description
connect_to_mcp Spawn and connect (use include to get tools/resources/prompts)
call_mcp_primitive Call a tool, read a resource, or get a prompt (auto-connects)
list_mcp_primitives List tools, resources, and/or prompts
get_server_notifications Inspect notifications the target emitted (list_changed, updates, logs)
subscribe_to_resource Exercise a server's resource-subscription support
reconnect_to_mcp Restart the target after a code edit and diff what changed
read_result Page through an oversized result spilled to disk
disconnect_from_mcp Tear down and reconnect after changes
mcp_server_status Check connection status
get_mcp_server_stderr View target server stderr output
validate_mcp_server Validate an MCP server command and collect diagnostics
list_available_mcp_servers List local MCP servers found in config files

REPL Mode Commands

Once connected via run-mcp <command>, the following shorthand commands are available:

Command Description
tools/list List all available tools
tools/describe <name> Show a tool's input schema
tools/call <name> [json] [opts] Call a tool (interactive if no json)
tools/scaffold <name> Generate argument template for a tool
resources/list List all available resources
resources/read <uri> Read a resource by URI
resources/templates List resource templates
resources/subscribe <uri> Subscribe to resource changes
resources/unsubscribe <uri> Unsubscribe from resource changes
prompts/list List all available prompts
prompts/get <name> [json_args] Get a prompt with arguments
ping Verify connection, show round-trip time
log-level <level> Set server logging verbosity
`history [count clear]`
`notifications [count clear]`
roots/list Show configured client roots
roots/add <uri> [name] Add a root directory
roots/remove <uri> Remove a root directory
!! / last Re-run the last command
reconnect Disconnect and reconnect
timing Show tool call performance stats
status Show target server status

Examples

# List available tools
> tools/list

# Inspect a tool's schema
> tools/describe screenshot

# Call a tool with arguments
> tools/call screenshot {"target": "#loginBtn"}

# Call with a custom timeout (5 seconds)
> tools/call long_running_tool {} --timeout 5000

# Arguments with spaces work fine
> tools/call send_message {"text": "hello world", "channel": "general"}

Direct Inline Tool Calls & Shorthand Arguments

Instead of prefixing every tool call with tools/call, you can invoke any target server tool directly by name, and provide arguments in shorthand key-value form:

# Direct inline tool execution with HTTPie shorthand parameters
> greet name=Bob count:=3

Interactive Wizard & Argument Memory

If you invoke a tool without JSON arguments, run-mcp will guide you through an interactive scaffolding wizard:

> tools/call send_message
✔ text (string) Message text to send: Hello World!
✔ Select optional arguments to provide: channel
✔ channel (string) The Slack channel: general
✔ Execute? Yes
  Calling send_message...

run-mcp actively remembers your inputs across identical interactive calls, scaffolding defaults based on your last execution! Use tools/forget or --clear if you need a clean slate.

Script Mode

You can automate REPL commands by writing them to a file:

# commands.txt
tools/list
tools/call get_status {}
tools/call screenshot {"save_path": "/tmp/test.png"}
run-mcp -s commands.txt -- node my-server.js
  • Lines starting with # are treated as comments
  • Exits with code 0 on success, 1 on first error

Testing your server's client-facing behavior

Some MCP features depend on what the client provides. run-mcp exposes these so an agent can exercise them:

  • Roots — pass roots to connect_to_mcp (or reconnect_to_mcp) and your server's roots/list calls get a real answer. run-mcp advertises the roots capability, so without this your server correctly sees an empty list. Roots persist across reconnects.
  • Log level — pass log_level to raise your server's logging verbosity.
  • Notificationsget_server_notifications shows what your server emitted (tools/list_changed, resources/updated, log messages). These travel outside the request/response flow, so a tool result will never reveal them.
  • Subscriptionssubscribe_to_resource, then trigger a change and confirm with get_server_notifications(method='resources/updated').

Agent Server Mode — How It Works

Run with no target command (or --mcp), run-mcp is itself an MCP server that exposes tools (connect_to_mcp, call_mcp_primitive, reconnect_to_mcp, …) so an agent can dynamically spawn and test local MCP servers. Tool-call responses are processed through the interceptor pipeline:

Feature Behavior
Image extraction type: "image" responses with base64 data are saved to disk. Replaced with [Image saved to /path/to/img.png (24KB)]
Audio extraction type: "audio" responses with base64 data are saved to disk. Replaced with [Audio saved to /path/to/audio.wav (12KB)]
Base64 detection Text responses that are entirely base64-encoded (1000+ chars) are also saved as images
Timeouts Tool calls are wrapped in a configurable timeout (default 5 minutes, use --timeout to change)
Truncation Text exceeding the limit (default 50K chars, --max-text to change) is saved in full to disk; the reply keeps the head plus a result id, navigable via the read_result tool

Architecture

For the detailed system architecture diagram and source module directory map, please refer to AGENTS.md.

Development

# Install dependencies
npm install

# Build (one-time)
npm run build

# Watch mode (rebuild on changes)
npm run dev

# Run directly
node dist/index.js -- <target_command...>

License

MIT

About

A thin MCP client for humans and agents — run any Model Context Protocol server, call its tools, read its stderr, and reconnect after each edit. REPL, headless CLI, or an MCP server your agent drives. No client config edits, no restarts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages