mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-02-19 11:54:58 -05:00
fix(memory): convert to modern TypeScript SDK APIs (#3015)
* fix(memory): convert to modern TypeScript SDK APIs Convert the memory server to use the modern McpServer API instead of the low-level Server API. Key changes: - Replace Server with McpServer from @modelcontextprotocol/sdk/server/mcp.js - Convert all 9 tools to use registerTool() instead of manual request handlers - Create reusable Zod schemas for Entity and Relation types - Use Zod schemas directly in inputSchema/outputSchema - Add structuredContent to all tool responses - Fix type literals to use 'as const' assertions The modern API provides: - Less boilerplate code (removed ~200 lines of schema definitions) - Better type safety with Zod - More declarative tool registration - Cleaner, more maintainable code * fix: exclude test files from TypeScript build Add exclude for test files and vitest.config.ts to tsconfig
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
@@ -226,243 +223,235 @@ export class KnowledgeGraphManager {
|
||||
|
||||
let knowledgeGraphManager: KnowledgeGraphManager;
|
||||
|
||||
// Zod schemas for entities and relations
|
||||
const EntitySchema = z.object({
|
||||
name: z.string().describe("The name of the entity"),
|
||||
entityType: z.string().describe("The type of the entity"),
|
||||
observations: z.array(z.string()).describe("An array of observation contents associated with the entity")
|
||||
});
|
||||
|
||||
const RelationSchema = 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")
|
||||
});
|
||||
|
||||
// The server instance and tools exposed to Claude
|
||||
const server = new Server({
|
||||
const server = new McpServer({
|
||||
name: "memory-server",
|
||||
version: "0.6.3",
|
||||
}, {
|
||||
capabilities: {
|
||||
tools: {},
|
||||
});
|
||||
|
||||
// Register create_entities tool
|
||||
server.registerTool(
|
||||
"create_entities",
|
||||
{
|
||||
title: "Create Entities",
|
||||
description: "Create multiple new entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
entities: z.array(EntitySchema)
|
||||
},
|
||||
},);
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [
|
||||
{
|
||||
name: "create_entities",
|
||||
description: "Create multiple new entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entities: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", description: "The name of the entity" },
|
||||
entityType: { type: "string", description: "The type of the entity" },
|
||||
observations: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observation contents associated with the entity"
|
||||
},
|
||||
},
|
||||
required: ["name", "entityType", "observations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["entities"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create_relations",
|
||||
description: "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
relations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
from: { type: "string", description: "The name of the entity where the relation starts" },
|
||||
to: { type: "string", description: "The name of the entity where the relation ends" },
|
||||
relationType: { type: "string", description: "The type of the relation" },
|
||||
},
|
||||
required: ["from", "to", "relationType"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["relations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add_observations",
|
||||
description: "Add new observations to existing entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
observations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityName: { type: "string", description: "The name of the entity to add the observations to" },
|
||||
contents: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observation contents to add"
|
||||
},
|
||||
},
|
||||
required: ["entityName", "contents"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["observations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete_entities",
|
||||
description: "Delete multiple entities and their associated relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityNames: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of entity names to delete"
|
||||
},
|
||||
},
|
||||
required: ["entityNames"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete_observations",
|
||||
description: "Delete specific observations from entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
deletions: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityName: { type: "string", description: "The name of the entity containing the observations" },
|
||||
observations: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observations to delete"
|
||||
},
|
||||
},
|
||||
required: ["entityName", "observations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["deletions"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete_relations",
|
||||
description: "Delete multiple relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
relations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
from: { type: "string", description: "The name of the entity where the relation starts" },
|
||||
to: { type: "string", description: "The name of the entity where the relation ends" },
|
||||
relationType: { type: "string", description: "The type of the relation" },
|
||||
},
|
||||
required: ["from", "to", "relationType"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
description: "An array of relations to delete"
|
||||
},
|
||||
},
|
||||
required: ["relations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read_graph",
|
||||
description: "Read the entire knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "search_nodes",
|
||||
description: "Search for nodes in the knowledge graph based on a query",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "The search query to match against entity names, types, and observation content" },
|
||||
},
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "open_nodes",
|
||||
description: "Open specific nodes in the knowledge graph by their names",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
names: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of entity names to retrieve",
|
||||
},
|
||||
},
|
||||
required: ["names"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
if (name === "read_graph") {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2) }] };
|
||||
outputSchema: {
|
||||
entities: z.array(EntitySchema)
|
||||
}
|
||||
},
|
||||
async ({ entities }) => {
|
||||
const result = await knowledgeGraphManager.createEntities(entities);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
||||
structuredContent: { entities: result }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
if (!args) {
|
||||
throw new Error(`No arguments provided for tool: ${name}`);
|
||||
// Register create_relations tool
|
||||
server.registerTool(
|
||||
"create_relations",
|
||||
{
|
||||
title: "Create Relations",
|
||||
description: "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice",
|
||||
inputSchema: {
|
||||
relations: z.array(RelationSchema)
|
||||
},
|
||||
outputSchema: {
|
||||
relations: z.array(RelationSchema)
|
||||
}
|
||||
},
|
||||
async ({ relations }) => {
|
||||
const result = await knowledgeGraphManager.createRelations(relations);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
||||
structuredContent: { relations: result }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
switch (name) {
|
||||
case "create_entities":
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.createEntities(args.entities as Entity[]), null, 2) }] };
|
||||
case "create_relations":
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.createRelations(args.relations as Relation[]), null, 2) }] };
|
||||
case "add_observations":
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.addObservations(args.observations as { entityName: string; contents: string[] }[]), null, 2) }] };
|
||||
case "delete_entities":
|
||||
await knowledgeGraphManager.deleteEntities(args.entityNames as string[]);
|
||||
return { content: [{ type: "text", text: "Entities deleted successfully" }] };
|
||||
case "delete_observations":
|
||||
await knowledgeGraphManager.deleteObservations(args.deletions as { entityName: string; observations: string[] }[]);
|
||||
return { content: [{ type: "text", text: "Observations deleted successfully" }] };
|
||||
case "delete_relations":
|
||||
await knowledgeGraphManager.deleteRelations(args.relations as Relation[]);
|
||||
return { content: [{ type: "text", text: "Relations deleted successfully" }] };
|
||||
case "search_nodes":
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.searchNodes(args.query as string), null, 2) }] };
|
||||
case "open_nodes":
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.openNodes(args.names as string[]), null, 2) }] };
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${name}`);
|
||||
// Register add_observations tool
|
||||
server.registerTool(
|
||||
"add_observations",
|
||||
{
|
||||
title: "Add Observations",
|
||||
description: "Add new observations to existing entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
observations: z.array(z.object({
|
||||
entityName: z.string().describe("The name of the entity to add the observations to"),
|
||||
contents: z.array(z.string()).describe("An array of observation contents to add")
|
||||
}))
|
||||
},
|
||||
outputSchema: {
|
||||
results: z.array(z.object({
|
||||
entityName: z.string(),
|
||||
addedObservations: z.array(z.string())
|
||||
}))
|
||||
}
|
||||
},
|
||||
async ({ observations }) => {
|
||||
const result = await knowledgeGraphManager.addObservations(observations);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
||||
structuredContent: { results: result }
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Register delete_entities tool
|
||||
server.registerTool(
|
||||
"delete_entities",
|
||||
{
|
||||
title: "Delete Entities",
|
||||
description: "Delete multiple entities and their associated relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
entityNames: z.array(z.string()).describe("An array of entity names to delete")
|
||||
},
|
||||
outputSchema: {
|
||||
success: z.boolean(),
|
||||
message: z.string()
|
||||
}
|
||||
},
|
||||
async ({ entityNames }) => {
|
||||
await knowledgeGraphManager.deleteEntities(entityNames);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: "Entities deleted successfully" }],
|
||||
structuredContent: { success: true, message: "Entities deleted successfully" }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register delete_observations tool
|
||||
server.registerTool(
|
||||
"delete_observations",
|
||||
{
|
||||
title: "Delete Observations",
|
||||
description: "Delete specific observations from entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
deletions: z.array(z.object({
|
||||
entityName: z.string().describe("The name of the entity containing the observations"),
|
||||
observations: z.array(z.string()).describe("An array of observations to delete")
|
||||
}))
|
||||
},
|
||||
outputSchema: {
|
||||
success: z.boolean(),
|
||||
message: z.string()
|
||||
}
|
||||
},
|
||||
async ({ deletions }) => {
|
||||
await knowledgeGraphManager.deleteObservations(deletions);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: "Observations deleted successfully" }],
|
||||
structuredContent: { success: true, message: "Observations deleted successfully" }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register delete_relations tool
|
||||
server.registerTool(
|
||||
"delete_relations",
|
||||
{
|
||||
title: "Delete Relations",
|
||||
description: "Delete multiple relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
relations: z.array(RelationSchema).describe("An array of relations to delete")
|
||||
},
|
||||
outputSchema: {
|
||||
success: z.boolean(),
|
||||
message: z.string()
|
||||
}
|
||||
},
|
||||
async ({ relations }) => {
|
||||
await knowledgeGraphManager.deleteRelations(relations);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: "Relations deleted successfully" }],
|
||||
structuredContent: { success: true, message: "Relations deleted successfully" }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register read_graph tool
|
||||
server.registerTool(
|
||||
"read_graph",
|
||||
{
|
||||
title: "Read Graph",
|
||||
description: "Read the entire knowledge graph",
|
||||
inputSchema: {},
|
||||
outputSchema: {
|
||||
entities: z.array(EntitySchema),
|
||||
relations: z.array(RelationSchema)
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
const graph = await knowledgeGraphManager.readGraph();
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(graph, null, 2) }],
|
||||
structuredContent: { ...graph }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register search_nodes tool
|
||||
server.registerTool(
|
||||
"search_nodes",
|
||||
{
|
||||
title: "Search Nodes",
|
||||
description: "Search for nodes in the knowledge graph based on a query",
|
||||
inputSchema: {
|
||||
query: z.string().describe("The search query to match against entity names, types, and observation content")
|
||||
},
|
||||
outputSchema: {
|
||||
entities: z.array(EntitySchema),
|
||||
relations: z.array(RelationSchema)
|
||||
}
|
||||
},
|
||||
async ({ query }) => {
|
||||
const graph = await knowledgeGraphManager.searchNodes(query);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(graph, null, 2) }],
|
||||
structuredContent: { ...graph }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register open_nodes tool
|
||||
server.registerTool(
|
||||
"open_nodes",
|
||||
{
|
||||
title: "Open Nodes",
|
||||
description: "Open specific nodes in the knowledge graph by their names",
|
||||
inputSchema: {
|
||||
names: z.array(z.string()).describe("An array of entity names to retrieve")
|
||||
},
|
||||
outputSchema: {
|
||||
entities: z.array(EntitySchema),
|
||||
relations: z.array(RelationSchema)
|
||||
}
|
||||
},
|
||||
async ({ names }) => {
|
||||
const graph = await knowledgeGraphManager.openNodes(names);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(graph, null, 2) }],
|
||||
structuredContent: { ...graph }
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
async function main() {
|
||||
// Initialize memory file path with backward compatibility
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.test.ts",
|
||||
"vitest.config.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user