Skip to content

sera-cx/sera-mcp

Repository files navigation

sera-mcp

The core MCP server for Sera Protocol. Turns any agent into a multi-currency agent by exposing Sera Protocol — stablecoin FX settlement — as one standard tool layer. 32 tools, 5 resources, 4 slash-prompt templates. Works with Claude Code, Claude Desktop, Cursor, OpenAI Agents SDK, OpenClaw, Hermes, NanoClaw, and any other MCP-compatible host.

Who this is for: agent builders and ops engineers who need their agent to discover currencies, quote, route, and execute stablecoin FX swaps through one standard tool interface.

Companion repo: Josh-sera/sera-agents — templates, examples, x402 services, and host integrations built on top of this MCP. Site: agents.sera.cx.

For deeper reading, see ARCHITECTURE.md, SECURITY-MODEL.md, and CHANGELOG.md.

This package also ships a sera CLI for cron jobs, CI scripts, and ops debugging — see CLI section below.

What you get

32 tools across 9 categories:

Category Tools
Discovery list_currencies, get_markets
Pricing & analytics get_fx_rate, compare_to_external_fx, multi_source_mid, spread_radar
Liquidity probing scan_markets, find_deals, probe_depth, round_trip_cost, infer_book
Quote & execute get_quote, prepare_swap, execute_swap, convert_and_send, quote_recipient_amount, find_cheapest_settlement_path, limit_watcher
Maker maker_quote_ladder
Treasury get_balances, treasury_value, exposure_report, rebalance_plan, pay_invoice
Settlement settlement_status
History fx_history, fx_volatility, corridor_pnl
Admin doctor

5 resources (sera://currencies, sera://markets, sera://config, sera://help/tools, sera://help/quickstart) — hosts can browse without burning tool-call budget.

4 slash-prompt templates for common workflows: sera.deal_scan, sera.treasury_brief, sera.invoice_optimizer, sera.fx_integrity_check.

Install

git clone <this-repo>
cd sera-mcp
npm install
npm run build

Requires Node 18.17+. Default install needs zero env vars — sensible defaults are baked in.

Wire it in

Claude Code (one line)

claude mcp add sera --scope user \
  --env SERA_NETWORK=mainnet \
  --env POLICY_PRESET=standard \
  -- node /absolute/path/to/sera-mcp/dist/index.js

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "sera": {
      "command": "node",
      "args": ["/absolute/path/to/sera-mcp/dist/index.js"],
      "env": {
        "SERA_NETWORK": "mainnet",
        "POLICY_PRESET": "standard"
      }
    }
  }
}

Cursor

Settings → MCP → add a stdio server pointing at node /absolute/path/to/sera-mcp/dist/index.js with the same env.

Any MCP client over stdio

SERA_NETWORK=mainnet POLICY_PRESET=standard node dist/index.js

Speaks MCP over stdio. The server prints structured JSON status to stderr; stdout is reserved for the protocol.

Streamable HTTP (remote / web-served)

# Default: localhost only, DNS-rebinding protection auto-enabled, stateful sessions
node dist/index.js --transport http --port 3848

# Bind public with allowedHosts header validation
node dist/index.js --transport http --host 0.0.0.0 --port 3848 \
  --allowed-hosts mcp.mydomain.com,localhost

# Serverless / stateless mode
node dist/index.js --transport http --stateless

Endpoints:

  • POST /mcp — JSON-RPC requests
  • GET /mcp — SSE stream for notifications (stateful mode)
  • DELETE /mcp — session terminate
  • GET /health — liveness probe

⚠️ NO BUILT-IN AUTH ON STREAMABLE HTTP

Anyone who can reach the bound port can call every registered Sera tool. The transport ships with DNS-rebinding protection for localhost binds and a public-bind startup guard that refuses to start when binding to a non-loopback host without --allowed-hosts or an explicit SERA_HTTP_ALLOW_UNAUTHENTICATED_PUBLIC=true acknowledgment. Do not set the ack env in production.

Safe options:

  • Localhost (default): --host 127.0.0.1 — DNS rebinding protection auto-enabled.
  • Behind an auth-handling reverse proxy: --host 0.0.0.0 --allowed-hosts mcp.mydomain.com. The proxy is the trust boundary; it must handle auth (OAuth, JWT, mTLS, Cloudflare Access — your call).
  • Future: OAuth 2.1 + RFC 8707 Resource Indicators per MCP spec v2025-06-18. Tracked in roadmap.

See SECURITY-MODEL.md for the full deployment matrix.

Verify install

In a new agent session, ask:

Call sera.doctor()

Returns a one-shot self-check: API health, network sanity, signer mode, policy summary, persistence state. If everything is green you're wired correctly.

Configure

Most installs need nothing. Override via env:

Network

Variable Default Effect
SERA_NETWORK mainnet mainnethttps://api.sera.cx/api/v1. sepoliahttps://api-testnet.sera.cx/api/v1. URLs are hardcoded; you cannot redirect them via env.

Signer

Mode What happens
external (default) Server never signs. get_quote returns route_params; your wallet signs externally; execute_swap accepts the signature. Safe for distribution.
local Server holds SIGNER_PRIVATE_KEY and signs in-process. Enables convert_and_send. Only use on a trusted server with a wallet you've intentionally funded.
readonly All execution tools refuse. Discovery + analytics only.

Policy presets

Pre-baked bundles. Pick one with POLICY_PRESET=:

Preset Symbols Per-tx cap Daily cap Slippage
starter USDC, USDT $1,000 $5,000 25 bps
standard (recommended) USDC, USDT, XSGD, JPYC, MYRT, TGBP, EURC $5,000 $50,000 10 bps
sg-retail USDC, USDT, XSGD $2,000 $10,000 15 bps
open (none) (none) (none) 0

Override individual fields by setting the matching env (POLICY_MAX_NOTIONAL_USD, POLICY_DAILY_VOLUME_CAP_USD, POLICY_ALLOWED_SYMBOLS, etc.). Each override beats the preset.

Other

Variable Effect
POLICY_DRY_RUN=true All execute_swap calls refuse, regardless of signer mode. Paper-trading mode.
SERA_ENABLE_EXECUTION_TOOLS=false Hide the execution tool category (execute_swap, convert_and_send) from the MCP host entirely. Default true. Set false for public / multi-tenant deployments. Other tools (discovery, pricing, liquidity, quote planning, treasury, history) keep working.
SERA_HISTORY_DB=/path/to/file.db Enables fx_history, fx_volatility, corridor_pnl. SQLite log of every fx_rate + quote call this MCP serves.
LOG_LEVEL trace | debug | info (default) | warn | error. Structured JSON to stderr.
SERA_API_KEY + SERA_API_SECRET Required for get_balances, treasury_value, exposure_report, rebalance_plan, pay_invoice, settlement_status.

See .env.example for the full list with comments.

Security model

Built for distribution. Every layer assumes the install instructions might be hostile.

  • Hardcoded base URLs: SERA_BASE_URL is ignored by default. Overrides require a separately-named env (SERA_BASE_URL_ALLOW_CUSTOM=true) and emit a loud boot warning. A malicious install snippet can't redirect API traffic by setting one env var.
  • No redirects: undici maxRedirections: 0. Even a sera.cx subdomain can't 301 us elsewhere.
  • Quote registry: every get_quote registers {uuid → frozen route_params}. execute_swap refuses unknown uuids in local-signer mode (won't sign arbitrary intents) and refuses route_params mismatches in any mode.
  • Server-derived notional: daily volume cap is computed from route_params.maxInputAmount × token's USD value. Caller cannot lie about it.
  • Prompt arg sanitization: every prompt template arg is type-validated (address regex, fiat regex, numeric regex, symbol-list regex) before substitution into LLM context. Newline/SQL/instruction injections are rejected.
  • Policy gates: symbol whitelist, recipient whitelist, per-tx notional cap, rolling 24h volume cap, dry-run kill-switch.
  • Signer modes: server defaults to external and holds no key.
  • Caching: read-only endpoints have TTL caches with in-flight de-dupe. Quotes never cached.
  • Logging: structured JSON to stderr; never to stdout (which is reserved for MCP transport).

Run sera.doctor in any agent session for a live posture check.

Execution flow (external signer)

  1. Agent calls sera.get_quote with from, to, amount, owner_address (or simulate: true to probe with the burn address).
  2. MCP validates the request against policy and calls Sera POST /swap/quote.
  3. MCP returns uuid + route_params (the exact EIP-712 Intent struct) + caches the binding.
  4. Wallet signs route_params under the Sera EIP-712 domain:
    const domain = { name: 'Sera', version: '1', chainId, verifyingContract: seraAddress };
    const types = { Intent: [
      { name: 'taker', type: 'address' },
      { name: 'inputToken', type: 'address' },
      { name: 'outputToken', type: 'address' },
      { name: 'maxInputAmount', type: 'uint256' },
      { name: 'minOutputAmount', type: 'uint256' },
      { name: 'recipient', type: 'address' },
      { name: 'initialDepositAmount', type: 'uint256' },
      { name: 'uuid', type: 'uint256' },
      { name: 'deadline', type: 'uint48' },
    ]};
    const sig = await signer.signTypedData(domain, types, route_params);
  5. Agent calls sera.execute_swap with { uuid, signature }.

Quotes are single-use. On QUOTE_STALE / 410, re-quote — do not retry the same uuid.

Architecture

┌────────┐    sera.get_quote        ┌───────────────┐     POST /swap/quote     ┌──────┐
│ Agent  │ ───────────────────────▶ │  sera-mcp     │ ───────────────────────▶ │ Sera │
│        │                          │ (cache+policy │                          │  API │
│        │ ◀── route_params + uuid  │  +registry)   │ ◀──── uuid + route       │      │
└────────┘                          └───────────────┘                          └──────┘
     │  sign route_params (EIP-712)
     ▼
 wallet / external signer
     │  signature
     ▼
sera.execute_swap (uuid + sig) ──▶ sera-mcp ──▶ POST /swap ──▶ Sera ──▶ on-chain settlement
                                       │
                                       └─ enforces uuid binding, daily cap, dry-run

Source layout:

src/
├── index.ts                    MCP server entrypoint, tool/resource/prompt registration
├── config.ts                   env loading, hardcoded URL allowlist, AppContext
├── resources.ts                MCP resources (sera://...)
├── prompts.ts                  slash-prompt templates with arg sanitization
├── sera/
│   ├── client.ts               REST client + TTL cache wrapper
│   ├── tokens.ts               token resolver, decimals math
│   └── types.ts
├── signer/signer.ts            EIP-712 signer (external | local | readonly)
├── policy/policy.ts            whitelist, caps, presets, dry-run, daily volume gate
├── tools/                      32 tool handlers
└── util/
    ├── cache.ts                TTL cache + in-flight de-dupe
    ├── limit.ts                bounded-concurrency runner
    ├── external_fx.ts          Frankfurter / open.er-api / exchangerate.host clients
    ├── persistence.ts          optional SQLite log
    ├── logger.ts               structured stderr JSON
    ├── quote_registry.ts       uuid → route_params binding
    └── sanitize.ts             prompt arg validators

Status

Honest read of what's hardened vs what's still moving:

Surface Status Notes
stdio MCP transport Stable Used in production by Claude Code / Claude Desktop / Cursor / OpenAI Agents SDK
Read tools (discovery, pricing, liquidity, history, treasury reads) Stable Cached, rate-limit-tolerant, no side effects
Policy gates (whitelist, caps, dry-run, daily volume) Stable Server-derived notional; quote-registry binding
Quote tools (get_quote, prepare_swap, quote_recipient_amount) Stable EIP-712 Intent surface stable
External signer execution (execute_swap with caller signature) Stable Server holds no key
Local signer execution (execute_swap server-signs, convert_and_send) Operator-managed Requires SERA_SIGNER_MODE=local + intentionally funded wallet on a trusted host
API-key treasury tools (get_balances, treasury_value, pay_invoice, settlement_status) Operator-managed Require SERA_API_KEY / SERA_API_SECRET
Tool annotations (readOnly / destructive / idempotent / openWorldHint) Stable (v0.5.0) Every tool carries annotations the host runtime can use for confirmation UX.
Tool grouping + execution opt-in (SERA_ENABLE_EXECUTION_TOOLS) Stable (v0.5.0) Default true; set false to hide execute_swap + convert_and_send entirely.
convert_and_send only registered when SERA_SIGNER_MODE=local Stable (v0.5.0) Tool no longer surfaces when it can't work.
Streamable HTTP transport Stable (v0.8.0) Additive to stdio; --transport http opts in. Localhost-default with DNS-rebinding protection. No OAuth (bind to localhost or front with auth proxy).
Per-tool outputSchema + structuredContent Partial (v0.7.0) Live on doctor, list_currencies, get_fx_rate, market_health. Remaining tools incremental.
Read/exec endpoint split (/mcp/read, /mcp/exec) Planned When OAuth lands.
OAuth 2.1 + RFC 8707 Resource Indicators for remote HTTP Planned Required before any public/multi-tenant deployment.

Roadmap

  • Streamable HTTP transport — additive to stdio; for ChatGPT connectors and hosted/remote agents. SSE is not on the roadmap (deprecated upstream).
  • Tool grouping + SERA_ENABLE_EXECUTION_TOOLS flag — hides execution tools by default; opt-in for trusted hosts.
  • Subscriptions — push deal alerts instead of polling. MCP spec supports it; needs server-side subscriber state.
  • Multi-hop SOR explorer — for pairs with no direct corridor, plan via intermediate fiats.
  • Address risk screening — sanctions / OFAC hooks (needs an external provider).
  • Approval/allowance managersera.approval_status for ERC-20 approvals to the Sera vault.
  • Per-tool rate limits — defend against polling-loop DoS on the upstream quote engine.

CLI

This package ships a sera CLI alongside the MCP server. Same code, same Sera plumbing — just a different entry point for shell scripts and ops use.

# After build, either run directly:
node dist/cli.js doctor

# Or install globally for the `sera` command:
npm install -g .          # from this directory
# Or:
npm link                  # development convenience

sera doctor
sera fx USD SGD
sera quote USDC XSGD 100 --simulate
sera deals --min-bps 25 --json | jq
sera ladder USDT JPYC 30000
sera spread-radar USD,SGD,MYR,EUR,GBP,JPY

Run sera --help for the full command reference.

Built for:

  • Cron jobs / CI — automated FX dumps, daily deal summaries, alerting
  • Ops debugging — Sera engineering checking quote behavior without an agent
  • Power users — terminal-first people who don't want an LLM in the loop

The CLI uses the same handlers as the MCP server so anything an agent can do via sera.* tools, the CLI exposes as a command. See src/cli.ts.

Add --json to any command to get raw JSON for piping into jq / scripts.

License

MIT

About

Multi-currency settlement MCP for AI agents. 32 tools over Sera Protocol's stablecoin FX rails. Open source.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages