Skip to content

Run the MCP server

The MCP server is the front door for agents. It speaks the Model Context Protocol, validates an agent's capability token on every call, and exposes exactly the version-control tools an agent is allowed to use — and not one it isn't. This page is a how-to: start the server, see its tools, and understand the validation it runs.

Phase 2 edge is implemented

The workspace package tovio-mcp-server is built on tovio-node-bindings and the Node SDK. It revalidates the capability token on every call and exposes scoped reads, semantic context, clear and protected writes, delete/move, commit, narrower sub-token delegation, rationale, and clear/protected whole-side and region-level conflict resolution. tovio mcp serve launches it over stdio or Streamable HTTP. Delegated sessions persist and reconstruct the signed root-to-leaf chain, revalidate every ancestor, and bind commits to the chain and canonical tool-manifest hash. The npm packages are publish-ready but have not been published.

Start the server

Build the workspace package once, ensure its tovio-mcp-server executable is on PATH (or point TOVIO_MCP_SERVER_BIN at it), then launch it from inside a repository:

$ pnpm --filter tovio-mcp-server build
$ tovio mcp serve --token cap_7r4qy9m2x8k3v6bd0n1p5h

tovio mcp serve resolves the repository, passes the token (from --token or TOVIO_TOKEN; without one it refuses with TVO-MCP-001 rather than falling back to your human identity), and runs the package. Stdio is the default. For Streamable HTTP, pass --transport http, or simply --port / --bind, which imply it; the listener defaults to 127.0.0.1:7744 and serves MCP at /mcp. Every HTTP listener is bearer-authenticated: a non-loopback --bind requires the shared secret in TOVIO_MCP_BEARER, and when that variable is unset on a loopback bind the server mints a random bearer for the run and prints it on stderr for the client that started it — the endpoint is never unauthenticated. All tool I/O is UTF-8 JSON. Most editor integrations spawn the server over stdio for you; see write an integration. You can also run the package directly with TOVIO_REPO and TOVIO_TOKEN in its environment (on PowerShell, $env:TOVIO_REPO and $env:TOVIO_TOKEN).

The server is a gateway, not a trust boundary

The MCP server owns no version-control logic and no cryptography of its own. Every operation is delegated to the Rust core. A compromised or buggy server cannot grant access the core would deny — read access is cryptographic and write access is proof-gated regardless of which edge made the request. The server's jobs are narrow: present the token on every call, refuse to even offer forbidden operations, and marshal results as clean JSON.

Why agents use MCP, not the raw CLI

An agent should never shell out to the tovio CLI. The MCP server exists because it makes four guarantees the CLI cannot:

  • Token scope is enforced at the protocol level. Every tool call carries the session's token and is validated before any work happens. The CLI assumes an ambient human identity; the MCP server has none — it acts only as the bound agent.
  • The forbidden surface is structurally absent. The catalog contains no tool for obliterate, policy modification, key management, or unconstrained token issuance. An agent cannot call what is not exposed — a stronger guarantee than denying it after the fact.
  • Provenance is injected automatically. Every commit records the model, task, prompt hash, delegation chain, server-generated session ID, and canonical tool-manifest hash without the agent supplying them.
  • Output is structured by construction. Tools return parsed JSON, never prose to scrape. Errors use the machine contract so agents branch on a code.

Connecting: token validation up front

An agent authenticates by presenting its capability token at connection time. The server hands it to the core, which runs the full validity check before any session exists: the version is tovio-capability-v1, the signature verifies, authorized_by is a human (or the delegation chain ends at one), and the token is unexpired and unrevoked. A token that fails any of these yields no session — the client gets the corresponding TVO-TOKEN-* error.

The Node SDK's internal connect handshake returns the effective scope to the server. The current stdio entry point binds the token before MCP initialization and prints a short, content-free summary to stderr:

tovio-mcp-server: ready — agent did:tovio:agent/cgm-sync, 2 scope glob(s), secret_clearance=false

The connection exposes the effective scope, and status includes in-session staged paths. The core still re-checks the bound token, every delegation ancestor, and scope on every call.

Token validation on every call

This is the heart of the MCP contract: every tool call — read or write — re-presents the session token to the core and re-runs the full enforcement order, in this exact order:

  1. Token validity — still unexpired, still not revoked, chain still valid.
  2. Path scope — target paths within scope — checked before any policy is read.
  3. Operation allow/deny — op allowed, not denied, capability flags satisfied.
  4. Policy / clearance / key availability — for paths touching protected content.

The server holds no authorization state of its own, so a token that expires or is revoked mid-session causes the next call to fail. There is no caching that could outlive a revocation the core has learned about, and no grace period on expiry.

agent ──tool call (+token)──▶ MCP server ──delegates──▶ tovio-core
                                                         ├─ 1. valid?
                                                         ├─ 2. in scope?
                                                         ├─ 3. op allowed?
                                                         └─ 4. policy / key / proof?
agent ◀────── result or structured error ────────────────┘

The tools it exposes

Every tool takes and returns JSON. Errors use the machine contract envelope.

Read tools

Read tools require read in the token's allowed ops and are path-scoped — any path argument is checked against scope before policy.

Tool Returns
effective_scope A read-only echo of the session's path, lane, operation, and clearance scope — informational; the core re-checks scope on every call.
status The current lane, the most recent change visible to the token, in-session staged paths as modified, and open conflicts filtered to the token's path scope — an out-of-scope tip or conflict is omitted, never leaked.
log Change history from HEAD (first-parent, default 20 entries): change id, author, message, timestamp, and agent provenance — metadata only, no file content or paths.
diff The current change's per-file diff, filtered to scope. Protected sides are returned only after the read gate and signed audit; otherwise only a redaction marker appears.
read_file The content of an in-scope committed file (the canonical path-scoped tool).
list_conflicts Conflicts whose path is in scope.
symbol_context A semantic graph for in-scope symbols (truncated when results were limited to scope).
search_symbols Ranked symbol definitions matching a name — the way to turn a half-remembered name into a moniker.
symbol_relations One-hop relations of a symbol: callers, tested_by, or supertypes.
change_impact The transitive dependents of one or more symbols plus the tests covering them (advisory).
project_context Scope-gated symbols, overlapping peer intents, readable rationale, and matching repository conventions.
provenance_graph The derivation graph for a target — human → token → agent → change → rationale — over already-signed data.

read_file shows the enforcement order most clearly: a path outside scope is rejected with TVO-TOKEN-001 before any policy is consulted; a path in scope but guarded by a policy the agent cannot satisfy returns TVO-PERM-001; and if secret_clearance is false against a clearance-gated policy, the key is simply unobtainable. On success the tool returns the file's bytes as text when they are valid UTF-8, or base64 with _meta["tovio/encoding"] = "base64" for a binary file.

// → read_file  { "path": "src/integrations/cgm/dexcom.ts" }
// ← text content
"export class DexcomSyncHandler { /* … */ }"

Write tools

Write tools require the matching operation and pass the full enforcement order. Protected content is sealed at commit. Provenance is auto-recorded on every successful commit — the agent never supplies it.

Tool Does
write_file Stages clear or protected bytes; protected content stays in zeroizing memory and is sealed at commit.
delete_file Stages deletion of a staged or committed in-scope path.
move_file Stages a move. Protected sources require an audited read and may move only under the same effective policy.
resolve_conflict Keeps ours/theirs for a whole conflict or one zero-based region; protected sides are audited and resealed.
commit Seals protected edits, advances the agent lane, and injects provenance plus an optional clear or sealed rationale.
// → commit
{ "message": "Add Dexcom G7 reconnect backoff", "reasoning_summary": "Stabilize CGM WebSocket under flaky network" }
// ← CommitResult
{
  "change_id": "chg:a3f7b2",
  "commit": "blake3:91be07…",
  "branch": "agent/cgm-sync",
  "verified": true
}

Delegation

The only delegation surface is issue_sub_token. The tool is always registered, but the core refuses it with TVO-TOKEN-004 unless the session token allows delegation (can_issue_sub_tokens and sub-token:issue, both granted by tovio agent new … --can-delegate). It enforces monotonic narrowing and returns the signed leaf as hex — the sub-agent's own TOVIO_TOKEN — see delegation in capability tokens.

Tools deliberately absent

The catalog has no tool for obliterate, policy modification, key management, tag-force, or unconstrained token issuance — and amend, direct lane creation, and sync are likewise outside the registered surface today. This is a structural property, not a runtime check — an agent cannot name what does not exist. A client that requests such a name gets the MCP protocol's own unknown-tool error, not a TOVIO envelope; the human-only path stays the CLI (tovio obliterate <hash> --confirm "obliterate <hash>").

TVO-TOKEN-004 is what a registered tool returns when the token does not permit its operation — for example issue_sub_token under a token issued without --can-delegate, or commit under a token whose allowed_ops lacks commit. A denial is the catalog envelope itself, returned as the tool result's text and flagged isError:

{
  "code": "TVO-TOKEN-004",
  "title": "Agent token does not permit this operation",
  "cause": "Operation `SubTokenIssue` is not in the token's allowed_ops, is denied, or a required capability flag is unset (§7 step 3).",
  "context": { "op": "SubTokenIssue" },
  "exit_code": 13
}

Expiry and revocation, surfaced cleanly

When a token expires or is revoked mid-session, the next call fails with TVO-TOKEN-002 (expired) or TVO-TOKEN-003 (revoked), verbatim in the structured envelope, and the server closes the session. The server never silently degrades, retries, or re-authorizes on the agent's behalf — the orchestrator is expected to request a fresh token and reconnect. Parent revocation cascades to every sub-token because the complete persisted chain is revalidated.

The server does not yet attach an approaching-expiry signal to tool responses. An orchestrator should read expires_at from the token (tovio agent show --json) and renew proactively rather than discover the deadline through a TVO-TOKEN-002 failure.

Protocol versioning

The server's protocol generation is tovio-mcp-v1.3, and tool schemas are versioned with it: every tool's name, version, and input schema is part of the canonical tovio-tool-manifest-v3 manifest whose BLAKE3 hash is recorded in each commit's provenance. Today the version is carried in that manifest and the connect handshake echoes the effective scope; the spec's single combined handshake object (protocol, version, expiry, coordination warnings) is not yet returned. The handshake can also echo a prompt_hash, but tovio mcp serve passes no prompt bytes, so that field is absent on a server-launched session. Backward compatibility is guaranteed for one minor generation back: a newer server keeps every prior tool name, input, and output working with unchanged meaning. Within a generation the server may add tools and optional fields but never remove or repurpose one without a major-version bump. These rules mirror the --json stability guarantees so the CLI, --json, and MCP surfaces never drift — see the machine contract.

Where this connects

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure