MCP DuckDB Knowledge Graph Memory Server

by IzumiSy
Verified
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { DuckDBKnowledgeGraphManager } from "./manager"; import { NullLogger } from "./logger"; import { join, dirname } from "path"; import { homedir } from "os"; import { existsSync, mkdirSync } from "fs"; import { EntityObject, ObservationObject, RelationObject } from "./types"; // Create an MCP server const server = new McpServer({ name: "duckdb-memory-server", version: "1.1.2", }); const logger = new NullLogger(); const knowledgeGraphManager = new DuckDBKnowledgeGraphManager( /** * Get the database file path based on environment variables or default location * @returns The path to the database file */ () => { if (process.env.MEMORY_FILE_PATH) { // Use environment variable if provided return process.env.MEMORY_FILE_PATH; } // Default path: ~/.local/share/duckdb-memory-server/knowledge-graph.json const defaultDir = join( homedir(), ".local", "share", "duckdb-memory-server" ); const defaultPath = join(defaultDir, "knowledge-graph.data"); // Create directory if it doesn't exist if (!existsSync(dirname(defaultPath))) { mkdirSync(dirname(defaultPath), { recursive: true }); } return defaultPath; }, logger ); // Create entities tool server.tool( "create_entities", "Create multiple new entities in the knowledge graph", { entities: z.array(EntityObject), }, async ({ entities }) => ({ content: [ { type: "text", text: JSON.stringify( await knowledgeGraphManager.createEntities(entities), null, 2 ), }, ], }) ); // Create relations tool server.tool( "create_relations", "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice", { relations: z.array(RelationObject), }, async ({ relations }) => ({ content: [ { type: "text", text: JSON.stringify( await knowledgeGraphManager.createRelations(relations), null, 2 ), }, ], }) ); // Add observations tool server.tool( "add_observations", "Add new observations to existing entities in the knowledge graph", { observations: z.array(ObservationObject), }, async ({ observations }) => ({ content: [ { type: "text", text: JSON.stringify( await knowledgeGraphManager.addObservations(observations), null, 2 ), }, ], }) ); // Delete entities tool server.tool( "delete_entities", "Delete multiple entities and their associated relations from the knowledge graph", { entityNames: z .array(z.string()) .describe("An array of entity names to delete"), }, async ({ entityNames }) => { await knowledgeGraphManager.deleteEntities(entityNames); return { content: [{ type: "text", text: "Entities deleted successfully" }], }; } ); // Delete observations tool server.tool( "delete_observations", "Delete specific observations from entities in the knowledge graph", { deletions: z.array( z.object({ entityName: z .string() .describe("The name of the entity containing the observations"), contents: z .array(z.string()) .describe("An array of observations to delete"), }) ), }, async ({ deletions }) => { await knowledgeGraphManager.deleteObservations(deletions); return { content: [{ type: "text", text: "Observations deleted successfully" }], }; } ); // Delete relations tool server.tool( "delete_relations", "Delete multiple relations from the knowledge graph", { relations: z .array( z.object({ from: z .string() .describe("The name of the entity where the relation starts"), to: z .string() .describe("The name of the entity where the relation ends"), relationType: z.string().describe("The type of the relation"), }) ) .describe("An array of relations to delete"), }, async ({ relations }) => { await knowledgeGraphManager.deleteRelations(relations); return { content: [{ type: "text", text: "Relations deleted successfully" }], }; } ); // Search nodes tool server.tool( "search_nodes", "Search for nodes in the knowledge graph based on a query", { query: z .string() .describe( "The search query to match against entity names, types, and observation content" ), }, async ({ query }) => ({ content: [ { type: "text", text: JSON.stringify( await knowledgeGraphManager.searchNodes(query), null, 2 ), }, ], }) ); // Open nodes tool server.tool( "open_nodes", "Open specific nodes in the knowledge graph by their names", { names: z.array(z.string()).describe("An array of entity names to retrieve"), }, async ({ names }) => ({ content: [ { type: "text", text: JSON.stringify( await knowledgeGraphManager.openNodes(names), null, 2 ), }, ], }) ); const main = async () => { const transport = new StdioServerTransport(); await server.connect(transport); logger.info("DuckDB Knowledge Graph MCP Server running on stdio"); }; main().catch((error) => { console.error(error); process.exit(1); });