UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

557 lines 23.1 kB
/** * AgentDB MCP Tools — Phase 6 of ADR-053 * * Exposes AgentDB v3 controller operations as MCP tools. * Provides direct access to ReasoningBank, CausalGraph, SkillLibrary, * AttestationLog, and bridge health through the MCP protocol. * * Security: All handlers validate input types, enforce length bounds, * and sanitize error messages before returning to MCP callers. * * @module v3/cli/mcp-tools/agentdb-tools */ // ===== Shared validation helpers ===== const MAX_STRING_LENGTH = 100_000; // 100KB max for any string input const MAX_BATCH_SIZE = 500; // Max entries per batch operation const MAX_TOP_K = 100; // Max results per query function validateString(value, name, maxLen = MAX_STRING_LENGTH) { if (typeof value !== 'string' || value.length === 0) return null; if (value.length > maxLen) return null; return value; } function validatePositiveInt(value, defaultVal, max) { if (typeof value !== 'number' || !Number.isFinite(value)) return defaultVal; const n = Math.floor(value); return n > 0 ? Math.min(n, max) : defaultVal; } function validateScore(value, defaultVal) { if (typeof value !== 'number' || !Number.isFinite(value)) return defaultVal; return Math.max(0, Math.min(1, value)); } function sanitizeError(error) { if (error instanceof Error) { // Strip filesystem paths from error messages return error.message.replace(/\/[^\s:]+\//g, '<path>/').substring(0, 500); } return 'Internal error'; } // Lazy-cached bridge module let bridgeModule = null; async function getBridge() { if (!bridgeModule) { bridgeModule = await import('../memory/memory-bridge.js'); } return bridgeModule; } // ===== agentdb_health — Controller health check ===== export const agentdbHealth = { name: 'agentdb_health', description: 'Get AgentDB v3 controller health status including cache stats and attestation count', inputSchema: { type: 'object', properties: {}, }, handler: async () => { try { const bridge = await getBridge(); const health = await bridge.bridgeHealthCheck(); if (!health) return { available: false, error: 'AgentDB bridge not available' }; return health; } catch (error) { return { available: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_controllers — List all controllers ===== export const agentdbControllers = { name: 'agentdb_controllers', description: 'List all AgentDB v3 controllers and their initialization status', inputSchema: { type: 'object', properties: {}, }, handler: async () => { try { const bridge = await getBridge(); const controllers = await bridge.bridgeListControllers(); if (!controllers) return { available: false, controllers: [], error: 'Bridge not available' }; return { available: true, controllers, total: controllers.length, active: controllers.filter((c) => c.enabled).length, }; } catch (error) { return { available: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_pattern_store — Store via ReasoningBank ===== export const agentdbPatternStore = { name: 'agentdb_pattern-store', description: 'Store a pattern directly via ReasoningBank controller', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Pattern description' }, type: { type: 'string', description: 'Pattern type (e.g., task-routing, error-recovery)' }, confidence: { type: 'number', description: 'Confidence score (0-1)' }, }, required: ['pattern'], }, handler: async (params) => { try { const pattern = validateString(params.pattern, 'pattern'); if (!pattern) return { success: false, error: 'pattern is required (non-empty string, max 100KB)' }; const bridge = await getBridge(); const result = await bridge.bridgeStorePattern({ pattern, type: validateString(params.type, 'type', 200) ?? 'general', confidence: validateScore(params.confidence, 0.8), }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_pattern_search — Search via ReasoningBank ===== export const agentdbPatternSearch = { name: 'agentdb_pattern-search', description: 'Search patterns via ReasoningBank controller with BM25+semantic hybrid', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, topK: { type: 'number', description: 'Number of results (default: 5)' }, minConfidence: { type: 'number', description: 'Minimum score threshold (0-1)' }, }, required: ['query'], }, handler: async (params) => { try { const query = validateString(params.query, 'query', 10_000); if (!query) return { results: [], error: 'query is required (non-empty string, max 10KB)' }; const bridge = await getBridge(); const result = await bridge.bridgeSearchPatterns({ query, topK: validatePositiveInt(params.topK, 5, MAX_TOP_K), minConfidence: validateScore(params.minConfidence, 0.3), }); return result ?? { results: [], controller: 'unavailable' }; } catch (error) { return { results: [], error: sanitizeError(error) }; } }, }; // ===== agentdb_feedback — Record task feedback ===== export const agentdbFeedback = { name: 'agentdb_feedback', description: 'Record task feedback for learning via LearningSystem + ReasoningBank controllers', inputSchema: { type: 'object', properties: { taskId: { type: 'string', description: 'Task identifier' }, success: { type: 'boolean', description: 'Whether task succeeded' }, quality: { type: 'number', description: 'Quality score (0-1)' }, agent: { type: 'string', description: 'Agent that performed the task' }, }, required: ['taskId'], }, handler: async (params) => { try { const taskId = validateString(params.taskId, 'taskId', 500); if (!taskId) return { success: false, error: 'taskId is required (non-empty string, max 500 chars)' }; const bridge = await getBridge(); const result = await bridge.bridgeRecordFeedback({ taskId, success: params.success === true, quality: validateScore(params.quality, 0.85), agent: validateString(params.agent, 'agent', 200) ?? undefined, }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_causal_edge — Record causal relationships ===== export const agentdbCausalEdge = { name: 'agentdb_causal-edge', description: 'Record a causal edge between two memory entries via CausalMemoryGraph', inputSchema: { type: 'object', properties: { sourceId: { type: 'string', description: 'Source entry ID' }, targetId: { type: 'string', description: 'Target entry ID' }, relation: { type: 'string', description: 'Relationship type (e.g., caused, preceded, succeeded)' }, weight: { type: 'number', description: 'Edge weight (0-1)' }, }, required: ['sourceId', 'targetId', 'relation'], }, handler: async (params) => { try { const sourceId = validateString(params.sourceId, 'sourceId', 500); const targetId = validateString(params.targetId, 'targetId', 500); const relation = validateString(params.relation, 'relation', 200); if (!sourceId) return { success: false, error: 'sourceId is required (non-empty string)' }; if (!targetId) return { success: false, error: 'targetId is required (non-empty string)' }; if (!relation) return { success: false, error: 'relation is required (non-empty string)' }; const bridge = await getBridge(); const result = await bridge.bridgeRecordCausalEdge({ sourceId, targetId, relation, weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined, }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_route — Route via SemanticRouter ===== export const agentdbRoute = { name: 'agentdb_route', description: 'Route a task via AgentDB SemanticRouter or LearningSystem recommendAlgorithm', inputSchema: { type: 'object', properties: { task: { type: 'string', description: 'Task description to route' }, context: { type: 'string', description: 'Additional context' }, }, required: ['task'], }, handler: async (params) => { try { const task = validateString(params.task, 'task', 10_000); if (!task) return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: 'task is required (non-empty string)' }; const bridge = await getBridge(); const result = await bridge.bridgeRouteTask({ task, context: validateString(params.context, 'context', 10_000) ?? undefined, }); return result ?? { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'fallback' }; } catch (error) { return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: sanitizeError(error) }; } }, }; // ===== agentdb_session_start — Session with ReflexionMemory ===== export const agentdbSessionStart = { name: 'agentdb_session-start', description: 'Start a session with ReflexionMemory episodic replay', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, context: { type: 'string', description: 'Session context for pattern retrieval' }, }, required: ['sessionId'], }, handler: async (params) => { try { const sessionId = validateString(params.sessionId, 'sessionId', 500); if (!sessionId) return { success: false, error: 'sessionId is required (non-empty string)' }; const bridge = await getBridge(); const result = await bridge.bridgeSessionStart({ sessionId, context: validateString(params.context, 'context', 10_000) ?? undefined, }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_session_end — End session + NightlyLearner ===== export const agentdbSessionEnd = { name: 'agentdb_session-end', description: 'End session, persist to ReflexionMemory, trigger NightlyLearner consolidation', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Session identifier' }, summary: { type: 'string', description: 'Session summary' }, tasksCompleted: { type: 'number', description: 'Number of tasks completed' }, }, required: ['sessionId'], }, handler: async (params) => { try { const sessionId = validateString(params.sessionId, 'sessionId', 500); if (!sessionId) return { success: false, error: 'sessionId is required (non-empty string)' }; const bridge = await getBridge(); const result = await bridge.bridgeSessionEnd({ sessionId, summary: validateString(params.summary, 'summary', 50_000) ?? undefined, tasksCompleted: validatePositiveInt(params.tasksCompleted, 0, 10_000), }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_hierarchical_store — Store to hierarchical memory ===== export const agentdbHierarchicalStore = { name: 'agentdb_hierarchical-store', description: 'Store to hierarchical memory with tier (working, episodic, semantic)', inputSchema: { type: 'object', properties: { key: { type: 'string', description: 'Memory entry key' }, value: { type: 'string', description: 'Memory entry value' }, tier: { type: 'string', description: 'Memory tier (working, episodic, semantic)', enum: ['working', 'episodic', 'semantic'], default: 'working', }, }, required: ['key', 'value'], }, handler: async (params) => { try { const key = validateString(params.key, 'key', 1000); const value = validateString(params.value, 'value'); if (!key) return { success: false, error: 'key is required (non-empty string, max 1KB)' }; if (!value) return { success: false, error: 'value is required (non-empty string, max 100KB)' }; const tier = validateString(params.tier, 'tier', 20) ?? 'working'; if (!['working', 'episodic', 'semantic'].includes(tier)) { return { success: false, error: `Invalid tier: ${tier}. Must be working, episodic, or semantic` }; } const bridge = await getBridge(); const result = await bridge.bridgeHierarchicalStore({ key, value, tier }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_hierarchical_recall — Recall from hierarchical memory ===== export const agentdbHierarchicalRecall = { name: 'agentdb_hierarchical-recall', description: 'Recall from hierarchical memory with optional tier filter', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Recall query' }, tier: { type: 'string', description: 'Filter by tier (working, episodic, semantic)' }, topK: { type: 'number', description: 'Number of results (default: 5)' }, }, required: ['query'], }, handler: async (params) => { try { const query = validateString(params.query, 'query', 10_000); if (!query) return { results: [], error: 'query is required (non-empty string, max 10KB)' }; const tier = validateString(params.tier, 'tier', 20); if (tier && !['working', 'episodic', 'semantic'].includes(tier)) { return { results: [], error: `Invalid tier: ${tier}. Must be working, episodic, or semantic` }; } const bridge = await getBridge(); const result = await bridge.bridgeHierarchicalRecall({ query, tier: tier ?? undefined, topK: validatePositiveInt(params.topK, 5, MAX_TOP_K), }); return result ?? { results: [], error: 'Bridge not available' }; } catch (error) { return { results: [], error: sanitizeError(error) }; } }, }; // ===== agentdb_consolidate — Run memory consolidation ===== export const agentdbConsolidate = { name: 'agentdb_consolidate', description: 'Run memory consolidation to promote entries across tiers and compress old data', inputSchema: { type: 'object', properties: { minAge: { type: 'number', description: 'Minimum age in hours since store (optional)' }, maxEntries: { type: 'number', description: 'Maximum entries to consolidate (optional)' }, }, }, handler: async (params) => { try { const bridge = await getBridge(); const result = await bridge.bridgeConsolidate({ minAge: typeof params.minAge === 'number' ? Math.max(0, params.minAge) : undefined, maxEntries: validatePositiveInt(params.maxEntries, 1000, 10_000), }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_batch — Batch operations (insert, update, delete) ===== export const agentdbBatch = { name: 'agentdb_batch', description: 'Batch operations on memory entries (insert, update, delete)', inputSchema: { type: 'object', properties: { operation: { type: 'string', description: 'Batch operation type', enum: ['insert', 'update', 'delete'], }, entries: { type: 'array', description: 'Array of {key, value} entries to operate on', items: { type: 'object', properties: { key: { type: 'string' }, value: { type: 'string' }, }, required: ['key'], }, }, }, required: ['operation', 'entries'], }, handler: async (params) => { try { const operation = validateString(params.operation, 'operation', 20); if (!operation) return { success: false, error: 'operation is required (string)' }; if (!['insert', 'update', 'delete'].includes(operation)) { return { success: false, error: `Invalid operation: ${operation}. Must be insert, update, or delete` }; } if (!Array.isArray(params.entries) || params.entries.length === 0) { return { success: false, error: 'entries is required (non-empty array)' }; } if (params.entries.length > MAX_BATCH_SIZE) { return { success: false, error: `Too many entries: ${params.entries.length}. Max is ${MAX_BATCH_SIZE}` }; } // Validate each entry const validatedEntries = []; for (let i = 0; i < params.entries.length; i++) { const entry = params.entries[i]; if (!entry || typeof entry !== 'object') { return { success: false, error: `entries[${i}] must be an object` }; } const key = validateString(entry.key, `entries[${i}].key`, 1000); if (!key) return { success: false, error: `entries[${i}].key is required (non-empty string)` }; const value = validateString(entry.value, `entries[${i}].value`); validatedEntries.push({ key, value: value ?? undefined }); } const bridge = await getBridge(); const result = await bridge.bridgeBatchOperation({ operation, entries: validatedEntries, }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_context_synthesize — Synthesize context from memories ===== export const agentdbContextSynthesize = { name: 'agentdb_context-synthesize', description: 'Synthesize context from stored memories for a given query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Query to synthesize context for' }, maxEntries: { type: 'number', description: 'Maximum entries to include (default: 10)' }, }, required: ['query'], }, handler: async (params) => { try { const query = validateString(params.query, 'query', 10_000); if (!query) return { success: false, error: 'query is required (non-empty string, max 10KB)' }; const bridge = await getBridge(); const result = await bridge.bridgeContextSynthesize({ query, maxEntries: validatePositiveInt(params.maxEntries, 10, MAX_TOP_K), }); return result ?? { success: false, error: 'Bridge not available' }; } catch (error) { return { success: false, error: sanitizeError(error) }; } }, }; // ===== agentdb_semantic_route — Route via SemanticRouter ===== export const agentdbSemanticRoute = { name: 'agentdb_semantic-route', description: 'Route an input via AgentDB SemanticRouter for intent classification', inputSchema: { type: 'object', properties: { input: { type: 'string', description: 'Input text to route' }, }, required: ['input'], }, handler: async (params) => { try { const input = validateString(params.input, 'input', 10_000); if (!input) return { route: null, error: 'input is required (non-empty string, max 10KB)' }; const bridge = await getBridge(); const result = await bridge.bridgeSemanticRoute({ input }); return result ?? { route: null, error: 'Bridge not available' }; } catch (error) { return { route: null, error: sanitizeError(error) }; } }, }; // ===== Export all tools ===== export const agentdbTools = [ agentdbHealth, agentdbControllers, agentdbPatternStore, agentdbPatternSearch, agentdbFeedback, agentdbCausalEdge, agentdbRoute, agentdbSessionStart, agentdbSessionEnd, agentdbHierarchicalStore, agentdbHierarchicalRecall, agentdbConsolidate, agentdbBatch, agentdbContextSynthesize, agentdbSemanticRoute, ]; //# sourceMappingURL=agentdb-tools.js.map