Spanly Docs
CLI

spanly proxy

Standalone HTTP/SSE reverse proxy in front of an existing MCP server.

spanly proxy is for the cases where you can't wrap the MCP server as a child process – typically:

  • A third-party MCP service you don't control.
  • A server already deployed behind k8s, where introducing a wrapping CLI is impractical.
  • Network-level interception where the proxy sits between many clients and one server.

The proxy is HTTP-only (no stdio). Inbound traffic on <bind> is forwarded to <upstream>; every JSON-RPC frame on inspected paths is captured.

Quickstart

# 1. Set your API keyexport SPANLY_API_KEY="$SPANLY_API_KEY"# 2. Run the standalone proxy in front of an existing MCP server.#    upstream  -> the MCP server you can't / don't want to wrap#    bind      -> the address your MCP client should connect to insteadnpx -y @spanly/spanly proxy localhost:3000 localhost:3001

Then point your MCP client at the bind address (localhost:3001) instead of the upstream.

Anatomy

┌─────────────┐      ┌──────────────────┐      ┌────────────────┐
│  MCP client │ ───▶ │  spanly proxy    │ ───▶ │  upstream MCP  │
└─────────────┘      │  <bind>          │      │  <upstream>    │
                     └──────────────────┘      └────────────────┘


                       Spanly ingest

The proxy is transparent at the HTTP layer:

  • Status codes, headers, and bodies are passed through unchanged.
  • SSE (text/event-stream) streams flow through with response buffering disabled, so they stay live.
  • Non-MCP paths (anything not matching --inspect-prefix, default /mcp,/sse) are forwarded without parsing.

Examples

# Loopback: monitor a server running on localhost:3000
spanly proxy localhost:3000 :3001

# In a Kubernetes Pod: front a sidecar MCP server
spanly proxy mcp:3000 0.0.0.0:3001

# Behind your own ingress / load balancer
spanly proxy upstream.svc.cluster.local:8080 :3001

# Multi-tenant tagging
spanly proxy --context-header=X-Tenant=environmentId \
  localhost:3000 :3001

SSE pass-through

MCP often uses text/event-stream for streaming notifications. The proxy:

  • Holds the upstream connection open for the duration of the SSE stream.
  • Parses each data: frame as a JSON-RPC packet and emits one telemetry event per frame.
  • Flushes immediately to the downstream client – there is no buffering between you and the upstream.

If you later put another reverse proxy (nginx, Caddy, Envoy) in front of spanly proxy, configure it for SSE pass-through. See Production for sample configs.

Per-request headers

Inbound headers recognized by the proxy:

HeaderEffect
X-Spanly-Monitor-IdOverride spanlyMonitorId for this request. Useful for joining many requests under one logical session.
Any header named via --context-headerMaps to the matching context field (environmentId, projectId, organisationId).

Comparison: run vs proxy

spanly runspanly proxy
stdio supportyesno
HTTP supportyesyes
Wraps child processyesno
Affects MCP client URLnoyes (point at bind)
Easiest to embed in MCP client configyesno
Best for third-party / unowned serversnoyes

If you can wrap, prefer run. Use proxy when you can't.

Flag reference

See the full flag reference for the complete list of flags. proxy accepts the same flags as run minus --port and the --child-* family.

On this page