MCP Memory Server

  • src
#!/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 { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; import path from "path"; import os from "os"; import { CreateMemorySchema, UpdateMemorySchema, SearchMemoriesSchema, ListMemoriesSchema, TagMemorySchema, RelateMemoriesSchema, BuildMemoryStoreSchema, MemoryTypeEnum, MemoryType } from "./types.js"; import { MemoryService } from "./memory.js"; // Get memory directory from environment variable or use default const memoryDir = process.env.MEMORY_DIR || path.join(os.homedir(), '.mcp-memory'); console.error(`Using memory directory: ${memoryDir}`); // Initialize memory service const memoryService = new MemoryService(memoryDir); // Initialize MCP server const server = new Server({ name: "mcp-memory", version: "0.1.0" }); // Register tools capability server.registerCapabilities({ tools: { listChanged: true } }); // Set up tool list handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "build_memory_store", description: "Build a new memory store in the specified directory", inputSchema: zodToJsonSchema(BuildMemoryStoreSchema), }, { name: "create_memory", description: "Create a new memory", inputSchema: zodToJsonSchema(CreateMemorySchema), }, { name: "update_memory", description: "Update an existing memory", inputSchema: zodToJsonSchema(UpdateMemorySchema), }, { name: "delete_memory", description: "Delete a memory", inputSchema: zodToJsonSchema(z.object({ id: z.string() })), }, { name: "get_memory", description: "Get a memory by ID and type", inputSchema: zodToJsonSchema(z.object({ id: z.string(), type: MemoryTypeEnum })), }, { name: "search_memories", description: "Search memories by query, type, and tags", inputSchema: zodToJsonSchema(SearchMemoriesSchema), }, { name: "list_memories", description: "List memories with optional filtering by type and tags", inputSchema: zodToJsonSchema(ListMemoriesSchema), }, { name: "add_tags", description: "Add tags to a memory", inputSchema: zodToJsonSchema(TagMemorySchema), }, { name: "remove_tags", description: "Remove tags from a memory", inputSchema: zodToJsonSchema(TagMemorySchema), }, { name: "relate_memories", description: "Create relationships between memories", inputSchema: zodToJsonSchema(RelateMemoriesSchema), }, { name: "unrelate_memories", description: "Remove relationships between memories", inputSchema: zodToJsonSchema(RelateMemoriesSchema), }, { name: "rebuild_index", description: "Rebuild the search index", inputSchema: zodToJsonSchema(z.object({})), }, ], }; }); // Set up tool call handler server.setRequestHandler(CallToolRequestSchema, async (request) => { await memoryService.initialize(); const { name: toolName, arguments: toolArgs } = request.params; // Ensure toolArgs is not undefined if (!toolArgs) { return { content: [ { type: "text", text: `Error: No arguments provided for tool ${toolName}` } ], is_error: true }; } try { switch (toolName) { case "build_memory_store": { const args = toolArgs as { directory: string; overwrite?: boolean }; await memoryService.buildMemoryStore({ directory: args.directory, overwrite: args.overwrite }); return { content: [ { type: "text", text: `Memory store successfully built in directory: ${args.directory}` } ] }; } case "create_memory": { const args = toolArgs as { title: string; type: MemoryType; tags?: string[]; related?: string[]; importance?: number; content: string; }; const memory = await memoryService.createMemory({ title: args.title, type: args.type, tags: args.tags, related: args.related, importance: args.importance, content: args.content }); return { content: [ { type: "text", text: `Memory created with ID: ${memory.id}` } ] }; } case "update_memory": { const args = toolArgs as { id: string; title?: string; tags?: string[]; related?: string[]; importance?: number; content?: string; }; const memory = await memoryService.updateMemory({ id: args.id, title: args.title, tags: args.tags, related: args.related, importance: args.importance, content: args.content }); return { content: [ { type: "text", text: `Memory ${memory.id} updated successfully` } ] }; } case "delete_memory": { const args = toolArgs as { id: string }; await memoryService.deleteMemory(args.id); return { content: [ { type: "text", text: `Memory ${args.id} deleted successfully` } ] }; } case "get_memory": { const args = toolArgs as { id: string; type: MemoryType }; const memory = await memoryService.getMemory(args.id, args.type); if (!memory) { return { content: [ { type: "text", text: `Memory ${args.id} not found` } ], is_error: true }; } return { content: [ { type: "json", json: memory } ] }; } case "search_memories": { const args = toolArgs as { query: string; types?: MemoryType[]; tags?: string[]; limit?: number; }; const results = await memoryService.searchMemories( args.query, { types: args.types, tags: args.tags, limit: args.limit } ); return { content: [ { type: "json", json: results } ] }; } case "list_memories": { const args = toolArgs as { types?: MemoryType[]; tags?: string[]; limit?: number; }; const results = await memoryService.listMemories({ types: args.types, tags: args.tags, limit: args.limit }); return { content: [ { type: "json", json: results } ] }; } case "add_tags": { const args = toolArgs as { id: string; tags: string[] }; const memory = await memoryService.addTags(args.id, args.tags); return { content: [ { type: "text", text: `Tags added to memory ${memory.id}` } ] }; } case "remove_tags": { const args = toolArgs as { id: string; tags: string[] }; const memory = await memoryService.removeTags(args.id, args.tags); return { content: [ { type: "text", text: `Tags removed from memory ${memory.id}` } ] }; } case "relate_memories": { const args = toolArgs as { sourceId: string; targetIds: string[] }; const memory = await memoryService.relateMemories(args.sourceId, args.targetIds); return { content: [ { type: "text", text: `Relationships created for memory ${memory.id}` } ] }; } case "unrelate_memories": { const args = toolArgs as { sourceId: string; targetIds: string[] }; const memory = await memoryService.unrelateMemories(args.sourceId, args.targetIds); return { content: [ { type: "text", text: `Relationships removed for memory ${memory.id}` } ] }; } case "rebuild_index": { await memoryService.rebuildIndex(); return { content: [ { type: "text", text: "Search index rebuilt successfully" } ] }; } default: return { content: [ { type: "text", text: `Unknown tool: ${toolName}` } ], is_error: true }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error: ${errorMessage}` }], is_error: true, }; } }); // Start server async function runServer() { try { await memoryService.initialize(); const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Memory Server running on stdio"); } catch (error) { console.error("Error initializing server:", error); process.exit(1); } } runServer().catch((error) => { console.error("Fatal error running server:", error); process.exit(1); });