Quick Start
Wrap an upstream MCP server with a logging middleware and serve it over stdio. Give your proxy a name and keep the upstream unchanged.
Prerequisites
- Node.js
20or newer. @modelcontextprotocol/sdk ^1.17.0as a peer dependency.- TypeScript is optional — mcpose ships ESM with first-class types.
Install
# core
npm install mcpose
# peer dependency — installed separately
npm install @modelcontextprotocol/sdk@"^1.17.0"
# compliance audit trails (optional)
npm install @mcpose/auditWrap a server
Connect to the upstream, define middleware, start the proxy. Middleware follows the onion model: each layer runs before and after the inner pipeline.
import { createBackendClient, startProxy } from 'mcpose';
import type { ToolMiddleware } from 'mcpose';
// 1 · Connect to the upstream MCP server (stdio)
const backend = await createBackendClient({
command: 'node',
args: ['/path/to/backend-server.mjs'],
});
// 2 · Define middleware — before and after the upstream call
const logging: ToolMiddleware = async (req, next) => {
console.error(`→ ${req.params.name}`);
const result = await next(req);
console.error(`← ${req.params.name} done`);
return result;
};
// 3 · Start the proxy on stdio
await startProxy(backend, {
name: 'my-proxy',
toolMiddleware: [logging],
});Middleware arrays use response-processing order: the first element processes the response first. To keep raw PII out of audit logs, write toolMiddleware: [piiMW, auditMW] — the audit layer then only ever sees redacted data.
Run it
Save the example as proxy.ts and replace the backend path with your upstream server.
Run npx tsx proxy.ts to launch it.
The proxy is a normal MCP server.
Point any client at it:
{
"mcpServers": {
"governed-search": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/proxy.ts"]
}
}
}Serving over HTTP instead?
Swap startProxy for startHttpProxy — mTLS, session limits, per-session identity resolution, and SSE reconnect replay live there.
Routing paths
For each tool or resource, the proxy picks one of three paths:
| Path | Option | Behavior |
|---|---|---|
| Hidden | hiddenTools | Omitted from lists; rejected with TOOL_HIDDEN at call time — still audited. |
| Pass-through | passThroughTools | Forwarded raw; transformers skipped, wrapped observers still run. |
| Middleware | everything else | Routed through the full toolMiddleware pipeline. |
Hidden beats pass-through: a tool listed in both stays hidden.