Skip to content

Commit d122124

Browse files
committed
test(mcp): derive the tools/list cursor from the live tool count
Registering get_file_outline makes 17 tools. The pagination test passed the literal cursor "8" and asserted no nextCursor came back, which silently encoded "there are at most MCP_TOOLS_PAGE_SIZE * 2 tools". At 17 the tail from offset 8 legitimately overflows one page, so the gate failed for a reason that had nothing to do with pagination being wrong. Production behaviour is correct and unchanged: the no-cursor path returns every tool, and a cursored page emits nextCursor exactly while tools remain after it. Only the test's hard-coded count assumption was wrong. The offset is now derived from the live count, so the final page is asserted wherever it falls and adding tool 18 will not break it. A positive control asserts that page 0 DOES advertise a cursor, so the no-cursor assertion cannot pass vacuously if paging stopped emitting altogether. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
1 parent 0b9df18 commit d122124

1 file changed

Lines changed: 28 additions & 4 deletions

File tree

‎tests/test_mcp.c‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,13 +1437,37 @@ TEST(server_handle_tools_list_defaults_to_all_tools_and_accepts_cursor) {
14371437
ASSERT_NOT_NULL(strstr(resp, "ingest_traces"));
14381438
free(resp);
14391439

1440-
resp = cbm_mcp_server_handle(
1441-
srv,
1442-
"{\"jsonrpc\":\"2.0\",\"id\":201,\"method\":\"tools/list\",\"params\":{\"cursor\":\"8\"}}");
1440+
/* A cursored page advertises nextCursor exactly while tools remain after
1441+
* it. This used to pass the literal cursor "8", which silently encoded
1442+
* "there are at most MCP_TOOLS_PAGE_SIZE * 2 tools" -- so registering a
1443+
* 17th tool broke it for a reason that had nothing to do with pagination.
1444+
* Derive the offset from the live count instead: the final page, wherever
1445+
* it falls, is the one that must not advertise more. */
1446+
resp = cbm_mcp_server_handle(srv, "{\"jsonrpc\":\"2.0\",\"id\":203,\"method\":\"tools/list\"}");
1447+
ASSERT_NOT_NULL(resp);
1448+
size_t total_tools = mcp_response_tool_count(resp);
1449+
free(resp);
1450+
ASSERT_TRUE(total_tools > 1U);
1451+
1452+
char last_page_req[160];
1453+
snprintf(last_page_req, sizeof(last_page_req),
1454+
"{\"jsonrpc\":\"2.0\",\"id\":201,\"method\":\"tools/list\","
1455+
"\"params\":{\"cursor\":\"%zu\"}}",
1456+
total_tools - 1U);
1457+
resp = cbm_mcp_server_handle(srv, last_page_req);
14431458
ASSERT_NOT_NULL(resp);
14441459
ASSERT_NOT_NULL(strstr(resp, "\"id\":201"));
14451460
ASSERT_NULL(strstr(resp, "\"nextCursor\""));
1446-
ASSERT_NOT_NULL(strstr(resp, "manage_adr"));
1461+
ASSERT_EQ(mcp_response_tool_count(resp), 1U);
1462+
free(resp);
1463+
1464+
/* ...and a page that does have tools after it MUST advertise the cursor,
1465+
* so the assertion above cannot pass merely because paging never emits. */
1466+
resp = cbm_mcp_server_handle(
1467+
srv,
1468+
"{\"jsonrpc\":\"2.0\",\"id\":204,\"method\":\"tools/list\",\"params\":{\"cursor\":\"0\"}}");
1469+
ASSERT_NOT_NULL(resp);
1470+
ASSERT_NOT_NULL(strstr(resp, "\"nextCursor\""));
14471471
free(resp);
14481472

14491473
cbm_mcp_server_free(srv);

0 commit comments

Comments
 (0)