Flexible, pluggable tracing middleware for Model Context Protocol (MCP) servers in JavaScript/TypeScript. Log every request, tool call, and response to local files, PostgreSQL, Supabase, Contexa, console, or your own backend—with full control over what gets logged.
- Features
- Quickstart
- Adapters
- Configurable Logging
- Requirements
- Contributing
- License
- Links & Acknowledgements
- 📦 Plug-and-play: Add tracing to any MCP server in seconds
- 🗃️ Pluggable adapters: Log to file, PostgreSQL, Supabase, Contexa, console, or your own
- 🛠️ Configurable logging: Enable/disable fields (tool args, responses, client ID, etc.)
- 🧩 Composable: Use multiple adapters at once
- 📝 Schema-first: All traces stored as JSON for easy querying
- 🔒 Privacy-aware: Control exactly what gets logged
- ⚡ TypeScript support: Full type safety and IntelliSense
npm install mcp-trace
import { TraceMiddleware, FileAdapter } from "mcp-trace-js";
const traceAdapter = new FileAdapter("trace.log");
const traceMiddleware = new TraceMiddleware({ adapter: traceAdapter });
mcpServer.use('/mcp', traceMiddleware.express())
See example/streamable-http-server.ts
for a complete MCP server using Streamable HTTP transport with tracing.
npm run example:streamable-http
Logs each trace as a JSON line to a file.
import { FileAdapter } from "mcp-trace-js";
const traceAdapter = new FileAdapter("trace.log");
const traceMiddleware = new TraceMiddleware({ adapter: traceAdapter });
app.use('/mcp', traceMiddleware.express())
Logs each trace to the console in a human-readable format (with colors).
import { ConsoleAdapter } from "mcp-trace-js";
const traceAdapter = new ConsoleAdapter();
const traceMiddleware = new TraceMiddleware({ adapter: traceAdapter });
app.use('/mcp', traceMiddleware.express())
Send traces to Contexa for cloud-based trace storage and analytics.
Requirements:
- Contexa API key (
CONTEXA_API_KEY
) - Contexa Server ID (
CONTEXA_SERVER_ID
)
Usage:
import { ContexaTraceAdapter } from "mcp-trace-js";
// Option 1: Set environment variables
// process.env.CONTEXA_API_KEY = 'your-api-key';
// process.env.CONTEXA_SERVER_ID = 'your-server-id';
// const contexaAdapter = new ContexaTraceAdapter();
// Option 2: Pass directly
const contexaAdapter = new ContexaTraceAdapter({
apiKey: "your-api-key",
serverId: "your-server-id",
// Optional: apiUrl, bufferSize, flushInterval, maxRetries, retryDelay
});
const traceMiddleware = new TraceMiddleware({ adapter: contexaAdapter });
// On shutdown, ensure all events are sent:
await contexaAdapter.flush(5000);
await contexaAdapter.shutdown();
app.use('/mcp', traceMiddleware.express())
Store traces in a PostgreSQL table for easy querying and analytics.
Table schema:
CREATE TABLE IF NOT EXISTS trace_events (
id SERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ NOT NULL,
type TEXT NOT NULL,
method TEXT,
session_id TEXT NOT NULL,
client_id TEXT,
duration INTEGER,
entity_name TEXT,
arguments JSONB,
response TEXT,
error TEXT
);
Usage:
import { PostgresTraceAdapter } from "mcp-trace-js";
const psqlAdapter = new PostgresTraceAdapter({
dsn: "postgresql://user:pass@host:port/dbname",
// Optional: tableName
});
const traceMiddleware = new TraceMiddleware({ adapter: psqlAdapter });
app.use('/mcp', traceMiddleware.express())
Log traces to Supabase (PostgreSQL as a service).
Table schema: (same as PostgreSQL above)
Install:
npm install @supabase/supabase-js
Usage:
import { createClient } from "@supabase/supabase-js";
import { SupabaseTraceAdapter } from "mcp-trace-js";
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const supabaseAdapter = new SupabaseTraceAdapter({ supabaseClient: supabase });
const traceMiddleware = new TraceMiddleware({ adapter: supabaseAdapter });
app.use('/mcp', traceMiddleware.express())
Send traces to multiple backends at once:
import {
FileAdapter,
PostgresTraceAdapter,
SupabaseTraceAdapter,
MultiAdapter,
} from "mcp-trace-js";
const fileAdapter = new FileAdapter("trace.log");
const psqlAdapter = new PostgresTraceAdapter({
dsn: "postgresql://user:pass@host:port/dbname",
});
const supabaseAdapter = new SupabaseTraceAdapter({ supabaseClient: supabase });
const multiAdapter = new MultiAdapter(
fileAdapter,
psqlAdapter,
supabaseAdapter
);
const traceMiddleware = new TraceMiddleware({ adapter: multiAdapter });
app.use('/mcp', traceMiddleware.express())
Control exactly which fields are logged by passing a logFields
dictionary to TraceMiddleware
. By default, all fields are logged unless set to false
.
Available fields:
type
,method
,timestamp
,session_id
,request_id
,client_id
,duration
tool_name
,tool_arguments
,tool_response
,tool_response_structured
Example: Only log tool name and response, hide arguments and client ID:
const traceMiddleware = new TraceMiddleware({
adapter: traceAdapter,
logFields: {
tool_name: true,
tool_response: true,
tool_arguments: false, // disables tool arguments
client_id: false, // disables client_id
// ...add more as needed
},
});
Skip tracing for specific requests by adding the X-Ignore-Traces
header:
# Skip tracing for this request
curl -H "X-Ignore-Traces: true" http://localhost:8080/mcp
Supported header formats:
X-Ignore-Traces: true
x-ignore-traces: true
When this header is present, the middleware will skip all tracing logic and continue to the next middleware without any performance overhead.
- Node.js 16+
- TypeScript 4.5+ (for TypeScript projects)
@modelcontextprotocol/sdk
(for MCP server integration)pg
(for PostgreSQL adapter)@supabase/supabase-js
(for Supabase adapter)
We love contributions! Please open issues for bugs or feature requests, and submit pull requests for improvements.
MIT
- Model Context Protocol — Model Context Protocol
- @modelcontextprotocol/sdk — Official MCP SDK