@codai/cbd
Version:
Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server
412 lines • 15.6 kB
JavaScript
/*!
* CBD MCP Server
* Model Context Protocol server for CBD (Codai Better Database)
* Provides direct vector database operations and management
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
import { createCBDEngine } from '../index.js';
import { getConfig, validateConfig } from './config.js';
import { healthCheck, quickHealthCheck } from './tools/monitoring/health.js';
import { getServerStats, getBasicStats } from './tools/monitoring/stats.js';
export class CBDMCPServer {
server;
engine;
config;
initialized = false;
constructor(config) {
this.config = { ...getConfig(), ...config };
// Validate configuration
const configErrors = validateConfig(this.config);
if (configErrors.length > 0) {
throw new Error(`Configuration errors: ${configErrors.join(', ')}`);
}
// Initialize MCP server
this.server = new Server({
name: this.config.server.name,
version: this.config.server.version,
}, {
capabilities: {
tools: {},
},
});
// Initialize CBD engine with config
this.engine = createCBDEngine({
storage: {
type: 'cbd-native',
dataPath: this.config.database.path || './cbd-mcp-data'
},
embedding: {
model: 'openai',
apiKey: process.env.OPENAI_API_KEY || undefined,
modelName: 'text-embedding-ada-002',
dimensions: this.config.database.dimension || 1536
},
vector: {
indexType: 'faiss',
dimensions: this.config.database.dimension || 1536,
similarityMetric: 'cosine'
},
cache: {
enabled: true,
maxSize: this.config.performance.cacheSize,
ttl: 3600000 // 1 hour
}
});
this.setupHandlers();
}
/**
* Setup MCP request handlers
*/
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
// Monitoring Tools
{
name: 'health_check',
description: 'Check CBD MCP server health status',
inputSchema: {
type: 'object',
properties: {
detailed: {
type: 'boolean',
description: 'Include detailed health information',
default: false
}
}
}
},
{
name: 'get_server_stats',
description: 'Get CBD server statistics and performance metrics',
inputSchema: {
type: 'object',
properties: {
detailed: {
type: 'boolean',
description: 'Include detailed statistics',
default: false
}
}
}
},
// Vector Operations (Phase 2)
{
name: 'vector_search',
description: 'Search vectors by similarity',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query text'
},
limit: {
type: 'number',
description: 'Maximum number of results',
default: 10
},
threshold: {
type: 'number',
description: 'Similarity threshold (0.0 to 1.0)',
default: 0.0
}
},
required: ['query']
}
},
{
name: 'vector_store',
description: 'Store a vector with metadata',
inputSchema: {
type: 'object',
properties: {
content: {
type: 'string',
description: 'Content to store'
},
metadata: {
type: 'object',
description: 'Additional metadata'
},
project: {
type: 'string',
description: 'Project name',
default: 'mcp'
},
session: {
type: 'string',
description: 'Session name',
default: 'default'
}
},
required: ['content']
}
},
// Memory Operations
{
name: 'search_memory',
description: 'Search stored memories by query',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
},
limit: {
type: 'number',
description: 'Maximum results to return',
default: 10
},
project: {
type: 'string',
description: 'Filter by project name'
},
session: {
type: 'string',
description: 'Filter by session name'
}
},
required: ['query']
}
},
{
name: 'get_memory',
description: 'Retrieve a specific memory by structured key',
inputSchema: {
type: 'object',
properties: {
key: {
type: 'string',
description: 'Structured key (project_date_session_sequence)'
}
},
required: ['key']
}
}
]
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
await this.ensureInitialized();
switch (name) {
case 'health_check':
return await this.handleHealthCheck(Boolean(args?.detailed));
case 'get_server_stats':
return await this.handleGetServerStats(Boolean(args?.detailed));
case 'vector_search':
return await this.handleVectorSearch(args);
case 'vector_store':
return await this.handleVectorStore(args);
case 'search_memory':
return await this.handleSearchMemory(args);
case 'get_memory':
return await this.handleGetMemory(args);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error?.message || 'Unknown error'}`);
}
});
}
/**
* Ensure the CBD engine is initialized
*/
async ensureInitialized() {
if (!this.initialized) {
await this.engine.initialize();
this.initialized = true;
if (this.config.logging.enabled) {
console.log(`🚀 CBD MCP Server initialized (${this.config.server.name} v${this.config.server.version})`);
}
}
}
/**
* Handle health check tool
*/
async handleHealthCheck(detailed = false) {
const health = detailed
? await healthCheck(this.engine, this.config)
: await quickHealthCheck(this.engine);
return {
content: [
{
type: 'text',
text: JSON.stringify(health, null, 2)
}
]
};
}
/**
* Handle get server stats tool
*/
async handleGetServerStats(detailed = false) {
const stats = detailed
? await getServerStats(this.engine, this.config, true)
: await getBasicStats(this.engine, this.config);
return {
content: [
{
type: 'text',
text: JSON.stringify(stats, null, 2)
}
]
};
}
/**
* Handle vector search tool
*/
async handleVectorSearch(args) {
if (!args?.query) {
throw new McpError(ErrorCode.InvalidParams, 'Query is required');
}
const searchResult = await this.engine.search_memory(args.query, args.limit || 10, args.threshold || 0.0);
return {
content: [
{
type: 'text',
text: JSON.stringify({
query: args.query,
limit: args.limit || 10,
summary: searchResult.summary,
results: searchResult.memories.length,
matches: searchResult.memories.map(result => ({
key: result.memory.structuredKey,
score: result.relevanceScore,
content: result.memory.userRequest + ' ' + result.memory.assistantResponse,
metadata: {
projectName: result.memory.projectName,
sessionName: result.memory.sessionName,
agentId: result.memory.agentId,
timestamp: result.memory.createdAt
}
}))
}, null, 2)
}
]
};
}
/**
* Handle vector store tool
*/
async handleVectorStore(args) {
if (!args?.content) {
throw new McpError(ErrorCode.InvalidParams, 'Content is required');
}
const structuredKey = await this.engine.store_memory(args.content, 'MCP Response', // Assistant response
{
projectName: args.project || 'mcp',
sessionName: args.session || 'default',
agentId: 'cbd-mcp-server',
...args.metadata
});
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
key: structuredKey,
project: args.project || 'mcp',
session: args.session || 'default'
}, null, 2)
}
]
};
}
/**
* Handle search memory tool
*/
async handleSearchMemory(args) {
if (!args?.query) {
throw new McpError(ErrorCode.InvalidParams, 'Query is required');
}
const searchResult = await this.engine.search_memory(args.query, args.limit || 10);
return {
content: [
{
type: 'text',
text: JSON.stringify({
query: args.query,
summary: searchResult.summary,
results: searchResult.memories.map(result => ({
key: result.memory.structuredKey,
score: result.relevanceScore,
userRequest: result.memory.userRequest,
assistantResponse: result.memory.assistantResponse,
projectName: result.memory.projectName,
sessionName: result.memory.sessionName,
agentId: result.memory.agentId,
timestamp: result.memory.createdAt
}))
}, null, 2)
}
]
};
}
/**
* Handle get memory tool
*/
async handleGetMemory(args) {
if (!args?.key) {
throw new McpError(ErrorCode.InvalidParams, 'Key is required');
}
const memory = await this.engine.get_memory(args.key);
if (!memory) {
return {
content: [
{
type: 'text',
text: JSON.stringify({ error: 'Memory not found' }, null, 2)
}
]
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
key: args.key,
structuredKey: memory.structuredKey,
userRequest: memory.userRequest,
assistantResponse: memory.assistantResponse,
projectName: memory.projectName,
sessionName: memory.sessionName,
agentId: memory.agentId,
timestamp: memory.createdAt,
sequenceNumber: memory.sequenceNumber,
confidenceScore: memory.confidenceScore
}, null, 2)
}
]
};
}
/**
* Start the MCP server
*/
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
if (this.config.logging.enabled) {
console.error('🤖 CBD MCP Server running on stdio');
}
}
/**
* Stop the MCP server
*/
async stop() {
if (this.initialized) {
await this.engine.shutdown();
}
}
}
export * from './types.js';
//# sourceMappingURL=server.js.map