Saphan StudioDocs
MCP server

The response budget

Five stacked mechanisms so a fleet read fits a client's tool-output limit - and a cap that errors rather than truncates.

An unbounded fleet_show on a large record can exceed an MCP client's tool-output limit, and the client then drops the response instead of handing it to the model. MCP offers no mechanism for a client to declare its budget to a server, so the server chooses defaults. Five mechanisms stack:

Default live horizon. fleet_show and wave_show default to horizon: "live" — terminal streams (merged, closed-without-merge) are excluded — on a mature record they are most of the payload. History is one explicit argument away (horizon: "all" — byte-identical to a pre-narrowing read), and a defaulted response names the default that fired rather than narrowing silently:

This query used the default live-only horizon because "horizon" was not provided
(merged/closed-without-merge streams are excluded). If results seem incomplete,
pass horizon:"all" to include them.

Honest summary. detail: "summary" (the default) carries the stream-identity and state fields, a four-field last_gate (no justification prose — that lives in stream_detail and full), and a trimmed liveness block. detail: "full" remains complete; the budget moves data behind an explicit request, it never removes data. The descriptors enumerate both levels exhaustively.

Cursor pagination as tool arguments. limit defaults to 25, maximum 100 — a value above the maximum is rejected with limit must be an integer between 1 and 100, never silently clamped. A truncated response is a successful bounded response (isError: false): it carries next_after, which names the exact next call (after: <cursor>), and total_count still answers how many streams exist unfiltered — a truncated list can never be mistaken for the complete set. (MCP protocol pagination deliberately does not cover tools/call; these are ordinary arguments read by the model from the descriptor, which is why the descriptor discipline above is load-bearing.)

Tabular formats. format: "csv" / "tsv" return rows only, a fixed narrow column set — noticeably fewer tokens than JSON for this row shape. This changes the read contract (a table, not the envelope) and is therefore opt-in.

A hard byte cap as an error, never a silence. After all narrowing has run, a response at or above 200,000 bytes is refused, class result-too-large, with the exact next move:

result-too-large: fleet_show response is <n> bytes, exceeds the 200000-byte cap —
narrow with a state/streams filter, lower limit below its current value, or omit
detail:full (summary, the default, is far smaller)

The reasoning: clients truncate oversized tool results silently, at boundaries the server did not choose, and the model then answers with full confidence from a fragment. If the server does not cut explicitly, something below will cut implicitly. Additionally, fleet_show / wave_show declare _meta["anthropic/maxResultSizeChars"] = 200000 — a transitional client-side allowance at the cap, never a substitute for the narrowing. The three aggregate tools carry the same 200,000-byte cap and refuse rather than truncate.