@sorandomains/mcp
v0.10.0
Published
MCP server for Soran — AI agents resolve, verify, own, and manage names on Stellar.
Maintainers
Readme
@sorandomains/mcp
Governed testnet migration sealed at ledger 4604192 on 10 September 2026. See the release status for package and service availability.
Soran for AI agents, over the Model Context Protocol.
An agent can resolve and verify names trustlessly, look up any wallet's
identity, create its own wallet, claim a namespace, hold names, and publish
a verified on-chain identity. A preconfigured SORAN_SECRET signs locally and
is not returned by the tools. The test-only create_wallet tool returns its new
secret in the MCP response, so it appears in the conversation transcript.
Two transports, one tool set:
- Local (
npx @sorandomains/mcp, stdio): read tools always; wallet + write tools when the agent has a key. This is the full surface. - Remote (
https://mcp.soran.domains/mcp, streamable HTTP, no auth): the read tools — what hosted agents (claude.ai connectors and friends) reach with no install.
Version 0.10.0 targets the native-claim Stellar testnet successor deployment. The default Registry, Lookup, Primary and Allocator pins belong to that deployment.
Earlier releases are retired: 0.8.x and older read the retired 5 and 6 September 2026 stacks (universal mode fails closed against today's Lookup, but direct mode can still read frozen state), and 0.9.5 and older lack the 0.9.6 hardening (confirmation literals on five owner tools, response caps, tool annotations). Pin 0.10.0 or later. See Older versions and retired stacks.
Install
Claude Code / any shell agent (full surface)
claude mcp add soran -- npx -y @sorandomains/[email protected]Any MCP client: command npx, args ["-y", "@sorandomains/[email protected]"]. Set env
SORAN_SECRET (the agent's Stellar secret, S…) to unlock the write tools.
Restart the MCP process after updating its configured version. If an older client
returns namespace_required during activation, retry the same namespace, explicit
policy and fee ceiling with this release. It selects the namespace and supplies
the required X-Soran-Namespace header; no browser tab is needed.
Other env: SORAN_HINT_URL (API base, default https://api.soran.domains; it
receives the console session token and sign-in signature, so it must be https://,
with plain http:// accepted only for localhost, 127.0.0.1 and [::1], and it must
not contain a username or password; 0.10.0 and later refuse anything else at startup),
SORAN_RPC_URL (Soroban RPC override), SORAN_PASSPHRASE,
SORAN_REGISTRY_ID, SORAN_LOOKUP_ID, SORAN_PRIMARY_ID (none disables),
SORAN_ALLOCATOR_ID (verified claim-fee contract), and SORAN_RESOLUTION_MODE.
Reads use Universal Lookup by default and check its Registry anchor/version.
Missing universal configuration fails closed. Deliberate direct mode retains
native Resolver discovery. Ordinary names
need no setup and return memo none; required memos are preserved. Missing
configured instructions, old unsupported Resolvers and read failures never fall
back to address-only routing. Namespace owners still choose and may upgrade their
Resolver; compatibility checks are not a clean-code attestation.
Child names
Resolution, metadata, identity and history tools accept one child level, such as
mail.alice.nova. History is still an indexer report; a missing indexed child is
not proof that it does not exist on chain. lookup_identity uses Universal Lookup
for child ownership metadata.
Local set_profile, set_payment and set_record also accept child names. Their
records remain controlled by the parent name holder, even when the child pays a
different wallet. Display-name elections remain authorized by the destination
wallet and require the exact live forward proof; knowing or controlling a parent
name does not bypass those checks. Independent child transfers, native claims and
renewals remain unsupported. Child creation/removal, namespace child policy and
multichain configuration are available through the Holder/Owner SDKs, not dedicated
MCP tools in this release.
Hosted agents (read tools, no install)
Point the agent's remote-MCP connector at:
https://mcp.soran.domains/mcpTools
Read (both transports — on-chain reads plus explicitly informational API reports):
| Tool | Answers |
| --- | --- |
| lookup_name | native payment or legacy address with explicitly unknown memo capability |
| name_metadata | ownership/generation metadata, separate from effective payment |
| holdings_page | cursor, verified candidates and explicit coverage/failure counts |
| claim_fee_quote | selected XLM fee, recipient, network and refund terms; no signature |
| resolve_payment | complete on-chain address + memo instruction through Universal Lookup |
| verify_payment | fresh comparison of address, memo type and exact value |
| resolve_name | legacy address-only result; refuses required memos |
| verify_name | legacy address-only comparison; use verify_payment for payments |
| lookup_identity | the full picture of a name: holder, expiry, namespace, policy, profile |
| wallet_names | primary, reverse names and first holdings page; inspect continuation/coverage |
| reverse_lookup | address → verified display name |
| check_availability | is a namespace label unclaimed |
| name_history | issued/transferred/reclaimed timeline (indexed, informational) |
| network_status · list_allocations | deployment health · the public claim queue |
Wallet + write (local only; create_wallet/my_wallet need no key, the rest need SORAN_SECRET):
| Tool | Does |
| --- | --- |
| create_wallet | new friendbot-funded testnet wallet — returns the secret once |
| my_wallet | own address, balance, names, primary |
| claim_namespace | announce a claim on a top-level namespace for this wallet (opens the objection window; unopposed claims become eligible for permissionless execution) |
| claim_status · withdraw_claim | watch a claim's window · cancel it before it elapses |
| activate_namespace | deploy the Registrar for a claimed namespace; permanent selects non-reclaimable zero-term issuance, without locking contract upgrades |
| deploy_namespace_resolver | deploy, attest and select the native Resolver; accept an existing attested Resolver regardless of its cosmetic prefix |
| confirm_namespace_resolver | verify a prior Resolver deployment without signing or resubmitting |
| confirm_namespace_activation | verify an existing deployment and its original reviewed policy without signing or submitting another transaction |
| cancel_namespace_activation | clear a namespace/role vanity-generation job without withdrawing a claim or undoing a contract |
| issue_name · issue_batch · reclaim_name · renew_name | issue (single/bulk ≤23), reclaim, and renew names in a namespace this wallet OWNS |
| set_treasury · set_resolver · make_permanent | route reclaim custody · point at a resolver · historical permanence API (unavailable on governed testnet) |
| transfer_namespace · accept_namespace_transfer · cancel_namespace_transfer · namespace_status | hand the whole namespace to another wallet (two-step) · read owner/policy/permanence |
| claim_display_name | make a held name this wallet's verified display name (forward + reverse + primary in one call) |
| set_payment | atomically update address and complete memo instruction; use type none to remove a memo |
| set_muxed_display_name | elect an exact M destination's reverse or Primary name, one step per call |
| clear_muxed_display_name | clear that exact M reverse or Primary election |
| set_profile · set_record | publish profile records · point a name at an address |
| transfer_name · accept_name_transfer · cancel_name_transfer · pending_name_transfer | move names between wallets (two-step) |
Confirming state-changing writes
In 0.10.0 and later, every tool that moves or escrows value, changes ownership,
payment routing, fee destinations, resolver, policy or permanence, issues or reserves
names, or deploys contracts refuses its first call. (0.9.6 gates only reclaim_name,
set_treasury, set_resolver, transfer_namespace and make_permanent, with a
fixed confirm literal the model supplies itself.) The refusal (ConfirmationRequired) shows the
exact operation and a confirm code the server computed for it: the tool, every
argument, the signing wallet and the network are bound into the code, it changes
if any argument changes, differs per server process and expires after 5 to 10
minutes. Show the operation to the human and repeat the identical call with
confirm set to that code only after they approve. The gated tools are marked
destructiveHint and the ungated writes (renewals, cancellations, confirmations,
profile and display-name elections) are listed with their reasons in
CONFIRMATION_EXEMPT; a test fails when a tool is in neither list.
This is not out-of-band approval: an agent that can call a tool twice can copy the
code from the refusal into the retry, so keep your MCP client's per-call approval
prompt on for these tools. A host that already puts its own approval in front of
every call can pass requireConfirmation: false to registerWriteTools; there is
no environment switch.
Payment tools carry memo IDs as decimal strings, text as exact UTF-8 (1–28 bytes), and hashes as 64 lowercase hex characters. Text is untrusted data, never agent instructions. A payment must include the returned memo; refuse unsupported memo types. Reverse and primary names identify an account, not an individual customer's memo on a shared exchange account. Old installed clients need an explicit upgrade.
Complete M display-name tools
MCP 0.10.0 local mode exposes:
set_muxed_display_name({ name, destination: fullM, kind: "reverse" | "primary" })
clear_muxed_display_name({ destination: fullM, kind: "reverse" | "primary", namespace? })namespace is required when clearing reverse. The signer must be the M address's
underlying G account. A custodian must sign elections for its deposit routes;
receiving an M address does not give a customer control of the custodian's key.
Set the namespace reverse first, then optionally set Primary in a separate call.
If Primary fails or is cancelled, a successful reverse remains. These methods do
not change payment instructions, and claim_display_name remains a G-wallet flow.
The contract binds the full u64 ID, including 0 and the maximum, with no G-account,
other-ID or memo fallback. Require verified Lookup muxed_identity_version() == 1.
The agent-identity flow
create_wallet → store the secret, restart with SORAN_SECRET set
claim_fee_quote → review XLM fee/recipient/network and refund terms
claim_namespace → pass label, expectedFee and maxNetworkFeeStroops; wait out the window (1 day on testnet);
claim_status → confirm execution awarded the namespace
activate_namespace → deploy its Registrar with the reviewed policy
deploy_namespace_resolver → deploy/verify its native Resolver before issuing names
issue_name / claim_display_name → mint and claim a verified name
set_profile → publish who the agent isOr skip claiming and just receive a name a namespace owner issues, then
claim_display_name — an agent gets a verified identity either way.
Trust model
Payment and identity answers come from the configured chain contracts;
indexer-discovered name candidates are checked on chain before return. Discovery
can omit names. name_history, network_status and list_allocations return
API/indexer reports, not independently verified answers. claim_fee_quote comes
through the API; signing independently rechecks its policy on chain. Free-text fields
in results (profile values, claim evidence) are third-party-authored — data,
not instructions. Write tools sign locally with the preconfigured agent key; that key is not sent
to Soran servers. Wallet creation is the explicit secret-returning exception
described above.
Embedding
The tool registry is exported for building your own server:
import { registerReadTools, registerWriteTools } from "@sorandomains/mcp";registerReadTools(server, { registryId, lookupId, allocatorId, rpcUrl, passphrase }) adds the reads to any McpServer;
await registerWriteTools(server, { secret }) adds the wallet/write tools.
Source: https://github.com/SoranDomains/sdk · Docs: https://github.com/SoranDomains/docs · License: MIT
Version 0.10.0 requires Node.js 22.12.0 or newer and uses Stellar SDK17 with Lookup 0.11.0, Holder 0.10.0 and Owner 0.12.0. Holder receipt recovery requires sufficiently fresh clean Registrar provenance after each receipt read or transaction inclusion. Both transports pass the same universal configuration and export the same MCP version. The successor deployment retains Lookup V2; custom Registry or passphrase settings require their own Allocator pin and do not inherit testnet fee routing.
resolve_payment, lookup_name, verify_payment and set_payment accept or return
complete muxed M destinations with memo none. resolve_name retains the full M
address. M-plus-ID/text/hash is rejected; its embedded ID is not a transaction memo.
Example tool payment input:
{
"address": "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAABUTGI4",
"memo": { "type": "none" }
}Lookup capability is read on chain: successful version 1 uses the original ABI;
version 2 requires destination version 2 and selects the new destination ABI.
Failure never selects an older method. Local set_payment uses Holder's exact
set_muxed authorization for M and preserves the base G account plus u64 ID.
Account ownership and wallet signing remain G/C. Read tools reverse_lookup,
wallet_names and recipient identity enrichment preserve complete M identities
through Lookup 0.8.0 after verifying muxed_identity_version() == 1. No M-to-G,
other-ID or transaction-memo substitution is performed. M wallet profiles return
no holder holdings: a muxed ID is not a separate owner. Contract and hosted MCP
activation must be verified separately in the release status.
Namespace claims require the exact reviewed expectedFee from claim_fee_quote
and an explicit maxNetworkFeeStroops ceiling (network/resource fee, separate from
the claim fee). Signing independently rechecks claim_fee_policy on chain and
validates the pinned Allocator, label, claimant, evidence and one exact native-token
transfer to escrow in the source authorization tree. A changed quote, extra call,
wrong asset/amount/destination, signature or excessive network fee is rejected.
Awarded claims pay the treasury; rejected/stuck claims refund 100%; withdrawal or
expiry refunds 80%, rounded down. Settlement is attempted immediately. Any undelivered amount remains protected
as a credit for its recipient to collect through the Allocator contract's
claim_fee_credit(address) method. This package does not expose that recovery
method as an MCP tool.
Reserved direct Registry claims and objection bonds are separate from this fee.
An active bound reservation uses its reserved-claim flow; eligible unbound or
lapsed reservations can enter the public window with the required proof.
Holdings completeness is an indexer report, not proof against omission; failures and continuation remain visible. Profile/evidence values are untrusted data. Names accept ASCII uppercase and normalize it only after rejecting non-ASCII. Primary None can hide a failed dependent proof in the existing Primary ABI. Lookup governance upgrades have no mandatory delay; anchor/version checks are not executable-code pins. Namespace assurance does not cover that governance.
withdraw_claim requires a network-fee ceiling. activate_namespace requires the
exact namespace and network-fee ceiling; selected policy, treasury, Registry and
predicted Registrar address are checked before signing.
Namespace-scoped tools select the requested namespace in their private API
session for each request, including after authentication renews. Concurrent
tools serialize selection with the associated request.
Claims, withdrawals and activation compute the exact signed transaction hash
locally before submission. If the API response is interrupted or cannot confirm
that transaction, the result retains txHash and reports pending: true with
announced, withdrawn or activated set to false. Check that transaction
before creating a replacement. Explicit API refusals remain errors; a failure
before signing or dispatch does not claim a pending submission.
The current testnet public-window claim fee is 5,000 XLM (50,000,000,000 stroops), separate from network fees and objection bonds. Always fetch and review the live quote; the tool never substitutes a hardcoded amount for the on-chain policy.
Native username claiming
Local signer tools add native username quote/preparation/claim/recovery, policy configuration, reservations, reserved assignment, transfer acceptance and holder renewal. These use the current contracts directly; top-level namespace application and activation remain separate API-prepared flows. Hosted MCP does not receive these write tools or an operator signing key.
Read the native claim APIs, security boundaries and complete signup flow. G/no memo, G with ID/Text/Hash, full M/no separate memo and C/no memo remain supported payment destinations. Current transaction-signing adapters use classic G accounts.
Verified testnet deployment
See the public deployment manifest for confirmed code hashes, transaction receipts and verification scope. Network passphrase: Test SDF Network ; September 2015.
| Contract | Address |
|---|---|
| Registry | CCSORANDPQINYOYB5SVO45WJP2LBBYKC72HHUIRVXB4J6RUZKDAUW7G4 |
| Lookup | CDSORANQAJK35UV2HR63CMB6M5NYISHMUBTB6EQY2CZ3Y7HJDIOHRJWA |
| Primary | CCSORAN7Y7ICQK2MBSVCJT3BUN5EHXKDKSTMGVB6QWSYXWMMLG2WIFJ6 |
| Allocator | CCSORANYFHUJUWETEQ7UWIDN4JTPV7NN5G6YUSSBZI57X7EQWVA63VMJ |
Mainnet has no deployment preset. Custom networks must supply their own verified addresses. Universal Lookup upgrades remain immediately executable; an address and ABI version do not pin the code that will execute after a governance upgrade.
Namespace activation and vanity addresses
The new testnet Registry derives Registrar and Resolver addresses from the namespace, contract role and caller nonce on chain. MCP validates the predicted Registrar and Resolver independently before signing; an API-supplied version or address is never sufficient authorization.
When activate_namespace returns status: "queued" or "mining" with
retryAfterMs, the service is generating a branded address. Retry the same
namespace, policy and fee limit after that interval. This stage signs and
submits no deployment.
After signing, an unresolved activation instead returns pending: true,
activated: false, txHash and predictedId. Call
confirm_namespace_activation({ namespace, predictedId, txHash, expectedPolicy })
with the original reviewed policy to check that deployment without redeploying.
Keep the identifiers when confirmation remains pending. If the original response
was lost, predictedId and txHash are optional: the API can discover the Registrar
from the Registry. The namespace and original expectedPolicy remain required.
Confirmation checks the namespace, wallet, Registrar and policy; it does not sign
or submit a transaction.
Custom API clients must select their namespace using
POST /console/session/namespace { namespace }, then send the same session token
and X-Soran-Namespace on namespace-scoped prepare, submit and confirm requests.
See the complete API flow.
The packaged testnet deployment defaults to salt version 1. For a custom
Registry, locally configure registryDeploymentSaltVersion: 1 (namespace-bound)
or 0 (legacy raw salt); stdio uses SORAN_REGISTRY_DEPLOYMENT_SALT_VERSION.
Unknown custom schemes fail closed. This setting changes address prediction,
not the Registry contract ABI or the destination address types supported by
payment resolution.
The MCP v1 activation flow also requires a C?SORAN… address, derived from
signed namespace and nonce, before signing. Direct Registry callers remain free
to choose ordinary addresses. To abandon or restart a queued, failed or ready
search, call cancel_namespace_activation with the exact namespace and
role: "registrar" or "resolver". Cancellation only clears the service's
address-generation job; it does not withdraw a claim or undo a contract.
Complete Resolver setup
After activate_namespace verifies the Registrar, call:
deploy_namespace_resolver({ namespace: "yourbrand", maxNetworkFeeStroops: "50000000" })This is a separate owner-signed transaction. It uses the official API's Resolver vanity search and the Registry factory to deploy, attest and select the Resolver. The signer checks the exact namespace, Resolver role, nonce, predicted address, network, authorization and fee ceilings before signing. The service's address prediction is independently recomputed locally.
A queued or mining result has no deployment transaction: retry the same tool
after retryAfterMs. A submission with an uncertain outcome instead retains
predictedId and the exact signed txHash. Recover it with:
confirm_namespace_resolver({ namespace: "yourbrand", predictedId, txHash })Confirmation reads the Registry directly and does not sign or submit. It verifies
ownership, the selected/attested Resolver and the Registry's native contract check
before returning resolverReady: true. Keep the identifiers while verification
is pending. If the original response was lost, both identifiers are optional;
the Registry can discover its existing Resolver. txHash is retained for recovery,
not independently checked as a transaction receipt by this confirmation tool.
An existing clean, attested native Resolver without C?SORAN is valid: the prefix
is cosmetic. Setup returns alreadyConfigured: true without preparing or submitting
anything. A cleared or mismatched pointer stops setup and identifies the attested
Resolver to review and restore with set_resolver; it never deploys a replacement.
Once resolution is ready, configure public claims with configure_native_claims or
issue names as the owner. Hosted MCP remains read-only.
Local transaction signing limits
Available in 0.9.5.
Every write, including older SDK operations and storage restoration, has a default 5 XLM total network-fee ceiling. The local operator may configure SORAN_MAX_NETWORK_FEE_STROOPS (canonical integer 1–4294967295), or WriteToolOptions.maxNetworkFeeStroops, after reviewing deployment/storage estimates. Agent tool arguments cannot increase that ceiling. The older SORAN_MAX_NATIVE_FEE_STROOPS setting remains supported: it also supplies the overall ceiling when the new setting is absent; if both are set, native methods use the lower value. This is a per-transaction limit, separate from username/namespace prices and any batch spending budget. Initial storage rent may exceed the default, in which case signing stops. See native claim behavior and limitations.
Write envelopes must be valid now and expire within five minutes. Unbounded, expired or later-expiring requests stop before the local key signs them.
Governed testnet code and migration recovery
On a governed Registry, native verification reads the exact per-namespace Registrar code pin and its upgrade history. Approved upgrades do not have to match the current factory default. Missing or malformed provenance still prevents signing and receipt confirmation; RPC failure never downgrades verification to a legacy rule.
Historical claim recovery is read-only and binds the original intent to the frozen source Registrar and sealed migration commitments. It never rewrites the intent to a successor Registry or treats an unavailable receipt as permission to submit again. Deployment migration and package publication are separate; check the release status for the active addresses.
Namespace activation rules
activate_namespace accepts policy: "reclaimable", policy: "permanent", or an explicit object with all five fields:
{
"default_term_secs": "0",
"reclaimable": true,
"trade_fee_bps": 0,
"tradeable": false,
"transferable": false
}Both presets use no expiry, allow transfers and disable trading with a zero trade fee. The explicit object lets an owner disable holder transfers or set a default term. Terms must be zero or 86,400–3,153,600,000 seconds. Trade fees must be whole basis points from 0 to 10,000. Unknown and incomplete policy fields are rejected. These stored trading preferences do not enable a marketplace. Username claim prices are configured separately through configure_native_claims.
The tool checks every policy field in the unsigned invocation and constructor authorization, then verifies the deployed policy on chain. If confirmation succeeds but verification is unavailable, the response reports activated: null and pendingVerification: true; use confirm_namespace_activation with the original expectedPolicy before taking further action. Do not activate again.
There is no ordinary setter for these five rules after activation. See the activation guide for API requests, exact limits and direct contract calls.
