feat: add cursor pagination to GET /api/threads and thread_list MCP tool#28
Merged
Killea merged 2 commits intoKillea:mainfrom Mar 2, 2026
Merged
feat: add cursor pagination to GET /api/threads and thread_list MCP tool#28Killea merged 2 commits intoKillea:mainfrom
Killea merged 2 commits intoKillea:mainfrom
Conversation
- thread_list() refactored to single dynamic query builder (replaces 3
duplicated SQL paths); adds optional limit (hard cap 200) and before
(ISO datetime keyset cursor) parameters. Default limit=0 preserves the
existing 'return all' behaviour (backward compatible).
- New thread_count() function for total count independent of pagination.
- Migration adds idx_threads_created_at index for efficient keyset queries.
- GET /api/threads updated with limit/before query params; validates before
format (HTTP 400 on invalid); normalizes URL-decoded '+' in timezone offset
before parsing; returns envelope {threads, total, has_more, next_cursor}.
- thread_list MCP tool schema updated with limit and before fields.
- handle_thread_list() in dispatch.py forwards new params, returns envelope.
- shared-threads.js updated to unpack response.threads from envelope.
- conftest.py: test port changed to 39769 (avoids production conflict);
compatibility check verifies pagination envelope support.
- 20 tests: 13 unit (in-memory DB) + 7 integration; full suite 201/201 passing.
- Combined UP-20 (pagination) and UP-22 (metrics) server checks in conftest.py - Preserved test_metrics.py from main - Resolved crud.py and main.py merge conflicts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: add cursor pagination to GET /api/threads and thread_list MCP tool (UP-20)
Problem
GET /api/threadsand thethread_listMCP tool return all threads with no limit. For bus instances with a large thread history, this causes unnecessary memory usage and latency on every listing request — even when the caller only needs the most recent page.By comparison,
GET /api/threads/{id}/messagesand themsg_listMCP tool already support cursor pagination viaafter_seq+limit. This PR extends the same pattern to thread listing.Solution
Add optional
limitandbeforeparameters toGET /api/threadsand thethread_listMCP tool. When omitted, the behaviour is identical to the current implementation (all threads returned). When provided, the response is paged using a keyset cursor oncreated_at.The response is now an envelope object instead of a flat array:
{ "threads": [ {"id": "...", "topic": "...", "status": "...", "system_prompt": "...", "created_at": "..."} ], "total": 42, "has_more": true, "next_cursor": "2026-03-02T10:00:00.123456+00:00" }threadstotalhas_moretruewhen more pages are availablenext_cursorbeforefor the next page.nullwhenhas_moreisfalsePagination parameters
limit00= all (no limit). Hard cap: 200.beforenext_cursorfrom a previous response.Example: walking pages
Implementation details
src/db/crud.py:thread_list()refactored to a single dynamic query builder (replaces 3 duplicated SQL paths). Newthread_count()function for total count without pagination.src/db/database.py: Migration addsidx_threads_created_at ON threads(created_at)for efficient keyset queries.src/main.py:GET /api/threadsupdated.+in timezone offset (+00:00) is normalized from URL-decoded space before parsing. Returns 400 on invalidbeforeformat.src/mcp_server.py:thread_listtool schema updated withlimitandbefore.src/tools/dispatch.py:handle_thread_list()forwards new params, returns envelope.src/static/js/shared-threads.js: Updated to unpackresponse.threadsfrom the envelope.Backward compatibility
limit=0(default) preserves the current "return all" behaviour.response.threadsinstead of the response directly.Tests
20 tests in
tests/test_thread_pagination.py:thread_countsemantics.Full test suite: 201 tests, all passing.