import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";
import * as os from "os";
const MEMORY_FILE = path.join(os.homedir(), ".code-mcp-memory.json");
// Helper to ensure memory file exists
async function ensureMemoryFile() {
try {
await fs.access(MEMORY_FILE);
} catch {
await fs.writeFile(MEMORY_FILE, JSON.stringify({}, null, 2));
}
}
export const saveMemorySchema = {
name: "save_memory",
description:
"Save a key-value pair to persistent memory. Use this to remember user preferences, project decisions, or important context across sessions.",
inputSchema: z.object({
key: z.string().describe("Unique key for this memory"),
value: z.string().describe("Value to store"),
project: z
.string()
.optional()
.describe("Optional project scope for organizing memories"),
}),
};
export const readMemorySchema = {
name: "read_memory",
description: "Read a value from persistent memory by key.",
inputSchema: z.object({
key: z.string().describe("Key to retrieve"),
}),
};
export const listMemoriesSchema = {
name: "list_memories",
description: "List all saved memories, optionally filtered by project scope.",
inputSchema: z.object({
project: z.string().optional().describe("Filter by project scope"),
}),
};
export const clearMemorySchema = {
name: "clear_memory",
description:
"Clear a specific memory by key, or clear all memories for a project.",
inputSchema: z.object({
key: z.string().optional().describe("Specific key to clear"),
project: z
.string()
.optional()
.describe("Clear all memories for this project"),
clearAll: z
.boolean()
.optional()
.describe("Clear ALL memories (use with caution)"),
}),
};
export async function saveMemoryHandler(args: any) {
await ensureMemoryFile();
const data = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
data[args.key] = {
value: args.value,
project: args.project,
timestamp: new Date().toISOString(),
};
await fs.writeFile(MEMORY_FILE, JSON.stringify(data, null, 2));
return { content: [{ type: "text", text: `Saved memory: ${args.key}` }] };
}
export async function readMemoryHandler(args: any) {
await ensureMemoryFile();
const data = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
const item = data[args.key];
if (!item) return { content: [{ type: "text", text: "Key not found." }] };
return { content: [{ type: "text", text: JSON.stringify(item, null, 2) }] };
}
export async function listMemoriesHandler(args: any) {
await ensureMemoryFile();
const data = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
const entries = Object.entries(data);
let filtered = entries;
if (args.project) {
filtered = entries.filter(
([_, v]: [string, any]) => v.project === args.project,
);
}
if (filtered.length === 0) {
return { content: [{ type: "text", text: "No memories found." }] };
}
const list = filtered
.map(
([key, v]: [string, any]) =>
`- **${key}**: ${v.value.substring(0, 50)}${v.value.length > 50 ? "..." : ""} (${v.project || "global"})`,
)
.join("\n");
return { content: [{ type: "text", text: `# Saved Memories\n\n${list}` }] };
}
export async function clearMemoryHandler(args: any) {
await ensureMemoryFile();
const data = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
if (args.clearAll) {
await fs.writeFile(MEMORY_FILE, JSON.stringify({}, null, 2));
return { content: [{ type: "text", text: "All memories cleared." }] };
}
if (args.key) {
delete data[args.key];
await fs.writeFile(MEMORY_FILE, JSON.stringify(data, null, 2));
return { content: [{ type: "text", text: `Memory cleared: ${args.key}` }] };
}
if (args.project) {
for (const key of Object.keys(data)) {
if (data[key].project === args.project) {
delete data[key];
}
}
await fs.writeFile(MEMORY_FILE, JSON.stringify(data, null, 2));
return {
content: [
{
type: "text",
text: `All memories for project '${args.project}' cleared.`,
},
],
};
}
return {
content: [{ type: "text", text: "Specify key, project, or clearAll." }],
};
}