MCP server
mcp.arcns.io — the arcns MCP server for AI agents. Every tool, the install snippets, the namespace/registry rule, the agent verification ladder, and what prepare_* returns and how to sign it.
https://mcp.arcns.io exposes arcns name resolution and records to AI agents over MCP — Streamable HTTP transport, stateless, no auth for read tools, no wallet/signer key material at all. It is a convenience surface, not a source of truth: the two resolution tools relay api.arcns.io byte-for-byte, every other read goes straight to Arc through @arcns/resolve, and the two prepare_* tools return an unsigned call for your own wallet — this server never holds a signing key and never signs or broadcasts. api.arcns.io now charges non-browser callers per read over x402; this server carries its own free-tier HTTP API key so resolve_name and reverse_lookup stay free for you to call — see Run your own for what that means if you self-host.
Rule
@jack, jack.arc and jack.circle are different namespaces that can have different owners, and jack.arc can live in more than one registry on the same chain. Every tool result carries its namespace and the registry it was read from. A mixed shape (@jack.arc) is refused as ambiguous; registries that disagree are listed under candidates. Nothing is ever picked for you.
Install
claude mcp add --transport http arcns https://mcp.arcns.io/mcpCursor (~/.cursor/mcp.json or .cursor/mcp.json):
{
"mcpServers": {
"arcns": {
"url": "https://mcp.arcns.io/mcp"
}
}
}VS Code (.vscode/mcp.json):
{
"servers": {
"arcns": {
"type": "http",
"url": "https://mcp.arcns.io/mcp"
}
}
}No auth header, no API key. arcns://llms.txt (also served at https://mcp.arcns.io/llms.txt) is the "Instructions for AI agents" block every client above hands to the model on connect — the source for this page's rules, in the form an agent reads first.
Tools
| Tool | Reads | Answers |
|---|---|---|
resolve_name(name) | relays GET /resolve/:name | Byte-identical to the resolver API — controller, address record + verified, namespace, registry, block. 409 ambiguous on a mixed shape; candidates[] when registries disagree. |
reverse_lookup(address) | relays GET /reverse/:address | The address's one forward-confirmed primary name, or "no primary set". |
check_availability(name) | Arc, one block | Registrable now on the arcns controller (valid, not registered, not reserved, controller not paused, TLD active) + price; for .arc / .circle also lists every pinned peer registry that already holds the name — it would resolve there, not here. |
quote_price(name) | Arc, one block | Registration price (USDC, 18 decimals) + the commit-reveal window. |
get_records(name, keys?) | Arc, current ownership epoch | Address records with verified, and text records — default keys are the agent.* set (agent.manifest, agent.x402, agent.mcp, agent.a2a, agent.erc8004, agent.capabilities) plus url / description / avatar. |
get_agent_manifest(name, probe402?) | Arc + the manifest URL + optionally the agent.x402 endpoint | The verification ladder below, the fetched + validated manifest, and payTo. |
search_agent_handles(q, limit?) | indexer query API | Prefix search over @handles with handleType: Agent. Answers code: "not-available" when the server has no query API configured. |
prepare_register(name, owner, handleType?, secret?) | Arc, one block | The commit + register calls (commit-reveal) for owner's wallet to sign. |
prepare_set_record(name, key, value) | Arc, one block | The setText call for the name's owner / operator / delegate to sign; agent.* values are checked against the convention below. |
Resources: arcns://llms.txt and arcns://schema/resolve — the exact /schema/resolve.json every resolve_name answer conforms to.
Agent records and the verification ladder
Agent-facing keys are ordinary ENSIP-5 text records on a name's resolver (docs/architecture/agent-records.md):
| Key | Value | Meaning |
|---|---|---|
agent.manifest | https://… or ipfs://… | The one document describing the agent's endpoints, capabilities and payment hints (shape below). |
agent.x402 | https://… | The x402-protected resource whose 402 answer is the authoritative price / asset / payTo. |
agent.mcp | https://… | The agent's own MCP server, if it has one. |
agent.a2a | https://… | The A2A agent card URL. |
agent.erc8004 | eip155:<chainId>:<IdentityRegistry>:<agentId> | Binds the name to an ERC-8004 identity. |
agent.capabilities | https://… | Optional capability document / JSON Schema URL. |
get_agent_manifest reports the highest level it checked, never one it assumed:
| Level | Check | What it proves |
|---|---|---|
| L0 — declared | agent.manifest or agent.erc8004 is set, or the handle's handleType is Agent | The current owner says this name is an agent. Nothing more. |
| L1 — identity-bound | ERC-8004 IdentityRegistry.ownerOf(agentId) == <name owner>, both read at one block | The same key controls the name and the ERC-8004 identity. |
| L2 — pay-to-confirmed | GET <agent.x402> answers 402 whose accepts[] names this chain (eip155:5042002) with a payTo equal to the name's Arc address record | The service the agent sells pays out to the address the name resolves to. |
The ladder as the server climbs it (sdk/src/agent.ts for L0–L1, the MCP server for L2), and the one check that sits beside it:
Rule
addr.verified is orthogonal to the ladder: it says the owner proved control of the payee address on chain, not that the name is an agent. Require L2 + verified: true before one agent pays another; verified: false means do not pay without a human.
What prepare_* returns, and how to sign it
{
"chainId": 5042002,
"to": "0x…",
"data": "0x…",
"value": "0",
"functionName": "commit",
"args": ["0x…"],
"description": "Commit to register @payday-bot (handle registry 0x520E…15d2)"
}- viem:
walletClient.sendTransaction({ to, data, value: BigInt(value), chain }) - Privy:
sendTransaction({ to, data, value }, { address }) - Circle CLI / a Circle developer-controlled wallet:
circle wallet execute --to <to> --data <data> --value <value> --chain ARC-TESTNET
prepare_register returns two steps (commit-reveal, SR-10): send step 1, wait at least minCommitmentAge seconds (and at most maxCommitmentAge) of chain time, then send step 2 with the same name / owner / secret / handleType — the secret is minted by the tool when you don't supply one and is never stored server-side; keep it yourself between the two steps.
x402 on the two prepare_* tools is out of scope for this server today (the x402 gateway lane owns payment code) — the server exposes a hook point for it without changing any tool's shape.
Run your own
Note
The fortiblox-arcns repository is private to FortiBlox Labs as of 2026-09-14 — git clone below will 404 without org access. The steps are accurate for engineers who have it; there is no public source drop yet (the same is true of the @arcns/resolve package — see Quickstart).
git clone https://forgejo.selfhsted.com/fortiblox/fortiblox-arcns.git
cd fortiblox-arcns/mcp && npm ci
ARC_CHAIN_ID=5042002 ARC_RPC_URL=https://rpc.testnet.arc.io \
API_URL=https://api.arcns.io MCP_BIND=0.0.0.0:8827 \
ARCNS_API_KEY=<your-free-tier-key> \
npm run build && npm startapi.arcns.io gates /resolve and /reverse behind x402 for non-browser callers (API) — ARCNS_API_KEY is a CEO-issued free-tier HTTP credential (a plain header value, not a wallet key) sent as X-API-Key on every call this server makes to API_URL, so resolve_name/reverse_lookup keep reading for free. Leave it unset only while upstream payments are off; once they're on, an unset or invalid key makes those two tools answer {code: "upstream-misconfigured"} rather than relay a raw 402 to your caller.
Leaving every *_ADDRESS variable unset falls back to the SDK's checked-in deployment for ARC_CHAIN_ID; set one and every one becomes required, so a half-configured override can never silently mix two deployments. See mcp/README.md in the repository for the full env table.