Proxy to capture MCP (Model Context Protocol) sessions as vCons and post them to a conserver for storage and processing.
This proxy wraps any MCP server to automatically capture all communication between MCP clients (like Claude Desktop) and servers. Each session is converted into an IETF-compliant vCon (Virtual Conversation) and posted to a conserver.
Output conforms to vCon core-02 (syntax 0.4.0). This package is the reference implementation for draft-howe-vcon-mcp-session, the IETF draft describing how to record MCP sessions as vCons.
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ MCP Client │────▶│ vCon MCP Proxy │────▶│ MCP Server │
│ (Claude, etc) │◀────│ │◀────│ (any server) │
└─────────────────┘ │ - Intercepts msgs │ └─────────────────┘
│ - Builds vCon │
│ - Posts to server │ ┌─────────────────┐
│ │────▶│ Conserver │
└──────────────────────┘ │ (HTTP POST) │
└─────────────────┘
npm install vcon-mcp-proxyOptional peer dependency for full vCon library support:
npm install vcon-jsimport { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { VconMcpProxy } from 'vcon-mcp-proxy';
// Create your MCP server as usual
const server = new Server(
{ name: 'my-server', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// Register your handlers...
server.setRequestHandler(/* ... */);
// Create the vCon MCP proxy
const proxy = new VconMcpProxy({
conserver: {
url: 'http://localhost:8000/api/vcon',
apiToken: process.env.CONSERVER_API_TOKEN,
},
vcon: {
serverName: 'my-server',
},
});
// Create and wrap the transport
const transport = new StdioServerTransport();
const wrappedTransport = proxy.wrapTransport(transport);
// Connect with wrapped transport
await server.connect(wrappedTransport);
// Handle graceful shutdown
process.on('SIGINT', async () => {
await proxy.shutdown();
process.exit(0);
});The only required field is conserver.url. Everything else has a sensible default.
const proxy = new VconMcpProxy({
// Required: Conserver settings
conserver: {
url: 'http://localhost:8000/api/vcon', // Required
apiToken: 'your-token', // Optional
ingressList: 'mcp_sessions', // Default: 'mcp_sessions'
timeoutMs: 30000, // Default: 30000
retryAttempts: 3, // Default: 3
retryDelayMs: 1000, // Default: 1000
},
// Optional: Session settings
session: {
timeoutMs: 300000, // Auto-finalize after 5 min inactivity
maxMessages: 10000, // Maximum messages per session
captureResources: true, // Capture resource content
capturePrompts: true, // Capture prompt content
maxInlineContentSize: 100000, // Max inline content size (bytes)
},
// Optional: vCon generation settings
vcon: {
serverName: 'my-server', // Server name in vCon
serverVersion: '1.0.0', // Server version
addAnalysis: true, // Add session analysis
tags: { // Additional tags
environment: 'production',
},
analysisVendor: 'vcon-mcp-proxy',
},
// Optional: Debug settings
debug: false,
logger: (level, message, data) => console.error(`[${level}] ${message}`, data),
});The proxy works well with environment variables:
CONSERVER_URL=http://localhost:8000/api/vcon
CONSERVER_API_TOKEN=your-token
VCON_SERVER_NAME=my-server
DEBUG=trueThe proxy emits events you can listen to:
// Session started
proxy.on('session:start', (session) => {
console.log(`Session started: ${session.id}`);
});
// Session ended
proxy.on('session:end', (session) => {
console.log(`Session ended: ${session.id}`);
console.log('Stats:', session.getStats());
});
// vCon created from session
proxy.on('vcon:created', (vcon, session) => {
console.log(`vCon created: ${vcon.uuid}`);
console.log(`Dialog count: ${vcon.dialog.length}`);
});
// vCon posted to conserver
proxy.on('vcon:posted', (result, vcon) => {
if (result.success) {
console.log(`Posted: ${vcon.uuid}`);
} else {
console.error(`Failed: ${result.message}`);
}
});
// Error during vCon processing
proxy.on('vcon:error', (error, session) => {
console.error(`Error for session ${session.id}:`, error);
});Each MCP session is converted to a vCon with the following structure:
{
"uuid": "generated-uuid",
"vcon": "0.4.0",
"created_at": "2025-01-15T10:00:00Z",
"subject": "MCP Session: my-server",
"parties": [
{
"name": "Claude Desktop",
"role": "user",
"meta": { "version": "1.0.0" }
},
{
"name": "my-server",
"role": "agent",
"meta": { "version": "1.0.0" }
}
],
"dialog": [
{
"type": "text",
"start": "2025-01-15T10:00:01Z",
"parties": [0],
"originator": 0,
"body": "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",...}",
"mediatype": "application/json",
"encoding": "none",
"meta": {
"mcp_type": "request",
"mcp_method": "tools/call",
"request_id": 1
}
}
],
"analysis": [
{
"type": "session_summary",
"vendor": "vcon-mcp-proxy",
"product": "vcon-mcp-proxy",
"dialog": [0, 1, 2],
"body": "{\"tool_calls\":5,\"duration_ms\":1234}",
"encoding": "json"
}
],
"attachments": [
{
"purpose": "tags",
"party": 0,
"dialog": 0,
"encoding": "json",
"body": "[\"source:mcp-proxy\",\"server_name:my-server\",\"tool_count:5\"]"
}
]
}For custom integrations, you can manage sessions manually:
import { SessionManager, VconBuilder, ConserverClient } from 'vcon-mcp-proxy';
const sessionManager = new SessionManager({ timeoutMs: 300000, ... });
const vconBuilder = new VconBuilder({ serverName: 'custom', ... });
const client = new ConserverClient({ url: '...', ... });
// Add messages
sessionManager.addMessage('request', { jsonrpc: '2.0', ... }, { sessionId: 'my-session' });
sessionManager.addMessage('response', { jsonrpc: '2.0', ... }, { sessionId: 'my-session' });
// Handle session end
sessionManager.on('session:end', async (session) => {
const vcon = vconBuilder.build(session);
await client.post(vcon);
});
// End session
sessionManager.endSession('my-session');Main proxy class.
constructor(config: ProxyConfigInput)- Create proxywrapTransport(transport: Transport, sessionId?: string)- Wrap MCP transportendSession(sessionId?: string)- Manually end a sessionendAllSessions()- End all active sessionsshutdown()- Graceful shutdowngetConfig()- Get current configurationgetSessionManager()- Access session managergetVconBuilder()- Access vCon buildergetConserverClient()- Access conserver client
Manages MCP sessions.
getOrCreateSession(sessionId?, options?)- Get or create sessionaddMessage(direction, content, options?)- Add message to sessionendSession(sessionId?)- End a sessionendAllSessions()- End all sessions- Events:
session:start,session:end,session:timeout,session:error,session:message
Builds vCons from sessions.
build(session: Session)- Build vCon from sessiontoJson(vcon)- Convert to JSON stringstatic isVconJsAvailable()- Check if vcon-js library is available
HTTP client for conserver.
post(vcon)- Post vCon to conserverhealthCheck()- Check conserver health
See docs/DESIGN.md for architecture, component breakdown, vCon-mapping rules, session lifecycle, and error-handling design.
MIT — see LICENSE.