Skip to content

Feature request: cap/truncate MCP tool results before they enter model contextΒ #3480

Description

@enkiEng

Problem

MCP tool results are passed into the model's context uncapped. A single oversized result β€” e.g. an agent running cat on a multi-MB build or job log through an SSH/exec MCP server β€” can exceed the model's entire context window. The next completion request is then rejected by the provider (in our case an OpenAI-compatible endpoint returned 400 with ~4.37M prompt tokens against a 131k-context model) and the turn is lost.

Built-in tools already handle this: their output goes through truncateOutput (see #2835, where one built-in tool missing that call was fixed as a bug). MCP tool results have no equivalent β€” there is no truncation path and no config knob for it. Checked against the config schema on current main: the only related knobs are the ls tool's max_depth/max_items and per-model max_tokens, neither of which bounds an MCP result. #824 looks like the same failure mode reported from the provider side ("probably some tool returned a large result").

Server-side caps can't be assumed: most third-party MCP servers return unbounded results, and users can't patch every server they connect. The client is the natural last line of defense, same as it already is for built-in tools.

Proposal

A config option to cap MCP tool result size before it is added to context β€” ideally settable globally with per-server override, e.g.:

{
  "mcp": {
    "myserver": {
      "type": "stdio",
      "command": "...",
      "max_tool_result_bytes": 131072
    }
  }
}

On overflow, keep the head (~75%) and tail (~25%) of the cap and insert a marker in between stating how much was dropped, e.g.:

[... 41.2 MB truncated (returned first 96 KB + last 32 KB). Narrow the command with head/tail/grep, or write to a file and sample it. ...]

The head+tail+marker shape matters: head keeps the command banner/first errors, tail keeps the final error/exit summary, and the marker teaches the model to self-correct with a narrower command on the next call instead of retrying the same one.

Prior art

We implemented exactly this server-side in an SSH MCP server (enkiEng/mcp-ssh-go#1 β€” bounded head + ring-buffer tail, per-call override, inline marker) and it works well: the model reliably narrows its next command when it hits the marker. But that only protects one server; a client-side cap would cover every MCP server crush connects to.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions