652 lines
28 KiB
JavaScript
652 lines
28 KiB
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
|
|
*/
|
|
import { validateIdentifier, validateText } from './validate-input.js';
|
|
// ===== 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: 'AgentDB bridge not available — @claude-flow/memory not installed or missing controller-registry. Use memory_store/memory_search tools instead.' };
|
|
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 vPattern = validateText(params.pattern, 'pattern', 100_000);
|
|
if (!vPattern.valid)
|
|
return { success: false, error: vPattern.error };
|
|
if (params.type) {
|
|
const vType = validateIdentifier(params.type, 'type');
|
|
if (!vType.valid)
|
|
return { success: false, error: vType.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vQuery = validateText(params.query, 'query', 10_000);
|
|
if (!vQuery.valid)
|
|
return { results: [], error: vQuery.error };
|
|
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 vTaskId = validateIdentifier(params.taskId, 'taskId');
|
|
if (!vTaskId.valid)
|
|
return { success: false, error: vTaskId.error };
|
|
if (params.agent) {
|
|
const vAgent = validateIdentifier(params.agent, 'agent');
|
|
if (!vAgent.valid)
|
|
return { success: false, error: vAgent.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vSourceId = validateIdentifier(params.sourceId, 'sourceId');
|
|
if (!vSourceId.valid)
|
|
return { success: false, error: vSourceId.error };
|
|
const vTargetId = validateIdentifier(params.targetId, 'targetId');
|
|
if (!vTargetId.valid)
|
|
return { success: false, error: vTargetId.error };
|
|
const vRelation = validateIdentifier(params.relation, 'relation');
|
|
if (!vRelation.valid)
|
|
return { success: false, error: vRelation.error };
|
|
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)' };
|
|
// Try native graph-node backend first (ADR-087)
|
|
try {
|
|
const graphBackend = await import('../ruvector/graph-backend.js');
|
|
if (await graphBackend.isGraphBackendAvailable()) {
|
|
const graphResult = await graphBackend.recordCausalEdge(sourceId, targetId, relation, typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined);
|
|
if (graphResult.success) {
|
|
// Also record in AgentDB bridge for compatibility
|
|
const bridge = await getBridge();
|
|
await bridge.bridgeRecordCausalEdge({ sourceId, targetId, relation, weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined }).catch(() => { });
|
|
return { ...graphResult, _graphNodeBackend: true };
|
|
}
|
|
}
|
|
}
|
|
catch { /* graph-node not available, fall through */ }
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vTask = validateText(params.task, 'task', 10_000);
|
|
if (!vTask.valid)
|
|
return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: vTask.error };
|
|
if (params.context) {
|
|
const vCtx = validateText(params.context, 'context', 10_000);
|
|
if (!vCtx.valid)
|
|
return { route: 'general', confidence: 0.5, agents: ['coder'], controller: 'error', error: vCtx.error };
|
|
}
|
|
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 vSessionId = validateIdentifier(params.sessionId, 'sessionId');
|
|
if (!vSessionId.valid)
|
|
return { success: false, error: vSessionId.error };
|
|
if (params.context) {
|
|
const vCtx = validateText(params.context, 'context', 10_000);
|
|
if (!vCtx.valid)
|
|
return { success: false, error: vCtx.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vSessionId = validateIdentifier(params.sessionId, 'sessionId');
|
|
if (!vSessionId.valid)
|
|
return { success: false, error: vSessionId.error };
|
|
if (params.summary) {
|
|
const vSummary = validateText(params.summary, 'summary', 50_000);
|
|
if (!vSummary.valid)
|
|
return { success: false, error: vSummary.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vKey = validateIdentifier(params.key, 'key');
|
|
if (!vKey.valid)
|
|
return { success: false, error: vKey.error };
|
|
const vValue = validateText(params.value, 'value');
|
|
if (!vValue.valid)
|
|
return { success: false, error: vValue.error };
|
|
if (params.tier) {
|
|
const vTier = validateIdentifier(params.tier, 'tier');
|
|
if (!vTier.valid)
|
|
return { success: false, error: vTier.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vQuery = validateText(params.query, 'query', 10_000);
|
|
if (!vQuery.valid)
|
|
return { results: [], error: vQuery.error };
|
|
if (params.tier) {
|
|
const vTier = validateIdentifier(params.tier, 'tier');
|
|
if (!vTier.valid)
|
|
return { results: [], error: vTier.error };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_search instead.' };
|
|
}
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vOp = validateIdentifier(params.operation, 'operation');
|
|
if (!vOp.valid)
|
|
return { success: false, error: vOp.error };
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vQuery = validateText(params.query, 'query', 10_000);
|
|
if (!vQuery.valid)
|
|
return { success: false, error: vQuery.error };
|
|
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: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
}
|
|
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 vInput = validateText(params.input, 'input', 10_000);
|
|
if (!vInput.valid)
|
|
return { route: null, error: vInput.error };
|
|
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: 'AgentDB bridge not available. Use hooks route instead.' };
|
|
}
|
|
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
|