MCP servers
This page covers how mono-agent attaches Model Context Protocol (MCP) servers to your agent through tools.mcpConfigPath, how the path is resolved and forwarded to the runtime, and the one rule that surprises people: external MCP-server tools are not gated by tools.allowedTools. App-owned MCP tools can define a narrower policy boundary; RunHistory, SessionHistory, MemoryJournal, SetConversationTitle, Remember, and the adapter send tools do. Coverage type: config.
What tools.mcpConfigPath does
Section titled “What tools.mcpConfigPath does”Point tools.mcpConfigPath at an mcp.json file describing one or more MCP servers (stdio, SSE, or streamable HTTP). The agent gains every tool those servers advertise.
{ "tools": { "allowedTools": ["Read", "Grep"], "disallowedTools": ["Bash"], "mcpConfigPath": "./mcp.json" }}| Key | Type | Notes |
|---|---|---|
tools.mcpConfigPath | string | Path to an mcp.json. Resolved against the workspace, not the config file. |
tools.mcpRequestContextServers | string[] | Opt-in stdio server names that receive trusted per-run producing-conversation, run-id, output-directory, current-request attachment, and scoped progress context. HTTP/SSE and unlisted servers are unchanged. |
tools.continuationServers | string[] | Opt-in stdio or loopback-HTTP server names that receive a host-bound claim capability for durable asynchronous results. Remote HTTP, SSE, and unlisted servers fail closed or remain unchanged. |
tools.allowedTools | string[] | Allowlist for built-in runtime tools (Read, Write, Edit, Glob, Grep, Exec, Bash, NodeRepl, WebFetch, WebSearch) and policy-gated app-owned tools such as RunHistory, SessionHistory, MemoryJournal, SetConversationTitle, Remember, and adapter send tools. Omit (or ["*"]) for allow-all; a specific list narrows to those names. Does not affect external MCP-server tools. |
tools.disallowedTools | string[] | Denylist; deny always wins, even under allow-all. Filters built-ins, ReadSkill, RunHistory, SessionHistory, MemoryJournal, SetConversationTitle, Remember, and adapter send tools. On the pi-native runtime it does not filter external MCP-server tools (see below). |
Environment overrides: MONO_AGENT_MCP_CONFIG_PATH sets tools.mcpConfigPath, MONO_AGENT_MCP_REQUEST_CONTEXT_SERVERS selects request-context stdio servers, and MONO_AGENT_CONTINUATION_SERVERS selects continuation-capable stdio/loopback-HTTP servers.
mcpConfigPath resolves against the workspace (runtime.workspace, default "."), so a relative path like ./mcp.json is read from the same folder the agent operates in. Keep the file beside your mono-agent.config.json and reference it relatively for portability.
Example mcp.json (stdio server)
Section titled “Example mcp.json (stdio server)”A stdio server is a child process the runtime spawns and talks to over stdin/stdout:
{ "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"], "env": { "LOG_LEVEL": "info" } } }}SSE and streamable HTTP servers use a url instead of command/args:
{ "mcpServers": { "remote-api": { "url": "https://mcp.example.com/sse" } }}HTTP/SSE header values in mcp.json are literal; mono-agent does not expand
environment references inside them. Do not put credentials in this file,
whether committed or merely untracked. There is currently no credential
indirection for authenticated HTTP/SSE entries, so do not declare one unless a
separate trusted mechanism supplies authentication without putting it in this
file.
Treat every declared stdio server as fully trusted. Environment delivery is
runtime-dependent: some runtimes inherit the full agent process environment,
including unrelated credentials loaded from .env or --env-file, while
others launch from a restricted safe list. Literal per-server env entries are
passed through. Do not rely on inherited variables for credential delivery or
on a restricted runtime for per-server secret isolation.
Run mono-agent validate to confirm the file is found; it reports the resolved
MCP config: path or an MCP config file is missing: warning.
Trusted request context for a stdio server
Section titled “Trusted request context for a stdio server”An MCP server that owns conversation-scoped data or long-running progress must not ask the model to supply a chat or conversation id. Opt it in by configured server name instead:
{ "tools": { "mcpConfigPath": "./.mcp.json", "mcpRequestContextServers": ["transcribe"] }}After all static, request, and authoritative tool-policy options are merged, mono-agent clones the selected stdio spec for that run and overwrites these env keys with host-owned values:
MONO_AGENT_MCP_PRODUCING_CONVERSATION_IDMONO_AGENT_MCP_PRODUCING_RUN_IDMONO_AGENT_MCP_RUN_OUTPUT_DIRMONO_AGENT_MCP_ATTACHMENTS_ROOTMONO_AGENT_MCP_ALLOWED_ATTACHMENT_PATHSMONO_AGENT_MCP_ALLOWED_ATTACHMENT_IDENTITIESMONO_AGENT_INTERACTION_PROGRESS_URLMONO_AGENT_INTERACTION_PROGRESS_TOKEN
The output directory is artifacts/outbound/<run-id>. It is scratch space for
the current run, not a durable artifact: mono-agent removes the exact directory
object it created only after the runtime and tool clients settle. The attachment
root is canonical. MONO_AGENT_MCP_ALLOWED_ATTACHMENT_PATHS is a JSON array of
the exact lexical paths saved successfully from this request. The accompanying
MONO_AGENT_MCP_ALLOWED_ATTACHMENT_IDENTITIES value is a JSON array of
{ "path", "dev", "ino" } objects captured from each file descriptor after
write and sync. Consumers must require both an allowed path and its matching
device/inode identity before reading. A later turn, another conversation, and
failed saves are never carried over. Authoritative empty arrays are injected
when there are no allowed files.
The progress bearer is valid only for that run and conversation, cannot call ask
routes, and is revoked at cleanup. The bridge revalidates it after reading the
request body, so a post stalled during run cleanup is rejected. /v1/progress
derives its destination from the bearer; a submitted conversationId cannot
redirect it. Configured spoof values lose to the trusted overlay, the shared MCP
config is not mutated, and the bridge master URL/token are explicitly blanked for
opted project MCPs.
You can also inline servers directly in config via tools.mcpServers (an object keyed by server name) instead of a separate file. The file (mcpConfigPath) and the inline form (mcpServers) carry the same per-server schema.
Durable continuation context
Section titled “Durable continuation context”tools.continuationServers is a separate trust decision from tools.mcpRequestContextServers. It lets one selected stdio or loopback-HTTP MCP service claim a host-bound continuation during the originating request, then submit an immutable result later. The host retains the channel/thread destination and runs the eventual synthesis and native delivery; the model and A2A payload never receive that routing authority.
{ "tools": { "mcpConfigPath": "./mcp.json", "continuationServers": ["work-control"] }}For stdio, mono-agent overwrites MONO_AGENT_CONTINUATION_CLAIM_URL, _TOKEN, _FINGERPRINT, and _MODE. For loopback HTTP it overwrites the matching x-mono-agent-continuation-claim-* headers. Only localhost, 127.0.0.1, and ::1 HTTP endpoints are supported; selecting SSE or a remote HTTP server raises a capability error before provider work starts.
The claim credential is short-lived and tied to the run, selected server, origin history, physical reply target, and mode. It must be exchanged while the MCP request is active. See Durable continuations for configuration, endpoint shapes, retries, named routes, and recovery.
Runtime support
Section titled “Runtime support”The Pi runtime inlines the servers into the runtime options it passes to the model session: mcp.json is read and its mcpServers are merged into the request. You author one mcp.json and mono-agent does the translation. See Providers for how the Pi runtime reaches the provider delivering the session.
External MCP tools are NOT gated by tools.allowedTools
Section titled “External MCP tools are NOT gated by tools.allowedTools”This is the load-bearing rule for declared external servers. tools.allowedTools / tools.disallowedTools filter built-in runtime tools (Read, Bash, …) and policy-gated app-owned tools. They do not suppress tools provided by an external MCP server.
Consequences:
- Under allow-all (the default) MCP tools are available because their server is declared, not because of the wildcard. Setting
tools.allowedTools: [](“no built-in tools”) still leaves every MCP tool available. - An MCP tool’s availability is governed by whether its server is declared in
mcp.json/tools.mcpServers, not by the allowlist. To withhold an MCP tool, remove or don’t declare its server. - On the pi-native runtime,
disallowedToolsdoes not filter external MCP-server tools — declaring the server is the only lever. To hard-restrict an external MCP tool on pi, don’t declare its server. - App-injected MCP tools define their own boundary.
MemoryRecallandAskCollaboratorare gated by their own enablement/composition switches;RunHistory,SessionHistory,SetConversationTitle,Remember, and adapter send tools are deliberately governed by the normal tool policy.MemoryJournalrequires both memory-read enablement/capability and normal app-tool policy.
The MemoryRecall description directs proactive recall of intentionally captured
durable facts, while explicitly routing requests to pick up, continue, or
recover interrupted work to RunHistory {} first. Empty recall results repeat
that exact conditional handoff instead of inviting repeated query rewrites.
This is behavioral guidance, not a gate — MemoryRecall’s availability is still
governed by config.memory.recallTool.enabled. See Capture & recall.
MemoryJournal: curated chronology
Section titled “MemoryJournal: curated chronology”MemoryJournal is an app-owned, request-scoped MCP tool for broad retrospectives
over curated local memory. It complements indexed MemoryRecall; it does not scan
arbitrary files, duplicate the journal, auto-inject journal bodies, expose BuJo
audit/ observations, or claim exact execution evidence.
The first call has one strict shape:
{ "fromDate": "2026-09-01", "throughDate": "2026-09-07", "timeZone": "Europe/Amsterdam", "limit": 10}Dates are inclusive local calendar dates in the named IANA zone and resolve to
[fromDate 00:00, day-after-throughDate 00:00) UTC instants. The range is at most
31 calendar days; limit defaults to 10 and caps at 25. Invalid, reversed, skipped,
or non-representable boundaries fail closed instead of falling back to the host zone.
A continuation accepts only { "cursor": "..." }; the opaque cursor authenticates
the exact issued offset against request-private state and is bound to the run, range,
zone, snapshot, and page size.
One first call freezes a request-local snapshot of at most 1,000 eligible entries and 2 MiB of UTF-8 record data; at most four snapshots may exist in one run. Entry text is capped at 2,048 UTF-8 bytes, serialized entries at 8 KiB per page, cursors at 2,048 bytes, and model-facing text at the shared 10,000-character ceiling. Pagination reads that frozen snapshot, so later inserts cannot create duplicates or skips. Coverage separates a next page from an incomplete range scan and reports truncation and the last included ordering key.
Every successful result is evidenceKind: "curated_memory_summary" and
untrusted: true. Entries include normalized timestamps, lifecycle/supersession state,
and only a safe relative canonical daily source such as daily/YYYY-MM-DD.md; memory
root paths, conversation/session provenance, access telemetry, dropped records, and raw
audit observations are excluded. Unsafe text or identifiers are replaced with a fixed
omission marker. Historical entry text is evidence, never instructions.
A complete empty range is status: "ok" with noData: true. Invalid input/cursors
and range bounds return closed error codes. Store failures return the generic
journal_unavailable error without raw paths or backend text. Unsupported capability is
different again: the endpoint is not composed. Lite, Journal, and BuJo support it over
their local canonical index without embedding/chat calls; Supermemory does not, and no
search fallback or fake empty result is provided.
Availability requires memory.recallTool.enabled, an affirmative local browse
capability, and app-tool policy. Under a restrictive allowlist name MemoryJournal,
mcp__mono-agent-memory-journal__MemoryJournal, or
mcp__mono-agent-memory-journal__*; exact/server/global deny wins. There is no legacy
alias. Use active conversation history for the current exchange, MemoryRecall for a
targeted durable fact, this tool for broad date-bounded summaries, and
RunHistory/SessionHistory for exact commands, results, failures, or recovery.
SetConversationTitle: web conversation naming
Section titled “SetConversationTitle: web conversation naming”SetConversationTitle is an app-owned request-scoped MCP tool, not an entry in
mcp.json. The host injects it only for an ordinary interactive web:<threadId>
request whose web service still considers the title writable. Its strict input
is { "title": "..." }: surrounding/internal whitespace is normalized, control
characters are rejected, and the result is capped at 80 characters. The tool
description asks the model to name the conversation as a whole and to keep
refining that name as a better whole-thread name emerges, never to use it as a
status line for the step in progress. The tool result is a proposal: the web
service applies it out of band and may decline it, so the tool never reports
that a rename landed.
Allow-all exposes the tool automatically on compatible routes. A restrictive
policy must name SetConversationTitle, while disallowedTools can remove it.
Trigger-created
and archived web threads never advertise title write authority. The web service
consumes only a successful structured result from this exact tool during the
active turn, performs a conditional SQLite update, and emits the normal thread
invalidation. A browser rename sets a permanent manual lock, so a later or
racing agent result cannot overwrite it. The call remains visible in the
collapsed Activity disclosure but creates no assistant message. If the tool is
absent or unused, the first user message remains the automatic fallback title.
Remember: durable memory writes
Section titled “Remember: durable memory writes”Remember is an app-owned request-scoped MCP tool that durably stores one
explicitly stated fact. It is deterministic and append-only and uses no chat
LLM, so a successful result means the fact is already recallable through
MemoryRecall rather than queued for later curation.
Its strict input is { "text": "..." }. The text is stored NFKC-normalized,
trimmed, and whitespace-collapsed to a single line — a memory is one markdown
bullet — and the result echoes back exactly what was written, so the model can
see how its input was normalized. Text over 500 characters once normalized is
rejected.
Availability is narrower than recall. The tool appears only when a memory block
is configured, memory.rememberTool.enabled is not false, tool policy allows
Remember, and the store affirms it can accept writes. That last condition
excludes read-only stores and the Supermemory backend, which implements the
shared MemoryStore contract but no durable write surface: a configured memory
block does not by itself grant a write tool. Unlike MemoryRecall, Remember
is gated by tools.allowedTools, so an operator can withhold durable writes
while keeping recall; disallowedTools removes it and deny wins.
Writes are idempotent across partial failure. The bullet id is derived from the content hash, so a retry recomputes it; the index is consulted first and then the whole canonical source, not just today’s file. A canonical bullet whose index row is missing — the window left by a crash between the two writes — is therefore completed rather than duplicated, including when the retry arrives after the UTC date rolls over.
Text that carries a configured credential value, a well-known token shape (OpenAI, GitHub classic and fine-grained, Slack bot and app-level, AWS access key ids, bearer/basic schemes, Telegram bot tokens, all case-insensitive), a credential assignment, or terminal/bidi control characters is rejected and nothing is written; the model is told to restate the fact without the secret. Rejecting rather than redacting keeps a mangled value from being stored while still reporting success. The check runs against the exact normalized text and compares it with configured values in the same Unicode domain, so a credential written in a compatibility form does not slip past.
The configured-value scan reads the environment the host resolved, not
process.env, so a secret supplied only through startMonoAgentApp({ env }) is
still recognized. Only *_ENV, *_ENV_VAR, *_PATH, *_FILE, and *_DIR
names are skipped, because those hold a variable name or a location rather than
a secret.
Names such as SERVICE_API_TOKENS are deliberately not skipped: the same
helper backs other persistence guards, so a broader carve-out would weaken
two surfaces at once. The accepted cost is a false rejection — a
credential-named budget like ..._KEEP_RECENT_TOKENS=8000 makes the literal
8000 unstorable through this tool.
As security notes, this is defense in depth rather than a guarantee: free-form model text can still carry sensitive data that no closed pattern set recognizes.
Two outcomes are reported distinctly. A failure before anything durable is written says the fact was not stored, and the model may reword it. A canonical write whose index projection then failed reports the fact as durable but unindexed and tells the model to retry the identical wording — which completes it idempotently — rather than reword and create a second memory for one fact.
Two states are refused rather than silently mishandled: a fact that was
explicitly forgotten through mono-agent memory forget is not re-storable
through the tool, and a date still kept only in the root-level legacy layout is
refused because creating the modern daily/<date>.md would hide the legacy file
from the next rebuild.
Memory is append-only. There is no edit or delete through this tool; removal
remains the explicit two-phase mono-agent memory forget workflow.
RunHistory: prior-run evidence
Section titled “RunHistory: prior-run evidence”RunHistory is an app-owned, read-only, request-scoped MCP tool over the existing local run artifacts. There is no new config key. Under allow-all it is exposed automatically on MCP-capable routes; under a restrictive policy, add the exact RunHistory name. The deprecated policy alias run_history is accepted in tools.allowedTools / tools.disallowedTools, but only RunHistory is registered and shown to the model.
The compact shorthand is designed for agent exploration:
| Call arguments | Result |
|---|---|
{} | List recent settled runs (succeeded, failed, cancelled, or interrupted). Start here for an unhinted request to pick up, continue, or recover interrupted work. |
{ "query": "north Spain flights" } | Search safe trigger and summary metadata, ranked by how many Unicode-normalized, case-folded terms a run carries. A single match also carries that run’s compact overview. |
{ "runId": "..." } | Return a compact overview: metadata and trigger, final visible output, warnings/failures, tool-name call/error counts, and a timeline cursor when detail exists. |
{ "runId": "...", "cursor": "..." } | Return the next timeline page (at most 10 entries and about 16 KiB). |
run_id is accepted as an input alias for runId. Explicit
action: "list" | "search" | "inspect" calls remain compatible. List and
search accept an optional limit (default 5, range 1–10) and expose an opaque
nextCursor when more matches remain. Tool-authored
navigation.guidance and navigation.nextActions[] are separate from the
untrusted evidence; each next action contains exact arguments the agent can
submit to continue, narrow, or return to an overview. Candidate actions carry
the run’s terminal status so a cancelled/interrupted run is not mistaken for a
successful one.
After inspecting a cancelled or interrupted candidate, its overview labels
the output as incomplete evidence and exposes a conditional exact run-scoped
sibling-tool handoff in navigation.relatedTools. Follow it only when
SessionHistory is available:
{ "action": "search", "runIds": ["cancelled-run-id"], "includeIsolated": true}Keep that search free of a states filter so every retained terminal tool state
is considered. If it is empty, do not broaden to another run or conversation.
Each search result distinguishes the invocation recordId from its terminal
resultRecordId. Follow the response’s trusted navigation to inspect the
result record when present and the invocation record when its arguments are
needed. Those exact get calls retain includeIsolated: true, use the supported
8192-byte chunk bound, and provide another exact get action whenever a cursor
must be followed. A bounded search preview is not the full record and must not
substitute for record-level inspection during interrupted-work recovery. This
path recovers grounded evidence only: it does not resume provider state, replay
a tool, rerun work, or guarantee continuation from the interrupted point.
Continuation is fresh work in the current run, using currently available tools
and fresh verification.
List/search matching scans retained summaries once per call. It does not open event JSONL to decide what matches and only considers sanitized trigger/user input, run id, dates, status/failure kind, source/detail, model, and effort. It never searches system prompts, reasoning, memory, visible assistant output, or tool output.
Search ranks rather than requiring every term, because a caller naming what
it wants (“unsubscribe group A newsletters”) usually says more than the trigger
it is looking for did. Runs carrying every term win outright and are returned
alone; ranked partial matches are offered only when no run carried the whole
query, in which case matchedAllTerms is false and navigation.guidance
names the terms the best candidate actually matched — from the same fields the
scorer read, so a run that ranked on its model or date says so. A run matching
no term is never returned. Because matching is substring matching, a lone ASCII
letter or digit (the “A” of “group A”) is dropped from scoring unless the whole
query is made of them.
When a search lands on exactly one run and no cursor was supplied, the result
also carries that run’s compact overview and offers the timeline cursor as the
next action — the “which one?” round trip is skipped. This overview
hydration is the one place a search touches event JSONL, and it is the same
bounded read and same safe projection an explicit { "runId" } performs: no
wider read than the follow-up call it replaces, and no change to what a search
matches on. An unreadable artifact for that run degrades to the plain search
result rather than a tool error.
The current or any running run is excluded, as are unrelated conversations and threads. When daily session rollover is configured, its #YYYY-MM-DD buckets are ignored for RunHistory scope, so rollover never partitions one logical conversation’s recorded history. The safe projection never returns system prompts, reasoning/thinking, recalled memory or turn-context payloads, raw artifact paths, or provider-session metadata. Ordinary filesystem spans are sanitized in place to [host-path] plus a bounded non-sensitive suffix, so surrounding commands, tool results, and assistant diagnostics remain visible; credentials and private run-artifact content are still omitted. Absolute roots, account/home prefixes, artifact roots, and private run paths never survive. Structured and artifact-shaped opaque tool results are scrubbed or omitted; nested RunHistory result bodies are always replaced with an omission marker so inspection cannot recursively embed prior inspections. Structured projected values first pass through the shared observability redactor: non-numeric values under sensitive-looking object keys are redacted; numeric values under matched keys are retained; free text is not content-scanned or scrubbed. RunHistory then applies an additional projection sanitizer to object keys as well as string values, with deterministic collision-safe key disambiguation. In that second pass, numeric values under credential, private_key, and bearer can remain visible; numeric values under apiKey, token, client_secret, password, authorization, and cookie are redacted. Assignment-shaped password or secret prose is content-scanned and replaced with the diagnostic or tool-result omission sentinel. An optionally quoted assignment value is exempt only when its complete value is exactly [redacted]; any prefix or suffix is omitted. Per-string and per-page bounds still apply, and incomplete event input is announced. All historical content is labelled untrusted evidence, never instructions.
Use active conversation history first for the current exchange. Use MemoryRecall for
a targeted intentionally captured durable fact, MemoryJournal for a broad
date-bounded curated summary, and RunHistory for exact settled evidence from an
earlier run or tool call, including interrupted-work recovery. See
Artifacts and traces.
SessionHistory: retained tool lifecycles
Section titled “SessionHistory: retained tool lifecycles”SessionHistory is the sibling read-only, request-scoped MCP tool over the canonical managed-tool sidecar, not an extension of RunHistory. It has no config key. Allow-all exposes it automatically on compatible routes; a restrictive policy must name SessionHistory (session_history is a deprecated policy alias), and disallowedTools can remove it. The endpoint is bound to a random per-request loopback capability path, requires the exact loopback Host header, creates a fresh stateless MCP server/transport for every HTTP request, and disappears at request cleanup.
The event-side writer keeps a 250 ms foreground ceiling. An accepted write that
has not answered by then is marked deferred, continues in the dedicated worker,
and is drained before bounded run finalization; only a definitive rejection is
marked failed. A deferred run event is not a final persistence claim. This
tool reads only committed rows, so after the run its result is authoritative for
retained lifecycle evidence, and every returned record remains untrusted data.
| Call arguments | Result |
|---|---|
{} or { "action": "search" } | Up to five completed prior managed-tool calls in the current logical session. |
{ "query": "needle", "tools": ["Read"], "states": ["error"], "runIds": ["..."], "fromMs": 0, "toMs": 9999999999999, "limit": 10 } | AND-match redacted stored text and filter by exact tool/state/run/time; returns an opaque nextCursor when more remain. |
{ "action": "get", "recordId": "sth1_...", "chunkBytes": 4096 } | One invocation or result plus a bounded payload chunk, truncation/byte metadata, terminal classification, recovery flag, and opaque artifact availability. |
{ "action": "get", "toolCallId": "..." } | The newest completed record for that provider call id in scope. Prefer recordId when search returned one. |
{ "action": "get", "cursor": "..." } | Continue the same record in chunks of at most 8 KiB. |
Search defaults to five and caps at ten results; tool/state/run filters cap at 20 values, queries at 512 UTF-8 bytes, previews at 1 KiB, and model-facing text blocks at 10,000 characters. Cursors are base64url structural tokens bound to the logical conversation, physical conversation, current run, and original filters; substitution, stale anchors, foreign records, and pruned anchors fail opaquely. Only the exact active (conversationId, runId) is blocked, so an older daily bucket that reused the same opaque run id remains visible. Daily rollover buckets share one configured logical-session scope, while a natural # in an id remains opaque when rollover is off. Isolated/proactive records require includeIsolated: true on both the originating call and any continuation.
Search responses provide trusted, structured navigation separately from the
untrusted previews. A search item uses recordId for the invocation and
resultRecordId for the terminal result. Navigation points to the result first
when it exists and to the invocation when its arguments are relevant, with
exact get arguments that preserve isolation and use chunkBytes: 8192.
Continue a paged get with the exact returned cursor action until the record is
complete. An empty exact-run search explicitly offers no broader search action.
Every returned preview/chunk is redacted, bounded, path-opaque, and marked untrusted: true with an explicit notice. Path opacity applies to object keys as well as string values: an opaque host-root token and at most two non-sensitive trailing components replace each filesystem path while useful command/result text, nested structure, punctuation, line/column suffixes, and web URLs remain. Safe keys retain their spelling, and deterministic bounded suffixes preserve distinct values when sanitized keys collide. Nested SessionHistory result bodies are replaced with an omission marker. A retained tombstone says that a known record was removed by age/count/byte retention without recreating its payload. Artifact references expose only an opaque id and availability. The host accepts and rechecks only regular files beneath the configured run-specific tool-output root; provider-supplied outside or symlinked paths are dropped without becoming filesystem probes. SessionHistory neither returns a path nor owns the artifact lifetime. Search/get never execute a tool, read an arbitrary path, mutate history, resume provider state, replay a tool, rerun work, or guarantee continuation from an interrupted point. Any continuation is fresh work in the current run with currently available tools and fresh verification.
Compaction only changes the active prompt projection. Within sidecar retention, older calls remain searchable even after their message context disappears. Cold reseed retains the newest fitting suffix in chronological order and caps the complete UTF-8 projection, including its truncation marker, at 64 KiB. Use SessionHistory for exact retained managed-tool invocations/results, and RunHistory for broader settled-run context such as status, warnings, and final visible output.
For the full allow/deny semantics of built-in tools, see Tool policy. For how Bash is confined, see Sandbox.
Related
Section titled “Related”- Tool policy — the allow/deny model and app-owned MCP exceptions.
- Tools & guards — built-in tool catalog and runtime guards.
- Capture & recall —
MemoryRecalland localMemoryJournal, app-injected MCP tools. - Artifacts and traces — the separate run records projected safely by
RunHistoryand the tool-lifecycle sidecar used bySessionHistory. - Durable continuations — trusted asynchronous claim, result, synthesis, and delivery.
- Slack team bot with MCP tools — end-to-end playbook wiring MCP servers into a channel agent.
- Need to register MCP servers from code instead of config? See Programmatic composition.