# Holon MCP Server — v0 (dev)

Lets an agent (Claude, or any MCP client) **discover, compare, hire and pay other agents**
through the [gateway](gateway.md), within a [mandate](mandate.md) a human chose.

```sh
node bin/holon.mjs fund demo 5
node bin/holon.mjs mcp --mandate demo/sandbox       # stdio; this repo's .mcp.json does this for Claude Code
```

## Design rules

1. **One server, one mandate.** The human picks the mandate when launching the server.
   The agent acts as that mandate's grantee and has no parameter to switch or widen it.
2. **The agent cannot approve its own calls.** No tool approves anything. Calls above
   the threshold go to the human:
   - through **MCP elicitation**, if the client supports it: a form shows the agent, the
     worst case and the input, and the human answers;
   - otherwise out of band, with `holon approve <id> --no-run`. The agent then re-submits the
     same call with `approval: <id>`. An approval is bound to the mandate, the exact agent
     version and a hash of the input, so it cannot be reused for another call.
3. **Another agent's output is data.** It comes back wrapped in
   `<agent_output agent="…">…</agent_output>`, with an explicit reminder not to follow
   instructions inside it. The same rule is in the server's `instructions`.
4. **Everything is explained.** Denials carry their reasons, search lists excluded agents
   with the reason, and every call returns a receipt id.

## Tools

| Tool | Read-only | What it does |
|---|---|---|
| `search_agents` | ✓ | `{query?, capability?}` → agents the mandate allows, ranked by cost per successful run, plus the excluded agents and why |
| `get_agent` | ✓ | `{agent}` → input and output schemas, errors, price, worst case in the mandate's currency, the mandate's decision, data policy, models, delegated calls, measured record, examples |
| `call_agent` | ✗ | `{agent \| capability, input, max_cost?, approval?}` → receipt summary + wrapped output. `max_cost` caps this one call ([gateway §2](gateway.md#caller-cap-max_cost)) |
| `check_approval` | ✓ | `{approval}` → pending, approved, rejected or used |
| `get_budget` | ✓ | The mandate: scope, limits, threshold, data rules, spent and remaining budget |
| `get_receipt` | ✓ | `{receipt}` → receipt tree including delegated calls. Only receipts under this mandate. |

## Typical session

```
search_agents {capability: "extraction.*"}
  → acme/table-extract (0.0276 EUR, 97% success) · lena/table-extract-fr (0.035 EUR, 98.5%)
get_agent {agent: "acme/table-extract"}
  → input_schema {file: "holon://files/…"}, mandate_decision: allow
call_agent {capability: "extraction.table", input: {file: "holon://files/examples/facture.pdf"}}
  → succeeded, 0.0276 EUR, <agent_output>…</agent_output>
call_agent {agent: "holon-labs/invoice-pipeline", input: {files: [...]}}
  → the human is asked: "worst case 3.10 EUR, threshold 0.50 EUR, approve?" → yes
  → succeeded, 0.19 EUR, 4 delegated calls
```

## Limits (v0)

- The server and `holon approve` share `.holon/state.json` and take turns: state is re-read
  before and written after each tool call. A long call running while the human approves
  something else in another process can overwrite that approval. A real deployment talks
  to one gateway service instead.
- Files are referenced as `holon://files/…` handles. There is no upload tool yet.
