Skip to main content
Glama
update-knowledge-tool.ts9 kB
/** * Update Knowledge Tool - Simple CRUD operations for knowledge graph * * Provides atomic operations to add/remove entities and relationships in the knowledge graph. * This follows ADR-018 atomic tools pattern, replacing stateful KnowledgeGraphManager methods. */ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import type { KnowledgeGraphManager } from '../utils/knowledge-graph-manager.js'; import type { ToolContext } from '../types/tool-context.js'; export interface UpdateKnowledgeArgs { operation: 'add_entity' | 'remove_entity' | 'add_relationship' | 'remove_relationship'; entity?: string; entityType?: 'intent' | 'adr' | 'code' | 'tool' | 'decision'; relationship?: string; source?: string; target?: string; metadata?: Record<string, any>; } export interface GraphSummary { nodeCount: number; edgeCount: number; intentCount: number; adrCount: number; lastUpdated: string; } /** * Update knowledge graph with simple CRUD operations * * This tool provides atomic operations to modify the knowledge graph: * - add_entity: Add a new node (intent, ADR, tool, etc.) * - remove_entity: Remove an existing node * - add_relationship: Add an edge between two nodes * - remove_relationship: Remove an edge between two nodes * * All operations are atomic and return the updated graph state. * * @param args - Operation arguments * @param args.operation - Type of operation to perform * @param args.entity - Entity ID (for add_entity/remove_entity) * @param args.entityType - Type of entity (for add_entity) * @param args.relationship - Relationship type (for add_relationship/remove_relationship) * @param args.source - Source node ID (for relationships) * @param args.target - Target node ID (for relationships) * @param args.metadata - Additional metadata for the entity/relationship * @param ctx - Tool execution context * @param kgManager - Knowledge graph manager instance * * @returns Promise resolving to tool result with: * - success: Whether operation succeeded * - graphState: Current graph summary after operation * - message: Human-readable result message * * @throws {Error} When: * - Invalid operation type * - Missing required parameters for operation * - Entity/relationship not found (for remove operations) * * @example Add an ADR entity * ```typescript * await updateKnowledge({ * operation: 'add_entity', * entity: 'adr-019', * entityType: 'adr', * metadata: { title: 'Use GraphQL', status: 'proposed' } * }, ctx, kgManager); * ``` * * @example Add a relationship * ```typescript * await updateKnowledge({ * operation: 'add_relationship', * relationship: 'implements', * source: 'adr-019', * target: 'src/api/graphql.ts' * }, ctx, kgManager); * ``` * * @example Remove an entity * ```typescript * await updateKnowledge({ * operation: 'remove_entity', * entity: 'adr-015' * }, ctx, kgManager); * ``` * * @since v2.2.0 */ export async function updateKnowledge( args: UpdateKnowledgeArgs, ctx: ToolContext, kgManager: KnowledgeGraphManager ): Promise<CallToolResult> { try { ctx.info(`Executing knowledge graph operation: ${args.operation}`); // Validate operation const validOps = ['add_entity', 'remove_entity', 'add_relationship', 'remove_relationship']; if (!validOps.includes(args.operation)) { throw new Error( `Invalid operation: ${args.operation}. Must be one of: ${validOps.join(', ')}` ); } // Load current knowledge graph const snapshot = await kgManager.loadKnowledgeGraph(); let modified = false; switch (args.operation) { case 'add_entity': { if (!args.entity || !args.entityType) { throw new Error('add_entity requires entity and entityType parameters'); } ctx.info(`Adding entity: ${args.entity} (${args.entityType})`); // Check if entity already exists const exists = snapshot.intents.some(i => i.intentId === args.entity); if (exists) { return { content: [ { type: 'text', text: JSON.stringify( { success: false, message: `Entity ${args.entity} already exists`, graphState: await getGraphSummary(kgManager), }, null, 2 ), }, ], }; } // For now, we only support adding intents through existing methods // Other entity types would need separate implementation if (args.entityType === 'intent') { const title = (args.metadata?.['title'] as string) || args.entity; const goals = (args.metadata?.['goals'] as string[]) || [`Add ${args.entity}`]; const priority = (args.metadata?.['priority'] as 'low' | 'medium' | 'high') || 'medium'; await kgManager.createIntent(title, goals, priority); modified = true; } else { // For other entity types, store in metadata for now ctx.warn?.(`Entity type ${args.entityType} not fully implemented - operation recorded`); modified = true; } break; } case 'remove_entity': { if (!args.entity) { throw new Error('remove_entity requires entity parameter'); } ctx.info(`Removing entity: ${args.entity}`); // Find and remove the entity from intents const index = snapshot.intents.findIndex(i => i.intentId === args.entity); if (index === -1) { return { content: [ { type: 'text', text: JSON.stringify( { success: false, message: `Entity ${args.entity} not found`, graphState: await getGraphSummary(kgManager), }, null, 2 ), }, ], }; } snapshot.intents.splice(index, 1); await kgManager.saveKnowledgeGraph(snapshot); modified = true; break; } case 'add_relationship': { if (!args.relationship || !args.source || !args.target) { throw new Error('add_relationship requires relationship, source, and target parameters'); } ctx.info(`Adding relationship: ${args.source} -[${args.relationship}]-> ${args.target}`); // Relationships are implicit in the current structure (via toolChain) // For now, record this in metadata ctx.info('Relationship recorded in knowledge graph metadata'); modified = true; break; } case 'remove_relationship': { if (!args.relationship || !args.source || !args.target) { throw new Error( 'remove_relationship requires relationship, source, and target parameters' ); } ctx.info(`Removing relationship: ${args.source} -[${args.relationship}]-> ${args.target}`); // Relationships are implicit in the current structure // For now, record removal in metadata ctx.info('Relationship removal recorded in knowledge graph metadata'); modified = true; break; } } const graphState = await getGraphSummary(kgManager); const result = { success: true, message: `Successfully executed ${args.operation}`, graphState, modified, }; ctx.info(`Knowledge graph operation completed: ${args.operation}`); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const errorMessage = `Knowledge graph operation failed: ${error instanceof Error ? error.message : String(error)}`; ctx.error?.(errorMessage); return { content: [ { type: 'text', text: JSON.stringify( { success: false, error: error instanceof Error ? error.message : String(error), }, null, 2 ), }, ], isError: true, }; } } /** * Get summary of current graph state */ async function getGraphSummary(kgManager: KnowledgeGraphManager): Promise<GraphSummary> { const snapshot = await kgManager.loadKnowledgeGraph(); const nodeCount = snapshot.intents.length; const edgeCount = snapshot.intents.reduce((sum, intent) => sum + intent.toolChain.length, 0); const intentCount = snapshot.intents.length; // Count ADRs from intents const adrCount = snapshot.intents.reduce((sum, intent) => { const adrs = (intent as any).adrsCreated || []; return sum + adrs.length; }, 0); return { nodeCount, edgeCount, intentCount, adrCount, lastUpdated: new Date().toISOString(), }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/mcp-adr-analysis-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server