Skip to main content

Model Context Protocol server for the Exfer blockchain — gives an AI agent direct, typed access to an exfer-walletd hot wallet

Project description

exfer-mcp

Model Context Protocol server for the Exfer blockchain. Gives an AI agent (Claude Desktop, Claude Code, any MCP-aware host) typed, direct access to an exfer-walletd hot wallet.

Warning — this is a hot wallet. By default there are no spend caps and no human-approval gates: anything that can talk to this MCP server can spend the wallet in full. Recent exfer-walletd adds operator-configured spend ceilings — set WALLETD_SPEND_CAP_PER_TX (max per spend) and/or WALLETD_SPEND_CAP_PER_PERIOD + WALLETD_SPEND_CAP_PERIOD_SECS (rolling-window total), in exfers. In managed mode the spawned walletd inherits these from the MCP host's environment, so setting them in your host config bounds the blast radius. Until you configure caps (or unless you only keep a small float here), run exfer-mcp only against accounts you would be okay losing in full.

What it exposes

22 tools spanning the full wallet, payment, HTLC, reputation, and EXFER-QUOTE surface.

Wallet & chain (read)

Tool What it does
exfer_generate_address Create a new managed address — returns {address, pubkey, index} (use the pubkey as a quote's payee_pubkey)
exfer_list_addresses List the managed addresses (with index / label)
exfer_get_balance Confirmed balance of a managed address
exfer_get_block_height Current chain tip {height, block_id}

Payments

Tool What it does
exfer_simulate_transfer Dry-run a payment — exact fee + inputs, no broadcast
exfer_transfer Build, sign, broadcast a payment (optional on-chain datum)
exfer_wait_for_tx Block until a tx reaches a confirmation depth
exfer_wait_for_payment Block until a payment to an address is seen — sub-second push, no polling
exfer_payment_uri_encode / _decode Build / parse a BIP21-style exfer: URI

Identity & EXFER-QUOTE

Tool What it does
exfer_sign_message / exfer_verify_message Sign / verify an arbitrary message (proof of key control)
exfer_quote_issue Issue a signed EXFER-QUOTE price credential (Spend-posture; signs, moves no money)
exfer_quote_verify Verify a signed EXFER-QUOTE (pure read)

Conditional payment & reputation

Tool What it does
exfer_htlc_lock / _claim / _reclaim / _status / _list Hash-time-locked contracts (atomic swaps)
exfer_get_address_history An address's on-chain activity (indexer-backed) — raw history you interpret, not a trust score

The reputation / history tools and non-owned HTLC lookups require walletd to be pointed at an exfer-indexer (it is, by default, in managed mode).

Two ways to run

exfer-mcp talks to an exfer-walletd hot wallet. You can either point it at a walletd you run, or let it spawn and supervise its own — like the browser MCP manages its own headless browser. The mode is selected automatically by whether WALLETD_URL is set:

External Managed (zero-setup)
Selected when WALLETD_URL set WALLETD_URL unset
Who runs walletd you, separately exfer-mcp spawns + supervises it
Required env WALLETD_URL + WALLETD_AUTH_TOKEN WALLETD_KEYSTORE_PASSPHRASE (+ EXFER_WALLETD_BIN if not on PATH)
Node / indexer whatever your walletd uses the project's public mainnet node + indexer (overridable)
Lifecycle independent of the MCP tied to the MCP — spawned on start, killed on exit (no orphans)

1. External walletd (original behaviour)

You already run exfer-walletd somewhere; exfer-mcp just connects to it. Set WALLETD_URL + WALLETD_AUTH_TOKEN and (for https:// with a self-signed cert) WALLETD_FINGERPRINT. Nothing about this path changed — see Configure (Claude Desktop) below.

// codex / Claude config — EXTERNAL mode
{
  "mcpServers": {
    "exfer": {
      "command": "uvx",
      "args": ["exfer-mcp"],
      "env": {
        "WALLETD_URL": "http://127.0.0.1:7448",
        "WALLETD_AUTH_TOKEN": "<paste your walletd token here>"
      }
    }
  }
}

2. Managed (zero-setup)

Leave WALLETD_URL unset. exfer-mcp then spawns its own walletd against the project's public mainnet reference node + indexer and wires the rest of the server to it automatically. You only have to provide a keystore passphrase:

// codex / Claude config — MANAGED mode (zero-setup)
{
  "mcpServers": {
    "exfer": {
      "command": "uvx",
      "args": ["exfer-mcp"],
      "env": {
        // No WALLETD_URL → managed mode.
        "WALLETD_KEYSTORE_PASSPHRASE": "<a strong passphrase for the managed wallet>"
      }
    }
  }
}

The walletd binary is obtained automatically. exfer-mcp resolves it in order: EXFER_WALLETD_BINexfer-walletd on PATH → otherwise it downloads the prebuilt binary for your platform from the pinned exfer-walletd release, verifies it against that release's SHA256SUMS, and caches it under ~/.cache/exfer-mcp/walletd/<version>/. Since the binary is a hot-wallet daemon, a download is run only after its checksum matches; a release without checksums or a mismatch is refused. Pin a version with EXFER_WALLETD_VERSION, or set EXFER_WALLETD_BIN to a walletd you built yourself to skip the download.

On first run, the managed wallet has no keystore, so exfer-mcp initialises a fresh seeded one and prints the 24-word recovery phrase prominently to stderr (the MCP host surfaces stderr to you). That phrase is the only backup for the funds in this wallet — write it down.

Warning — the managed wallet is a hot wallet. Anything that can reach this MCP server can spend its funds (see Safety). The keystore + scoped bearer tokens live under WALLETD_DATADIR (default ~/.exfer-walletd-mcp) and persist across restarts, so the phrase is shown once — back it up the first time.

Managed mode is lazy / non-blocking — the MCP handshake is instant, so the host (codex / Claude) never appears frozen on startup:

  1. Picks a loopback bind — 127.0.0.1:7448 by default, or a free loopback port if 7448 is busy (so a managed walletd never collides with a walletd you run yourself) — synchronously, so the bind is known immediately.
  2. Answers the MCP handshake + list_tools right away (the tool list is static and needs no walletd); walletd is brought up in the background.
  3. In the background: initialises a seeded keystore under WALLETD_DATADIR if one isn't there yet (surfacing the recovery phrase; reuses an existing keystore otherwise), then spawns exfer-walletd --datadir … --bind … --node-rpc … --indexer-rpc … with your passphrase in its env, forwarding its logs to the MCP's stderr with a [walletd] prefix (bearer tokens redacted).
  4. The first tool call waits until walletd answers a get_block_height health probe (typically a few seconds; up to ~30 s), reads the spend-scope bearer token, and caches the client — so the agent sees at most one slightly-slow first call, never a frozen handshake. A ready-timeout returns a clear error, not a hang.
  5. On shutdown (clean exit, SIGINT, or SIGTERM) terminates walletd — SIGTERM, then SIGKILL after a grace period; the first-run init child is torn down too. No orphaned walletd processes.

Install

Recommended — uvx (zero global install)

uvx runs the server in an isolated, on-demand environment. No pip install, no virtual-env management, no PATH wrestling. The host (Claude Desktop / Claude Code / Cursor / …) spawns it on demand and uv handles the rest.

Install uv once:

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Then wire uvx exfer-mcp into your MCP host config — examples below. The first time the host launches the server, uv fetches the package from PyPI; subsequent runs are cached.

Fallback — pip install

For developer / CI use, or environments where adding uv is awkward:

pip install exfer-mcp        # installs the `exfer-mcp` console script

Requires Python ≥ 3.10. Pulls exfer-walletd ≥ 0.8.0 (the JSON-RPC client) and mcp ≥ 1.0 (the MCP server framework).

Configure (Claude Desktop)

Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or the equivalent path on your OS), then restart Claude Desktop:

{
  "mcpServers": {
    "exfer": {
      "command": "uvx",
      "args": ["exfer-mcp"],
      "env": {
        "WALLETD_URL": "http://127.0.0.1:7448",
        "WALLETD_AUTH_TOKEN": "<paste your walletd token here>"
      }
    }
  }
}

The token is whatever exfer-walletd was started with — by default it's written to ~/.exfer-walletd/token on first run (chmod 0600).

If walletd is running with --tls and a self-signed cert, pin its fingerprint:

{
  "mcpServers": {
    "exfer": {
      "command": "uvx",
      "args": ["exfer-mcp"],
      "env": {
        "WALLETD_URL": "https://127.0.0.1:7448",
        "WALLETD_AUTH_TOKEN": "<token>",
        "WALLETD_FINGERPRINT": "sha256:<paste from cert.fingerprint>"
      }
    }
  }
}

For a publicly-fronted walletd (e.g. behind a reverse proxy with a CA-signed cert), drop the WALLETD_FINGERPRINT field — the SDK falls back to the system CA chain.

Pin a specific version

"args": ["exfer-mcp@0.1.0"]

Configure (Claude Code)

One-shot via claude mcp add:

claude mcp add exfer \
  -e WALLETD_URL=http://127.0.0.1:7448 \
  -e WALLETD_AUTH_TOKEN=<token> \
  -- uvx exfer-mcp

Or by editing the project / global Claude Code MCP config directly:

{
  "mcpServers": {
    "exfer": {
      "command": "uvx",
      "args": ["exfer-mcp"],
      "env": {
        "WALLETD_URL": "http://127.0.0.1:7448",
        "WALLETD_AUTH_TOKEN": "<token>"
      }
    }
  }
}

Configure (other MCP hosts)

Cursor, Cline, Continue.dev, and most other MCP-aware hosts accept the same command / args / env shape. Use the uvx exfer-mcp invocation above.

Environment

WALLETD_URL is the mode switch. Set it → External mode (connect to your walletd). Leave it unset → Managed mode (exfer-mcp spawns its own walletd against the public mainnet defaults below).

External mode

Variable Required Default Meaning
WALLETD_URL yes (set → external) walletd base URL
WALLETD_AUTH_TOKEN yes walletd bearer token
WALLETD_FINGERPRINT only for https:// with a self-signed cert SHA-256 of walletd's TLS cert (sha256:<hex>)

Managed mode (WALLETD_URL unset)

Variable Required Default Meaning
WALLETD_KEYSTORE_PASSPHRASE yes Passphrase to unlock (and, on first run, create) the managed wallet keystore. Required in managed mode.
EXFER_WALLETD_BIN only if exfer-walletd isn't on PATH auto-detect exfer-walletd on PATH Full path to the walletd binary
EXFER_NODE_RPC http://64.176.231.198:9334,http://89.127.232.155:9334 Upstream Exfer node(s) — the project's public mainnet reference node + a backup, comma-separated. walletd round-robins and fails over across them.
EXFER_INDEXER_RPC http://64.176.231.198:9335 The project's public mainnet indexer, for observability queries that need data outside this wallet's own keys. Set to an empty string to disable indexer delegation.
WALLETD_DATADIR ~/.exfer-walletd-mcp Where the managed keystore + bearer tokens live. Persists across restarts; reused if present.
EXFER_WALLETD_BIND 127.0.0.1:7448 Preferred loopback host:port. If the port is busy, exfer-mcp picks a free loopback port instead so it never collides with a walletd you run yourself.

Common to both modes

Variable Required Default Meaning
EXFER_MCP_DEFAULT_FEE_RATE walletd default fee_rate (exfers/byte) for spends when the agent didn't specify
EXFER_MCP_HTTPX_TIMEOUT 30 per-RPC timeout in seconds

Recommended agent flow

When the user asks the agent to send a payment, the expected sequence is:

  1. exfer_simulate_transfer → compute exact fee
  2. Show the user the fee and ask for confirmation
  3. exfer_transfer → broadcast
  4. exfer_wait_for_tx → confirm

The simulate-first pattern means the agent always knows the cost before committing. The user is the one who decides whether the cost is acceptable.

Safety

  • WALLETD_AUTH_TOKEN is all-or-nothing access to the wallet. Treat it like a payment-card number.
  • exfer-mcp does no per-call confirmation by itself — that's the host's job. For spend caps, configure them on the walletd side with --spend-cap-per-tx / --spend-cap-per-period (or the WALLETD_SPEND_CAP_* env vars; the managed walletd inherits them from this process's environment). A spend that would breach a cap is refused before broadcast with AllowanceExceeded (-32038). Caps default to unlimited, so also consider running a walletd that only holds a small float you would be comfortable losing.
  • The MCP transport is stdio. The agent does not see the wire token; only this process does.
  • Errors from walletd surface as MCP isError=true content the agent reads and reacts to, including specific cases like InsufficientBalanceError (over-spend) and WaitTimeoutError (confirmation depth not reached in time).
  • Managed mode is also a hot wallet. The walletd exfer-mcp spawns binds loopback only and its bearer tokens never leave the local machine — exfer-mcp also redacts any 64-hex token from walletd's forwarded [walletd] log lines, so the first-run spend token never lands in the host's stderr log file. Anything that can reach the MCP server can still spend its funds. The 24-word recovery phrase is printed to stderr once, on first run (BACK UP THIS RECOVERY PHRASE) — it is the only backup for that wallet, so write it down then. Treat WALLETD_KEYSTORE_PASSPHRASE and the contents of WALLETD_DATADIR (default ~/.exfer-walletd-mcp) as wallet secrets, and keep only a float you'd be comfortable losing in the managed wallet.

Coming soon

  • .mcpb desktop bundle — Anthropic's one-click .mcpb install format for Claude Desktop, with the env vars surfaced as a form at install time.
  • PyPI release — a uvx exfer-mcp / pip install exfer-mcp one-liner, once the exfer-walletd client is published to PyPI.
  • MCP directory — submission to Anthropic's curated MCP directory + community directories.

Development

git clone https://github.com/exfer-stack/exfer-mcp
cd exfer-mcp
uv venv && source .venv/bin/activate
uv pip install -e '.[dev]'
pytest                          # unit tests
mypy && ruff check               # lint
python -m scripts.e2e_smoke      # end-to-end smoke (needs a live walletd)

See scripts/e2e_smoke.py for a runnable example that exercises the full Exfer stack — walletd, indexer, MCP — against a real deployment.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

exfer_mcp-0.2.0.tar.gz (50.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

exfer_mcp-0.2.0-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file exfer_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: exfer_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 50.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exfer_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 845ee16874f7ba533caabdcdcf81afc23823ce12e2b82610438f9624b1a824c6
MD5 9d6490b5c3e4a645e57c2edd096a189d
BLAKE2b-256 ada272cea8428ccfaa1bcddad57113eb3cb1655c993097d8f5e25f12ab0c3a08

See more details on using hashes here.

Provenance

The following attestation bundles were made for exfer_mcp-0.2.0.tar.gz:

Publisher: release.yml on exfer-stack/exfer-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exfer_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: exfer_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exfer_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e2867adb3eaac0b2c0df806cd5ca376694870c1fac4aa02a73b10fc8c6e7bbfe
MD5 3b5bc50fe8a776772f57c02ca46770ca
BLAKE2b-256 26811d396e1ec2a205971c653f4fb7d9524704b449bc205a301976c03cf93902

See more details on using hashes here.

Provenance

The following attestation bundles were made for exfer_mcp-0.2.0-py3-none-any.whl:

Publisher: release.yml on exfer-stack/exfer-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page