Skip to content

MCP search pagination unstable across mutations; no cursor anchor #1462

Description

@Widthdom

Summary

search pagination is unstable across index mutations and the response carries no anchor that would let a client detect drift. Two paginated calls with the same query and limit can return different result sets if index runs in between, with no way for the caller to notice.

Evidence

  • src/CodeIndex/Database/DbSearchReader.cs:325 — limit-only ordering, no cursor field. The response also has no generation counter / snapshot timestamp.
  • Reproducer:
    1. cdidx search "foo" --limit 10 → record results A.
    2. Trigger index to add 5 new matching files.
    3. cdidx search "foo" --limit 10 → results B may differ from A even though the user asked for the same "first 10" twice.

Impact

  • Agents that paginate through results in chunks see duplicates and gaps.
  • No way to write a stable "show me page 2" UX; the underlying contract is undocumented.

Proposed direction

Two layered fixes:

  1. Detect drift: include a result_stable_at timestamp (or a DB generation counter) in every search response. Clients comparing two responses can tell whether they're seeing a consistent slice.
  2. Allow stable pagination: accept an optional cursor parameter that anchors on (rank, rowid) from the previous response. The handler then continues from that anchor rather than re-running the query from the top.

Document the contract in DEVELOPER_GUIDE.md.

Repro env

  • Branch: main @ 2ee912d (release v1.21.0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions