{"record":{"id":"82d02624b62e6266","repo":"Hmbown/CodeWhale","slug":"exceeded-the-item-catalogue-limit","errorCode":null,"errorMessage":"{} exceeded the {}-item catalogue limit","messagePattern":"(.+?) exceeded the (.+?)-item catalogue limit","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":1319,"sourceCode":"    }\n\n    fn observe_page(\n        &mut self,\n        result: &serde_json::Value,\n        item_count: usize,\n    ) -> Result<Option<String>> {\n        self.pages = self.pages.saturating_add(1);\n        self.items = self.items.saturating_add(item_count);\n        self.bytes = self.bytes.saturating_add(serde_json::to_vec(result)?.len());\n        if self.pages > MAX_MCP_CATALOG_PAGES {\n            anyhow::bail!(\n                \"{} exceeded the {}-page catalogue limit\",\n                self.method,\n                MAX_MCP_CATALOG_PAGES\n            );\n        }\n        if self.items > MAX_MCP_CATALOG_ITEMS {\n            anyhow::bail!(\n                \"{} exceeded the {}-item catalogue limit\",\n                self.method,\n                MAX_MCP_CATALOG_ITEMS\n            );\n        }\n        if self.bytes > MAX_MCP_CATALOG_BYTES {\n            anyhow::bail!(\n                \"{} exceeded the {}-byte aggregate catalogue limit\",\n                self.method,\n                MAX_MCP_CATALOG_BYTES\n            );\n        }\n        let cursor = result\n            .get(\"nextCursor\")\n            .and_then(|value| value.as_str())\n            .map(str::to_owned);\n        if let Some(cursor) = cursor.as_ref()\n            && !self.seen_cursors.insert(cursor.clone())","sourceCodeStart":1301,"sourceCodeEnd":1337,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L1301-L1337","documentation":"Second leg of McpCatalogBudget: the running item count across pages of one catalogue method must stay within MAX_MCP_CATALOG_ITEMS = 4096 (crates/tui/src/mcp.rs:1281, 1318-1324). Exceeding it aborts that method's discovery and thus the connection - a guard against unbounded tool/resource/prompt catalogues.","triggerScenarios":"A server whose tools/list (or resources/prompts list) exposes more than 4096 items in total across its pages.","commonSituations":"Large API gateways, monorepo tool aggregators, or meta-servers that federate many upstreams into one catalogue.","solutions":["Trim the catalogue server-side: expose fewer tools/resources/prompts.","Split the server into multiple focused MCP servers, each under 4096 items.","If you control the server, consider filtering by scope/profile so each consumer sees a bounded catalogue."],"exampleFix":"# before: one gateway server exposing 6000 tools\n# after: three MCP servers (git-tools, ci-tools, docs-tools), each < 4096 items","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\n# Probe total items across pages; must stay <= 4096 per method.\nurl=\"https://mcp.example.com/mcp\"; cursor=\"\"; items=0\nwhile :; do\n  body=$(curl -s \"$url\" -H 'content-type: application/json' \\\n    -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/list\\\"${cursor:+,\\\"params\\\":{\\\"cursor\\\":\\\"$cursor\\\"}}}\")\n  items=$((items + $(printf '%s' \"$body\" | jq '.result.tools | length')))\n  [ \"$items\" -gt 4096 ] && { echo \"would exceed 4096 items\"; exit 1; }\n  cursor=$(printf '%s' \"$body\" | jq -r '.result.nextCursor // empty');\n  [ -z \"$cursor\" ] && break\ndone; echo \"items=$items ok\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep each server's catalogue under 4096 items.","Disable unused tool groups server-side.","Federate large catalogues across several MCP servers."],"tags":["mcp","pagination","limits","tools"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}