Skip to main content
Glama

mem_save

Records workflow memories with FIFO eviction to maintain up to 1000 tokens, capturing what was done, why, and outcomes for AI-assisted task tracking.

Instructions

Record workflow memories with FIFO eviction (keep ≤ 1000 tokens)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectPathYesProject path (provided by AI)
entriesYesList of memory entries

Implementation Reference

  • The handler function for 'mem_save' tool. It appends new memory entries to memory.json, enforces a 1000 token limit via FIFO eviction, updates metadata, and returns success/error response.
    async ({ projectPath, entries }) => {
        try {
            // 1. 讀取現有 memory.json
            const memoryPath = path.join(projectPath, '.memory', 'memory.json');
            let memory: Memory = readJSON(memoryPath) || {
                entries: [],
                meta: {
                    total_entries: 0,
                    estimated_tokens: 0,
                    last_updated: ""
                }
            };
    
            // 2. 新增 entries
            memory.entries.push(...entries);
    
            // 3. 計算總 tokens
            let totalTokens = memory.entries.reduce(
                (sum, e) => sum + estimateTokens(e),
                0
            );
    
            // 4. FIFO 刪除(保持 ≤ 1000 tokens)
            while (totalTokens > 1000 && memory.entries.length > 1) {
                const removed = memory.entries.shift()!;
                totalTokens -= estimateTokens(removed);
            }
    
            // 5. 更新 meta
            memory.meta = {
                total_entries: memory.entries.length,
                estimated_tokens: totalTokens,
                last_updated: new Date().toISOString().split('T')[0]
            };
    
            // 6. 寫回檔案
            writeJSON(memoryPath, memory);
    
            return {
                content: [{
                    type: "text" as const,
                    text: JSON.stringify({
                        success: true,
                        message: `Recorded ${entries.length} entries, current total ${memory.meta.total_entries} entries (approx. ${memory.meta.estimated_tokens} tokens)`
                    }, null, 2)
                }]
            };
        } catch (error) {
            return {
                content: [{
                    type: "text" as const,
                    text: JSON.stringify({
                        success: false,
                        message: `Write failed: ${error}`
                    }, null, 2)
                }],
                isError: true
            };
        }
    }
  • Input schema for 'mem_save' tool using Zod, defining projectPath and entries array with structured fields.
    {
        projectPath: z.string().describe("Project path (provided by AI)"),
        entries: z.array(z.object({
            what: z.string().describe("What was done"),
            why: z.string().describe("Why it was done"),
            outcome: z.string().describe("What was the outcome"),
            task_context: z.string().optional().describe("Task context"),
            constraints: z.string().optional().describe("Constraints"),
            dependencies: z.string().optional().describe("Dependencies")
        })).describe("List of memory entries")
    },
    async ({ projectPath, entries }) => {
  • src/index.ts:66-140 (registration)
    Registration of the 'mem_save' tool on the MCP server using server.tool(), including name, description, schema, and handler.
    server.tool(
        "mem_save",
        "Record workflow memories with FIFO eviction (keep ≤ 1000 tokens)",
        {
            projectPath: z.string().describe("Project path (provided by AI)"),
            entries: z.array(z.object({
                what: z.string().describe("What was done"),
                why: z.string().describe("Why it was done"),
                outcome: z.string().describe("What was the outcome"),
                task_context: z.string().optional().describe("Task context"),
                constraints: z.string().optional().describe("Constraints"),
                dependencies: z.string().optional().describe("Dependencies")
            })).describe("List of memory entries")
        },
        async ({ projectPath, entries }) => {
            try {
                // 1. 讀取現有 memory.json
                const memoryPath = path.join(projectPath, '.memory', 'memory.json');
                let memory: Memory = readJSON(memoryPath) || {
                    entries: [],
                    meta: {
                        total_entries: 0,
                        estimated_tokens: 0,
                        last_updated: ""
                    }
                };
    
                // 2. 新增 entries
                memory.entries.push(...entries);
    
                // 3. 計算總 tokens
                let totalTokens = memory.entries.reduce(
                    (sum, e) => sum + estimateTokens(e),
                    0
                );
    
                // 4. FIFO 刪除(保持 ≤ 1000 tokens)
                while (totalTokens > 1000 && memory.entries.length > 1) {
                    const removed = memory.entries.shift()!;
                    totalTokens -= estimateTokens(removed);
                }
    
                // 5. 更新 meta
                memory.meta = {
                    total_entries: memory.entries.length,
                    estimated_tokens: totalTokens,
                    last_updated: new Date().toISOString().split('T')[0]
                };
    
                // 6. 寫回檔案
                writeJSON(memoryPath, memory);
    
                return {
                    content: [{
                        type: "text" as const,
                        text: JSON.stringify({
                            success: true,
                            message: `Recorded ${entries.length} entries, current total ${memory.meta.total_entries} entries (approx. ${memory.meta.estimated_tokens} tokens)`
                        }, null, 2)
                    }]
                };
            } catch (error) {
                return {
                    content: [{
                        type: "text" as const,
                        text: JSON.stringify({
                            success: false,
                            message: `Write failed: ${error}`
                        }, null, 2)
                    }],
                    isError: true
                };
            }
        }
    );
  • Helper function to estimate token count for memory entries, used in eviction logic.
    function estimateTokens(entry: MemoryEntry): number {
        const text = JSON.stringify(entry);
        const chinese = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
        const english = (text.match(/[a-zA-Z]/g) || []).length;
        const symbols = text.length - chinese - english;
    
        return Math.ceil(chinese * 1.3 + english * 0.3 + symbols * 0.6);
    }
  • Helper function to write Memory data to JSON file, ensuring directory exists.
    function writeJSON(filePath: string, data: Memory): void {
        const dir = path.dirname(filePath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
    }
Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Artin0123/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server