RocketChat/Rocket.Chat · error · Error

MCP batch response exceeds the ${formattedLimit} limit

Error message

MCP batch response exceeds the ${formattedLimit} limit

What it means

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.

Source

Thrown at apps/meteor/ee/server/api/mcp/dispatch.ts:28

export type McpResponseBudget = {
	consume: (bytes: number) => void;
};

const TOOL_CALL_TIMEOUT_MS = 20_000;
const MAX_TOOL_RESPONSE_BYTES = 5 * 1024 * 1024;
const BYTES_PER_MEBIBYTE = 1024 * 1024;

const formatByteLimit = (bytes: number): string =>
	bytes % BYTES_PER_MEBIBYTE === 0 ? `${bytes / BYTES_PER_MEBIBYTE} MiB` : `${bytes} bytes`;

export const createMcpResponseBudget = (maxBytes = MAX_TOOL_RESPONSE_BYTES): McpResponseBudget => {
	let remainingBytes = maxBytes;
	const formattedLimit = formatByteLimit(maxBytes);

	return {
		consume(bytes) {
			if (bytes > remainingBytes) {
				throw new Error(`MCP batch response exceeds the ${formattedLimit} limit`);
			}

			remainingBytes -= bytes;
		},
	};
};

const readResponseText = async (response: Response, responseBudget?: McpResponseBudget): Promise<string> => {
	const contentLength = Number(response.headers.get('content-length'));
	if (Number.isFinite(contentLength) && contentLength > MAX_TOOL_RESPONSE_BYTES) {
		throw new Error('MCP tool response exceeds the 5 MiB limit');
	}

	if (!response.body) {
		return '';
	}

	const reader = response.body.getReader();

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Split the tools/call request so each batch's combined responses stay well under 5 MiB
  2. Pass the pagination args the tool exposes (count, offset, limit) to shrink each response
  3. Redesign the tool to return IDs/summaries plus a follow-up tool for full records
  4. If you own the tool mapping, cap the underlying REST endpoint's response size

Example fix

// before: one batch, three heavy list tools, combined > 5 MiB
toolsCall([listChannels({}), listUsers({}), listRooms({})]);

// after: one paginated call per request, each within budget
toolsCall([listChannels({ count: 50, offset: 0 })]);
toolsCall([listUsers({ count: 50, offset: 0 })]);
Defensive patterns

Strategy: try-catch

Validate before calling

// Byte totals are unknown before the call; constrain batch shape instead
const MAX_CALLS_PER_BATCH = 2;
const batches = chunk(requestedToolCalls, MAX_CALLS_PER_BATCH);

Try / catch

try {
  await dispatchBatch(tools);
} catch (error) {
  if (error instanceof Error && error.message.startsWith('MCP batch response exceeds')) {
    // budget is per-batch: re-run each tool on its own so each gets the full 5 MiB
    for (const tool of tools) {
      await dispatchSingle(tool);
    }
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/740aea5c9c092756. Report an issue: GitHub.