{"record":{"id":"740aea5c9c092756","repo":"RocketChat/Rocket.Chat","slug":"mcp-batch-response-exceeds-the-formattedlimit-l","errorCode":null,"errorMessage":"MCP batch response exceeds the ${formattedLimit} limit","messagePattern":"MCP batch response exceeds the (.+?) limit","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/api/mcp/dispatch.ts","lineNumber":28,"sourceCode":"export type McpResponseBudget = {\n\tconsume: (bytes: number) => void;\n};\n\nconst TOOL_CALL_TIMEOUT_MS = 20_000;\nconst MAX_TOOL_RESPONSE_BYTES = 5 * 1024 * 1024;\nconst BYTES_PER_MEBIBYTE = 1024 * 1024;\n\nconst formatByteLimit = (bytes: number): string =>\n\tbytes % BYTES_PER_MEBIBYTE === 0 ? `${bytes / BYTES_PER_MEBIBYTE} MiB` : `${bytes} bytes`;\n\nexport const createMcpResponseBudget = (maxBytes = MAX_TOOL_RESPONSE_BYTES): McpResponseBudget => {\n\tlet remainingBytes = maxBytes;\n\tconst formattedLimit = formatByteLimit(maxBytes);\n\n\treturn {\n\t\tconsume(bytes) {\n\t\t\tif (bytes > remainingBytes) {\n\t\t\t\tthrow new Error(`MCP batch response exceeds the ${formattedLimit} limit`);\n\t\t\t}\n\n\t\t\tremainingBytes -= bytes;\n\t\t},\n\t};\n};\n\nconst readResponseText = async (response: Response, responseBudget?: McpResponseBudget): Promise<string> => {\n\tconst contentLength = Number(response.headers.get('content-length'));\n\tif (Number.isFinite(contentLength) && contentLength > MAX_TOOL_RESPONSE_BYTES) {\n\t\tthrow new Error('MCP tool response exceeds the 5 MiB limit');\n\t}\n\n\tif (!response.body) {\n\t\treturn '';\n\t}\n\n\tconst reader = response.body.getReader();","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/api/mcp/dispatch.ts#L10-L46","documentation":"Thrown by the shared MCP response budget (createMcpResponseBudget, default maxBytes = MAX_TOOL_RESPONSE_BYTES = 5 MiB). One MCP tools/call request can batch several tool invocations, and every response chunk read by readResponseText is charged to the same budget via consume(). Once the cumulative bytes of all tool responses in the batch would exceed the limit, consume() throws, aborting the whole batch.","triggerScenarios":"An MCP client sends a single tools/call request containing multiple tool invocations (e.g. several list/search tools) whose combined response bodies exceed 5,242,880 bytes; a later tool's chunk pushes consume() past remainingBytes.","commonSituations":"LLM agents batching many read-heavy tools per request; tools mapped to unpaginated REST endpoints (large channel/user/room lists); converting a bulky REST endpoint into an MCP tool without pagination.","solutions":["Split the tools/call request so each batch's combined responses stay well under 5 MiB","Pass the pagination args the tool exposes (count, offset, limit) to shrink each response","Redesign the tool to return IDs/summaries plus a follow-up tool for full records","If you own the tool mapping, cap the underlying REST endpoint's response size"],"exampleFix":"// before: one batch, three heavy list tools, combined > 5 MiB\ntoolsCall([listChannels({}), listUsers({}), listRooms({})]);\n\n// after: one paginated call per request, each within budget\ntoolsCall([listChannels({ count: 50, offset: 0 })]);\ntoolsCall([listUsers({ count: 50, offset: 0 })]);","handlingStrategy":"try-catch","validationCode":"// Byte totals are unknown before the call; constrain batch shape instead\nconst MAX_CALLS_PER_BATCH = 2;\nconst batches = chunk(requestedToolCalls, MAX_CALLS_PER_BATCH);","typeGuard":null,"tryCatchPattern":"try {\n  await dispatchBatch(tools);\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('MCP batch response exceeds')) {\n    // budget is per-batch: re-run each tool on its own so each gets the full 5 MiB\n    for (const tool of tools) {\n      await dispatchSingle(tool);\n    }\n  } else {\n    throw error;\n  }\n}","preventionTips":["Keep one tool call per MCP request when response sizes are unknown","Always pass explicit count/limit args to list-type tools","Remember the 5 MiB budget is shared across the whole batch, not per tool"],"tags":["mcp","response-size","payload-limit","batch"],"backgroundTag":"payload-too-large","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}