Repository navigation
Description
Title
tools/list returns a non-advancing nextCursor on the last/only page → spec-compliant clients paginate infinitely (tool list inflates to ~160 in Claude Code)
Summary
tools/list always includes a nextCursor even when the whole tool set fits in a
single page, and the cursor does not advance: re-requesting with that cursor
returns the exact same tools plus the same nextCursor. Any MCP client that follows
nextCursor pagination (per the MCP spec) loops indefinitely and accumulates the
tool list. In Claude Code this shows up as ~160 tools (8 unique × ~20).
Environment
codebase-memory-mcp0.9.0 (linux-amd64)- Server exposes 8 tools:
get_architecture, get_code_snippet, get_graph_schema, index_repository, query_graph, search_code, search_graph, trace_path - Exposed over MCP via
mcp-proxy0.12.0 (reproduces on both SSE/sseand streamable-http/mcp). The offendingtools/listpayload originates fromcodebase-memory-mcp(mcp-proxy forwards it verbatim). - Clients: Claude Code 2.1.218 (affected), Codex (not affected — see Impact).
Steps to reproduce
- Initialize an MCP session against the server.
- Call
tools/list(no cursor). Response returns 8 tools and"nextCursor":"8". - Call
tools/listagain withparams.cursor = "8". - Response returns the same 8 tools and the same
"nextCursor":"8"— the cursor never advances and pagination never terminates.
Observed responses
First page (tools/list, no cursor):
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [ /* 8 tools: get_architecture, ..., trace_path */ ],
"nextCursor": "8"
}
}Follow-up page (tools/list with cursor:"8"):
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"tools": [ /* the SAME 8 tools again */ ],
"nextCursor": "8"
}
}→ identical tools + identical cursor every time = infinite pagination.
Expected behavior
Per the MCP spec, nextCursor should be present only when there are more results.
With 8 tools total returned in one page, the result should either:
- omit
nextCursorentirely (preferred, since it is the last/only page), or - return an advancing cursor whose next fetch yields the remaining items and then terminates.
Actual behavior
nextCursor is always "8" (appears to be = total tool count / offset), the supplied
cursor is ignored, and the same page is returned forever. Pagination never ends.
Impact
- Claude Code 2.1.218 follows
nextCursorpagination and appends results without
dedup, so it loops (~20 iterations to an internal cap) and displays 8 × ~20 = 160
tools — bloating the tool picker and the model context. - Codex does not paginate
tools/list(ignoresnextCursor), so it correctly shows 8. - Any spec-compliant paginating client is affected.
Likely root cause
The tools/list handler appears to always set nextCursor to the number of returned
tools ("8") and does not treat the incoming cursor as a real offset nor detect
end-of-list, so it never signals completion.
Suggested fix
Only emit nextCursor when offset + returned < total; otherwise omit it. Honor the
incoming cursor as an offset so a follow-up request advances and eventually returns an
empty final page (or no cursor).
Workaround (for others hitting this before a fix)
Strip nextCursor from tools/list responses at a reverse proxy. Example nginx (the
tool count is fixed at 8, so the cursor value is the literal "8"):
proxy_set_header Accept-Encoding ""; # ensure uncompressed so sub_filter matches
sub_filter ',"nextCursor":"8"' '';
sub_filter '"nextCursor":"8",' '';
sub_filter_once off;
sub_filter_types text/event-stream application/json;After stripping nextCursor, Claude Code stops paginating and shows the correct 8 tools.
Activity
[-]1[/-][+]`tools/list` returns a non-advancing `nextCursor` on the last/only page → spec-compliant clients paginate infinitely (tool list inflates to ~160 in Claude Code)[/+]DeusData commentedon Jul 24, 2026
Thanks for the protocol-level report. The direct server code does advance and terminate
tools/listpagination:v0.9.0parsesparams.cursor, and current tests require page 1 to returnnextCursor: 8while thecursor: 8page omitsnextCursor. Your reproduction is throughmcp-proxyover SSE/HTTP, so the remaining boundary is whether that path forwards the request parameters and server profile unchanged. Please attach one raw direct stdio request/response pair against the same binary forcursor: 8, pluscodebase-memory-mcp --versionand the binary SHA-256. That will tell us whether this is a direct server regression or a proxy/profile translation defect. It remains high priority in0.9.1-rcunder #395.sqpsqp0208 commentedon Jul 27, 2026
▎ Confirmed it's the mcp-proxy path, not your server — thanks for the pointer.
▎
▎ Direct stdio against the same binary paginates and terminates correctly:
▎ - codebase-memory-mcp --version: 0.9.0
▎ - binary SHA-256: 9f5a306d54a8248f736939a42cd19678273731d735f6a1c9a92659b5e2585e32 (statically-linked ELF, BuildID sha1 63ee2bae5f46ede4585deb3ede61156fec27c34b)
▎ - page 1 (tools/list, no cursor): 8 tools + "nextCursor":"8"
▎ - page 2 (tools/list, cursor:"8"): the other 6 tools (list_projects, delete_project, index_status, detect_changes, manage_adr, ingest_traces), no nextCursor → terminates.
▎
▎ {"jsonrpc":"2.0","id":3,"result":{"tools":[{"name":"list_projects",...},{"name":"delete_project",...},{"name":"index_status",...},{"name":"detect_changes",...},{"name":"manage_adr",...},{"name":"ingest_trac
▎ es",...}]}}
▎
▎ Through mcp-proxy 0.12.0 (SSE/streamable-http), the same cursor:"8" request returns page 1 again (same 8 tools + nextCursor:"8") — i.e. mcp-proxy does not forward params.cursor, so the server always returns
▎ page 1. That both (a) makes paginating clients loop → 160 tools, and (b) hides the 6 page-2 tools. Not a server regression — feel free to close #395 as external; I'll file against mcp-proxy.
sqpsqp0208 commentedon Jul 27, 2026
DeusData commentedon Jul 28, 2026
Thanks for closing the loop with a direct stdio comparison and for identifying the exact proxy behavior.
Your final evidence settles the boundary: codebase-memory-mcp 0.9.0 returns page 1, then page 2, then no nextCursor; mcp-proxy 0.12.0 drops params.cursor and repeatedly asks for page 1. That matches the server's pagination tests and means there is no CBM regression to fix here.
I am closing this as resolved externally. The raw request comparison will be useful to anyone who encounters the same inflated tool list through a proxy. Thank you for documenting the working bridge as well.