MCP Server Memory File
by g0t4
- src
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
CallToolResult,
} from "@modelcontextprotocol/sdk/types.js";
import { createRequire } from "module";
import { verbose_log } from "./logs.js";
import {
listMemory,
appendMemory,
deleteMemory,
searchMemory,
readMemories,
} from "./memories.js";
const createServer = async () => {
const require = createRequire(import.meta.url);
const {
name: package_name,
version: package_version,
} = require("../package.json");
const server = new Server(
{
name: package_name,
version: package_version,
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
verbose_log("INFO: ListTools");
let memories = await readMemories();
verbose_log("INFO: memories", memories);
if (memories) {
memories = "Here are your memories:\n" + memories;
}
return {
tools: [
{
name: "append_memories",
description:
"Add new memory line(s), use newline to separate",
inputSchema: {
type: "object",
properties: {
text: { type: "string" },
},
required: ["text"],
},
},
{
name: "search_memory",
description: "Return memory entries containing the query",
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
// PRN no query == ALL? then get rid of list_memory?
},
required: ["query"],
},
},
{
name: "delete_memory",
description: "Delete memory entries containing the query",
inputSchema: {
type: "object",
properties: {
// TODO do I need to mention trim leading/trailing whitespace (esp new lines)
query: { type: "string" },
},
required: ["query"],
},
},
{
name: "list_memory",
description:
// ! TODO I guess in reality, I might have a chat type called "with memory" in which case Claude Desktop app can inject the memories with smth like slash commands and Prompt requests (instead of ToolRequest) ... TBD... that way I can leave instructions too about when to add new memories, update, etc... ok yeah that makes sense
"newline delimited list of all memory entries, " +
memories,
inputSchema: {
type: "object",
},
},
// IDEAS:
// - extract_keywords/extract_word_cloud (pull from memories, like a word cloud, to help devise a search, esp if memory grows large - can even include frequency if useful, as a more salient memory!?)
// speaking of salience, what all could correlate to generate salience (i.e. emotional state in humans imbues salience, i.e. car crash you can remember minute details about a car that 20 years later you can't recall in general but for the accident)
// - touch_memory
],
};
});
server.setRequestHandler(
CallToolRequestSchema,
async (request): Promise<{ toolResult: CallToolResult }> => {
verbose_log("INFO: ToolRequest", request);
switch (request.params.name) {
case "append_memories": {
const text = request.params.arguments?.text as string;
if (!text) {
throw new Error("Memory's text is required");
}
return { toolResult: await appendMemory(text) };
}
case "list_memory": {
return { toolResult: await listMemory() };
}
case "search_memory": {
const query = request.params.arguments?.query as string;
if (!query) {
throw new Error("query is required");
}
return { toolResult: await searchMemory(query) };
}
case "delete_memory": {
const query = request.params.arguments?.query as string;
if (!query) {
throw new Error("query is required");
}
return { toolResult: await deleteMemory(query) };
}
default:
throw new Error("Unknown tool");
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
};
createServer().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});