DocsSupervision (zirv ctx)
MCP bridge
zirv ctx mcp serve lets an MCP host such as Codex or Claude Code read zirv
state through structured tools. The host launches it as a local subprocess;
stdin/stdout carry MCP JSON-RPC and diagnostics go to stderr. Stdio is the
only transport (--stdio is optional). --repo fixes the authorized directory
at startup; without it, the server uses its launch directory. Use an absolute
path to the installed zirv executable and an explicit repository path in host
configuration so a host's working directory cannot select the wrong project.
| Tool | Arguments | Result |
|---|---|---|
session_snapshot | {} | Up to 64 session registry records for this directory, requested policy, and memory gates. Liveness and host enforcement are explicitly unverified; stale records are preserved. |
memory_search | query, optional limit and max_bytes | Ranked private/global/shared facts with provenance and verification dates. Uses the existing retrieval engine and trusted-key precedence. |
workflow_status | {} | Active workflow, current step/skill, approval state, and up to 64 registered artifact IDs, newest first. An absent workflow stays absent. |
artifact_read | id, optional offset and max_bytes | A UTF-8 text page from an artifact registered with zirv artifact render. Returns next_offset for subsequent pages. |
worker_status | Optional id, cursor, limit | Repository-scoped delegation/report records with recorded phase, attempt, exit code where available, report outcome and truncation. Also carries conditions: typed reasons recorded alongside phase (e.g. workspace_ready, launched, task_claimed, budget_ok, reporting, contract_valid), each rendered "<reason>@<unix time>"; empty for a worker known only from its report. Worker IDs sort lexicographically; follow next_cursor. |
result_read | id, optional offset, revision, max_bytes | A page of the persisted worker result JSON, including report text, structured result, validation errors and undeclared changes. Use an ID from worker_status. |
inbox_read | Optional cursor, limit, max_bytes | Unread message previews for the launch-bound recipient, with sender labels, truncation and next_cursor. Never consumes, acknowledges, claims, expires or retries delivery. |
self | {} | THIS worker's own delegation envelope (decoded through the same parse_envelope_env the enforcement path uses), its bound task card claim, its declared result contract, and its parent delegation handle. Requires a session bound at launch; refuses otherwise. Never accepts a session id. Each field is absent, not fabricated, when it does not apply. |
skill_list | Optional query, phase, limit | Every registered skill as a metadata-only digest (never instruction text) with no query; with a query, the best-matching skills ranked by the deterministic activation scorer, each with score and reasons. |
skill_load | id (accepts id@version) | The skill's dependency-ordered instruction stack, content hash, and resource list. Refused before any text is returned if this session's capability report does not support the skill's required capabilities or integrations; a repository-sourced skill is marked untrusted data. Records one activation-journal entry on success. |
skill_read_resource | id, path | One bundle resource body (reference doc, script or asset) by its bundle-relative path. |
All successful responses contain captured_at (Unix seconds), repository,
and data, with both an output schema and structured JSON. Tool failures
return isError: true with an explanation. Each serialized structured result
is capped at 32 KiB (the MCP text fallback repeats that JSON). Memory queries
must be nonblank and at most 2048 bytes. Memory limits default to 6 entries /
2048 bytes, may be lowered, and cannot exceed 32 entries / 16384 bytes or the
operator's configured retrieval budgets. Artifact pages default to 8192 bytes
(range 4..8192); files must be regular UTF-8 text no larger than 1 MiB.
Offsets are byte positions on UTF-8 boundaries. Registered files remain live
files, so callers should restart pagination if they change between reads.
Worker listings default to 16 records (maximum 64). id selects one exact
worker and cannot be combined with cursor. Ordinary harness reports have no
delegation phase or exit code unless a corresponding durable delegation exists;
those fields remain null. Report outcomes such as reported, validated,
contract_failed and exited_no_report (a clean exit with no extractable
report -- still a durable record, just with no report text) describe stored
evidence, not process liveness or task correctness.
Unreadable, oversized or unscoped reports are omitted from listings; an explicit
result_read returns an error. Reports written before repository provenance was
recorded require a matching scoped delegation reference, never just a filename.
Result pages default to 8192 bytes (range 4..8192). The first page returns a
SHA-256 revision; pass it with every nonzero offset. A changed report refuses
continuation, so restart at offset zero. Pages reconstruct the stored JSON,
including escaped report text. Stored files must be regular UTF-8 JSON at most
8 MiB (allowing JSON escaping of the existing 1 MiB report-body limit).
Inbox pages default to six messages (maximum 32), oldest first. max_bytes
controls each body preview, default 1024, range 4..4096. Current
mail.max_message_bytes and the aggregate mail.max_delivered_bytes also apply;
body_truncated and body_bytes describe omitted text. Use the existing inbox
CLI/full-body references when a preview is insufficient. Cursors are exclusive
and remain usable when earlier messages are consumed; concurrent arrivals can
require another scan from the beginning. mail.enabled = false returns an
explicitly disabled, empty inbox.
Bind directed inbox reads using serve --session <full-session-id-or-exact-short>
or by forwarding the supervisor's ZIRV_CTX_SESSION to the MCP subprocess.
The server resolves that identity once against the session registry, requires
its canonical repository to match --repo, and derives the agent and mailbox
from the record. Unknown, ambiguous or foreign bindings refuse startup. Tool
arguments cannot supply or change the recipient. Without a binding, only
undirected mail addressed to any in this repository is visible. With a binding,
existing directed delivery envelopes can locate that recipient's mail across
mailbox directories; unrelated broadcast mail stays repository-scoped.
self reuses the identical binding: it reads the SAME Reader a bound
--session/ZIRV_CTX_SESSION resolves, and refuses outright when unbound --
there is no repository-wide fallback the way inbox_read has, because there
is no meaningful "self" without one. Its envelope comes from decoding this
server process's own inherited ZIRV_ENVELOPE (absent for a root,
non-delegated session); its task card is this session's own claim in the
repository's task store; its result contract is this session's own declared
ZIRV_CTX_RESULT_SCHEMA/ZIRV_CTX_RESULT_WORKDIR; its parent is the
delegation record naming this session as the worker. The token figure shown
is the envelope's ceiling, not remaining spend -- live usage lives in the
supervising process, not this stateless server.
New Claude Code and Codex sessions launched through Zirv register this bridge
automatically. This includes chat/wrap, dashboard panes, headless workers,
loop cycles and their supervised restarts. No .mcp.json or manual server
entry is needed. The launch pins the current Zirv executable, canonical repository,
operator state directory and stable inbox address. Install the updated Zirv
binary and start a new session to discover its tools; an already-running host
keeps its existing server process until restarted.
Zirv supplies a launch-only zirv server entry using Claude's --mcp-config
or Codex's -c overrides. Other named servers remain configured. The zirv
name is reserved for this automatic entry during the launch. No project or
user host settings are edited. On Windows, Zirv writes Claude's generated JSON
under the private state directory's mcp-launch/ to keep JSON off shell-shim
command lines; users do not create or maintain that file.
Claude launches also pre-approve the bridge's eleven named read-only tools so
headless dontAsk sessions can use them. Native deny/ask rules and the server's
current policy gates still apply; other servers receive no added permissions.
Set ZIRV_CTX_MCP_AUTOREGISTER=0 (also false or off) to opt out.
Claude's explicit --strict-mcp-config is respected and suppresses automatic
registration. wrap --no-supervise remains passthrough. Unsupported hosts and
sessions launched directly outside Zirv retain their own registration setup.
An unavailable automatic configuration prints a warning and leaves the host
launch available.
For a direct Codex launch outside Zirv, add a server entry to the operator's
~/.codex/config.toml, using the real absolute paths:
[mcp_servers.zirv]
command = "/absolute/path/to/zirv"
args = ["ctx", "mcp", "serve", "--repo", "/absolute/path/to/project"]For a direct Claude Code launch outside Zirv, pass this JSON through
--mcp-config (an inline JSON string or an operator-owned file):
{
"mcpServers": {
"zirv": {
"command": "/absolute/path/to/zirv",
"args": ["ctx", "mcp", "serve", "--repo", "/absolute/path/to/project"]
}
}
}On Windows, use the installed zirv.exe path; forward slashes work in JSON
and TOML paths. Preserve the host's existing server entries. If the host
must use a custom zirv state directory or operator environment override,
forward that setting through its MCP server environment configuration.
See the official Codex MCP and
Claude Code MCP configuration references.
The server's ten tools neither consume mail nor write memory, run commands, launch workers, or advance workflows. Existing hooks, CLI checkpoints and supervisor recovery continue independently. Reading a registered report does not certify that its claims are correct. Mail mutations, dispatch, and incoming event delivery are follow-on work tracked in issue #658.
Check the local server connection:
zirv ctx mcp doctor --repo /absolute/path/to/projectThe diagnostic launches the current executable, negotiates MCP, validates all
ten tool definitions and invokes session_snapshot. It prints JSON on success
and exits nonzero on a denied call, protocol error or timeout. It inherits the
same state/configuration environment and optional session binding as serve,
and terminates and reaps its subprocess on all paths. This verifies the local
server connection; it does not inspect or certify host registration. No host
settings, workflows, mail or memory are changed.
MCP reads use the current repository state buckets and never migrate legacy buckets. If an older installation's state has not been adopted yet, run the ordinary zirv CLI for that repository before starting the host.
MCP trust boundary#
| Surface | Authority and enforcement |
|---|---|
Executable and --repo | Pinned by the Zirv supervisor for automatic registration, or chosen by explicit operator host configuration; tool arguments cannot change either. The repository directory is opened once for artifact access. |
| Automatic registration | A launch-only zirv entry for Claude Code/Codex. No project/user host config writes or changes to other named servers; operator opt-out and Claude's strict MCP selection are respected. Codex receives operator environment override names, never credential values on argv. |
policy.tool_access | Re-read for every call. deny and ask refuse calls; this server has no approval-granting channel. A malformed configuration also refuses reads. |
| Memory | Existing memory.enabled, memory.shared_enabled, and retrieval budgets apply. A shared key cannot shadow an enabled private/global key, even if that trusted fact misses the query budget. |
| Artifact IDs | Resolved in this repository's existing artifact registry. Payload access uses a directory capability to reject traversal and symlink escapes; arbitrary file paths are not tool arguments. |
| Worker results | IDs resolve inside the operator-owned result store. Canonical repository provenance or a matching scoped delegation authorizes each read; unscoped legacy filenames grant nothing. Directory capabilities reject report traversal and symlink escapes. |
| Inbox recipient | Operator launch configuration/inherited supervisor identity selects a registered session in this repository. Agent and mailbox come from that record, never tool arguments. The binding is not an OS authentication boundary. |
self scope | Reads only the bound session's own inherited env (ZIRV_ENVELOPE/ZIRV_CTX_RESULT_SCHEMA/ZIRV_CTX_RESULT_WORKDIR) and this repository's own task/delegation records filtered to that exact session id. Refuses outright when unbound; never accepts a session id as an argument. |
| Mail policy and receipts | Current mail.enabled and delivery budgets apply to every read. Existing expiry and fan-out visibility rules are reused without receipt or mailbox mutations. Sender labels remain claims. |
| Returned text | Memory provenance is retained; artifacts, worker reports, mail and shared facts are labeled untrusted information, never operator instructions. |
| OS account and local state | Operator-owned state is trusted as with the CLI. This local service does not isolate mutually hostile processes that already share filesystem access under the same account. |