Persistent memory system for Cursor IDE — remembers context across sessions
cursor-mem implements a thoughtful 3-layer progressive disclosure workflow with clear naming conventions (memory_search, memory_timeline, memory_get) that follow verb_noun patterns. Descriptions are well-written and explain both WHAT the tool does and WHEN to use it. However, schema completeness varies: memory_search and memory_get have fully typed parameters with descriptions, while memory_important lacks any input properties (though intentionally, as it's a guide tool) and memory_timeline has optional parameters where some lack full constraint documentation. Parameter descriptions generally meet the 10-1024 character range. Output schemas are NOT documented in the source, responses are inferred from handler implementations (search returns compact table, get returns full details), but LLMs cannot see these return types without additional documentation. Error handling is minimal, no recovery guidance, categorization, or actionable messages visible in the handlers. The server demonstrates good intent around token efficiency and user experience but falls short of production-grade tool design in schema transparency and error recovery patterns.
Step 3: Fetch full observation details by IDs (~500-1000 tokens per observation). Only use AFTER filtering with memory_search/memory_timeline. Always batch multiple IDs in a single call.
3-layer search workflow guide (always visible). Read this FIRST to understand how to query cursor-mem efficiently. 3-LAYER WORKFLOW (ALWAYS FOLLOW): 1. memory_search(query) → compact index with IDs (~50-100 tokens/result) 2. memory_timeline(anchor=ID) → context around interesting results (~100-200 tokens/result) 3. memory_get(ids=[...]) → full details ONLY for filtered IDs (~500-1000 tokens/result) NEVER fetch full details without filtering first. This achieves ~10x token savings.
Step 1: Search the cursor-mem index. Returns a compact table of IDs, titles, types, and dates (~50-100 tokens per result). Always start here to survey what exists before fetching details.
Step 2: Get chronological context around a specific observation. Use 'anchor' (observation ID) with depth_before/depth_after to see what happened before and after. ~100-200 tokens per entry.
Output schemas are not documented. Descriptions state what results look like ('compact table', 'full details', '~50-100 tokens') but JSON Schema output structure is absent. LLMs cannot infer return field names, types, or whether results are paginated.
Error handling and recovery guidance are missing. No visible error responses, categorization (retryable/user-fixable/fatal), or actionable recovery hints. If a search fails or returns zero results, the LLM has no guidance on what to try next.
memory_important has no input schema properties, not even an empty object with no required fields explicitly typed. While intentional (it's a guide tool), the schema declaration is incomplete: 'type': 'object', 'properties': {} lacks 'required': [] for clarity.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 57 | <=2025-11-25 | v2 |
| 2026-03-09 | D | 50 | - | v1 |
memory_timeline has optional parameters (anchor, query, session_id) where fallback logic and mutual exclusivity are unclear. Parameter descriptions do not document 'if anchor is not provided, query is used to find it; if both missing, session_id is used as fallback', this dependency must be explicit.
Pagination is partially addressed: memory_search and memory_timeline accept limit/offset, but memory_get lacks pagination. If filtered IDs list is large, memory_get may return partial results without indicating total count or next_cursor. Handler default is limit=20, but no guidance on when to paginate or how to request more.
Constraint documentation for enum/regex fields is weak. 'type' parameter in memory_search lists 'shell|file_edit|mcp|prompt' in the description but does not declare an enum in inputSchema. LLMs cannot distinguish between a free-form string and a constrained choice without an enum.
No field chaining documentation. If an LLM calls memory_search and gets observation IDs, can it immediately pass those IDs to memory_get? Or does it need additional fields (session_id, project_id) from memory_timeline first? Chaining path is implicit, not explicit.