Skip to main content

AI agent integration

Ophis is designed to be agent-friendly. The Intent API accepts free-form natural language and returns structured JSON your agent can map directly to a pre-filled swap link. The agent does the parsing and routing; in this deep-link flow, the human always reviews and signs. The MCP server and framework adapters can also support programmatic signing; use the policy controls in Autonomous agent trading before giving an agent signing authority.

New: the full walkthrough

For a narrative guide, including the MEV and key-safety pitfalls of letting an agent trade, read How to let an AI agent swap tokens on the Ophis blog.

The fastest way to give an MCP-capable agent full Ophis access is the hosted Model Context Protocol server:

https://mcp.ophis.fi/mcp

It speaks streamable-HTTP MCP and exposes fourteen tools:

The current server release is v0.1.1. Its package metadata, runtime handshake, discovery response, and official registry manifest are checked as a single versioned unit in CI.

ToolWhat it does
parse_intentParse a natural-language request into a structured intent.
resolve_tokenResolve a token symbol to its canonical address from the trusted Ophis/CoW token list; fails closed (anti-spoof). Call this before quoting or building so you never trade against a spoofed address.
get_quoteFetch an executable quote for a parsed intent.
build_orderBuild a bounded, ready-to-sign order (receiver unconditionally pinned to the owner).
submit_orderSubmit a signed order to the correct per-chain orderbook.
validate_orderOffline preflight for an order you built outside build_order: catches the silent-failure modes (wrong appCode, wrong orderbook host, wrong EIP-712 domain, appData-hash mismatch, unpinned receiver) before you sign.
lookup_tierLook up a wallet's 30-day volume tier / rebate status.
get_integrator_earningsLook up what an integrator's own-fee / referral routing earned, by appCode: routed volume, the Ophis base fee, your stacked fee, and rebate paid-to-date with payout tx links.
list_chainsResolve supported chains and their settlement / orderbook hosts.
get_balancesRead a wallet's native and ERC-20 balances on one chain via a public RPC.
get_portfolioRead a wallet's token balances across multiple chains.
get_gasFetch the current gas price for a chain.
get_token_chartFetch a token's OHLCV price chart.
expected_surplusEstimate how much better an Ophis sell-quote beats the open market (beatBps).

Point any MCP client (Claude, Cursor, or a custom agent) at that URL. Ophis never holds keys: build_order returns a bounded order the agent signs locally; the signature is the trust boundary (see the warning below). A bare request without an Accept: text/event-stream header returns HTTP 406; that is the transport negotiating, not an outage.

Connect your MCP client

The server is public and keyless, so there is nothing to sign up for. Copy the block for your client.

Claude Code (one command):

claude mcp add --transport http ophis https://mcp.ophis.fi/mcp

Claude Desktop (claude_desktop_config.json, via the mcp-remote bridge):

{
"mcpServers": {
"ophis": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ophis.fi/mcp"]
}
}
}

Cursor (~/.cursor/mcp.json, or the project .cursor/mcp.json):

{
"mcpServers": {
"ophis": {
"url": "https://mcp.ophis.fi/mcp"
}
}
}

VS Code (.vscode/mcp.json):

{
"servers": {
"ophis": {
"type": "http",
"url": "https://mcp.ophis.fi/mcp"
}
}
}

OpenAI Agents SDK, LangChain, or any custom client: point a streamable-HTTP MCP transport at https://mcp.ophis.fi/mcp. The LangChain tool and function-calling examples below show the call path without an MCP client at all.

After connecting, ask the agent to "quote 100 USDC to ETH on Optimism" and it will call resolve_token, get_quote, and build_order; it returns a bounded order for you (or your wallet) to sign.

If you'd rather make a single REST call than wire up the full toolset, use the Intent API directly, as shown next.

The integration flow

  1. Parse. POST the user's request (or your agent-generated trade idea) to https://ophis.fi/api/intent.
  2. Read. Receive a ParsedIntent with normalized sellToken, buyToken, amount, and chain entities.
  3. Build a deep link. Map the chain slug to its chain ID and construct https://swap.ophis.fi/#/<chainId>/swap/<sellToken>/<buyToken>.
  4. Hand off. Open the link for the user to review and sign. Ophis never auto-signs, every order requires explicit wallet approval.
The signature is the trust boundary

Ophis intentionally does not implement x402 or any HTTP-native payment automation. In the Intent API / deep-link flow, an order only becomes real when the user signs it in their wallet. Agents using this flow must hand off to the user for review and signing. Autonomous integrations instead need the deterministic signing policies described below.

Server-side callers (no browser Origin header) are allowed, subject to the 30 req/min/IP rate limit. Honour 429 + Retry-After.

Minimal example (curl)

curl -sS https://ophis.fi/api/intent \
-H 'content-type: application/json' \
-d '{"text":"swap 100 USDC for ETH on Base"}'
{
"ok": true,
"data": {
"intent": "swap",
"entities": [
{ "type": "amount", "value": "100", "raw": "100", "start": 5, "end": 8 },
{ "type": "sellToken", "value": "USDC", "raw": "USDC", "start": 9, "end": 13 },
{ "type": "buyToken", "value": "ETH", "raw": "ETH", "start": 18, "end": 21 },
{ "type": "chain", "value": "base", "raw": "Base", "start": 25, "end": 29 }
]
}
}

Python helper

import requests

INTENT_API = "https://ophis.fi/api/intent"
SWAP_APP = "https://swap.ophis.fi"

# The 13 EVM chains the Intent API can return, mapped to their chain IDs.
# Keep in sync with the API's supported-network list; build_deeplink()
# raises on any future slug not listed here rather than misrouting it.
CHAIN_SLUG_TO_ID = {
"ethereum": 1,
"optimism": 10,
"bnb": 56,
"gnosis": 100,
"polygon": 137,
"base": 8453,
"ink": 57073,
"linea": 59144,
"arbitrum": 42161,
"avalanche": 43114,
"plasma": 9745,
"unichain": 130,
"robinhood": 4663,
}


def parse_intent(text: str) -> dict:
"""Call the Ophis Intent API and return the ParsedIntent payload."""
resp = requests.post(INTENT_API, json={"text": text}, timeout=10)
resp.raise_for_status()
body = resp.json()
if not body["ok"]:
raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]}')
return body["data"]


def build_deeplink(parsed: dict) -> str:
"""Turn a ParsedIntent into a swap deep link for the user to sign."""
by_type = {e["type"]: e["value"] for e in parsed["entities"]}
sell = by_type.get("sellToken", "_")
buy = by_type.get("buyToken", "_")
chain_slug = by_type.get("chain")
if chain_slug is None:
chain_id = 1 # no chain in the request -> default to Ethereum
elif chain_slug in CHAIN_SLUG_TO_ID:
chain_id = CHAIN_SLUG_TO_ID[chain_slug]
else:
# The parser may return a chain this map doesn't cover yet. Fail
# loud instead of silently routing the user to the wrong chain.
raise ValueError(f"unmapped chain slug {chain_slug!r}; update CHAIN_SLUG_TO_ID")
# The user sets/confirms the amount and signs in the app.
return f"{SWAP_APP}/#/{chain_id}/swap/{sell}/{buy}"


intent = parse_intent("swap 100 USDC for ETH on Base")
print(build_deeplink(intent)) # https://swap.ophis.fi/#/8453/swap/USDC/ETH

LangChain tool

Wrap the API as a LangChain tool your agent can call when a user wants to trade:

from langchain_core.tools import tool


@tool
def ophis_swap_intent(text: str) -> dict:
"""Parse a natural-language swap request into a structured Ophis intent
and a deep link the user can open to review and sign. Use this
whenever a user wants to swap, buy, or sell a crypto token.
The link must be shown to the user, never auto-execute a trade."""
parsed = parse_intent(text)
return {"intent": parsed, "deeplink": build_deeplink(parsed)}

The tool returns both the structured intent (so your agent can reason about the trade) and a link (so the user can sign it).

AutoGPT / function-calling agents

Any function-calling agent. AutoGPT commands, OpenAI Assistants, or a custom tool loop, can register the parser with this schema:

{
"type": "function",
"function": {
"name": "ophis_parse_intent",
"description": "Parse a natural-language crypto swap request into a structured intent (sellToken, buyToken, amount, chain). Returns a deep link the user opens to review and sign. Never auto-executes a trade.",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "The swap request in natural language, e.g. 'swap 100 USDC for ETH on Base'. Max 280 characters."
}
},
"required": ["text"]
}
}
}

Implement the handler by POSTing { "text": <text> } to https://ophis.fi/api/intent (see the Python helper above), then surface the resulting deep link to the user.

Drop-in framework adapters

Everything above keeps a human in the signing loop. If instead you are building an agent that executes swaps itself and you are on a common framework, you do not have to hand-roll the order flow in the next section. Four published npm packages wrap quote, EIP-712 sign, relayer approval, and submit into one call, and each stamps your referral code into every order when one is supplied, so the rebate accrues:

PackageVersionForRegisters
@ophis/agentkit-ophisv0.3.2Coinbase AgentKitan OphisActionProvider_swap action
@ophis/plugin-goatv0.3.2GOAT SDKan ophis_swap tool
@ophis/plugin-elizaosv0.3.2elizaOSa swap action
@ophis/agent-swapv0.3.2any custom EOA frameworkthe executeOphisSwap() core

The v0.3.2 adapter family is built and published against @ophis/sdk v0.4.2, so its fee policy, chain list, orderbook hosts, settlement contracts, and vault relayers match the current SDK.

Coinbase AgentKit, in one line:

import { AgentKit } from '@coinbase/agentkit';
import { ophisActionProvider } from '@ophis/agentkit-ophis';

const agentKit = await AgentKit.from({
walletProvider, // any EvmWalletProvider (Viem, CDP, Privy, ZeroDev)
actionProviders: [ophisActionProvider({ referralCode: process.env.OPHIS_REFERRAL_CODE })],
});

GOAT SDK:

import { ophis } from '@ophis/plugin-goat';

const tools = await getOnChainTools({
wallet: viem(walletClient),
plugins: [ophis({ referralCode: process.env.OPHIS_REFERRAL_CODE })],
});

elizaOS (the agent signs with its own EVM_PRIVATE_KEY; set OPHIS_REFERRAL_CODE in the character settings to earn the rebate):

import { ophisPlugin } from '@ophis/plugin-elizaos';

export const character = {
name: 'Trader',
plugins: [ophisPlugin], // registers a natural-language `swap` action
};

The AgentKit and GOAT tools take sellToken, buyToken, sellAmount (whole units, e.g. "1.5"), and an optional slippageBps (default 50 = 0.5%); the elizaOS action reads the tokens and amount from the user's message and uses the default 0.5% slippage. Each quotes against the Ophis orderbook, signs the order EIP-712 with the agent's own wallet, approves the CoW vault relayer once, submits, and returns the order UID plus an explorer URL. ERC-20 to ERC-20 only (native-ETH sells need CoW eth-flow, a separate path, so wrap to WETH first). The agent's wallet is the order owner and receiver, so funds only ever move through the audited CoW settlement contract, back to the same wallet.

The 1 bp base fee applies to every supported pair. Every drop-in adapter (AgentKit, GOAT, elizaOS) and the platform integrations below detect stable pairs from a verified stablecoin list so the reduced price-improvement policy is selected automatically: 50% capped at 20 bps for stable pairs, versus 80% capped at 99 bps for volatile pairs.

The referralCode is optional: omit it and swaps still work and settle, you just forgo the rebate. Mint one below, then ship, no redeploy of the swap path needed to start earning.

More platform integrations

Beyond the npm packages above, Ophis maintains swap integrations for more agent platforms, each built on the same audited Ophis order flow (the TypeScript ones reuse @ophis/agent-swap; the Python ones mirror the same order construction and fund-safety guards): elizaOS (published, in the table above), plus HeyAnon, Swarms, the MetaMask Agent Wallet, and Bankr. Their source lives under integrations/ in the Ophis repo; each is being submitted to its platform's own registry, so availability follows that platform's review. The MCP server and Intent API above already work with any of these agents today.

Markdown skill family (shell-capable agents)

Agents that can run shell commands (Claude Code and similar local runtimes with curl, jq, and Foundry's cast) do not need the MCP transport at all: Ophis publishes a self-describing agent-skill family the agent reads and executes directly.

The umbrella's frontmatter carries a machine-readable policy block: the pinned per-chain settlement and vault-relayer contracts (the only allowed approve spenders), the EIP-712 signing domains, the orderbook hosts, and slippage latches. Policy-enforcing runtimes can apply it mechanically; CI in the Ophis repo pins the block against the deployed addresses so the published skills cannot drift. The skills cover all three Ophis-operated chains (Optimism, Unichain, and Robinhood Chain); for other chains use the MCP server above, which resolves per-chain contracts via list_chains.

The same canonical family is published as @ophis/agent-skills v0.1.1 for runtimes that install skills from npm.

Get a referral code

Every order these adapters (or the SDK below) build already carries the Ophis partner fee. Add your referral code and that same order also credits you with the affiliate rebate on its volume, currently 8 to 12 percent, paid on-chain. The code rides in the order's appData, so there is nothing for the end user to sign or opt into.

  1. Open the Rewards page and connect a wallet.
  2. Mint a code (about 30 seconds). It is yours permanently.
  3. Pass it to any adapter as referralCode, or export OPHIS_REFERRAL_CODE and the adapters pick it up automatically.

The code is optional: without one your agent still swaps normally, it just earns no rebate. You can ship first and add the code later.

Submitting orders programmatically

The Intent API only normalizes language, it does not place orders. To submit orders programmatically, build and sign a CoW Protocol order yourself. Four things must each be exactly right, every one fails silently (a rejected order, a wrong-chain trade, or zero fee collected) if you guess.

If your agent runs on Coinbase AgentKit or GOAT, the drop-in adapters above already get all four right, hand-roll this only if you are on neither. The @ophis/sdk helpers below are also what those adapters call under the hood.

The helpers below live in @ophis/sdk, published on npm (v0.4.2, public). Install it with npm install @ophis/sdk, or copy the values from the call-outs if you prefer to vendor them.

1. Resolve the orderbook host from the chain ID

Optimism, Unichain, and Robinhood Chain do not live on api.cow.fi

Optimism, Unichain, and Robinhood Chain break the api.cow.fi/<slug> pattern. Ophis self-hosts their orderbooks at optimism-mainnet.ophis.fi, unichain-mainnet.ophis.fi, and robinhood-mainnet.ophis.fi. Posting one of their orders to api.cow.fi/<slug> (a host that does not serve Ophis) silently bypasses the Ophis solver and zeroes the partner fee. Resolve hosts via @ophis/sdk getOphisOrderbookUrl per chain rather than hardcoding.

import { getOphisOrderbookUrl } from '@ophis/sdk';

const orderbookUrl = getOphisOrderbookUrl(10); // -> https://optimism-mainnet.ophis.fi
// Throws on an invalid or unsupported chainId rather than guessing a host.

2. Build the partner-fee appData correctly

The appData base is 1 bp on every supported chain and pair. The keyless MCP build_order and high-level SDK builders select it automatically. Use the CIP-75 volume shape, not the price-improvement shape { priceImprovementBps, maxVolumeBps, recipient }: the two shapes use different denominators, so slotting a value into the wrong field is a silent magnitude error. Hash the appData with cow-sdk's deterministic serializer, never keccak256(JSON.stringify(doc)). JSON key order isn't stable, so the hash won't match what solvers expect.

For a manual builder, call ophisVolumeBpsForChainAndPair(chainId, isStablePair). This keeps manual builders aligned with the canonical policy. The drop-in adapters above derive stable-pair status from a verified stablecoin list.

import { MetadataApi, stringifyDeterministic } from '@cowprotocol/cow-sdk';
import { keccak256, toUtf8Bytes } from 'ethers';
import { buildOphisAppDataPartnerFee } from '@ophis/sdk';

// buildOphisAppDataPartnerFee(chainId) REQUIRES a chainId and THROWS on a
// missing/invalid one (a forgotten arg fails loud, not as a silent `undefined`).
// It returns the metadata.partnerFee value on every chain in the SDK's
// OPHIS_FEE_CHAIN_IDS (the Ophis-operated chains plus the CoW-hosted chains the
// fork serves), or `undefined` on any other chain.
//
// On Optimism, Unichain, and Robinhood Chain this returns the required 1 bp
// base. Their backends enforce the same 1 bp anti-bypass floor and separately
// apply capped price-improvement capture.
const partnerFee = buildOphisAppDataPartnerFee(10);
// -> { volumeBps: 1, recipient }

const metadataApi = new MetadataApi();
const doc = await metadataApi.generateAppDataDoc({
appCode: 'ophis',
metadata: {
partnerFee,
hooks: {}, // pin empty, appData hooks are arbitrary on-chain calls
},
});
const fullAppData = await stringifyDeterministic(doc);
const appDataHash = keccak256(toUtf8Bytes(fullAppData)); // bytes32 -> order.appData

3. Sign with the correct EIP-712 domain

CoW orders are signed with EIP-712 typed data (signTypedData), never signMessage. The verifyingContract is chain-specific, and the Ophis-operated chains do not use CoW's canonical settlement.

The Optimism, Unichain, and Robinhood Chain settlements are not the canonical CoW one

On Optimism, Ophis's GPv2Settlement is 0x310784c7…B859, on Unichain it is 0x108A678716e5E1776036eF044CAB7064226F714E, and on Robinhood Chain it is 0x886d9fd312F442C4E1f3cdeAE7b4AB73493e57cD, not the canonical 0x9008D19f…ab41. cow-sdk defaults to the canonical address, so signing an OP order with the SDK default yields a domain separator the deployed contract rejects, every order fails. Build the domain from the chain ID instead.

import { getOphisOrderDomain } from '@ophis/sdk';

// CoW's EIP-712 order struct is named `Order` (the Solidity library is
// GPv2Order, but the EIP-712 type name, which feeds the type hash, is
// `Order`; a wrong name produces a valid-looking but unusable signature).
const ORDER_TYPES = {
Order: [
{ name: 'sellToken', type: 'address' },
{ name: 'buyToken', type: 'address' },
{ name: 'receiver', type: 'address' },
{ name: 'sellAmount', type: 'uint256' },
{ name: 'buyAmount', type: 'uint256' },
{ name: 'validTo', type: 'uint32' },
{ name: 'appData', type: 'bytes32' },
{ name: 'feeAmount', type: 'uint256' },
{ name: 'kind', type: 'string' },
{ name: 'partiallyFillable', type: 'bool' },
{ name: 'sellTokenBalance', type: 'string' },
{ name: 'buyTokenBalance', type: 'string' },
],
};

// ethers v6, signer.signTypedData(domain, types, value). The domain's
// verifyingContract must be the Ophis OP settlement (getOphisOrderDomain).
const signature = await wallet.signTypedData(getOphisOrderDomain(10), ORDER_TYPES, order);
// NOT wallet.signMessage(order), that produces an invalid order signature.

4. Pin the order receiver

A CoW order's receiver is part of the signed payload and is fully caller-controlled. Pin it to the order owner: a non-owner receiver sends the bought tokens elsewhere on settlement, and the signature makes that irreversible. In the UI a wallet prompt gates this; an autonomous signer has no such gate, so guard it in code before signing.

import { assertReceiverIsOwner } from '@ophis/sdk';

assertReceiverIsOwner(owner, order.receiver); // throws if receiver !== owner

Autonomous agent trading (advanced)

Everything above keeps a human in the signing loop. For an agent that signs without human review, off-chain helpers are not enough, a compromised or prompt-injected agent will sign whatever it is told. Safety has to be enforced where the agent cannot reach it:

  1. Funds in a smart account (Safe). The agent never holds the fund-owning key; it only proposes orders. The account's EIP-1271 validator (or a Safe module) approves only order hashes that satisfy policy.
  2. A deterministic policy gate between the (untrusted) LLM and any signature, owning every order field:
    • token resolution from a chain-scoped allowlist only, never an LLM-emitted address;
    • receiver pinned to the account;
    • appData pinned to the Ophis canonical, hooks forced empty;
    • limit price within X% of an independent, staleness-checked oracle (CoW guarantees you won't fill below your limit, not that your limit is sane);
    • per-trade notional + rolling daily caps; short validTo; avoid presign.
  3. Containment: a bounded vault-relayer allowance (the blast radius if policy fails once), a guardian key that can revoke signing or pause, keys in an HSM/TEE, and a tamper-evident audit trail.
  4. Defense in depth: enforce the policy in two places, the EIP-1271 validator/signer and server-side at orderbook ingestion.
The signing gate must be in code, not prose

In the human-mediated flow, "the human reviews and signs" is a documented social contract, not an enforced boundary. Autonomous signing is fine to pursue, but only once that promise is replaced by the policy-enforced kit above. Otherwise an autonomous integrator is one unpinned receiver away from draining itself.