UNPKG

@bdmarvin/mcp-server-memory

Version:

MCP Server for LLM Long-Term Memory using KG and Google Drive

177 lines 12.7 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { z } from 'zod'; import { UpdateKgNodeInputSchema, AddKgRelationshipInputSchema, LogDecisionInputSchema, GetKgNodeDetailsInputSchema, GetProjectSummaryFromKgInputSchema, SearchKgInputSchema, StoreDocumentInProjectDriveInputSchema, FindRelevantDocumentsInKgInputSchema, GetDocumentContentFromDriveInputSchema, GetDocumentSummaryFromDriveInputSchema, RetrieveKgInputSchema, TraverseKgInputSchema, QueryKgByAttributesInputSchema, // New Deletion Schemas DeleteKgNodeInputSchema, DeleteKgRelationshipInputSchema } from './toolSchemas.js'; import * as kgService from './services/kgService.js'; import * as driveService from './services/driveService.js'; import { TARGET_BASE_FOLDER_NAME_EXPORTED } from './services/driveService.js'; const packageVersion = process.env.npm_package_version || '0.2.1'; // Updated to 0.2.1 const server = new Server({ name: 'llm-memory-mcp-server', version: packageVersion }, { capabilities: { tools: {} } }); server.setRequestHandler(ListToolsRequestSchema, async () => { console.error('DEBUG: ListToolsRequestSchema request received by mcp-server-memory'); return { tools: [ // KG Modification Tools { name: 'tool_update_kg_node', description: UpdateKgNodeInputSchema.description || 'Creates or updates a node in the Knowledge Graph (stored in Drive).', inputSchema: zodToJsonSchema(UpdateKgNodeInputSchema), }, { name: 'tool_add_kg_relationship', description: AddKgRelationshipInputSchema.description || 'Creates a relationship between two nodes in the Knowledge Graph (stored in Drive).', inputSchema: zodToJsonSchema(AddKgRelationshipInputSchema), }, { name: 'tool_log_decision', description: LogDecisionInputSchema.description || 'Logs a project decision in the Knowledge Graph (stored in Drive).', inputSchema: zodToJsonSchema(LogDecisionInputSchema), }, { name: 'tool_delete_kg_node', description: DeleteKgNodeInputSchema.description || 'Deletes a specific KG node and its connected relationships.', inputSchema: zodToJsonSchema(DeleteKgNodeInputSchema), }, { name: 'tool_delete_kg_relationship', description: DeleteKgRelationshipInputSchema.description || 'Deletes a specific KG relationship by its ID.', inputSchema: zodToJsonSchema(DeleteKgRelationshipInputSchema), }, // KG Retrieval Tools { name: 'tool_get_kg_node_details', description: GetKgNodeDetailsInputSchema.description || 'Retrieves details of a specific KG node (from Drive).', inputSchema: zodToJsonSchema(GetKgNodeDetailsInputSchema), }, { name: 'tool_get_project_summary_from_kg', description: GetProjectSummaryFromKgInputSchema.description || 'Retrieves a summary for a project from the KG (from Drive).', inputSchema: zodToJsonSchema(GetProjectSummaryFromKgInputSchema), }, { name: 'tool_search_kg', description: SearchKgInputSchema.description || 'Performs a flexible search of the KG (from Drive) based on natural language query.', inputSchema: zodToJsonSchema(SearchKgInputSchema), }, { name: 'tool_retrieve_kg', description: RetrieveKgInputSchema.description || 'Retrieves the entire Knowledge Graph for a project.', inputSchema: zodToJsonSchema(RetrieveKgInputSchema), }, { name: 'tool_traverse_kg', description: TraverseKgInputSchema.description || 'Traverses the KG from a start node along specified relationships.', inputSchema: zodToJsonSchema(TraverseKgInputSchema), }, { name: 'tool_query_kg_by_attributes', description: QueryKgByAttributesInputSchema.description || 'Finds KG nodes based on a structured query of their attributes.', inputSchema: zodToJsonSchema(QueryKgByAttributesInputSchema), }, // Drive Tools { name: 'tool_store_document_in_project_drive', description: StoreDocumentInProjectDriveInputSchema.description || 'Stores a document in Google Drive and links it in the KG.', inputSchema: zodToJsonSchema(StoreDocumentInProjectDriveInputSchema), }, { name: 'tool_find_relevant_documents_in_kg', description: FindRelevantDocumentsInKgInputSchema.description || 'Queries the KG (from Drive) for document references matching criteria.', inputSchema: zodToJsonSchema(FindRelevantDocumentsInKgInputSchema), }, { name: 'tool_get_document_content_from_drive', description: GetDocumentContentFromDriveInputSchema.description || 'Fetches document content from Google Drive.', inputSchema: zodToJsonSchema(GetDocumentContentFromDriveInputSchema), }, { name: 'tool_get_document_summary_from_drive', description: GetDocumentSummaryFromDriveInputSchema.description || 'Generates and retrieves a summary of a document from Drive.', inputSchema: zodToJsonSchema(GetDocumentSummaryFromDriveInputSchema), }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const toolName = request.params.name; const allArgsFromController = request.params.arguments; const googleAccessToken = allArgsFromController.__google_access_token__; const googleUserEmail = allArgsFromController.__google_user_email__; const originalToolArgs = {}; for (const key in allArgsFromController) { if (key !== '__google_access_token__' && key !== '__google_user_id__' && key !== '__google_user_email__') { originalToolArgs[key] = allArgsFromController[key]; } } console.error(`DEBUG: CallToolRequest - Tool: ${toolName}, User: ${googleUserEmail || 'N/A'}, Args: ${JSON.stringify(originalToolArgs)}`); if (!googleAccessToken) { const errorMsg = `Tool '${toolName}' requires Google Drive access for KG & document operations, but __google_access_token__ was not provided.`; console.error(`ERROR: ${errorMsg} (Tool: ${toolName})`); throw new Error(errorMsg); } try { let responseData; switch (toolName) { // KG Modification Tools case 'tool_update_kg_node': { const parsedArgs = UpdateKgNodeInputSchema.parse(originalToolArgs); responseData = await kgService.updateKgNode(googleAccessToken, parsedArgs); break; } case 'tool_add_kg_relationship': { const parsedArgs = AddKgRelationshipInputSchema.parse(originalToolArgs); responseData = await kgService.addKgRelationship(googleAccessToken, parsedArgs); break; } case 'tool_log_decision': { const parsedArgs = LogDecisionInputSchema.parse(originalToolArgs); responseData = await kgService.logDecision(googleAccessToken, parsedArgs); break; } case 'tool_delete_kg_node': { const parsedArgs = DeleteKgNodeInputSchema.parse(originalToolArgs); responseData = await kgService.deleteKgNode(googleAccessToken, parsedArgs); break; } case 'tool_delete_kg_relationship': { const parsedArgs = DeleteKgRelationshipInputSchema.parse(originalToolArgs); responseData = await kgService.deleteKgRelationship(googleAccessToken, parsedArgs); break; } // KG Retrieval Tools case 'tool_get_kg_node_details': { const parsedArgs = GetKgNodeDetailsInputSchema.parse(originalToolArgs); responseData = await kgService.getKgNodeDetails(googleAccessToken, parsedArgs); break; } case 'tool_get_project_summary_from_kg': { const parsedArgs = GetProjectSummaryFromKgInputSchema.parse(originalToolArgs); responseData = await kgService.getProjectSummary(googleAccessToken, parsedArgs); break; } case 'tool_search_kg': { const parsedArgs = SearchKgInputSchema.parse(originalToolArgs); responseData = await kgService.searchKg(googleAccessToken, parsedArgs); break; } case 'tool_retrieve_kg': { const parsedArgs = RetrieveKgInputSchema.parse(originalToolArgs); responseData = await kgService.retrieveKg(googleAccessToken, parsedArgs); break; } case 'tool_traverse_kg': { const parsedArgs = TraverseKgInputSchema.parse(originalToolArgs); responseData = await kgService.traverseKg(googleAccessToken, parsedArgs); break; } case 'tool_query_kg_by_attributes': { const parsedArgs = QueryKgByAttributesInputSchema.parse(originalToolArgs); responseData = await kgService.queryKgByAttributes(googleAccessToken, parsedArgs); break; } // Drive Tools case 'tool_store_document_in_project_drive': { const parsedArgs = StoreDocumentInProjectDriveInputSchema.parse(originalToolArgs); responseData = await driveService.storeDocument(googleAccessToken, parsedArgs); break; } case 'tool_find_relevant_documents_in_kg': { const parsedArgs = FindRelevantDocumentsInKgInputSchema.parse(originalToolArgs); responseData = await kgService.searchKg(googleAccessToken, { project_id: parsedArgs.project_id, query_description: parsedArgs.keywords.join(' '), entity_types: ['document_reference'], max_results: parsedArgs.max_results, max_depth: 2, }); break; } case 'tool_get_document_content_from_drive': { const parsedArgs = GetDocumentContentFromDriveInputSchema.parse(originalToolArgs); responseData = await driveService.getDocumentContent(googleAccessToken, parsedArgs); break; } case 'tool_get_document_summary_from_drive': { const parsedArgs = GetDocumentSummaryFromDriveInputSchema.parse(originalToolArgs); responseData = await driveService.getDocumentSummary(googleAccessToken, parsedArgs); break; } default: console.warn(`WARN: Unknown tool requested: ${toolName}`); throw new Error(`Unknown tool: ${toolName}`); } console.error(`DEBUG: Successfully processed tool '${toolName}'. Response preview: ${JSON.stringify(responseData)?.substring(0, 200)}`); return { content: [{ type: 'text', text: JSON.stringify(responseData ?? null, null, 2) }] }; } catch (error) { console.error(`ERROR: Error in CallToolRequest for '${toolName}': ${error.message}. Stack: ${error.stack}. Args: ${JSON.stringify(originalToolArgs)}`); if (error.response?.data) { console.error(`ERROR: Underlying API Error Details for '${toolName}': ${JSON.stringify(error.response.data)}`); } if (error instanceof z.ZodError) { const messages = error.errors.map((e) => `${e.path.join('.') || 'argument'}: ${e.message}`); console.warn(`WARN: Invalid arguments for tool '${toolName}': ${JSON.stringify(error.errors)}`); throw new Error(`Invalid arguments for tool '${toolName}': ${messages.join('; ')}`); } const errorMessage = error.message || `An unexpected error occurred in tool '${toolName}'`; throw new Error(errorMessage); } }); async function runServer() { const transport = new StdioServerTransport(); await server.connect(transport); console.error(`INFO: LLM Memory MCP Server (v${packageVersion}) running on stdio.`); console.error("INFO: All tools now require __google_access_token__ for KG (on Drive) and Drive document persistence."); const baseFolderName = process.env.MCP_MEMORY_DRIVE_BASE_FOLDER_NAME || TARGET_BASE_FOLDER_NAME_EXPORTED; console.error(`INFO: KG JSON files ('knowledge_graph.json') will be stored in project-specific folders within the Drive folder: '${baseFolderName}'.`); } runServer().catch((error) => { console.error(`FATAL: Fatal error running LLM Memory MCP Server: ${error.message}. Stack: ${error.stack}`); process.exit(1); }); //# sourceMappingURL=index.js.map