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
JavaScript
/**
* 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