vcon-mcp-proxy
v0.1.0
Published
Proxy to capture MCP sessions as vCons and post to a conserver
Maintainers
Readme
vCon MCP Proxy
Proxy to capture MCP (Model Context Protocol) sessions as vCons and post them to a conserver for storage and processing.
Overview
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) │
└─────────────────┘Installation
npm install vcon-mcp-proxyOptional peer dependency for full vCon library support:
npm install vcon-jsQuick Start
Wrap an Existing MCP Server
import { 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);
});Configuration
The only required field is conserver.url. Everything else has a sensible default.
Full Configuration Options
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),
});Environment Variables
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=trueEvents
The 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);
});vCon Structure
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\"]"
}
]
}Manual Session Management
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');API Reference
VconMcpProxy
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
SessionManager
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
VconBuilder
Builds vCons from sessions.
build(session: Session)- Build vCon from sessiontoJson(vcon)- Convert to JSON stringstatic isVconJsAvailable()- Check if vcon-js library is available
ConserverClient
HTTP client for conserver.
post(vcon)- Post vCon to conserverhealthCheck()- Check conserver health
Design
See docs/DESIGN.md for architecture, component breakdown, vCon-mapping rules, session lifecycle, and error-handling design.
License
MIT — see LICENSE.
