> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useotto.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Otto Builds It, You Sign It

> The agent rail: point any MCP client at mcp.ottoai.services, ask for a swap on an address you supply, and read back a decoded, self-checked, unsigned execution plan. Otto does not sign it and does not submit it.

Your agent can trade through Otto without handing Otto your money.

Point any MCP client at `https://mcp.ottoai.services`, supply an address you
control, and ask for a swap. What comes back is an **unsigned execution plan**:
the ordered steps, the decoded calldata, the address the plan is bound to, an
expiry, a digest, and the checks the constructor already ran on its own output.
You sign it and broadcast it yourself. Otto has no path in that response by
which it signs or submits.

The four `otto_prepare_*` constructors are **free to call**.

## Try it in one call

```bash theme={null}
curl -sX POST https://mcp.ottoai.services/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "otto_prepare_swap",
      "arguments": {
        "accountProfile": { "kind": "eoa", "address": "0xYourAddress", "chainId": 8453 },
        "chainId": 8453,
        "fromToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "toToken":   "0x4200000000000000000000000000000000000006",
        "fromAmount": "1000000",
        "slippage": { "maxBps": 100 }
      }
    }
  }'
```

That is 1 USDC into WETH on Base, prepared for whatever address you put in
`accountProfile.address`. Put in an address you control and the plan is one only
you can execute.

## What comes back

| Field                             | What it holds                                                                                                                |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `account_binding`                 | The address you supplied, and the protocol roles it fills. On a LI.FI swap it is both `sender` and `receiver`.               |
| `payload.steps`                   | The ordered steps, each with `to`, `value`, `data`, and a `kind`: `approve_reset`, then `approve`, then `call`.              |
| `assertions`                      | The checks the constructor ran on its own output, each with its result, listed in the response.                              |
| `valid_until`                     | When the plan goes stale. It is committed into the artifact digest.                                                          |
| `submission.payload_digest`       | A digest over the returned plan, so an altered plan is detectable.                                                           |
| `submission.adapter_owned_fields` | The fields your own signer fills in: `nonce`, `gas`, `serialization`.                                                        |
| `required_capabilities`           | What your signer needs to execute it, for example `send_evm_transaction`.                                                    |
| `fee_attribution`                 | The fee mechanism and its configuration version. The fee leg itself is decodable from the returned calldata before you sign. |
| `replay_disclosure`               | What the plan does **not** guarantee.                                                                                        |

Each constructor's own description states it verbatim: **"never signs or
submits."**

### The plan states its own limits

`replay_disclosure` comes back on every envelope:

```json theme={null}
{
  "artifact_role": "construction_aid",
  "freshness_boundary": "valid_until",
  "onchain_replay_boundary": "signer_account_nonce",
  "tool_guarantees_single_execution": false
}
```

The constructor cannot enforce single execution. Your signer's account nonce is
the on-chain replay boundary, and on the Polymarket and Hyperliquid rails the
venue's own order hash or signer nonce is.

## The twelve checks

An `otto_prepare_swap` envelope carries twelve assertions, each reported with
its result in the response. A check that does not hold **refuses the call by
name** rather than returning a plan.

| Assertion                          | What it establishes                                                                                                                                              |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `lifi_integrator_exact`            | The decoded calldata carries Otto's exact integrator string and no other.                                                                                        |
| `lifi_fee_recipient_exact`         | Every fee recipient in the fee body is one of the reviewed, versioned set. An unrecognized recipient is refused.                                                 |
| `lifi_fee_amount_exact`            | Otto's own distribution carries exactly the configured amount for this gross input.                                                                              |
| `lifi_fee_recipient_set_versioned` | The fee body names the complete versioned recipient set, once each — no duplicates, none missing.                                                                |
| `lifi_fee_aggregate_cap`           | The sum of all fee distributions is at or below the configured cap for this gross input.                                                                         |
| `lifi_receiver_account_bound`      | The receiver decoded out of the calldata, and the quote's sender and receiver, are the address you supplied.                                                     |
| `lifi_min_out_slippage_bound`      | The minimum output in the calldata equals the quoted minimum and sits at or above your slippage floor, and at or above your own `minAmountOut` when you set one. |
| `lifi_token_amount_bound`          | Every deposit leg spends only your input token and they total exactly your input amount; the final leg's output token is the one you asked for.                  |
| `lifi_targets_value_bound`         | The approval target and the call target are both the reviewed LI.FI Diamond, and the call moves zero native value.                                               |
| `payload_digest_committed`         | The returned plan is hashed and the digest travels with it in `submission.payload_digest`.                                                                       |
| `erc20_allowance_reset_ordered`    | The plan clears the allowance to zero before setting the new one, so it is executable from any prior allowance state, including on approval-race-guarded tokens. |
| `artifact_expiry_committed`        | `valid_until` is committed into the artifact digest, so a re-dated plan no longer matches.                                                                       |

## The four constructors

| Tool                            | Builds                                                                                                                                                                         | Bound to                     |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- |
| `otto_prepare_swap`             | A same-chain EVM ERC-20 swap, as ordered allowance-reset, approval, and call steps                                                                                             | Your own EOA or Safe         |
| `otto_prepare_bridge`           | A cross-chain native-USDC bridge over the enumerated Circle CCTP route set (Ethereum, Polygon, Base, Arbitrum, Avalanche); any other chain, token, or route is refused by name | Your own EOA or Safe         |
| `otto_prepare_polymarket_order` | An unsigned Polymarket CTF Exchange V2 limit order (GTC), as order args plus the typed data your signer signs                                                                  | Your own Polygon EOA         |
| `otto_prepare_perp_order`       | An unsigned Hyperliquid perpetuals limit order, optionally with reduce-only take-profit and stop-loss triggers, as the canonical L1 action plus the exact EIP-712 recipe       | Your own Hyperliquid account |

Connection details for Claude Code, Claude Desktop, Cursor, Codex CLI, and
ChatGPT, plus the full tool list, are on
[Otto Intel MCP](/acp-swarm/otto-intel-mcp).

## Optional: a bounded permission to submit

Signing it yourself is the default and needs nothing from Otto. If you would
rather Otto submitted for you, you can grant a **bounded, revocable
permission** for Base swaps, and take it back whenever you want.

| Bound        | Value                                                                                            |
| ------------ | ------------------------------------------------------------------------------------------------ |
| Per-swap cap | **5,000 USD** on a single swap. There is no daily and no total limit — this cap bounds one swap. |
| Input token  | USDC on Base                                                                                     |
| Route        | The reviewed LI.FI Diamond, through the Model-B swap shapes only                                 |
| Expiry       | **90 days** by default, **180 days** maximum                                                     |
| Revocation   | Any time, confirmed by read-back                                                                 |
| Concurrency  | One prepared plan at a time per user                                                             |

The permission itself is fenced by a single project-scope policy, and that
fence is **publicly readable** — call `otto_delegation_fence_status` with no
arguments and no header:

```json theme={null}
{
  "present": true,
  "policy_name": "OTTO model b swap fence v1",
  "per_swap_cap_usd": 5000,
  "project_id": "40120109-9da9-4da0-ad88-5d9718c4429b",
  "rules_digest": "e04119dbd4779365477cc4f42303fa069cd1978278b90114c93667dd2168dfd8"
}
```

It returns the fence's public promise only: whether the policy is installed and
current, its versioned name, the digest over its rules, the per-swap cap, and
the public project id an agent binds its grant to. It never returns a user, a
grant, or an address.

Before any delegated send, Otto's server confirms in order that the fence reads
back by name and rules digest, that your grant is still active past the
envelope's `valid_until`, that the account is one of your own, that the
envelope passes the verifier for exactly the request that produced it, and that
the plan sits inside the fence. Where the output lands is Otto's own
server-side check. The three steps then go out in order, each waited to a
sealed block; a halt clears the allowance and reports the read-back.

Granting a permission today runs through `otto-execute delegate`, a command-line
tool that is built but **not yet published**. Until it is, the constructors
above are the reachable path, and the fence status is readable by anyone.

## Which rail you are on

This page describes the **agent rail**. Otto's `/app` deposit surface runs on a
different arrangement — a Safe your own wallet solely owns, carrying an
Otto-operated trading permission. The two are mapped side by side on
[Your Safe Account & Custody](/introduction/safe7579-architecture). Read the
rail you are on.

## Related

* [Otto Intel MCP](/acp-swarm/otto-intel-mcp) — connecting the hosted MCP, and the full tool list
* [Your Safe Account & Custody](/introduction/safe7579-architecture) — who signs on each Otto surface
* [Empire Access Layer](/account-and-settings/enterprise-agent-layer) — paying Otto's endpoints per call from your own agent's wallet
* [What's Live & What's Next](/introduction/roadmap) — current product status
