Propose
“Here is what I want to do.” Only the intent — never the balances or budget used to judge it.
For agents · Model Context Protocol
Connect an agent over MCP. It can read the rules it is bound by and submit an action to be checked against them — and nothing else. There is no tool that signs, and none can be added, because the API it talks to does not offer one.
The division of labour
Conflating any pair of these is the mistake this page exists to prevent.
“Here is what I want to do.” Only the intent — never the balances or budget used to judge it.
Policy runs on the server against state it loads itself. Still nothing has moved.
The only step where value actually moves. ROOST holds no key and cannot do this for anyone.
Connect
{
"mcpServers": {
"roost": {
"command": "node",
"args": ["/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}
Issued by an owner from the Agents screen. It is shown once and only its hash is stored, so a lost one means a new one.
Revoking an agent invalidates it on the very next request — not at the next refresh, not on a timer.
If a tool reports the credential is invalid, stop and say so. Do not look for another route to the same outcome.
Quickstart
The parts people get wrong are the absolute path to the entry point and the config key, which is not the same in every client. Neither should be typed by hand.
Sign in, open Agents, create one, copy it. It is shown once and stored only as a hash — a lost one means issuing a new one.
Let the repo write it, with the right path and the right key for your client.
npm run build -w @mandate/mcp
npm run mcp:config -- codex --token rst_…
Takes claude-code, claude-desktop, codex, cursor, vscode or windsurf.
Ask the client to call roost_whoami. It reports the agent, the network, the mandates it is bound to, and what it may not do.
.mcp.json in the project root, or ~/.mcp.json for every project
{
"mcpServers": {
"roost": {
"command": "node",
"args": ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}~/.codex/config.toml
[mcp_servers.roost]
command = "node"
args = ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"]
[mcp_servers.roost.env]
ROOST_BASE_URL = "https://app.roostagent.xyz"
ROOST_AGENT_TOKEN = "rst_…"~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"roost": {
"command": "node",
"args": ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}.cursor/mcp.json in the project, or ~/.cursor/mcp.json
{
"mcpServers": {
"roost": {
"command": "node",
"args": ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}.vscode/mcp.json — note the key is servers, not mcpServers
{
"servers": {
"roost": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"roost": {
"command": "node",
"args": ["/absolute/path/to/roost/mcp/dist/mcp/src/index.js"],
"env": {
"ROOST_BASE_URL": "https://app.roostagent.xyz",
"ROOST_AGENT_TOKEN": "rst_…"
}
}
}
}Client marks are their owners’ trademarks, shown only to identify which configuration belongs to which tool. Codex has no mark here, so it carries a neutral one.
A credential belongs to one ROOST instance. Each has its own database and its own hashes, so one issued locally is meaningless against production.
The surface
Read tools cost nothing and can be called freely. The one write tool records a decision and may reserve budget — but it cannot itself move value.
roost_whoamiWhich agent this is, which chain it operates on, which mandates it is bound to, and exactly what it may not do.
readroost_list_mandatesEvery rule in human units, plus how much daily budget is left. Call this before proposing, so the proposal is shaped to fit the rules rather than discovered to break them.
readroost_propose_actionSubmit a transfer to be checked. Returns the decision and the full rule-by-rule trace. It does not move funds and does not broadcast anything.
writeroost_get_actionLifecycle, latest trace and any chain execution. Only a status of “confirmed” means the chain accepted it.
readroost_list_actionsRecent proposals, for reconciling what was actually decided against what the agent believes it did.
readroost_get_receiptA signed receipt: payload, signature and signing key id.
readroost_verify_receiptCheck a receipt against the published key. Needs no credential — that is the point of it.
readAuthority
An agent that can approve its own actions has no approval step. The restriction lives in the API, not in the MCP server’s good manners.
Reading a decision
The trap is reporting an allowed decision as a completed transfer. It is not one, and the tools say so on every call.
Policy was satisfied. No funds have moved.
The owner must still dispatch and sign, and everything is re-checked first. Never report this as sent, paid or complete.
Within policy, but a person must decide.
The request expires. Poll the action rather than assuming approval because time passed.
The mandate refused. Nothing was reserved and nothing will execute.
Read the failing rules and stop. Do not retry unchanged, split the amount to slip under a cap, or try another destination.
The skill
You propose. ROOST decides.
The owner signs.
Look before you propose.
Read the decision honestly.
When something is blocked, stop.
Only “confirmed” means it happened.
The skill exists because the failure mode is not a crash — it is an agent that is confidently wrong about what happened.
So it spends most of its words on two things: never calling a policy decision a settlement, and never treating a refusal as an obstacle to route around. Splitting an amount to slip under a cap is not resourcefulness. It defeats the thing the agent was asked to operate within.
Honest limits
Stated as limits, not as failures. An agent should report them rather than work around them.