Skip to content

Paginate list reads; cap the MCP list_memories tool#70

Merged
imonroe merged 1 commit into
mainfrom
claude/wizardly-planck-pwvl93-i65
Jun 9, 2026
Merged

Paginate list reads; cap the MCP list_memories tool#70
imonroe merged 1 commit into
mainfrom
claude/wizardly-planck-pwvl93-i65

Conversation

@imonroe

@imonroe imonroe commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Closes #65

What

  • MCP list_memories is no longer unbounded. It previously returned the entire store into the model's context on every call; it now takes limit (1–100, default 50) and offset, with validation errors for out-of-range values.
  • REST GET /api/v1/memories gains offset (0–10000) alongside the existing limit, so the whole store is enumerable past the old 100-item ceiling.
  • Both responses now carry "pagination": {"limit", "offset", "has_more"}; clients advance offset by limit while has_more is true.

How

mem0's get_all() has no offset parameter, so list_paginated() in app/memory.py emulates it: fetch offset + limit + 1 items, slice the page, and use the extra item purely as the has_more signal. This keeps mem0's result shaping byte-identical (no coupling to private vector-store APIs) at the cost of over-fetching on deep pages — bounded by the MAX_LIST_OFFSET = 10000 cap, which is far beyond a single-user store's realistic depth.

Documented behaviors (also in the user guide):

  • Ordering is stable (vector-store scroll order, by internal ID) but not chronological.
  • exclude_expired=true filters after the page is cut, so a page may hold fewer than limit items while has_more stays true.
  • MCP reads still span all agents — no agent_id filtering was introduced (CLAUDE.md invariant).

Compatibility

digest/, capture/, and importers/ all read only the results key from list responses, so the added pagination key is non-breaking (verified during review). The only behavior change for existing callers is the MCP tool's default cap of 50 — which is the point of the issue.

Tests

10 new tests across tests/test_memory.py, tests/test_rest.py, tests/test_mcp.py: page boundaries, has_more on last page, offset past end, validation rejections (REST 422s and MCP ToolErrors), exclude_expired interaction, and tolerance of bare-list / None get_all returns (the None case was a real crash caught while writing tests). Full suite: 177 passed, ruff clean.

Review

Adversarial review (line-by-line, removed-behavior, cross-file angles) found no defects; consumer compatibility in digest/, capture/, importers/ was explicitly traced.

https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx


Generated by Claude Code

GET /api/v1/memories gains offset (0-10000) alongside limit, and responses
carry a pagination object with has_more so the full store is enumerable.
The MCP list_memories tool, previously unbounded (it returned the entire
store into the model's context), now defaults to a page of 50 (max 100)
with the same offset/has_more contract.

mem0's get_all has no offset parameter, so list_paginated() in app/memory.py
emulates it: over-fetch offset+limit+1, slice, and use the extra item as the
has_more signal. This keeps mem0's result shaping intact and avoids coupling
to private vector-store APIs; the offset cap bounds the per-request fetch.

Closes #65

https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx
@imonroe imonroe merged commit 263282c into main Jun 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add pagination to list/search endpoints and a result cap to the MCP list_memories tool

2 participants