Agents & MCP
Give an agent the treasury as a tool over MCP, with scopes that make the dangerous calls impossible.
One treasury, many agents
You create one treasury, and one dashboard for it. Each agent you run gets its own API key inside that treasury, carrying only the scopes it needs.
| What it is | How many | |
|---|---|---|
| Treasury | The money and the rules. One dashboard, shared by your team. | One per business |
| Agent | An API key with scopes. It reads and proposes; it cannot approve unless you give it that scope. | As many as you run |
| Pool | A share of the money with its own limits — Operating, Reserve, Vendors. | Several per treasury |
| Person | A member with a role, who decides the things an agent must not. | As many as you invite |
The limits belong to the treasury, not to the key. Five agents spending on the same day are bounded together, and every one of their payments lands in the same ledger, the same approvals queue and the same ceiling — which is the point. You set the bounds once, and they hold for everything you point at them.
The MCP endpoint
Elpis speaks the Model Context Protocol at POST /api/mcp (JSON-RPC 2.0), so any MCP-capable agent can call the treasury as a tool — no bespoke integration.
{
"mcpServers": {
"elpis": {
"url": "https://elpis.devilabs.dev/api/mcp",
"headers": { "authorization": "Bearer $ELPIS_API_KEY" }
}
}
}read + propose key cannot approve anything — so an agent can suggest, but a person still decides.The tools
The treasury tools, and the settle-on-proof tools. Same rules for both: a tool returns a decision, never a fabricated success.
| Tool | Scope | What it does |
|---|---|---|
get_state | read | Treasury state: accounts, balances, reserves, custody, what is settling. |
list_actions | read | Recent actions and their outcomes. |
get_action | read | One action, with its rules, condition, window and receipt. |
evaluate_payment | read | Dry-run a payment; persists nothing. |
propose_payment | propose | Runs every control and returns the decision. Never sends money. |
submit_evidence | propose | Settles a staged payment against its condition. |
approve_payment | approve | Records a human decision. Not available to agent keys. |
draft_condition | propose | Draft an acceptance condition from what has already settled. |
settle_job | propose | Settle a committed job: release, refund, or hold. |
replay_decision | read | Re-run a settled decision and confirm it reproduces the attestation. |
get_attestation | read | The reproducible settlement receipt. |
propose_payment and settle_job are separate from approve_payment, so an agent key that can suggest cannot also authorise.A minimal call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "propose_payment",
"arguments": {
"accountId": "acc_vendors",
"amount": "250.00",
"address": "0x…",
"label": "Acme",
"documentRef": "INV-1042",
"idempotencyKey": "invoice-1042"
}
}
}The tool result is the decision, in the same shape the REST API returns — including blockedBy when a rule refused it, so the agent can explain itself rather than guess.
Two rules for the agent
- Pass an idempotency key. Retries then reuse the same intent instead of creating a second payment.
- Read the outcome. An agent should never treat a proposal as a payment. STAGED, APPROVAL_REQUIRED and BLOCKED are all successful tool calls describing different realities.