Spanly Docs
CLI

spanly run

Wrap an MCP server as a child process – stdio or HTTP, zero code changes.

spanly run is the default path for most users. It launches your MCP server as a child process and captures every JSON-RPC frame on its transport. Works identically for stdio servers, HTTP servers, and SSE servers.

30-second demo

# 1. Set your API key (region auto-detected from the prefix)export SPANLY_API_KEY="$SPANLY_API_KEY"# 2. Wrap your MCP server. stdio:npx -y @spanly/spanly run -- node ./server.js# Or HTTP – the wrapper takes your port; the child gets a random one:npx -y @spanly/spanly run --port 3000 -- node ./server.js

That's it. Run your MCP client (Claude Desktop, Cursor, …) as usual. Telemetry shows up in the Spanly dashboard.

stdio mode (default)

When --port is not set, the CLI runs in stdio mode. It spawns your server, hooks stdin/stdout, and forwards JSON-RPC frames in both directions – capturing them in the process.

spanly run -- node ./server.js
spanly run -- python -m my_mcp
spanly run -- ./my-go-binary

Your MCP client sees exactly the same stdio behavior as if it had launched the server directly. There is no protocol change.

HTTP mode

Set --port to switch to HTTP mode:

spanly run --port 3000 -- node ./server.js

What happens:

  • The CLI binds to port 3000 (the port your MCP client connects to).
  • The child server gets a random free port via the PORT environment variable (rename with --child-port-env).
  • Every HTTP request to /mcp / /sse (configurable via --inspect-prefix) is captured. Everything else is forwarded untouched.

Your MCP client URL doesn't change – it still points at port 3000. The fact that there's a wrapper in between is transparent.

Pinning the child port

If your server can't pick its own port:

spanly run --port 3000 --child-port 3001 -- ./server

Multi-tenant tagging via headers

Map inbound HTTP headers to context fields with --context-header:

spanly run --port 3000 \
  --context-header=X-Tenant=environmentId \
  --context-header=X-Org=organisationId \
  -- ./server

In the dashboard the captured requests can then be sliced by environmentId or organisationId – no code change in the server required.

Examples

# Node MCP, stdio
spanly run -- node server.js

# Python MCP, HTTP on port 3000
spanly run --port 3000 -- python -m my_mcp

# Go MCP, HTTP with admin metrics
spanly run --port 3000 --admin-addr=:9090 -- ./my-mcp-server

# Multi-tenant tagging from request header
spanly run --port 3000 \
  --context-header=X-Tenant=environmentId \
  -- ./srv

Embedding in MCP client configs

Claude Desktop / Cursor / Windsurf

{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["-y", "@spanly/spanly", "run", "--", "node", "./server.js"],
      "env": {
        "SPANLY_API_KEY": "spanly_us_xxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

The user-facing behavior is identical to running node ./server.js directly – they see the same prompts and tools – but every interaction is now visible in Spanly.

Flag reference

See the full flag reference for the complete list of flags shared by run and proxy (buffering, retry, admin endpoints, OTLP co-export).

On this page