Skip to main content
Glama
dongsheng123132

MoneySwitch MCP Server

MoneySwitch

License CI npm

Give your AI an API key for money.

Website: moneyswitch.dev · 中文说明 →

An AI agent holds a mk_live_xxx MoneyKey — not a wallet private key — and spends USDC through x402-priced HTTP APIs on the Monad testnet. MoneySwitch checks policy (budgets, per-request limits, an approval threshold, a host allowlist, an SSRF guard) before every payment, then signs the USDC authorization itself with a locally-held, low-balance wallet. The agent never sees the private key.

Why

The industry already taught AI agents one pattern: hold a string that starts with a prefix (sk-…, mk_live_…), put it in an Authorization header, and the rest is handled for you. MoneySwitch reuses exactly that pattern for money instead of inventing a new one — no wallet extension, no seed phrase, no signing UI for the agent to click through. The agent calls an HTTP API with a bearer token; MoneySwitch is the thing standing between that token and an on-chain USDC transfer, enforcing the same kind of limits an API-key-issuing platform already enforces on rate and spend.

Related MCP server: obol-mcp

30 seconds

Try it — fully offline, no real money (Node.js 22+):

npx moneyswitch demo

A throwaway MoneySwitch starts on free local ports with a mock wallet, a demo LLM channel, two MoneyKeys ("Claude Code", "Codex"), a toll booth in front of a demo API and a few payments, and the Dashboard opens already signed in. Send a message in the Playground ($0.01), try to buy a $5 report and watch the per-request limit block it, then look at the toll booth's income. Every screen says DEMO · simulated settlement; settlement goes through a local mock facilitator and never touches a chain. Ctrl+C stops it and deletes the data. (moneyswitch demo downloads the separate, AGPL-3.0-only moneyswitch-server package; if your npm mirror has not synced it yet, add --registry=https://registry.npmjs.org/.)

Self-host the server + Dashboard in one command:

npx moneyswitch-server            # http://127.0.0.1:4020, data in ~/.moneyswitch/server

On first start it prints a one-time setup link (http://127.0.0.1:4020/setup#ms_setup_…) that signs you in and walks you through wallet → channel → first key → connecting an agent. --data-dir, --port and --host change the defaults.

Connect your local Claude Code / Codex to a MoneySwitch server with a MoneyKey:

npx moneyswitch connect --server http://127.0.0.1:4020 --key mk_live_xxx --apply

This detects Claude Code / Codex on your machine and wires up the MoneySwitch MCP server (without --apply it only prints the planned changes). npx moneyswitch status --server … --key … checks a MoneyKey's remaining budget; npx moneyswitch remove --apply undoes it; npx moneyswitch ui opens the local desktop console. The Dashboard's Connect agent page and the "send to employee" message generate these commands with the right address.

Charge AI for your own API (no server needed):

npx moneyswitch sell --upstream http://localhost:8000 --price 0.01 --pay-to 0xYourPublicAddress

From a checkout (contributors):

pnpm install
pnpm build
pnpm demo:local

pnpm demo:local starts a mock x402 facilitator, a demo x402 seller, and the MoneySwitch server (with the Dashboard UI) from the repository, entirely offline — no real payment happens. See docs/quickstart.md for the real Monad-testnet path (pnpm demo:testnet) and the manual step-by-step version.

Architecture

                      mk_live_xxx (MoneyKey, not a private key)
                              │
   ┌──────────────┐   MCP / OpenAI-compatible / REST   ┌────────────────────┐
   │  AI Agent     │ ───────────────────────────────►  │  MoneySwitch server │
   │ (Claude Code, │                                    │  (apps/server)      │
   │  Codex, ...)  │ ◄─────────────────────────────── │  policy → wallet     │
   └──────────────┘        result / usage / error       │  (apps/dashboard)   │
                                                          └─────────┬──────────┘
                                                                    │ x402 (EIP-3009 USDC auth)
                                                                    ▼
                                                          ┌────────────────────┐
                                                          │   x402 facilitator  │
                                                          │  (settles on-chain) │
                                                          └─────────┬──────────┘
                                                                    ▼
                                                          ┌────────────────────┐
                                                          │   Monad testnet     │
                                                          │  USDC Transfer      │
                                                          └─────────┬──────────┘
                                                                    ▼
                                                          ┌────────────────────┐
                                                          │  x402 seller /      │
                                                          │  priced API         │
                                                          └────────────────────┘

The server holds the private key in an encrypted, low-balance local wallet (packages/wallet); the agent process never has it. Every payment is checked against policy (packages/core) inside a database transaction before a signature is produced.

Three ways to plug an agent in

  1. MCP (apps/mcp, stdio) — money_status, paid_fetch, money_history tools. npx moneyswitch mcp, or moneyswitch connect --apply wires it into Claude Code / Codex automatically. See docs/claude.md, docs/codex.md.

  2. OpenAI / NewAPI-compatible gateway — set Base URL = http://<server>/v1, API Key = mk_live_xxx in any OpenAI-SDK-compatible client (openai SDK, Cherry Studio, Open WebUI, NewAPI upstream channel). GET /v1/models, POST /v1/chat/completions, and the legacy OpenAI billing endpoints are implemented — see SPEC-v0.2.md and docs/money-api-v0.md.

  3. REST — POST /v1/fetch { url, method?, headers?, body?, max_price? } fetches any x402-priced URL through the policy engine directly. See docs/money-api-v0.md.

Get paid: toll booths (v0.5)

MoneySwitch can also receive money. Put a toll booth in front of an API you already run: AI agents pay USDC per call over x402, the money goes straight to your receiving address, and the Dashboard shows it like revenue. You need no secret to sell — only a public receiving address — and your service does not change a single line.

The three things (read this first)

Thing

Think of it as

Give it to

In MoneySwitch

Private key

the key to the safe

nobody

never shown anywhere; it only lives (decrypted) in the server's memory

MoneyKey mk_live_…

a capped company card for an employee

only your own AI

🔒 amber, "Secret: whoever holds it can spend within its limits — never send it to a seller"

Receiving address 0x…

your payment QR code

anyone

✅ green, "Public: people pay you with it, safe to share"

Paste a MoneyKey, admin token, private key or recovery phrase into any receiving-address field and it is blocked and explained (the server refuses it too with INVALID_PAY_TO); paste a 0x… address into a key field and it is blocked the same way. The receiving address defaults to this MoneySwitch's own wallet (one wallet receives and pays); you can point it at any other address you control, e.g. a cold wallet — MoneySwitch then cannot spend that money for you.

In the Dashboard

Toll booths → New toll booth: ① which service (http://127.0.0.1:8000, with a free "test connection") ② how to charge (templates such as "the whole service, $0.01 per call" or "/v1/chat/completions $0.01, everything else free"; rules are METHOD /path or /prefix/*, the most specific wins; paths that match no rule are charged a default price, passed through free, or refused) ③ where the money goes. You get a public address https://<server>/t/<slug>/… to hand to buyers, plus ready-made buyer snippets. Earnings shows today / 7 days / all, per toll booth and per rule, every payment (payer, route, amount, tx, upstream status) and a CSV export.

# anyone: 402 + price + pay_to
curl -i http://127.0.0.1:4020/t/weather/v1/today
# a buyer, with THEIR OWN MoneyKey
curl -s http://127.0.0.1:4020/v1/fetch \
  -H "Authorization: Bearer mk_live_xxx" -H "Content-Type: application/json" \
  -d '{"url":"http://127.0.0.1:4020/t/weather/v1/today"}'

An OpenAI-compatible upstream behind a toll booth can be added as a channel in another MoneySwitch (Base URL = https://<server>/t/<slug>/v1).

Buyers are only charged when your service answers 2xx/3xx. The payment is verified first, the request is forwarded, and it is settled only on success; on 4xx/5xx/timeouts it is cancelled and recorded as "not charged". Your upstream receives X-MoneySwitch-Payer, X-MoneySwitch-Amount and X-MoneySwitch-Tollbooth, never the buyer's Authorization/Cookie or payment headers. Only /t/* has to be reachable by buyers — keep the admin API and Dashboard private (see docs/security.md); set MONEYSWITCH_PUBLIC_URL to the address buyers use. API details: docs/money-api-v0.md.

Without a server: moneyswitch sell

npx moneyswitch sell \
  --upstream http://localhost:8000 --price 0.01 --pay-to 0xYourPublicAddress \
  --route "POST /v1/chat/completions=0.02" --route "GET /health=0"

A single process on your machine (official @x402/express), with the same rule matching and forwarding code as the server's toll booths. It prints the public address and your (public) receiving address, and refuses a MoneyKey or private key as --pay-to.

Roles

Role

Holds

Uses

Can do

Admin (owner / finance)

ms_admin_…

Dashboard

wallet, channels, issue/revoke MoneyKeys, approve payments, see all usage

Employee

one or more mk_live_…

Dashboard "My Budget" view + desktop CLI

see own budget/history, Playground, one-command connect to their own local agent; cannot see others, cannot touch the wallet

Agent (Claude Code / Codex / Cherry Studio / …)

mk_live_… in env or MCP config

MCP or OpenAI-compatible interface

spend money, bounded by policy

Seller

only a public receiving address 0x… (no secret)

a MoneySwitch toll booth, moneyswitch sell, or any x402 server

receive USDC; with a toll booth, see income under Earnings

Security model & guardrails

  • The agent never holds a private key. Only a mk_live_… MoneyKey, scoped by budgets/allowlist/approval — the same shape as any other API key an agent already knows how to use.

  • Policy runs before signing, inside one database transaction: per-request limit → max_price cap → daily/total budget → approval threshold. Nothing is signed until every check passes.

  • Host allowlist governs every outbound request, including free ones — POST /v1/fetch is fundamentally an outbound proxy. An empty allowlist denies everything.

  • SSRF guard: MoneySwitch always refuses to let an agent point it back at its own listening address, in any common literal form (127.0.0.1/localhost/0.0.0.0/::1). This is literal/hostname matching, not a DNS-rebinding defense — see SECURITY.md for the exact limitation.

  • Secrets never round-trip. The full MoneyKey and admin token are shown exactly once, at creation; only their SHA-256 hash is stored. The wallet private key exists decrypted only in-process memory while unlocked, never on disk, never logged, never returned by any API response.

  • unknown payment outcomes count as spent, conservatively, rather than risk silently exceeding a budget when an upstream result can't be determined (timeout, dropped connection). Audit them manually via GET /v1/admin/usage.

Full threat model: docs/security.md / SECURITY.md.

Monad testnet

MoneySwitch's x402 client and mock-facilitator test suite are built against these testnet facts, checked against a live RPC as part of the repo's own pnpm test:testnet (see SPEC.md §1 for the full, dated table):

Value

CAIP-2 network

eip155:10143

RPC

https://testnet-rpc.monad.xyz

USDC (testnet)

0x534b2f3A21130d7a60830c2Df862319e593943A3 (6 decimals)

Facilitator

https://x402-facilitator.molandak.org

Payment scheme

exact (EIP-3009 transferWithAuthorization)

A real settlement (no mock) is a paid_fetch//v1/fetch call that produces a tx_hash, independently checkable by fetching that transaction's receipt and finding a matching USDC Transfer(from=wallet, to=seller, value=…) log — see tools/m6/verify-transfer.mjs. MoneySwitch never auto-funds a wallet or auto-requests testnet tokens; that's always a manual, out-of-band step (see docs/quickstart.md).

As of 2026-09-26, 11 real (non-mock) x402 payments have been settled on the Monad testnet this way — paid fetches, OpenAI-SDK chat completions and an approval-gated payment — all visible on-chain; 4 of them (first payment, Claude Code via MCP, OpenAI SDK chat, approval-gated) were checked Transfer-by-Transfer with tools/m6/verify-transfer.mjs. All were gas-free for the agent's wallet (the facilitator relays and pays gas for exact/EIP-3009 settlement), first tx 0x1c83a45d…4d4d (block 65595248).

Roadmap

  • v0.1 (done): MoneyKey, policy engine, local wallet, x402 client, MCP, Dashboard, offline (T1/T2) and testnet-read (T3) test suites.

  • v0.2 (done): OpenAI/NewAPI-compatible gateway (/v1/chat/completions, /v1/models, billing endpoints), channels, Playground.

  • v0.3 (done): employee-facing Dashboard view, one-command desktop connect (moneyswitch-connect → the moneyswitch npm package).

  • v0.4 (done): child MoneyKeys (multi-level delegation) and the local desktop console (moneyswitch ui).

  • v0.5 (done): toll booths — sell any API to AI for USDC (/t/<slug>, Earnings, moneyswitch sell); v0.5.1: npx moneyswitch demo (offline tour) and npx moneyswitch-server (one-command self-host).

  • Next: per-token pricing (x402 upto), MetaMask / OKX wallet drivers, multi-user organizations with department budgets and approval flows.

  • Not planned (see SPEC.md §12 for the full list and why): wallet-extension import, mainnet-by-default, fiat on-ramp.

License

MoneySwitch is dual-licensed by component: the parts an agent or seller embeds in their own process are permissive; the self-hosted server stays copyleft so that hosted forks give improvements back.

Component

License

apps/mcp, apps/connect, apps/cli (the moneyswitch npm package) — client-side code

Apache-2.0

apps/server-pkg (the moneyswitch-server npm package: server + Dashboard bundled for npx)

AGPL-3.0-only

apps/demo-seller (x402 seller example)

Apache-2.0

packages/tollbooth (toll booth rules, forwarding, pay-to checks — shared by the server and moneyswitch sell)

Apache-2.0

Everything else (apps/server, apps/dashboard, the other packages/*)

AGPL-3.0-only

The two npm packages are published separately: moneyswitch (Apache-2.0) never contains server code; moneyswitch demo only runs moneyswitch-server (AGPL-3.0-only) through npx as a separate process.

To embed the server in a closed-source product, open an issue to discuss a commercial license.

Contributing

See CONTRIBUTING.md. Contributions require agreeing to CLA.md (so the project can keep offering the dual-license terms above). Please read CODE_OF_CONDUCT.md first.

Related MCP Connectors

Related MCP Servers