Architecture
Deep dive into the vCon MCP Server architecture, design patterns, and implementation details.
Overview
The vCon MCP Server is built as a layered architecture connecting AI assistants to conversation data through the Model Context Protocol (MCP).
┌─────────────────────────────────────────────────────────────┐
│ AI Assistants │
│ (Claude Desktop, Custom Clients) │
└────────────────────┬────────────────────────────────────────┘
│ MCP Protocol (stdio/HTTP)
┌────────────────────▼────────────────────────────────────────┐
│ MCP Server Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Tools │ │Resources │ │ Prompts │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼────────────────────────┘
│ │ │
┌───────▼─────────────▼─────────────▼────────────────────────┐
│ Business Logic Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Query │ │Validation│ │ Plugin │ │
│ │ Engine │ │ Engine │ │ System │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼────────────────────────┘
│ │ │
┌───────▼─────────────▼─────────────▼────────────────────────┐
│ Database Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Supabase │ │PostgreSQL│ │ pgvector │ │
│ │ Client │ │ Core │ │ Extension│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
Core Principles
1. Spec Compliance First
IETF vCon Core (
draft-ietf-vcon-vcon-core-00
) complianceStrict validation of all vCon objects
Corrected field names (
schema
notschema_version
, etc.)Required fields enforced (e.g.,
vendor
in Analysis)
2. Type Safety
TypeScript for compile-time type checking
Zod for runtime validation
Complete type definitions for all vCon objects
No
any
types in core code
3. Extensibility
Plugin system for custom functionality
Lifecycle hooks at all operation points
Custom tools and resources registration
Minimal core, maximal flexibility
4. Performance
Efficient database queries with proper indexing
Vector search with HNSW indexing
Materialized views for tag queries
Caching strategies
5. Developer Experience
Clear error messages
Comprehensive documentation
Example code for all features
Testing utilities and fixtures
Component Architecture
MCP Server Layer
Server Initialization
const server = new Server(
{
name: 'vcon-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
prompts: {},
},
}
);
The server implements three MCP interfaces:
Tools - Operations (create, read, update, delete, search)
Resources - URI-based data access (
vcon://uuid/...
)Prompts - Query templates and guidance
Request Flow
1. Client Request → stdio transport
2. MCP Protocol Parsing → JSON-RPC
3. Request Handler → Tool/Resource/Prompt
4. Plugin Hooks → beforeCreate, afterRead, etc.
5. Business Logic → Validation, queries
6. Database Operations → Supabase client
7. Response Formatting → MCP protocol
8. Plugin Hooks → afterCreate, afterUpdate, etc.
9. Response → stdio transport
Business Logic Layer
Query Engine (src/db/queries.ts
)
src/db/queries.ts
)Handles all database operations with a focus on correctness and performance.
export class VConQueries {
constructor(private supabase: SupabaseClient) {}
// CRUD operations
async createVCon(vcon: VCon): Promise<{ uuid: string }>
async getVCon(uuid: string): Promise<VCon>
async updateVCon(uuid: string, updates: any): Promise<void>
async deleteVCon(uuid: string): Promise<void>
// Search operations
async searchVCons(criteria: SearchCriteria): Promise<VCon[]>
async searchVConsContent(query: string, options: SearchOptions): Promise<SearchResult[]>
async searchVConsSemantic(query: string, options: SemanticOptions): Promise<SemanticResult[]>
async searchVConsHybrid(query: string, options: HybridOptions): Promise<HybridResult[]>
// Component operations
async addDialog(vconUuid: string, dialog: Dialog): Promise<number>
async addAnalysis(vconUuid: string, analysis: Analysis): Promise<number>
async addAttachment(vconUuid: string, attachment: Attachment): Promise<number>
// Tag operations
async getTags(vconUuid: string): Promise<Record<string, string>>
async setTag(vconUuid: string, key: string, value: string): Promise<void>
async removeTag(vconUuid: string, key: string): Promise<void>
async searchByTags(tags: Record<string, string>): Promise<VCon[]>
}
Design Decisions:
Transactional - Uses database transactions for multi-step operations
Normalized - Stores vCons in normalized form for efficient querying
Denormalized responses - Returns complete vCon objects to clients
Error handling - Wraps database errors with context
Validation Engine (src/utils/validation.ts
)
src/utils/validation.ts
)Ensures all vCons comply with IETF spec before storage.
export class VConValidator {
validate(vcon: VCon): ValidationResult {
// Core validation
this.validateVersion(vcon)
this.validateUUID(vcon.uuid)
this.validateParties(vcon.parties)
// Component validation
if (vcon.dialog) this.validateDialogs(vcon.dialog)
if (vcon.analysis) this.validateAnalysis(vcon.analysis)
if (vcon.attachments) this.validateAttachments(vcon.attachments)
// Relationship validation
this.validateReferences(vcon)
return this.result
}
}
Validation Checks:
✅ vCon version is
0.3.0
✅ UUID is valid RFC 4122 format
✅ At least one party exists
✅ Dialog types are valid
✅ Analysis has required
vendor
field✅ References (party indexes, dialog indexes) are valid
✅ Encoding values are valid
✅ Dates are ISO 8601 format
Plugin System (src/hooks/
)
src/hooks/
)Allows extending functionality without modifying core code.
export interface VConPlugin {
name: string
version: string
// Lifecycle
initialize?(config: PluginConfig): Promise<void>
shutdown?(): Promise<void>
// Operation hooks
beforeCreate?(vcon: VCon, context: RequestContext): Promise<VCon>
afterCreate?(vcon: VCon, context: RequestContext): Promise<void>
beforeRead?(uuid: string, context: RequestContext): Promise<void>
afterRead?(vcon: VCon, context: RequestContext): Promise<VCon>
beforeUpdate?(uuid: string, updates: any, context: RequestContext): Promise<void>
afterUpdate?(vcon: VCon, context: RequestContext): Promise<void>
beforeDelete?(uuid: string, context: RequestContext): Promise<void>
afterDelete?(uuid: string, context: RequestContext): Promise<void>
beforeSearch?(criteria: any, context: RequestContext): Promise<any>
afterSearch?(results: VCon[], context: RequestContext): Promise<VCon[]>
// Registration
registerTools?(): Tool[]
registerResources?(): Resource[]
registerPrompts?(): Prompt[]
}
Plugin Lifecycle:
1. Server Start
↓
2. Load Plugins (from VCON_PLUGINS_PATH)
↓
3. Call plugin.initialize()
↓
4. Register tools/resources/prompts
↓
5. Hook into request handlers
↓
6. Process requests with hooks
↓
7. On shutdown, call plugin.shutdown()
Database Layer
Schema Design
Normalized Storage:
vcons (main table)
├── parties (1:N)
├── dialog (1:N)
├── analysis (1:N)
├── attachments (1:N)
│ └── tags (special attachment type)
└── groups (1:N)
Benefits:
Efficient querying by component
Easy to add/remove components
Proper foreign key constraints
Scalable to millions of vCons
Supabase Client (src/db/client.ts
)
src/db/client.ts
)Manages database connection with retry logic and error handling.
let supabase: SupabaseClient | null = null
export function getSupabaseClient(): SupabaseClient {
if (!supabase) {
const url = process.env.SUPABASE_URL
const key = process.env.SUPABASE_ANON_KEY
if (!url || !key) {
throw new Error('Missing Supabase credentials')
}
supabase = createClient(url, key, {
auth: {
persistSession: false
},
db: {
schema: 'public'
}
})
}
return supabase
}
Search Architecture
Three Search Modes:
Metadata Search (
searchVCons
)Filters by subject, party, dates
Uses B-tree indexes
~50ms response time
Content Search (
searchVConsContent
)Full-text search with trigram matching
Uses GIN indexes
~100ms response time
Semantic Search (
searchVConsSemantic
)Vector similarity with embeddings
Uses HNSW index
~200ms response time
Hybrid Search (
searchVConsHybrid
)Combines keyword + semantic
Weighted scoring
~300ms response time
Search RPC Functions:
-- Keyword search
search_vcons_keyword(query_text, start_date, end_date, tag_filter, max_results)
-- Semantic search
search_vcons_semantic(query_embedding, tag_filter, match_threshold, match_count)
-- Hybrid search
search_vcons_hybrid(keyword_query, query_embedding, tag_filter, semantic_weight, limit_results)
Tag Management
Storage: Tags are stored as special attachments:
{
"type": "tags",
"encoding": "json",
"body": "[\"department:sales\", \"priority:high\", \"status:open\"]"
}
Materialized View:
CREATE MATERIALIZED VIEW vcon_tags_mv AS
SELECT
vcon_id,
split_part(elem, ':', 1) AS key,
split_part(elem, ':', 2) AS value
FROM attachments
CROSS JOIN LATERAL jsonb_array_elements_text(body::jsonb) AS elem
WHERE type = 'tags' AND encoding = 'json'
Benefits:
Fast tag searches (indexed materialized view)
No schema changes needed for new tags
Easy to query and update
Compatible with vCon spec (as attachments)
Data Flow Examples
Creating a vCon
User: "Create a vCon for a support call"
↓
Claude calls: create_vcon tool
↓
MCP Server receives request
↓
Plugin: beforeCreate hook (add metadata)
↓
Validation: Check vCon structure
↓
Database: Transaction starts
├── Insert into vcons table
├── Insert parties
├── Insert dialog (if provided)
└── Insert analysis (if provided)
↓
Database: Transaction commits
↓
Plugin: afterCreate hook (log, webhook)
↓
Response: { success: true, uuid: "..." }
↓
Claude receives response
Semantic Search
User: "Find frustrated customers from last week"
↓
Claude calls: search_vcons_semantic tool
↓
MCP Server receives request
↓
Plugin: beforeSearch hook (add tenant filter)
↓
Generate embedding for query
├── Call OpenAI API (or local model)
└── Get 384-dimensional vector
↓
Database: Call search_vcons_semantic RPC
├── Vector similarity search (HNSW index)
├── Filter by tags (materialized view)
├── Apply threshold
└── Order by similarity
↓
Reconstruct full vCon objects
├── Join with parties table
├── Join with dialog table
└── Join with analysis table
↓
Plugin: afterSearch hook (filter sensitive)
↓
Response: { count: N, results: [...] }
↓
Claude receives results
Performance Considerations
Database Optimization
Indexes
B-tree on UUIDs, dates
GIN on text fields (trigram)
HNSW on embeddings (vector)
Composite indexes on common queries
Query Patterns
Use prepared statements
Batch operations where possible
Limit result sets
Use cursors for large result sets
Caching
Materialize frequently accessed views
Cache embeddings
Use connection pooling
Memory Management
Streaming large responses
Limit result set sizes
Clean up plugin resources
Close database connections properly
Scalability
Horizontal Scaling:
Stateless server design
Multiple server instances behind load balancer
Read replicas for queries
Vertical Scaling:
Increase database resources
Optimize queries and indexes
Use faster embedding models
Security Architecture
Authentication
Supabase Row Level Security (RLS)
API key validation
User context in plugin hooks
Authorization
RLS policies on all tables
Plugin-based access control
Audit logging via plugins
Data Protection
Encryption at rest (Supabase)
Encryption in transit (TLS)
Redaction via plugins
PII handling compliance
Testing Architecture
Unit Tests
Individual function testing
Mocked dependencies
Zod schema validation
Integration Tests
Full request/response cycle
Real database (test instance)
Plugin integration
Compliance Tests
IETF spec validation
Field name correctness
Required field enforcement
Load Tests
Concurrent requests
Large result sets
Search performance
Deployment Architecture
Development
Local Machine
├── Node.js runtime
├── TypeScript compiler
├── Supabase local (Docker)
└── Environment variables
Production
Cloud Provider (AWS, GCP, Azure)
├── Node.js runtime (18+)
├── Process manager (PM2, systemd)
├── Reverse proxy (nginx)
├── SSL termination
└── Supabase managed instance
Monitoring
Health check endpoint
Performance metrics
Error tracking
Query performance
Extension Points
Adding New Tools
Define tool schema (
src/tools/
)Implement handler in server
Add validation
Write tests
Document in API reference
Adding New Search Modes
Create RPC function in database
Add method to VConQueries
Create MCP tool definition
Implement in tool handler
Add performance tests
Creating Plugins
Implement VConPlugin interface
Register lifecycle hooks
Add custom tools/resources
Package as npm module
Publish and document
Best Practices
Code Organization
One tool per file
Shared types in
types/
Database logic in
db/
Utilities in
utils/
Tests alongside code
Error Handling
Use typed errors
Provide context
Log errors properly
Return user-friendly messages
Performance
Profile before optimizing
Use indexes effectively
Cache when appropriate
Monitor query performance
Documentation
JSDoc for all public APIs
README for each directory
Architecture decisions recorded
Examples for complex features
Future Architecture
Planned Enhancements
Streaming Responses - For large result sets
GraphQL API - Alternative to RPC
Real-time Subscriptions - WebSocket support
Distributed Tracing - OpenTelemetry integration
Multi-tenant - Tenant isolation patterns
Additional Resources
Last updated