Skip to main content
Glama
andy-builds-ai

Bitcoin Node MCP Server

README.md
# Bitcoin Node MCP Server

Read-only MCP server that monitors a Bitcoin Core full node (Umbrel on a
Raspberry Pi 5) over its JSON-RPC interface. Part of a small series of
personal MCP servers — see [nerdaxe-mcp](https://github.com/andy-builds-ai/nerdaxe-mcp).

Built with the official MCP Python SDK (`mcp`), served over stdio.

## Tools

Four read-only views on a single whitelisted RPC helper:

- `get_node_status` — chain, block height, headers, sync progress,
  initial block download, human-readable uptime
- `get_network_info` — Core version, connection counts (total/in/out),
  active networks
- `get_mempool_info` — transaction count, memory usage, minimum fee
- `get_peer_info` — peer counts in/out and a latency range; no full
  peer dump

## Security model

The interesting part of this server is what it refuses to do:

- **RPC whitelist.** Exactly five read-only methods
  (`getblockchaininfo`, `getnetworkinfo`, `getmempoolinfo`,
  `getpeerinfo`, `uptime`), hardcoded as a constant. Any other method
  is rejected before a request leaves the machine — no wallet RPCs, no
  parameters.
- **Field allowlist per tool.** Every tool returns explicitly chosen
  fields, never "everything except X". Fields added by a future Core
  version cannot leak through; `localaddresses` (the node's own public
  address) is excluded by design.
- **No addresses in output.** Logs and error messages never contain the
  node URL or IP. The RPC URL is validated at startup; on failure the
  server aborts without echoing it.
- **Errors as messages, not tracebacks.** A `safe_output` boundary
  catches failures at the tool edge and returns a plain explanation;
  details go to the server-side log only.

## Live proof

`tests/test_protocol.py` run against the real node — all four tools with
live values, plus the whitelist rejecting an unregistered method:

```
> .venv\Scripts\python tests\test_protocol.py

  ok  get_node_status: Report the node's chain sync status.
  ok  get_network_info: Report the node's network and connection status.
  ok  get_mempool_info: Report the node's mempool status.
  ok  get_peer_info: Report a short summary of the node's peer connections.
PASS listing: exactly the four read-only tools, no parameters

  ok  get_mempool_info: Transactions: 37040
  ok  get_network_info: Version: 310000
  ok  get_node_status: Chain: main
  ok  get_peer_info: Peers: 70 (in: 60, out: 10)
PASS calls: all four tools returned well-formed text

  ok  breakout: unknown tool 'stop' rejected -> Unknown tool: stop
PASS breakout: only the four registered tools are reachable

ALL PROTOCOL CHECKS PASSED
```

## Setup

Windows:

    py -m venv .venv
    .venv\Scripts\pip install -r requirements.txt

Linux / macOS:

    python3 -m venv .venv
    .venv/bin/pip install -r requirements.txt

Then copy `.env.example` to `.env` and fill in `NODE_RPC_URL`,
`NODE_RPC_USER`, `NODE_RPC_PASSWORD` (Umbrel: Bitcoin app → "Connect").

## Usage

Run directly for local testing:

    .venv\Scripts\python main.py

Or wire it into `claude_desktop_config.json` as `bitcoin-node`, using
the absolute path to the venv's Python and to `main.py`.

## Testing

Verified on three levels against a live node:

1. Direct function calls, including forced error paths (node offline,
   wrong credentials, non-whitelisted method).
2. `tests/test_protocol.py` — a programmatic MCP client over stdio with
   asserts; the regression run after every change:
   `.venv\Scripts\python tests\test_protocol.py`
3. Live smoke test from Claude Desktop: all four tools, plus a
   whitelist-escape attempt that was correctly rejected.

## Limits

- Read-only. No wallet operations, no configuration changes, no
  transaction broadcasting.
- Single node, no multi-node support.
- No caching — every call hits the node live.