{"record":{"id":"b847168dc32fd7f8","repo":"Hmbown/CodeWhale","slug":"mcp-exceeded-the-item-catalog-limit","errorCode":null,"errorMessage":"MCP {} exceeded the {}-item catalog limit","messagePattern":"MCP (.+?) exceeded the (.+?)-item catalog limit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":376,"sourceCode":"        let values = page.get(field).and_then(Value::as_array).with_context(|| {\n            format!(\n                \"MCP {} response did not contain a '{field}' array\",\n                self.method\n            )\n        })?;\n\n        self.pages = self.pages.saturating_add(1);\n        self.items = self.items.saturating_add(values.len());\n        self.bytes = self.bytes.saturating_add(serde_json::to_vec(page)?.len());\n        if self.pages > self.max_pages {\n            bail!(\n                \"MCP {} exceeded the {}-page catalog limit\",\n                self.method,\n                self.max_pages\n            );\n        }\n        if self.items > self.max_items {\n            bail!(\n                \"MCP {} exceeded the {}-item catalog limit\",\n                self.method,\n                self.max_items\n            );\n        }\n        if self.bytes > self.max_bytes {\n            bail!(\n                \"MCP {} exceeded the {}-byte aggregate catalog limit\",\n                self.method,\n                self.max_bytes\n            );\n        }\n\n        let next_cursor = match page.get(\"nextCursor\") {\n            None => None,\n            Some(Value::String(cursor)) => Some(cursor.clone()),\n            Some(_) => bail!(\"MCP {} returned a non-string nextCursor\", self.method),\n        };","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L358-L394","documentation":"observe_page tracks the cumulative number of catalog items seen across pages. When the total exceeds max_items, the whole listing fails instead of silently returning a truncated catalog, honoring the fail-loud budget contract.","triggerScenarios":"list_paginated accumulates items across successive pages and the running total (self.items) surpasses max_items after a page is observed.","commonSituations":"Very large MCP server catalogs (thousands of tools/resources); max_items tuned for a smaller deployment; a server bug duplicating entries across pages.","solutions":["Increase max_items in the catalog budget","Reduce the server's exposed catalog size (namespacing/filtering tools)","Check for a server bug that re-lists the same items on every page"],"exampleFix":"// before\nmax_items: 100\n// after\nmax_items: 1000","handlingStrategy":"try-catch","validationCode":"// estimate expected item volume before listing\nlet expected_items = known_tool_count + known_resource_count;\nassert!(max_items >= expected_items, \"max_items below expected catalog size\");","typeGuard":null,"tryCatchPattern":"match client.list_resources_with_metadata().await {\n    Ok(entries) => use(entries),\n    Err(e) if e.to_string().contains(\"-item catalog limit\") => {\n        eprintln!(\"catalog exceeds item budget: {}\", e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Budget max_items against the server's real catalog size","Deduplicate server-side to avoid re-listed entries","Re-tune budgets when servers add tools/resources"],"tags":["mcp","pagination","limits"],"backgroundTag":"payload-too-large","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}