@x207/mcp
An MCP server for x207, the mutual-credit settlement layer for x402 agent payments on NEAR. It lets a model such as Claude read a circle: its rules and members, one member's position and limit, the result of an epoch, a clearing proposal checked by the contract, the marketplace, and one order.
Status: version 0.0.1, pre-release. By default the server has read tools only: it signs nothing, sends nothing and holds no key. The eight write tools exist only on stdio, only when you turn them on, and only behind the guards below. Node 22.22.2 or newer.
The six read tools
All six are marked readOnlyHint: true. Credit amounts are decimal strings in the smallest unit (6 decimals). A tool that fails returns an error result with a short reason, and a part of an answer that could not be read (for example a read API that is down) is reported in warnings while the rest is returned. A result longer than 100,000 characters is cut and says so.
Configuration
Everything comes from the environment. No variable holds a secret.
Use with Claude Desktop (stdio)
Add the server to claude_desktop_config.json (Claude Desktop) or .cursor/mcp.json (Cursor). npx fetches the package the first time, so there is nothing to install:
Use version 0.0.2 or newer: 0.0.1 was published without its x207-mcp command. To keep a copy on your machine, npm install -g @x207/mcp and use "command": "x207-mcp" without args. From a checkout of the repository, build once with npm run build and point command at node with "args": ["/path/to/sdk/packages/mcp/dist/cli.js"]. Restart Claude Desktop, then ask for example: "Give me the overview of the x207 circle", "Inspect the member alice.x207-v013.testnet", or "Report epoch 1".
Write tools (stdio only, off by default)
Set X207_MCP_WRITE=1 and give the agent's account and keys in the environment. The HTTP server never has these tools, whatever the environment says.
A tool whose key or switch is missing is not listed at all. The voucher key is an off-chain ed25519 key of the agent (ed25519:<base58>); it is not a NEAR access key. The NEAR key is an access key of the agent's account.
The guards
- Budget of the session. Every voucher signed, every payment an acceptance would make, every milestone confirmed and every stake or residual paid counts against
X207_MAX_SPEND_SESSION. A commitment that does not fit is refused before it is signed; one that fails afterwards is given back.
- Price per call. An amount above
X207_MAX_PRICE_PER_CALL is refused. The price in a 402 comes from the seller, so the server does not trust it: the check runs on the requirement before the voucher is signed.
- Risky tools are off.
x207_join and x207_settle stay off until the operator sets X207_ALLOW_OUTFLOWS=1.
- Counterparties. The other party of a payment, offer, acceptance or milestone must be an active member of the circle (a contract view), must be in
X207_ALLOWED_COUNTERPARTIES when that list is set, and is never the agent itself.
- Network. The URL given to
x207_pay_request comes from a model, so it can come from a prompt injection. The tool refuses a URL whose host is, or resolves to, a loopback, private, link-local (the metadata service of a cloud machine), carrier-grade NAT, multicast or unspecified address, and names such as localhost, *.local and *.internal. It does not follow redirects (the answer says where a redirect points), gives each call 30 seconds, and reads at most 1 MiB of an answer. X207_ALLOW_PRIVATE_URLS=1 turns the address check off for a seller on your own machine.
- Keys. Keys come from the environment only. No result, error or log line contains a key: every text that leaves the server is scrubbed of the configured keys and of anything shaped like an ed25519 secret key. Before a tool sends a transaction the server reads the permission of the NEAR key (
view_access_key). A function-call key cannot attach a deposit, and join, stake, settle and claim all attach one (NEAR protocol rule), so with a function-call key those tools refuse and send nothing. With a full-access key they run only if X207_ALLOW_FULL_ACCESS_KEY=1; use a dedicated agent account with a small balance. The tools that sign with the voucher key send no transaction, so they do not need a NEAR key at all.
Also: the backend returns the terms of a bundle together with their hash, and x207_accept_offer refuses when the hash does not match the terms; x207_make_offer refuses when the listing the book returns does not hash to the hash that was asked for.
Security limit: the balance of the agent account is the largest possible loss
A NEAR deposit cannot be attached by a function-call key, so the tools that send one (x207_join, x207_settle, x207_claim) need a full-access key of the agent account. A full-access key can do anything the account can do, and none of the guards above limits what a stolen or misused key does outside this server. So use a dedicated agent account that holds only what the agent may lose: whatever NEAR and USDT sit on that account is the most that can be lost. The session budget and the price cap protect against a model that misbehaves; they do not protect against someone who has the key. Leave X207_ALLOW_FULL_ACCESS_KEY unset, and the three tools off, if the agent only needs to pay, list, offer, accept and confirm: those tools sign with the voucher key and never need a NEAR key.
Claude Desktop with the write tools
Keep the keys out of any file you commit. Then ask for example: "List a translation service for 0.05 credit", "Pay https://seller.example/api/summarize", or "Confirm milestone 1 of order …".
Use over Streamable HTTP
The endpoint is POST http://127.0.0.1:3207/mcp. It is stateless (no session) and serves only the read tools, so it can be shown to anybody who can already read the contract and the read API. GET /healthz answers 200. It listens on 127.0.0.1 unless --host says otherwise; put a reverse proxy with TLS and a rate limit in front of it before you expose it. On a loopback address it serves only the Host names of the loopback (127.0.0.1, localhost, [::1]), so a web page cannot reach it through DNS rebinding; behind a proxy set X207_ALLOWED_HOSTS=mcp.example.org (comma separated) to serve exactly those names.
To try it with the MCP Inspector:
Use as a library
Example output (real data)
Run against the circle x207-v013.testnet (contract views from the testnet RPC) and a local read API (backend) that served the read model of that circle with its three demo accounts. The tool results below are real; long fields (signatures, hashes of the order spec) are left out and marked by the keys that remain.
x207_member_inspect with {"account": "x207-demo-seller.testnet"}:
x207_search_listings with {"account": "x207-demo-seller.testnet", "limit": 1}:
x207_order_status with the hash of the order of x207-demo-buyer.testnet:
Without a read API the same server still answers x207_circle_overview, x207_member_inspect, x207_epoch_report and x207_simulate_clearing from the contract alone: on x207-v013.testnet x207_circle_overview returns member_count 24 and the rules (epoch_length 600, fees 10 and 5 bps, prune_delay_epochs 2), and x207_epoch_report for epoch 1 shows the phase finalized with 210 edges and 15 participants.
Safety
- The read tools call contract views and
GET requests only; there is no code path in them that signs, sends or changes anything. The write tools are described above and are off by default.
- Inputs are checked before any call: account ids, hashes, amounts as integer strings, at most 300 edges for a simulation, at most 200 items per page.
- Path segments are encoded, so an input cannot reach another endpoint of the read API.
- A circle on testnet reads from a testnet endpoint by default.
Develop
The tests play the NEAR RPC and the read API with a mock fetch, run the HTTP transport on a local port, and drive the built x207-mcp over stdio (that test skips itself when dist is not built).
License
Apache-2.0