Skip to main content
MCP tools can return responses far larger than an LLM’s context window — a single Kubernetes workload listing or log export can be tens of thousands of tokens. Without intervention, this fills the context and degrades reasoning quality. Scratchpad solves this by intercepting large tool outputs and storing them on disk. The LLM gets a summary and eight read-only exploration tools (head, slice, grep, schema, item_schema, get_in, iterate_over, read) to selectively pull in only the data it needs. Scratchpad works in both single-agent and orchestration modes.

Configuration

Configure at [agent.scratchpad] (applies to the single agent, or provides defaults for orchestration workers) and optionally override per worker at [orchestration.worker.<name>.scratchpad]. Set a top-level memory_dir for persistence. See [agent.scratchpad] in the configuration reference for the full field table, and [mcp.servers.<name>.scratchpad] for per-tool interception thresholds.

Storage locations

  • Single-agent: {memory_dir}/scratchpad/
  • Orchestration: {memory_dir}/{run_id}/iteration-{n}/scratchpad/ (legacy [orchestration.artifacts].memory_dir still works as a fallback)

Per-tool interception thresholds

Per-tool interception thresholds are configured at [mcp.servers.<server>.scratchpad]. Keys are glob patterns (default threshold 5_120 if omitted) that are matched against tool names at interception time:
When multiple patterns match the same tool, the longest (most specific) pattern wins; on length ties the smallest threshold wins.

Token counting

Token counting uses real tokenization, not byte/character heuristics, so min_tokens and the context budget reflect actual model token cost. Dispatch is provider-aware:
  • OpenAI: tiktoken-rs, using o200k_base for GPT-5/4o/o-series models and cl100k_base for older models.
  • Gemini: an embedded Gemma 3 SentencePiece model (exact, fully local).
  • Anthropic / Bedrock-Claude: a calibrated cl100k_base × 1.1 approximation, since Claude ships no public tokenizer.
  • Everything else falls back to o200k_base.

Per-call extraction limit

max_extraction_tokens (default 10_000): every exploration tool checks the size of its result before returning. If a single call would exceed this cap (or the cumulative context budget), the tool returns a structured JSON error like {"error": "head_too_large", "estimated_tokens": ..., "suggestions": [...]} instead of the content. The LLM sees this as a successful tool result and retries with smaller params — each retry consumes a turn, which is why turn_depth_bonus exists. When an iterate_over result is too large, the iterate_over_too_large error leads with a ready-to-run paginated retry over a narrower window that halves the current window, ahead of suggestions to request fewer fields, use get_in, or use grep.

Paginate iterate_over with offset and limit

iterate_over can project the requested fields over a window of a large array instead of the whole array, using two optional arguments. This lets a call that would otherwise exceed max_extraction_tokens succeed over a slice. Windowed rows keep their absolute array position in the _index field. That position is offset plus the row’s position in the window, so paths that get_in resolves stay valid no matter which window produced the row. An offset past the end of the array returns an empty result [] rather than an error, so the agent can correct course. Each exploration tool call ends its result with a one-line footer that summarizes what it returned. When you paginate, that footer reports the window as a half-open interval over the total, with the start index inclusive and the end index exclusive. For example, the footer output iterate_over: $.results (items [3..7) of 120 total, fields: [id,title]) covers the items at index 3, 4, 5, and 6. With no offset or limit, the footer reports a full scan instead, for example 2 items. get_in and item_schema take the same offset and limit arguments. get_in paginates a large string value by line, and item_schema windows a large array by item.

Budget and usage reporting

Each agent (single-agent or orchestration worker) gets a fresh context budget scoped to that agent’s effective LLM’s context_window — workers never share an “orchestrator-level” budget. LLM-reported per-turn token counts feed back into the budget as ground truth, so remaining budget reflects actual context pressure. A per-agent aura.scratchpad_usage SSE event (with agent_id, tokens_intercepted, tokens_extracted) is emitted when the agent finishes — the same event name fires for both single-agent and worker contexts (it lives in the base aura.* namespace, not aura.orchestrator.*). See Streaming API Guide for the full event reference.

Result artifacts and read_artifact

In orchestration, large task results are saved to artifact files under {memory_dir}/{run_id}/artifacts/. The scratchpad read tools resolve files anywhere under a per-agent read root, not just the scratchpad subdir — for orchestration workers the read root is the session dir, so result artifacts are explorable in place without copying. When a worker reads an artifact back with read_artifact, the same budget rules apply: an artifact that fits is inlined and recorded against the budget; one that exceeds the limit comes back as a scratchpad pointer that the worker explores in place with the read tools (head, grep, slice, …) — never copied into the scratchpad. The coordinator has no scratchpad, so its read_artifact always returns inline content.