A security-conscious observability gateway for the Model Context Protocol over Streamable HTTP.
MCP Trace sits between an MCP client and a fixed upstream server. It preserves JSON and SSE traffic while adding structured logs, Prometheus metrics, OpenTelemetry spans, sanitized NDJSON recordings, inspection, and controlled replay.
MCP client ──POST / GET / DELETE──▶ MCP Trace ──transparent HTTP──▶ MCP server
│
├── Prometheus metrics
├── OpenTelemetry spans
└── sanitized NDJSON recording
The gateway does not implement MCP methods, terminate authorization, or mutate JSON-RPC bodies. That narrow boundary is intentional: the upstream server remains the protocol authority.
Generic reverse proxies can measure HTTP, but they do not understand tools/call, resources/read,
MCP protocol revisions, JSON-RPC identifiers, or header/body mismatches. Application-level logging
has the opposite problem: it often captures tool arguments and credentials too freely.
MCP Trace provides MCP-aware signals with conservative defaults:
- streams
application/jsonandtext/event-streamresponses without buffering them; - preserves 2026 routing headers and 2025 session/resumption headers;
- extracts and forwards W3C Trace Context;
- exposes method/status counters and latency histograms without tool-name metric labels;
- records metadata only unless body capture is explicitly enabled;
- hashes legacy session IDs and never records authorization or cookie headers;
- redacts common credentials, configured secret keys, and secrets in JSON/SSE text;
- makes replay a dry run unless
--executeis supplied.
| MCP transport revision | Status | Behavior |
|---|---|---|
2026-07-28 Streamable HTTP |
Supported | POST, JSON or request-scoped SSE, Mcp-Method, Mcp-Name, Mcp-Param-* |
2025-03-26 through 2025-11-25 |
Supported | POST/GET/DELETE, Mcp-Session-Id, Last-Event-ID, standalone SSE |
2024-11-05 HTTP+SSE |
Not targeted | Its separate endpoint discovery flow is outside this fixed-endpoint gateway |
| stdio | Not yet supported | Use an HTTP bridge or follow the roadmap |
See the official 2026 Streamable HTTP specification and 2025 transport specification.
Requirements: Node.js 20.19 or newer and pnpm 10.34.0.
git clone https://github.com/ryux1/mcp-trace.git
cd mcp-trace
corepack enable
pnpm install --frozen-lockfile
pnpm build
node dist/cli.js proxy \
--upstream http://127.0.0.1:3001/mcpPoint the MCP client at http://127.0.0.1:7331/mcp. The gateway binds only to 127.0.0.1 by
default.
For an authenticated upstream, keep the credential out of command history:
export MCP_UPSTREAM_AUTHORIZATION='Bearer replace-me'
node dist/cli.js proxy \
--upstream https://mcp.example.com/mcp \
--upstream-header-env Authorization=MCP_UPSTREAM_AUTHORIZATIONThe example server in examples/ provides a local end-to-end demo.
Metadata-only recording is the default:
node dist/cli.js proxy \
--upstream http://127.0.0.1:3001/mcp \
--record ./traffic.ndjsonPayload capture requires a second, explicit switch:
node dist/cli.js proxy \
--upstream http://127.0.0.1:3001/mcp \
--record ./traffic.ndjson \
--record-bodies \
--redact-key tenant-secretRecording files are forced to owner-only mode (0600). Inspect them without starting a server:
node dist/cli.js inspect ./traffic.ndjsonThe output summarizes request counts, failures, bytes, and p50/p95/p99 latency by MCP method. See the recording schema and security model before capturing production traffic.
Replay is a dry run by default. It reports how many requests are replayable and skips truncated, binary, body-less, and redacted entries.
node dist/cli.js replay ./traffic.ndjson \
--upstream http://127.0.0.1:3001/mcpExecute only against a system where repeating tool calls is safe:
node dist/cli.js replay ./traffic.ndjson \
--upstream http://127.0.0.1:3001/mcp \
--execute \
--concurrency 4 \
--rate 20Authorization can be supplied with --header-env. Legacy Mcp-Session-Id values are never recorded
or replayed, so replay is best suited to stateless 2026 traffic.
The gateway exposes two local administrative endpoints:
GET /__mcp_trace/healthzGET /__mcp_trace/metrics
Prometheus metrics include request totals, in-flight requests, recording failures, and latency histograms. Method-label cardinality is bounded; tool/resource names are not used as metric labels.
Export spans to any OTLP/HTTP collector:
export OTEL_AUTHORIZATION='Bearer collector-token'
node dist/cli.js proxy \
--upstream http://127.0.0.1:3001/mcp \
--otlp-endpoint http://127.0.0.1:4318 \
--otlp-header-env Authorization=OTEL_AUTHORIZATIONEach span includes the HTTP method/status, MCP method, protocol revision, upstream address, and
detected header/body mismatch fields. Tool/resource names appear only on individual spans. MCP Trace
preserves JSON _meta.traceparent, _meta.tracestate, and _meta.baggage fields without rewriting
them, and separately propagates standard HTTP trace headers.
docker build -t mcp-trace:local .
docker run --rm \
--network host \
mcp-trace:local proxy \
--host 127.0.0.1 \
--upstream http://127.0.0.1:3001/mcpWhen binding to 0.0.0.0 or ::, at least one --allow-host value is required. This fail-closed
behavior prevents a wildcard bind from silently accepting arbitrary Host headers.
pnpm verifyThe gate runs formatting, ESLint, strict TypeScript checks, the unit/integration suite with coverage thresholds, and a clean build. The integration tests use real HTTP servers and cover JSON, streamed SSE, modern and legacy transports, origin/host rejection, payload limits, recording, redaction, metrics, OTLP export, and replay.
Run the reproducible local microbenchmark with:
pnpm benchmarkMethodology and interpretation are documented in docs/benchmarks.md.
MCP Trace is not a data-loss-prevention system, authorization server, or protocol validator. Redaction is defense in depth, not proof that a recording contains no sensitive data. Review recordings before sharing them.