Write an integration¶
This page wires a real agent to a scoped TOVIO session: Cursor, Claude Code, or your own custom MCP client. The pattern is always the same — issue a token, start the workspace MCP package with that token in its environment, point the client at it — so once you have done one, the rest are small variations.
Phase 2 server and contract are implemented
The MCP server lives in the workspace as tovio-mcp-server. The configurations
below target the implemented tool contract. The package is not yet published to npm, so integrations
currently run it from a built workspace checkout (pnpm --filter tovio-mcp-server build).
The shape of every integration¶
Every MCP-based agent integration has the same three moving parts:
- A capability token issued by a human, scoping what the agent may touch.
- The MCP server — the built
tovio-mcp-serverpackage (node …/packages/tovio-mcp-server/dist/index.js, ortovio mcp servefrom inside the repository), which the client spawns over stdio. - The client's MCP config, telling it how to launch the server and present the token.
human ── tovio agent new ──▶ token ─┐
├─▶ client launches `tovio-mcp-server` with `TOVIO_TOKEN`
editor / agent (MCP client) ────────┘ │
▲ ▼
└──────── JSON tool calls ◀────── tovio-core (validates every call)
The key idea: the client never touches the repository directly. It only calls MCP tools, and every one of those calls is re-validated against the token by the core. You are not trusting the editor — you are trusting the token.
Step 1 — issue a scoped token¶
tovio agent new issues the safe default described below --- broad access to clear code, structurally
excluded from every policy-protected path --- and always sets a deadline (--expires-in, in hours,
defaulting to 24):
$ tovio agent new cursor-cgm \
--model anthropic:claude-sonnet \
--task "stabilize the CGM integration" \
--expires-in 8 \
--json
{
"token_id": "cap_7r4qy9m2x8k3v6bd0n1p5h",
"object": "3f9c…",
"agent": "did:tovio:agent/cursor-cgm",
"model": "anthropic:claude-sonnet",
"authorized_by": "did:tovio:dustin",
"path_scope": ["**"],
"denied_path_scope": ["config/production/**", "secrets/**"],
"branch_scope": ["agent/cursor-cgm/**"],
"secret_clearance": false,
"issued_at": 1782482400,
"expires_at": 1782511200,
"revoked": false,
"signature_valid": null
}
Capture the token_id; the client config references it. See capability tokens
for what each field means.
CLI narrowing is still a gap
agent new currently issues the §10 default: broad clear-code access with every protected policy
path in denied_path_scope. There is no --scope flag yet. To narrow, issue the root token with
--can-delegate and mint a narrower leaf with the issue_sub_token tool; the leaf's hex is the
sub-agent's own TOVIO_TOKEN.
Step 2 — wire up a client¶
Each MCP client has its own config file but the same essentials: a command to launch the server and the token to present.
Add a TOVIO entry to Cursor's MCP settings (.cursor/mcp.json in the project, or your global MCP
config):
{
"mcpServers": {
"tovio": {
"command": "node",
"args": ["/absolute/path/to/tovio-checkout/packages/tovio-mcp-server/dist/index.js"],
"env": {
"TOVIO_REPO": "/absolute/path/to/your-repo",
"TOVIO_TOKEN": "cap_7r4qy9m2x8k3v6bd0n1p5h"
}
}
}
}
Cursor spawns the server over stdio and discovers its tool catalog — the same 18 tools for every
session; scope is enforced on each call, not by hiding tools. The agent then uses read_file,
commit, diff, and the rest as ordinary tools.
Register the server in the project's .mcp.json (or with claude mcp add):
{
"mcpServers": {
"tovio": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/tovio-checkout/packages/tovio-mcp-server/dist/index.js"],
"env": {
"TOVIO_REPO": "/absolute/path/to/your-repo",
"TOVIO_TOKEN": "cap_7r4qy9m2x8k3v6bd0n1p5h"
}
}
}
}
Claude Code negotiates MCP over stdio. The effective_scope tool echoes the session's scope, and
enforcement revalidates the complete capability chain on every call.
Any MCP-capable client connects the same way. Spawn the server over stdio with TOVIO_REPO and
TOVIO_TOKEN in its environment (a cap_… id, or the hex leaf a delegating agent handed out),
perform the standard MCP initialization, and call tools:
// process environment
{
"TOVIO_REPO": "/absolute/path/to/your-repo",
"TOVIO_TOKEN": "cap_7r4qy9m2x8k3v6bd0n1p5h"
}
A client that prefers a socket can start the server with tovio mcp serve --port 7744 instead and
send the bearer the readiness line prints — see the MCP server. Branch your logic
on the error envelope's code, never on prose — see the machine contract.
Step 3 — verify the session is bounded¶
A good first call is status: it returns the repository state filtered to the token's scope, which
confirms the agent sees only its own working set.
// → status
// ← RepositoryStatus (relay ahead/behind appears only while a fresh authenticated sync observation exists)
{
"branch": "agent/cursor-cgm/dexcom-backoff",
"change": { "changeId": "chg:a3f7b2", "message": "Add Dexcom G7 reconnect backoff" },
"modified": [
{ "path": "src/integrations/cgm/dexcom.ts", "status": "M", "encrypted": false }
],
"openConflicts": []
}
Then prove the boundary by reaching for something out of scope — the session refuses it cleanly,
before policy is even consulted. The denial is the error envelope, returned as the tool result's
text and flagged isError:
// → read_file { "path": "config/production/api-keys.env" }
// ← isError (note: no policy_requires — policy was never read)
{
"code": "TVO-TOKEN-001",
"title": "Agent token is not scoped to this path",
"cause": "The read targeted `config/production/api-keys.env`, outside the token's path_scope (minus denied_path_scope); it is rejected before any policy or envelope is consulted (§7 step 2).",
"context": { "path": "config/production/api-keys.env" },
"exit_code": 13
}
That clean refusal is the whole point: the agent is bounded by the token, not by hoping it behaves.
Step 4 — handle expiry and renewal¶
A short task fits inside one token. A long one will outlive its token, so handle the deadline:
- Track the deadline yourself:
expires_atis in the token (tovio agent show <id> --json, Unix seconds). The server does not yet attach an approaching-expiry signal to tool responses. - Renew proactively (around 80% of the token's TTL) so the active call always runs under a valid token.
- If a call returns
TVO-TOKEN-002(expired) orTVO-TOKEN-003(revoked), the session is closed. Request a fresh token from the human authorizer and reconnect — do not retry blindly.
// pattern: branch on the structured code, never the message text
const envelope = result.isError ? JSON.parse(result.content[0].text) : null;
if (envelope?.code === "TVO-TOKEN-002") {
// token expired — renew and reconnect
} else if (envelope?.code === "TVO-TOKEN-001") {
// out of scope — the path is not this agent's concern
}
Orchestrating sub-agents¶
If you are building an orchestrator that fans work out to sub-agents, issue the orchestrator's token
with tovio agent new … --can-delegate, then call issue_sub_token per sub-agent with a narrower
scope and hand the returned hex leaf to that sub-agent as its own TOVIO_TOKEN. The flow gives each
sub-agent its own chain-verified session and makes parent revocation cascade to every child. The
narrowing rules are in capability tokens.
Delegated sessions preserve the authorization chain
A delegated session persists and reconstructs the canonical root-to-leaf chain, rejects leaf mutation, revalidates every ancestor and revocation, and records the complete chain plus the tool-manifest hash in commit provenance.
Checklist¶
- [ ] Token issued with
--expires-in; its broad-clear default is acceptable or a narrower sub-token is used. - [ ] Client config launches the workspace MCP package with
TOVIO_REPOandTOVIO_TOKEN. - [ ] First
statuscall returns only in-scope state. - [ ] An out-of-scope call is cleanly refused (
TVO-TOKEN-001). - [ ] The client branches on the envelope's
code, not prose. - [ ] Long-running jobs renew before expiry.
Where this connects¶
- Scoping the token you issue: capability tokens.
- The server the client connects to: the MCP server.
- Parsing responses and errors: the machine contract.
- What a session means across renewals: agent sessions.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure