Skip to content

CLI reference

The CLI ships as a single self-contained binary, mcppipe, with two subcommands: up and detect.

Terminal window
mcppipe [GLOBAL OPTIONS] <up|detect> [OPTIONS]

See Install for how to get the binary, and the Quickstart for an end-to-end walkthrough.

These apply to every subcommand.

FlagEnvironment variableDefaultDescription
--broker <URL>MCPPIPE_BROKERws://127.0.0.1:7100WebSocket URL of the mcppipe service.
--api-key <KEY>MCPPIPE_API_KEYThe tunnel’s CLI key (mtk_…), created in the dashboard.
-v, -vv, -vvvRUST_LOGinfoIncrease log verbosity. Default logs at info; -v adds debug logging for the CLI itself; -vv enables debug globally; -vvv enables trace. An explicit RUST_LOG takes precedence.
--no-update-checkMCPPIPE_NO_UPDATE_CHECKoffDisable the background “newer release available” check.
--no-telemetryMCPPIPE_NO_TELEMETRYoffDisable crash/error reporting (Sentry). Nothing is sent off-machine.
--versionPrint the version and exit.
--helpPrint help and exit.

mcppipe up prints its telemetry posture as the very first line, before any other output:

  • Default: · crash reports enabled (opt out: --no-telemetry or MCPPIPE_NO_TELEMETRY=1)
  • With --no-telemetry: · crash reports disabled

Crash reporting only sends panic/error reports; it never streams MCP traffic.

Start a tunnel that forwards to one or more local MCP servers.

Terminal window
mcppipe up [--upstream <URL>]... [--stdio "<CMD>"]... [--upstreams-file <PATH>] \
[--name <NAME>] [--config <PATH>]... [--metrics-addr <ADDR>] \
[--inspect] [--inspect-addr <ADDR>] [--inspect-admin]
FlagEnvironment variableDescription
--upstream <URL>Forward to a local HTTP (Streamable HTTP) MCP server. Repeatable for multi-upstream layouts (see below).
--stdio "<CMD>"Spawn a stdio MCP server and bridge it. Shell-style, supports quotes, e.g. --stdio "npx -y @modelcontextprotocol/server-filesystem .". Repeatable.
--upstreams-file <PATH>Load a multi-upstream layout from a TOML file. Conflicts with --upstream / --stdio.
--name <NAME>Friendly name for the tunnel, shown in the dashboard and used as the config-snippet entry name.
--config <PATH>Force auto-detect to read this config file. Repeatable; bypasses the well-known-paths scan.
--metrics-addr <ADDR>MCPPIPE_METRICS_ADDRBind a Prometheus listener at HOST:PORT serving /metrics and /healthz. Off by default.
--inspectStart the local web inspector on 127.0.0.1:4040 (falls back to a free port if taken). Shows live MCP JSON-RPC traffic and lets you replay a call. Off by default; the URL (with a one-time token) is printed at startup.
--inspect-addr <ADDR>MCPPIPE_INSPECT_ADDROverride the inspector bind address (implies --inspect). Bind loopback-only unless you know what you’re doing.
--inspect-adminMCPPIPE_INSPECT_ADMINEnable the inspector’s Config tab — edit, apply (reconnect), and save your upstreams live. Implies --inspect. Off by default; the mutation routes don’t exist without it.
MCPPIPE_INSPECT_TOKENUse a fixed inspector token instead of a fresh random one — handy for a stable bookmarkable URL.

The CLI works out where to send traffic from the flags you pass:

  • No --upstream/--stdio/--upstreams-file — the CLI auto-detects MCP servers configured in local clients (Claude Desktop, Cursor, Windsurf, VS Code, Cline, Continue.dev, Zed). In an interactive terminal it prompts you to pick; see auto-detection below.
  • Exactly one --upstream or one --stdio — single-upstream mode. These two are mutually exclusive in the one-upstream case; the bare value (no name= prefix) is forwarded directly.
  • Two or more --upstream/--stdio flags (mixed is fine) — multi-upstream aggregator mode. Each flag must use name=value syntax, e.g. --upstream brave=http://127.0.0.1:3001 --stdio fs="npx ... server-filesystem .". Names must be unique and match ^[a-z][a-z0-9_-]{0,31}$. See the aggregator guide for how tools/prompts get prefixed.

A one-entry --upstreams-file collapses to the single-upstream fast path automatically.

--upstreams-file <PATH> loads a TOML file with one section per upstream, each setting http xor stdio:

[upstreams.filesystem]
stdio = "npx -y @modelcontextprotocol/server-filesystem ."
[upstreams.brave]
http = "http://127.0.0.1:3001"

The CLI rejects the file if a section sets both transports, sets neither, has no [upstreams.*] sections, uses an invalid name, or repeats a name.

On a fresh connection the CLI prints the public URL, the upstream it forwards to, and a paste-ready JSON snippet for your remote MCP client:

{
"mcpServers": {
"mcppipe": {
"url": "https://<subdomain>--<account-slug>.<zone>/mcp"
}
}
}

The entry name defaults to mcppipe; if you pass --name, the key becomes <name>-via-tunnel (with any unsafe characters replaced by -). VS Code wraps the same block under "mcp": { "servers": { ... } }. The endpoint path is always /mcp over Streamable HTTP, with no interstitial page on any tier. After a reconnect that resumes the prior session, the snippet is not reprinted.

If mcppipe rejects the API key, the CLI prints authentication failed with a hint to check --api-key / MCPPIPE_API_KEY, and exits with a non-zero status. It does not retry — an invalid key never becomes valid. Any other transient failure (network blip, service restart) reconnects with exponential backoff (1s up to 30s).

On SIGTERM (Unix; sent by docker stop and Kubernetes pod termination) or Ctrl-C, the CLI says goodbye, drains in-flight requests, and exits cleanly so rolling updates don’t drop calls. A signal during reconnect backoff exits promptly. The in-flight drain is bounded by MCPPIPE_DRAIN_GRACE_SECS (default 10 seconds); after it elapses the connection closes even if a handler is still running, so set it at or below your orchestrator’s termination grace (for example Kubernetes terminationGracePeriodSeconds). See Reliability for the session resume and shutdown details.

With --metrics-addr 127.0.0.1:9090, the CLI binds a tiny HTTP/1.1 listener serving GET /metrics (text exposition) and GET /healthz (ok). The counters describe aggregator behavior — upstream recoveries/failures, fan-out, */list snapshot hits, unknown-prefix errors, and id-rewriter evictions. If the bind fails, traffic forwarding keeps running and a warning is logged.

List MCP servers configured in local clients without exposing anything. Useful for sanity-checking what the auto-detect prompt would offer, or for piping into scripts.

Terminal window
mcppipe detect [--config <PATH>]... [--json]
FlagDescription
--config <PATH>Read this config file instead of scanning well-known paths. Repeatable.
--jsonPrint machine-readable JSON instead of a formatted list.

The JSON entries include each server’s name, the source client it was found in, and the transport (stdio with command/args/env, or http with url).

Both up (with no upstream flags) and detect scan the well-known config locations for Claude Desktop, Cursor, Windsurf, VS Code, Cline, Continue.dev and Zed (plus workspace-local .cursor/mcp.json and .vscode/mcp.json). Files that don’t exist are skipped; files that exist but fail to parse log a warning and are skipped — detection never aborts the binary.

When up auto-detects:

  • One server found — exposed directly.
  • Several found, interactive terminal — you’re asked whether to expose one (single-upstream) or aggregate several (multi-upstream), then pick.
  • Several found, no TTY (CI/Docker) — all of them are aggregated and a warning is logged so you can switch to explicit --upstream name=url flags if the auto-picked set is wrong.

Detected names are sanitized into valid aggregator names (lowercased, non-[a-z0-9_-] replaced with -, __ collapsed, prefixed srv- if needed, truncated to 32 chars).

--stdio spawns the MCP server as a child process and supervises it:

  • If the child exits, it’s restarted with exponential backoff (500ms up to 10s). A child that survives 2s is treated as healthy and the backoff resets.
  • If the child crashes (or fails to spawn) 3 times in a row within the stable-runtime window, the CLI prints a one-time crash hint showing the command and the last captured stderr line (usually the real cause — a missing env var, command not found, etc.).
  • On Windows, bare names like npx/pnpm/yarn are resolved via PATH + PATHEXT; .cmd/.bat shims are launched through cmd /C.

If an HTTP upstream is unreachable, the remote MCP client gets a synthetic 502 whose body names the URL and the underlying cause.

Terminal window
# Expose a local HTTP MCP server
mcppipe up --api-key mtk_xxx --upstream http://127.0.0.1:3000
# Bridge a stdio MCP server
mcppipe up --api-key mtk_xxx --stdio "python my_server.py"
# Aggregate several upstreams behind one tunnel (name=value required)
mcppipe up --api-key mtk_xxx \
--upstream brave=http://127.0.0.1:3001 \
--stdio fs="npx -y @modelcontextprotocol/server-filesystem ."
# Load a multi-upstream layout from a file
mcppipe up --api-key mtk_xxx --upstreams-file tunnel.toml
# Auto-detect and pick interactively
mcppipe up --api-key mtk_xxx
# Expose with Prometheus metrics on :9090
mcppipe up --api-key mtk_xxx --upstream http://127.0.0.1:3000 \
--metrics-addr 127.0.0.1:9090
# Just list what is configured locally
mcppipe detect --json
# Verbose logs, update + crash reporting off
mcppipe -vv --no-update-check --no-telemetry \
up --api-key mtk_xxx --upstream http://127.0.0.1:3000