feat(mcp): add listMemories tool for enumerating stored memories#1183
feat(mcp): add listMemories tool for enumerating stored memories#1183abhay-codes07 wants to merge 1 commit into
Conversation
The MCP server had no way to enumerate what is stored - only top-K recall results, or save/forget one at a time. This blocked audit and cleanup workflows (list everything on file, prune stale memories). Add a listMemories tool that pages through documents via the existing getDocuments client method and returns only the extracted memory facts, grouped by source document, newest first. Document content is never included and each fact is capped at 500 chars, keeping responses well within client output limits (the concern that stalled the previous attempt in supermemoryai#1044). Forgotten and superseded memory entries are filtered out. Pagination is page/limit based (limit capped at 50 documents) to match the underlying documents API, with a next-page hint in the output. Testing: unit tests for the formatter (vitest config now also picks up src/**/*.test.ts), a key-gated e2e spec following the existing suite conventions, and README docs for the new tool. Fixes supermemoryai#1030
There was a problem hiding this comment.
Summary
Reviewed — found 1 issue(s). I reviewed the new MCP listMemories formatter/tool implementation and related docs/tests for correctness, output-safety, and client-facing behavior.
Findings
apps/mcp/src/format.ts
- The formatter caps each memory string but not the total number of rendered memory entries, so one response can still grow without a practical bound.
Verdict
listMemories cannot exceed MCP/client output limits or force large Worker string allocations.
| } | ||
|
|
||
| memoryCount += activeEntries.length | ||
| const lines = activeEntries.map((entry) => { |
There was a problem hiding this comment.
formatMemoriesList truncates each individual entry.memory, but this loop still renders every active memoryEntries item for every document on the page. Since the limit only caps documents, a single document with hundreds or thousands of memories, or a full page of documents with many memories each, can produce unbounded MCP output and large Worker string allocations. Please add an overall response character cap and/or a per-document/page memory cap with a clear truncation message, such as asking the caller to request a narrower scope or later page.
TestingThe testing subagent exercised the new MCP Commands run: bun install
cd apps/mcp && bun run dev:app
bunx vitest run e2e/list-memories.test.ts
bunx vitest run src/format.test.ts
# Sanitized MCP smoke scripts calling listTools() and listMemories
NODE_OPTIONS=--max-old-space-size=8192 bunx tsc --noEmit -p apps/mcp/tsconfig.json
# Compared MCP type-check failures against refs/remotes/origin/mainResult: Text logs from the run: Verdict
|
What
Fixes #1030
Adds a
listMemoriestool to the MCP server so agents can enumerate what is stored — audit before a save/forget, bulk cleanup of stale memories, or a plain "show me everything you remember" — none of whichrecall's top-K search can do reliably.How this differs from #1044
The earlier attempt was closed because it called the documents list API with
includeContent: true, so a single ~48k-char document blew Claude's output limits even atlimit: 5. This implementation takes the direction suggested in that review:getDocumentsclient method (same onememory-graphuses), which returns documents with their extractedmemoryEntries— the short memory facts — plus title/type/date for context. NoincludeContentanywhere.limitis capped at 50 documents per page (default 10), each memory fact is whitespace-flattened and truncated at 500 chars, and forgotten (isForgotten) and superseded (isLatest: false) entries are filtered out.Sample output:
The tool description steers agents to keep using
recallfor topic search andlistMemoriesonly for enumeration/audit, and thecontainerTagscoping follows the samehasRootContainerTagpattern as the other tools (root-scoped connections can't override it).Changes
src/server.ts— registerlistMemories+ handler, mirroring the existing tool patterns (zod schema, error shape)src/format.ts—formatMemoriesList(), a pure formatter over the existingDocumentsApiResponsetypesrc/format.test.ts— 8 unit tests: empty store vs out-of-range page, grouping, forgotten/superseded filtering, untitled docs, docs still extracting, multi-line flattening + truncation, next-page hinte2e/list-memories.test.ts— key-gated spec following the suite's conventions (unique markers, polling for eventual consistency, teardown forget): discovery, save→list round-trip, pagination,limitvalidationvitest.config.ts— also pick upsrc/**/*.test.tsREADME.md— document the tool and the new e2e fileTesting
vitest run src/format.test.ts— 8/8 passvite build+wrangler deploy --dry-run— worker bundles cleanly with the new toolbiome check— clean on all touched filesSUPERMEMORY_API_KEY(same as the rest of the suite), so CI stays safe without secrets; happy to run it against staging if someone can trigger it with a keye2e/oauth.test.tsno-secret failures I see locally reproduce onmaintoo (the production authorize endpoint now returns 200 instead of a 302 to /login) — unrelated to this changecc @ved015 — this picks up where #1044 left off, with the output-size concern from your review addressed at the data-shape level.
Session Details
(aside)to your comment to have me ignore it.