o1-mcp
by mannutech
README.md
# o1-mcp
[](https://github.com/mannutech/o1-mcp-server/actions/workflows/ci.yml)
[](LICENSE)
An MCP server that exposes the [o1.exchange](https://o1.exchange) DEX aggregator — live swap quotes and routing on **Base** — to any MCP client (Claude Desktop, Claude Code, …). **Read-only and safe by default:** no private keys, no signing, no broadcasting. It returns routing data and, optionally, **unsigned** calldata you sign in your own wallet.
## Tools
| Tool | Endpoint | What it does |
|---|---|---|
| `o1_health` | `GET /health` | Liveness check. No auth. |
| `o1_get_quote` | `POST /quote` | **Primary.** Quote a swap → compact route summary + `raw`. |
| `o1_list_supported_dexes` | — | The DEX venues the router can use on Base. |
| `o1_build_swap_transaction` | `POST /submit` | **Gated.** Turn a `quoteId` into **unsigned** calldata. |
| `o1_execute_swap` | `POST /execute` | **Gated.** One-shot quote + unsigned calldata. |
The two gated tools register only when `O1_ENABLE_BUILD_TX=true`. They never sign or broadcast — you do, in your own wallet.
**`o1_get_quote`** takes `tokenIn`, `tokenOut` (an EVM address or `"ETH"`), and `amountIn` (base units / wei, as a string `> 0`). Optional: `slippageBps` (default `100`, capped at 2000 unless `O1_ALLOW_HIGH_SLIPPAGE=true`), `chainId`, `maxHops`, `splitEnabled`, `allowedDexes`, `feeBps`, `timeBudgetMs`. It returns:
```jsonc
{
"quoteId": "…", "expiresAt": 1782700000000,
"expectedAmountOut": "…", "minAmountOut": "…",
"feeBps": 5, "gasUnits": "210000",
"venues": ["AERODROME_V2LIKE", "UNIV3"],
"routesCount": 2,
"note": "quote expires ~10s",
"raw": { /* full /quote response */ }
}
```
> **Selling ETH?** Set `useNativeIn: true` on the gated tools, or the calldata's `value` comes back `0`. Quotes expire in ~10s and are never cached.
## Demo
Verified live in **Claude Code** — the read-only tools load and respond against production:


> `o1_health` → ✅ `/health` returned `{ ok: true }` · `o1_list_supported_dexes` → **21 DEX venues on Base** (chain ID 8453). The gated `o1_build_swap_transaction` and `o1_execute_swap` tools appear once `O1_ENABLE_BUILD_TX=true`.
<details>
<summary>Text version</summary>
```text
Tools for o1 — 3 tools
1. o1_health read-only, open-world
2. o1_get_quote read-only, open-world
3. o1_list_supported_dexes read-only
```
`o1_list_supported_dexes` → `UNIV2`, `UNIV3`, `UNIV4`, `AERODROME_V2LIKE`, `AERODROME_CL`, `PANCAKE_V2`, `PANCAKE_V3`, `PANCAKE_INFINITY_CL`, `HYDREX`, `QUICKSWAP_V4`, `ALIEN_BASE_V3`, `CURVE`, `PROPSWAP`, `TESSERA`, `ELFOMOFI`, `LUNARBASE`, `FELTIR`, `DODO_V2`, `WOOFI`, `GYROSCOPE_ECLP`, `MAVERICK_V2`.
</details>
## Quick start
Requires **Node 20+**.
```bash
npm install
npm run build
cp .env.example .env # set O1_API_KEY and O1_API_BASE_URL
```
Add it to your MCP client (stdio):
```jsonc
{
"mcpServers": {
"o1": {
"command": "node",
"args": ["/absolute/path/to/o1-mcp/dist/index.js"],
"env": {
"O1_API_BASE_URL": "https://quiet-bloodhound-531.convex.site",
"O1_API_KEY": "your-key-here"
}
}
}
}
```
Claude Code: `claude mcp add o1 -- node /absolute/path/to/o1-mcp/dist/index.js`.
### Environment
| Var | Required | Default | Notes |
|---|---|---|---|
| `O1_API_BASE_URL` | ✅ | — | Aggregator base URL, no trailing slash. Prod: `https://quiet-bloodhound-531.convex.site`. |
| `O1_API_KEY` | for non-health | — | Sent as `x-api-key` (issued by o1). Redacted from all output. |
| `O1_DEFAULT_SLIPPAGE_BPS` | | `100` | Default slippage (bps) when a quote omits it. |
| `O1_ALLOWED_CHAIN_IDS` | | `8453` | Chain allowlist. Base only (phase 1). |
| `O1_ENABLE_BUILD_TX` | | `false` | Register the gated `/submit` + `/execute` tools. |
| `O1_HTTP_TIMEOUT_MS` | | `8000` | Per-request timeout (ms). |
| `O1_MAX_RETRIES` | | `2` | Retries for `/health` & `/quote` on 429/5xx. |
| `O1_ALLOW_HIGH_SLIPPAGE` | | `false` | Allow slippage above the 2000 bps cap. |
| `O1_LOG_LEVEL` | | `info` | `debug`/`info`/`warn`/`error`. Logs go to stderr only. |
## Safety
- **Read-only by default** — swap-building tools are opt-in via `O1_ENABLE_BUILD_TX`.
- **No private keys, ever** — no tool accepts one, and output is always **unsigned**.
- **Inputs validated before any network call** — address format, `amountIn > 0`, slippage cap, chain allowlist.
- **Secrets redacted** from every log and error; **logs go to stderr only** (stdout carries only the MCP stream).
- **Errors mapped** — `400` invalid input · `401` auth · `429` rate-limited (with backoff) · `5xx` upstream. Reads (`/health`, `/quote`) retry with backoff + jitter; writes (`/submit`, `/execute`) never do.
## Development
```bash
npm run typecheck # strict tsc
npm run lint # eslint
npm test # vitest (mocked fetch — no live network)
npm run dev # run from source via tsx
```
## License
MIT
TDQS
A4.4/5.0
Scored across 3 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: quotes, health check, and listing supported DEXs. No overlap or ambiguity.
Naming Consistency4/5
All tools follow the 'o1_' prefix with descriptive names. While 'o1_health' is a noun instead of verb_noun, the pattern is still clear and consistent.
Tool Count5/5
Three tools is appropriate for a focused read-only aggregator interface. Each tool serves a necessary function without excess.
Completeness4/5
Covers core read operations (quote, health, supported DEXs) for the aggregator. Missing swap execution or token details, but the scope is explicitly read-only.
Maintenance
ActivityStale
ResponsivenessNo issues