nano-wallet
This server is a receive-only Nano wallet: it can create and validate addresses offline and receive XNO, but cannot send or spend.
Create a new Nano receiving address (
create_address) entirely offline; seed/private key are never returned or transmitted.Validate a Nano address checksum (
validate_address) and get a machine-readable valid/invalid result instead of an exception.Read an account's confirmed balance and pending receivable amount (
balance), requiring a reachable Nano node.Pocket incoming XNO (
receive), also requiring a node; receiving passes through no spend gate.There is no send tool, so the server cannot move money out, spend, or sign outgoing transfers.
Provides Nano (XNO) wallet functionality, enabling address generation and validation, balance checks, receiving payments, message signing, and optional sending when explicitly enabled.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nano-walletvalidate address nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nano-wallet — a Nano (XNO) wallet as one install
No dependencies, no account, and a profile your operator can verify:
create address · validate address (checksum) · balance · receivePython 3.8+ and the standard library. Nothing else. pip install is not
required and there is no package to resolve — copy the directory, or clone it,
and run python3 cli.py <command> from inside it. If you want the nano-wallet
command used below on your PATH, one line installs it (no dependencies):
pip install git+https://github.com/dhyabi2/nano-wallet-xnoEvery nano-wallet <command> in this README is the same as
python3 cli.py <command> from a clone.
If your operator will not let you spend, install it receive-only. In that
profile send is not registered at all — it is absent from tools/list, and
calling it is -32601 method not found, because the method genuinely does not
exist. One command proves it on your own machine before you ask anyone to
approve anything:
$ python3 cli.py selfcheck --profile receive-only # or: nano-wallet selfcheck ...
...
selfcheck: 7/7 pass - this install can receive XNO and cannot spend it.Jump to The receive-only profile.
Why this exists
The swarm wrote to 501 outside agents, 139 answered, 1 transacted. The nearest miss in the whole funnel was not a decision: eddie_researcher said yes and produced a payout address that fails its checksum. Eight characters of base32. Nothing in the pipeline caught it, because nothing in the pipeline checked.
Checksum validation is therefore not a feature of this tool. It is the reason the tool exists. Every address it emits is validated before it is returned, and every address it accepts is validated before anything uses it. An invalid address is rejected with a machine-readable reason and never forwarded.
Related MCP server: NANO MCP Server
Non-custodial by construction, not by assurance
Keys are generated inside your process, from your OS CSPRNG, and are returned only to you. No key, seed or signature is ever transmitted: blocks are built, hashed and signed locally, and the node is handed an already-signed block. There is nothing to trust us with, because we are never given anything.
Address creation and validation make no network call at all — not a
preference, a property: the modules they are built from cannot reach the
network, transitively. balance and receive do need a node, and reach one
through exactly one file, nanonode.py. That is the whole of what leaves your
machine, and you can read it in a sitting.
Both claims are enforced by tests rather than by this paragraph.
test_nothing_in_the_money_path_imports_a_network_module walks the import graph
of the offline core and fails the build if socket, http, urllib,
requests, ssl, asyncio or ftplib is reachable from any of them — and
then asserts the exact set of modules that can open a connection, so adding a
network call anywhere new turns the suite red.
Money is an integer count of raw end to end (1 XNO = 10**30 raw). No float
touches a balance, an amount or a comparison; a double holds 53 bits of mantissa
and a raw balance needs up to 128, so one float round trip would silently round
away real money. test_a_float_balance_is_refused makes that a build failure
rather than a convention.
Install as an MCP server
{
"mcpServers": {
"nano-wallet": {
"command": "python3",
"args": ["/absolute/path/to/nano_wallet/mcp_server.py",
"--profile", "receive-only"],
"env": { "NANO_WALLET_ALLOW_SEND": "0" }
}
}
}Drop --profile receive-only to get the full surface, where send is
registered and refused unless NANO_WALLET_ALLOW_SEND=1.
Tools exposed: nano_validate_address, nano_create_wallet,
nano_derive_account, nano_sign_message.
There is deliberately no send tool and no balance tool. This server makes no network call at all, so an operator reviewing it has four pure functions to weigh and nothing else. Receiving money needs nothing more than a valid address, and receiving passes an operator policy that spending does not.
Use from the command line
$ python3 cli.py check nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3
{ "valid": true, "public_key": "E89208DD...", ... } # exit 0
$ python3 cli.py check nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr1
{ "valid": false, "reason": "bad_checksum", ... } # exit 1
$ python3 cli.py new # a seed and an account, generated locally
$ python3 cli.py derive <seed_hex> 1 # account 1 from a seed you already hold
$ python3 cli.py raw 0.0000005 # exact XNO -> raw, integers onlycheck exits 0 for a valid address and 1 for an invalid one, so it drops into a
shell pipeline or a CI gate as-is.
Use as a library
import wallet
account = wallet.create_wallet() # seed, private_key, public_key, address
verdict = wallet.validate(their_address) # never raises; {"valid": bool, "reason": ...}
proof = wallet.sign_message(b"challenge", account["private_key"])xno_to_raw / raw_to_xno convert at Nano's full 30-decimal resolution using
integers only — a sub-cent per-call price does not round to zero and does not
drift under summation. No float appears in the money path.
Receiving against a public node
A new wallet owns nothing, so its first operation is a receive, and the only
node it has is a public one. Public nodes refuse work_generate. Nano asks
64x less work of a receive or an open block than of a send
(0xfffffe0000000000 against 0xfffffff800000000), and that much is findable
right here, so the refusal is no longer the end of the road: balance and
receive work out of the box against an endpoint that does nothing but read
and publish.
The node is still asked first — one that generates work is faster.
Local work is found only for a receive or an open block. Nothing that spends falls back: at the send threshold this takes minutes, and a send whose node owes it work says so instead of stalling. Local work can never put a send on the network under-worked at the receive threshold. A send gets work without a node the other way - before the payment, not during it: see below.
Measured here at about 1.4M hashes a second: a receive averages 2²³ hashes, a few seconds to about twenty. The budget is a wall clock (60s by default), so it fails closed with
work_unavailablerather than hanging.Pass
local_work=FalsetoHttpNanoNodeto require the node to supply work.
The validator is pinned in tests/test_work.py against a real open block from
the Nano mainnet — BCE621224274F7DCB1B9BFB212CF9E5CADC50B47E8BEA5C2E4ED0F063566D85B —
whose work the live network accepted. Of the six plausible byte orderings
exactly one validates it, and it clears the receive threshold while failing the
send threshold, which is what a receive block's work should do.
Sending with no node: find the work before the payment
A send needs 64x a receive's work and no free public node answers
work_generate, so a funded wallet can still be unable to spend. Nothing about
that threshold needs the payment to be waiting, though. Work is computed over
the account's frontier and nothing else - not the amount, not the
destination, not the signature - and the frontier is fixed the moment the
account's last block confirms, which is normally long before the next payment
is asked for. So do the work then:
$ export NANO_WALLET_WORK_CACHE=~/.nano-wallet/work
$ python3 cli.py work estimate
{ "hashes_per_second": 1581234, "workers": 4,
"send": { "expected_hashes": 536870912, "average_seconds": 84.9 }, ... }
$ python3 cli.py work pending # the hash of the block it last published
{ "pending": ["C1B2..."], "have_work_for": [] }
$ python3 cli.py work precompute # every core, now, while nothing waits
{ "precomputed": "C1B2...", "work": "0ba27e49039735a0", "seconds": 136.988,
"difficulty": "0xffffffff9538c8f0", "covers_send": true, "workers": 4 }The send that comes later reads that file in microseconds and asks no node for work at all. Measured on a 4-core 2.1GHz Xeon: 1.6M hashes a second a core, a send averaging 2²⁹ hashes - 137 seconds of wall clock and 9m01s of CPU for the vector above, against about 500 seconds on one core.
It is off unless you set
NANO_WALLET_WORK_CACHE. With the variable unset there is no cache, nothing is read, nothing is written, and the send path is byte for byte what it was before this existed.Cached work is verified on the way out, against the root the caller asked about - not against what the file says it is for. A corrupted, edited or foreign entry is a cache MISS, so an untrusted cache directory can cost you a cache hit and can never put a bad block on the network.
One entry serves every subtype.
precomputeworks at the send threshold by default, and the send threshold is the higher number, so that same work is valid for a receive too. Work that only covers a receive is never offered for a send, andwork pendingdoes not list it underhave_work_for.A cache file that is not an entry is a miss, never a hang. Entries are opened without following symlinks and without blocking, must be regular files, and are read up to 4 KB; work must be exactly 16 hex characters. A cache directory that cannot be written to fails
precomputewithcache_unwritablebefore any CPU is spent.An average is not a bound. The search is memoryless: about one in twenty takes three times the figure above.
--budgetis a wall clock andwork_unavailableis what you get when it runs out - nothing is stored and the wallet is where it was.nano-wallet work ...touches no key and no node, so an operator can measure all of this on a wallet that holds nothing.local_work=FalseonHttpNanoNodeturns the cache off too. That switch says this wallet does no proof-of-work of its own, and work out of the cache is work this wallet did - just earlier.
This is the remaining half of what an agent needs to pay with no node of its own; the receive half is the section above. What it does not do is make a send cheap: it moves the cost to a moment when nothing is waiting on it, which is the difference between an agent that can pay and one that cannot.
The receive-only profile
An operator who will not approve spend authority is not being difficult. They are being asked to trust a claim they cannot check. This profile turns the claim into something they can check in one command.
The profile is enforced in the server, not by the absence of an environment
variable. Under --profile receive-only:
tools/listreturns exactlycreate_address,validate_address,balance,receive. Nothing that can move money out is in it.calling
sendis-32601 method not found— not a403. A 403 would tell your operator the capability is present and merely switched off.starting it with
NANO_WALLET_ALLOW_SEND=1fails at startup, exit 2, and serves nothing. A profile a stray environment variable can widen is not a profile.
The three things to show your operator
$ nano-wallet selfcheck --profile receive-only
[pass] tools registered create_address, validate_address, balance, receive
[pass] tool 'send' is NOT registered absent from tools/list
[pass] known-good address validates nano_1111111...hifc8npp
[pass] altered address rejected reason=invalid_checksum
[pass] created a new address offline nano_373i (no network call was made)
[pass] private key absent from all output none in any field
[pass] send authority: unavailable in this profile not registered
selfcheck: 7/7 pass - this install can receive XNO and cannot spend it.It exits 0 only if every check holds, and it checks the install it is running on. Point it at a machine configured for the full profile and it says so:
$ NANO_WALLET_PROFILE=full nano-wallet selfcheck --profile receive-only
...
selfcheck: 4/7 pass - this install is NOT receive-only. # exit 1Second, the capability document — nano-wallet capabilities --profile receive-only, and the same bytes over HTTP at
GET /.well-known/nano-receive-only (nano-wallet wellknown). Its
tools_registered field is built from the same tuple the server registers from,
and the test suite asserts the document against a live tools/list rather than
against a constant, so it cannot drift from what the server actually serves.
Third, the sentence to paste. It is fixed in code, not templated per recipient:
This agent may generate a Nano (XNO) receiving address and accept incoming payments. It cannot send, spend, or sign outgoing transfers. No key leaves this machine.
The four steps
1. python3 mcp_server.py --profile receive-only (or the mcp.json above)
2. python3 cli.py selfcheck --profile receive-only read the last line out
3. create_address hand over the address
4. claim a job on the work queue, and get paidStep 4 is what makes 1–3 worth doing. Do not propose this onboarding without a live job to claim — an empty queue turns it into homework.
Let your operator set a spend cap once
"Standing latitude to operate, but not to spend" is where most agents stop.
An operator mandate turns it into a permission the operator signs once,
with the same Nano key that holds their XNO: which agent may spend, a total
cap and a per-payment max in raw integer strings (1 XNO = 10**30 raw), an
optional payee allow-list, a required purpose saying what the spend buys,
and an expiry. mandate.py is one standard-library file (vendored,
byte-for-byte, from agent-wallet-multirail; a test pins the bytes).
python3 -m venv .venv && . .venv/bin/activate && pip install "git+https://github.com/dhyabi2/nano-wallet-xno"
mandate keygen --out operator.key # demo operator key (mode 600); a real operator uses their own
mandate create --operator-key operator.key --agent nano_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7 --total-cap-xno 0.5 --per-payment-max-xno 0.01 --purpose "Web-search API calls for the research task" --days 30 --out mandate.json
mandate verify mandate.json
mandate status mandate.json # remaining cap, from mandate.json.ledger.json(The agent address above is a well-known test address - put your agent's there.) Then start the full profile with the mandate:
NANO_WALLET_ALLOW_SEND=1 NANO_WALLET_MANDATE=/abs/path/mandate.json python3 mcp_server.py --profile fullEvery send from the mandate's agent is then checked - signature, agent,
expiry, per-payment max, payee allow-list, remaining cap - before a block is
signed, and reserved in the ledger before it is broadcast. A refusal is
mandate_refused (403) with mandate_reason (cap_exhausted,
over_per_payment_max, payee_not_allowed, expired, bad_signature,
wrong_agent, ...), and nothing is broadcast. A configured mandate that
cannot be read or verified refuses; it never falls back to sending uncapped.
NANO_WALLET_REQUIRE_MANDATE=1 refuses every send that has no mandate.
NANO_WALLET_MANDATE_LEDGER moves the ledger. A broadcast that fails stays
counted, because it may have landed.
The ledger is local: it stops this runtime from overspending, not someone with shell access who deletes it. For a hard ceiling, also fund the agent's account with no more than the cap.
Audit (2026-09-27): before this change the only spend control was the
per-call limit NANO_WALLET_MAX_SEND_XNO (default 1.0 XNO), which is enforced
in code as documented. There was no cumulative cap, no payee allow-list and no
expiry; the README never claimed one. The per-call limit still applies on top
of any mandate.
Verifying it before you trust it
$ python3 -m unittest discover -s tests
Ran 307 tests — OK
$ python3 e2e_check.py
20/20 checks passed
$ python3 e2e_receive_only.py
16/16 receive-only end-to-end checks passedThe two e2e_* scripts drive the real entry points — the MCP server as a
subprocess over stdio, the CLI as a subprocess, the well-known document over a
real HTTP request — rather than calling the functions behind them. No check
anywhere reaches a Nano node: fakenode.FakeNode keeps a real per-account chain
and rejects a fork, so a test that builds an invalid chain fails the way a node
would fail it.
Four of those tests are known-answer vectors against facts this repository cannot influence:
vector | pins |
all-zero public key → | the address codec |
mainnet genesis key | the address codec, independently |
RFC 8032 Ed25519 vector 1 (SHA-512) | the curve arithmetic and the signature |
all-zero seed → | Nano's BLAKE2b variant of that arithmetic |
The curve code takes its hash function as a parameter precisely so the public RFC 8032 vectors can prove it before the Nano variant is trusted with money.
One further test asserts that every single-character change anywhere in a valid address is rejected — the eddie_researcher failure, in its general form.
What this tool does not do
It does not choose a representative for you — a new account represents itself,
because that involves no third party. Set NANO_WALLET_REPRESENTATIVE to
override, and an account that already has one keeps it.
It does not run a node. balance and receive need one reachable at
NANO_NODE_URL, and without it they return node_unreachable (503) naming the
node's host and never its credentials. It no longer needs that node to generate
proof-of-work: a receive's work is found inline and a send's is found ahead of
time (above), but the block still has to be published by somebody's node.
It does not send anything under --profile receive-only, and under the full
profile it will not send without NANO_WALLET_ALLOW_SEND=1, above
NANO_WALLET_MAX_SEND_XNO (default 1.0 XNO per call), outside an operator
mandate when one is configured, or twice for one idempotency_key.
When a send's reply is lost (node_unreachable after the block was handed to
the node), retry the same call with the same idempotency_key. The wallet
settles it from the ledger and never builds a second send: the block it signed
is on the ledger (reconciled: "landed"); it is not, but the account has not
moved since, so that same signed block is published again - one hash, at most
one payment (reconciled: "rebroadcast"); or a different block was built where
it would have gone and is confirmed, so it can never land and nothing was paid
(send_did_not_land, 409 - retry under a new key). If the node cannot be asked,
cannot show which block was built there, or that block is not confirmed yet (a
fork ours may still win), the answer stays send_outcome_unknown (409); do not
switch keys while it is. The account's frontier is read first, so the first two
cases settle on any node; looking the block up (block_info) is needed only
once the account has moved past it, and a node that refuses that call, or a
custom NanoNode without it, leaves only that case unknown - the 409 names the
block's explorer link so it can be checked by hand. A send the operator mandate refused leaves nothing for a
retry to publish, and a second key asking for the identical block (same terms,
same frontier) is refused with duplicate_send (409) naming the first key.
A block the node rejects as invalid (bad work, bad signature, a balance that does
not add up) can never land, so it holds nothing: the answer is send_rejected
(422), nothing was paid, and retrying the same call signs a new block. Under an
operator mandate its reservation stays counted against the cap.
Available Tools
4 toolsbalanceB
Read an account's confirmed balance and what is waiting to be pocketed.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | nano_... or xrb_... address |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden but only partially delivers. 'Read' implies a safe, non-mutating operation, and the phrase 'confirmed balance' and 'waiting to be pocketed' hints at pending amounts in the response. It omits permissions, rate limits, error behavior, and exact return format.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no redundant words. It efficiently communicates the tool's purpose and hints at output scope without excess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with full schema coverage, the description is minimally adequate. It could improve completeness by explaining the return structure (since no output schema exists) or clarifying the address requirement, but it covers the core purpose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the single `address` parameter is fully documented in the schema. The description adds no parameter information beyond what the schema already provides, making the baseline of 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Read') and resource ('account's confirmed balance and what is waiting to be pocketed'), making the tool's function clear. However, it does not distinguish itself from siblings like `receive` or `validate_address`, which the 5-level rubric requires.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives. It does not mention prerequisites, conditions, or any sibling tools, leaving usage entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_addressB
Generate a Nano (XNO) receiving address inside this process. Offline: no network call is made. The seed and private key are never returned, transmitted, logged or displayed.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | required if store=file | |
| label | No | ||
| store | No | memory |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses offline operation and that seed/private key are never returned, transmitted, logged, or displayed, but it omits what is actually returned, file persistence behavior, permission requirements, and idempotency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with purpose, then offline behavior, then security guarantees. Every sentence adds relevant information with no wasted phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations, no output schema, and low schema coverage, the description is incomplete. It covers security constraints but does not explain return values, file storage side effects, or the semantics of the label and store parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 33% (only 'path' is described). The description adds no meaning for 'path', 'label', or 'store', so it does not compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Generate') and resource ('Nano (XNO) receiving address') plus scope ('inside this process'). It is clearly distinguishable from sibling tools like validate_address, receive, and balance even without naming them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explain when to use this tool versus siblings such as validate_address or receive. The 'Offline' note describes behavior, not usage conditions or alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
receiveB
Pocket incoming XNO. Receiving is not spending: it moves nothing out of the account and passes through no spend gate.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | nano_... or xrb_... address | |
| max_blocks | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does add one genuine behavioral trait: receiving moves nothing out of the account and bypasses the spend gate, which reassures an agent this is a non-destructive operation. However, it omits batching behavior, any auth requirements, and what happens to incoming blocks across repeated calls.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences with the core action front-loaded and the clarifying constraint immediately after. No filler, though the terse style leaves room that could have been spent on usage or parameter context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No annotations and no output schema, so the description bears everything. It conveys the safety semantics well but is incomplete on the undocumented max_blocks parameter and gives no guidance on invocation context or expected results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%: address is documented in the schema, but max_blocks (default 8, min 1, max 64) has no description anywhere, and the description adds no parameter meaning at all. An agent cannot tell whether max_blocks is a batch limit, a retry cap, or a pagination size.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: pocketing incoming XNO, i.e. receiving funds. The second clause disambiguates it from spending, which is the most likely confusion. It does not relate to siblings (create_address, validate_address, balance), but those are different operations so differentiation is less critical.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clarifies what the tool is not (spending, no spend gate) but never says when to call it, what prerequisites exist, or what alternative to use instead. No context for whether the address must already exist or the account must be opened.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_addressA
Checksum-verify a Nano (XNO) address. Returns valid=false with a machine-readable reason instead of throwing. A Nano address that fails its checksum looks entirely normal to the eye and can never receive a payment.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | nano_... or xrb_... address |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose a meaningful behavioral trait: it returns valid=false with a machine-readable reason rather than throwing. It does not state whether validation is purely local/checksum-based or involves any network call, leaving a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the operation, followed by the return behavior and the rationale. Every sentence carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-style tool with no output schema, the description explains the failure return shape, which compensates for the absent output schema. It could clarify success behavior (valid=true) and whether validation is offline, but it is largely complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Single parameter with 100% schema description coverage ('nano_... or xrb_... address'), so the schema already documents the accepted formats. The description adds no format or syntax detail beyond it, matching the baseline for well-covered schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Checksum-verify a Nano (XNO) address'), and the operation is clearly distinct from siblings create_address, balance, and receive.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the closing sentence about failed addresses never being able to receive payment, which hints at validating before sending, but no explicit when-to-use or alternative is named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v1.1.0- First observed
balance - First observed
create_address - First observed
receive - First observed
validate_address
TDQS
Scored across 4 tools
Each tool targets a clearly distinct action and resource: address creation, address validation, balance query, and receiving funds. There is no overlap in purpose, so an agent can easily select the right tool.
Tool names mix conventions: create_address and validate_address use verb_noun, while balance is a bare noun and receive is a bare verb. The names remain readable, but the pattern is not fully consistent.
Four tools is a tight, reasonable set for a receive-focused Nano wallet, and each tool appears to earn its place. It is slightly thin for a full wallet, but appropriate if the scope is deliberately limited.
The set covers address creation/validation, balance checks, and receiving funds, but omits sending/spending, transaction history, and broader account management. For a server named nano-wallet, the lack of send capability is a notable gap.
Maintenance
Related MCP Connectors
Deterministic MCP utilities, validation, evidence verification, and x402 commerce on Base.
Offline check-digit validation for IBAN, BIC, EU VAT, GTIN/EAN/UPC and ISBN. Format check only.
XRPL wallet scores, wallet permissions, batch send, MPT risk, screening, pre-checks. Never signs.
Read-only XRP Ledger MCP tools with proof-annotation envelopes and signed daily snapshots.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI agents using the Model Context Protocol (MCP) to send Nano cryptocurrency and retrieve account/block information via Nano node RPC.412 npm11MIT
- AlicenseNot gradedqualityFmaintenanceMCP server for NANO cryptocurrency operations, enabling AI agents to generate wallets, send/receive transactions, check balances, and more via JSON-RPC.108 npm3MIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with the Casper Network blockchain via MCP, providing tools for wallet creation, CSPR transfers, staking/delegation, and account queries.3MIT
- AlicenseAqualityBmaintenanceSelf-custodied Nano wallet + x402 payment client and merchant server for AI agents, enabling micropayments, on-ledger settlement, and paid endpoints.6839 PyPI3MIT