mcp2cli

Transports

mcp2cli supports three transport modes for connecting to MCP servers. Each transport handles the JSON-RPC communication layer differently.


Overview

graph TB
    subgraph "mcp2cli"
        ENGINE["Protocol Engine"]
    end

    ENGINE -->|"JSON-RPC over HTTP"| HTTP["Streamable HTTP<br/>Remote servers"]
    ENGINE -->|"JSON-RPC over stdin/stdout"| STDIO["Stdio<br/>Local subprocesses"]
    ENGINE -->|"File-backed state"| DEMO["Demo<br/>Offline testing"]
TransportConfig ValueWhen to Use
Streamable HTTPstreamable_httpRemote servers, production, shared infrastructure
StdiostdioLocal development, testing, subprocess servers
Demostreamable_http + demo.invalid endpointLearning, offline testing, CI fixtures

Streamable HTTP

Connects to a remote MCP server over HTTP with JSON-RPC request/response cycles and SSE (Server-Sent Events) for streaming. Both http (handy for local development) and https (TLS via rustls, required for real or authenticated servers) endpoints are supported.

Configuration

server:
transport: streamable_http
endpoint: http://127.0.0.1:3001/mcp # plain HTTP for local/dev

For a real or authenticated server, use an https endpoint so the connection is encrypted with TLS (rustls):

server:
transport: streamable_http
endpoint: https://mcp.example.com/email # TLS via rustls

How It Works

Behavior depends on the negotiated MCP protocol revision (see Protocol version selection):

MCP 2026-07-28 (stateless):

  1. Era probe: POSTs server/discover; a DiscoverResult selects the stateless revision
  2. No sessions: the Mcp-Session-Id header is never sent
  3. Request metadata headers: every POST carries MCP-Protocol-Version, Mcp-Method, and (for tools/call / resources/read / prompts/get) Mcp-Name; values outside the header-safe ASCII range use the spec’s =?base64?…?= encoding
  4. Custom param headers: tool parameters annotated with x-mcp-header in the input schema are mirrored as Mcp-Param-* headers; tools with invalid annotations are excluded from discovery
  5. SSE responses: the server may stream request-scoped notifications followed by the final response
  6. Server input requests: delivered as resultType: "input_required" results (MRTR) and resolved by retrying the request — never as server-initiated JSON-RPC requests
  7. Change notifications: delivered on a subscriptions/listen POST response stream
  8. Cancellation: closing the response stream cancels the request

MCP 2025-11-25 (legacy):

  1. Initialization: sends initialize request with client capabilities
  2. Session negotiation: server may return a session ID via Mcp-Session-Id header
  3. Operations: each MCP operation is a POST with JSON-RPC body
  4. SSE responses: the server can stream responses as SSE events
  5. Notifications: server notifications arrive via the SSE stream

Protocol version selection

By default (server.protocol_version: auto) mcp2cli probes with server/discover and falls back to the 2025-11-25 initialize handshake when the server does not answer with a recognised modern response — the exact backward-compatibility algorithm from the 2026-07-28 spec. Pin a revision to skip detection:

server:
transport: streamable_http
endpoint: https://mcp.example.com/email
protocol_version: "2026-07-28" # or "2025-11-25", or auto (default)

Authentication

Add an Authorization header automatically via auth login:

Terminal window
email auth login
# Prompted for token → stored in instances/<name>/tokens.json
# All subsequent requests include the Authorization header

Running a Test Server

3001/mcp
npx @modelcontextprotocol/server-everything streamableHttp

Custom CA certificates (SSL_CERT_FILE)

By default, HTTPS connections trust the bundled Mozilla root set (webpki-roots) — no system trust store is needed. In corporate environments that run a TLS-inspection proxy (mitmproxy, ZScaler, Cloudflare WARP, Fortinet, …), outbound HTTPS traffic is re-signed with a private root CA that isn’t in that bundle, and connections fail with:

Error: streamable HTTP request failed: client error (Connect)

Set SSL_CERT_FILE to a PEM file containing the extra CA certificate(s) to trust — the same convention curl, Python requests, Go’s net/http, and Ruby’s OpenSSL bindings use:

Terminal window
export SSL_CERT_FILE=/opt/corp/mitmproxy-ca-cert.pem
email ls

The bundled roots are never removed — SSL_CERT_FILE only adds to the trust store, and unsetting it restores the default behavior exactly. This applies to every outbound HTTPS connection mcp2cli makes: the MCP transport, telemetry shipping, and OAuth login. An unset or unreadable SSL_CERT_FILE degrades to the bundled roots with a warning rather than failing outright.


Stdio

Spawns a local subprocess and communicates via line-delimited JSON-RPC over stdin/stdout.

Configuration

server:
transport: stdio
stdio:
command: npx
args: ['@modelcontextprotocol/server-everything']
cwd: /path/to/project # Optional working directory
env: # Optional environment variables
API_KEY: sk-abc123
NODE_ENV: development

How It Works

  1. Spawn: mcp2cli starts the subprocess with the given command/args
  2. Initialize: Sends JSON-RPC initialize over stdin
  3. Operations: Writes JSON-RPC requests to stdin, reads responses from stdout
  4. Notifications: Server notifications arrive on stdout between responses
  5. Shutdown: Sends stdin EOF when done; subprocess exits

Key Behaviors

  • Process lifetime: The subprocess lives for the duration of the CLI command. Use Daemon Mode to keep it alive.
  • stderr passthrough: The subprocess stderr is inherited for debugging (server log messages appear in terminal)
  • Environment variables: The env map is merged with the current environment

Running a Test Server

Terminal window
# This is what mcp2cli spawns internally:
npx @modelcontextprotocol/server-everything

Demo Mode

A file-backed backend that simulates an MCP server without network or subprocess. Ideal for learning and testing.

Configuration

server:
transport: streamable_http
endpoint: https://demo.invalid/mcp # Magic demo endpoint

How It Works

When the endpoint is demo.invalid, mcp2cli activates the demo backend:

  • Discovery: Returns a static set of tools, resources, and prompts
  • Tool calls: Echo back arguments with mock responses
  • State: Persisted to ~/.local/share/mcp2cli/demo-remote-state.json
  • No network: Everything runs locally in-process

Use Cases

  • Learning mcp2cli without setting up a real server
  • CI/CD tests that need deterministic MCP behavior
  • Documentation examples
Terminal window
mcp2cli config init --name demo --app bridge \
--transport streamable_http --endpoint https://demo.invalid/mcp
mcp2cli use demo
mcp2cli ls # Shows demo capabilities
mcp2cli echo --message "hello"

Ad-Hoc Transport Selection

Skip config files entirely with --url or --stdio:

Terminal window
# HTTP — creates ephemeral streamable_http config
mcp2cli --url http://127.0.0.1:3001/mcp ls
# Stdio — creates ephemeral stdio config
mcp2cli --stdio "npx @modelcontextprotocol/server-everything" ls
# Stdio with environment
mcp2cli --stdio "python server.py" --env API_KEY=secret echo --message test

See Ad-Hoc Connections for full details.


Transport Comparison

FeatureStreamable HTTPStdioDemo
Remote servers
Local subprocess
Offline operation
Session persistenceVia Mcp-Session-IdProcess lifetimeFile-backed
Auth support✅ Bearer tokens
SSE streamingN/AN/A
Daemon compatible
Startup latencyNetwork RTTProcess spawn~0ms

See Also