bolthub logobolthub
SDKs & Tools

MCP Server

@bolthub/mcp: one MCP config entry — the bolthub marketplace, specific L402 gateways, and your other MCP servers, on one shared Lightning budget.

Overview

@bolthub/mcp is the bolthub MCP server. One entry in your MCP client config; behind it, three kinds of tool source sharing one wallet and one Lightning budget:

  • The bolthub marketplace: search, inspect, and call every listed API through the marketplace meta-tools (search_apis, get_api_details, preview_cost, call_api, mint_scoped_token, revoke_token, plus the Node Launcher tools). New listings appear automatically, with no config changes.
  • Specific L402 gateways: a gateway's OpenAPI endpoints become directly-named tools.
  • Your other MCP servers: local or remote, proxied transparently. Free tools pass straight through; a tool that answers with an L402 payment challenge is paid inside your budget and retried.

It replaces @bolthub/mcp-registry and @bolthub/mcp-bridge (both deprecated; see migration).

Quick start (zero config)

Use directly with npx (no install required). Add to your MCP client config (Cursor, Claude Desktop, Claude Code, etc.):

{
  "mcpServers": {
    "bolthub": {
      "command": "npx",
      "args": ["-y", "@bolthub/mcp"],
      "env": {
        "LND_REST_HOST": "https://your-lnd-node:8080",
        "LND_MACAROON": "<hex-admin-or-pay-macaroon>"
      }
    }
  }
}

With no config file this runs in marketplace mode: every API on bolthub.ai is available to your agent through the meta-tools.

To expose one specific gateway's endpoints as named tools instead, pass --gateway:

npx @bolthub/mcp --gateway https://btc-intel.gw.bolthub.ai

No install-time dependencies to manage. Everything ships in one bundle, including NWC wallet support and the @bolthub/pay payment core.

The server is listed in the official MCP registry as ai.bolthub/mcp — the namespace is cryptographically verified against the bolthub.ai domain, so MCP clients that support registry discovery can find and install it by that name.

Client-specific setup

Cursor: Open Settings > Features > MCP Servers > Add new MCP server. Or paste the JSON config into .cursor/mcp.json in your project root.

Claude Desktop: Open Settings > Developer > Edit Config. Paste into the mcpServers section.

Claude Code: Run claude mcp add bolthub -- npx -y @bolthub/mcp and export your wallet env vars in your shell.

The config file

For anything beyond one source, use ~/.bolthub/mcp.json (picked up automatically when present, or pass --config <path>). The mcpServers block is the exact shape your MCP client already uses, so paste your existing entries in wholesale; remote entries take {url, headers}:

{
  "marketplace": true,
  "gateways": ["https://btc-intel.gw.bolthub.ai"],
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/notes"]
    },
    "remote-tools": {
      "url": "https://tools.example.com/mcp",
      "headers": { "Authorization": "Bearer …" }
    }
  },
  "budget":     { "sat": 10000 },   // lifetime ceiling for this run, ALL sources combined
  "maxPerCall": { "sat": 500 },     // per-call ceiling
  "namespace":  "prefix",           // "prefix" (default) or "flat"
  "telemetry":  false               // reserved; v1 sends nothing anywhere
}

Then point the client at it:

{
  "mcpServers": {
    "bolthub": {
      "command": "npx",
      "args": ["-y", "@bolthub/mcp", "--config", "~/.bolthub/mcp.json"],
      "env": { "PHOENIXD_URL": "…", "PHOENIXD_PASSWORD": "…" }
    }
  }
}

mcpServers keys may not contain __ (reserved as the namespace separator).

Flags

bolthub-mcp [flags]                      zero config = marketplace mode
bolthub-mcp --gateway <url>              a specific gateway's endpoints as tools
bolthub-mcp --config ~/.bolthub/mcp.json full config (marketplace + gateways + mcpServers)
FlagDescription
--config <path>Config file (default: ~/.bolthub/mcp.json when present)
--gateway <url>Add a gateway source (repeatable)
--marketplace / --no-marketplaceForce the marketplace source on / off
--budget <sats>Lifetime budget for this run (budget.sat)
--max-per-call <sats>Per-call ceiling (maxPerCall.sat)
--api-url <url>Override the directory API base URL
--helpUsage text

What the agent sees

  • Marketplace meta-tools, unprefixed: search_apis, get_api_details, preview_cost, call_api, buy_credit, mint_scoped_token, revoke_token, deploy_node, node_status, and (when a wallet is configured) wallet_status.
  • Account tools (0.5.x), unprefixed: connect_account, connect_status, create_workspace, connect_wallet, get_onboarding_state, list_api, analyze_listing, publish_listing, get_earnings, usage_summary. These act on YOUR bolthub account and need the account token (see below); buying never does.
  • Gateway endpoints, prefixed by gateway slug: btc-intel__get_v1_history_candles, ….
  • Downstream MCP tools, prefixed by their config key: filesystem__read_file, ….

Two servers can both expose a search tool; prefixing keeps them apart. namespace: "flat" passes bare names through instead and fails at startup on any collision (a tool is never silently shadowed).

Marketplace meta-tools

The marketplace source exposes seven tools to your AI agent: five for using marketplace APIs and two for deploying your own Lightning node via the bolthub Node Launcher.

search_apis

Search the marketplace for APIs by keyword or tag.

search_apis({ query: "weather" })
search_apis({ tag: "finance" })
search_apis()  // list all available APIs

get_api_details

Get full details for a specific API: endpoints, pricing, examples.

get_api_details({ slug: "btc-intel" })

preview_cost

Preview the cost of calling an API endpoint without making the request or paying. Use this to check pricing before committing.

preview_cost({ slug: "btc-intel", path: "/v1/history/candles" })

call_api

Call any API endpoint. Lightning payments are handled automatically.

call_api({ slug: "btc-intel", path: "/v1/history/candles", method: "GET" })
call_api({ slug: "my-api", path: "/analyze", method: "POST", body: { text: "hello" }, max_cost_sats: 50 })

The optional max_cost_sats parameter is enforced against the invoice amount: invoices above it are refused, not paid.

After each call, the response includes spending information: how many sats were spent and how much budget remains.

For streaming (SSE) endpoints, pass stream_events and/or stream_seconds to read a bounded window of live events (defaults: 20 events or 10 seconds, whichever first). One call buys one window. Zero events in a window is normal for event-driven feeds; the result says so explicitly.

call_api({ slug: "btc-intel", path: "/v1/derivatives/liquidations/stream", stream_seconds: 30 })

open_stream, read_stream, close_stream

Continuous monitoring of a streaming endpoint on one payment. open_stream pays for the connection and holds it in the background; read_stream returns events since your previous read for free, and wait_seconds (up to 25) turns it into "wake me when something happens"; close_stream ends it with a summary. Streams also close themselves when the gateway's limits or the paid window end, and after 10 minutes without a read.

open_stream({ slug: "btc-intel", path: "/v1/derivatives/liquidations/stream", query_params: { min_size_usd: "100000" } })
read_stream({ stream_id: "stream-1", wait_seconds: 20 })
close_stream({ stream_id: "stream-1" })

At most 3 streams can be open at once (override with the BOLTHUB_MAX_STREAMS env var).

buy_credit

Pay once, then call a provider many times. buy_credit buys a sats budget spendable across all of one provider's endpoints. After it, call_api to any of that provider's endpoints draws the credit with no further Lightning payment, until it runs out.

buy_credit({ slug: "btc-intel", path: "/v1/history/candles", credit_sats: 10000 })
buy_credit({ slug: "btc-intel", path: "/v1/history/candles", credit_sats: 10000, max_cost_sats: 12000 })

Use it when you know you'll call several of a provider's endpoints: sum their costs and buy that much credit in one payment. Credit is face-value (the provider charges exactly the sats you ask for, no discount tiers) and per-provider: it never covers a different provider, since a single Lightning payment settles to one provider's wallet and bolthub never holds a pooled balance. Calling across several providers is several buy_credit payments, one each. Unused credit at expiry is non-refundable, so size it to what you expect to spend. max_cost_sats caps the purchase price: a dearer price is refused and nothing is paid.

mint_scoped_token

Hand a sub-agent a budget, not your wallet. mint_scoped_token takes a prepaid-credit credential this server holds for the provider (run buy_credit first) and narrows it offline into a tighter child credential (fewer uses, a spend cap, a path subtree, an expiry), then returns the child as an L402 Authorization value the worker can use directly.

mint_scoped_token({ slug: "acme", path: "/v1/data", n_uses: 20, spend_cap_sats: 300, path_prefix: "/v1/data/reports" })

Minting is offline: no payment and no round-trip. Attenuation is tighten-only, so a child can never widen scope or exceed the parent's remaining uses or sats. spend_cap_sats is reserved from your budget the moment you mint, so the parent and every child together can never spend more than the parent's budget (a cap over your remaining budget is refused, minting nothing). The worker spends the child through a plain client or call_api with no special handling; the gateway enforces every cap.

revoke_token

Cut off a sub-agent. revoke_token revokes the grant behind a credential this session holds, which kills the whole delegation tree minted from it. Every child (and the parent credential) fails on its next request within about 15 seconds.

revoke_token({ slug: "acme", path: "/v1/data" })
revoke_token({ slug: "acme", path: "/v1/data", released_sats: 300 })  // also return reserved child budget

Revocation is tree-level: there is no per-child revocation, since children are minted offline and share one grant. Pass released_sats to return budget you had reserved for children back to your budget.

deploy_node

Deploy a non-custodial Lightning node on your own VPS via the bolthub Node Launcher. A guided flow: call it repeatedly as the conversation progresses.

deploy_node({})                                   → provider menu with prices
deploy_node({ provider: "vultr" })                → sign-up + access-token steps
deploy_node({ region: "ewr" })                    → server sizes with monthly prices
deploy_node({ region: "ewr", size: "recommended" }) → deploys

Supported providers: hetzner, digitalocean, lunanode, vultr, scaleway. The VPS access token is entered once at the dashboard deploy page, never in chat; deploys then run from the stored credential. Returns a node_id for node_status. The user completes wallet setup (writing down their seed phrase) on their own node page; the seed never touches bolthub. Once ready, connect_wallet with the node_id binds the node as a workspace's payout wallet.

node_status

Check the status of a node deployed via deploy_node. Returns the current state (provisioninginstallingwallet_pendingsyncingready), IP address, sync progress, and setup instructions when applicable.

node_status({ node_id: "<node-id-from-deploy_node>" })

To drive the flow unattended, pass wait_for and the call blocks until a milestone is reached: "wallet_pending" (VPS up, the user's seed ceremony is next), "ready" (wallet created, macaroon minted), or "payable" (an active channel with inbound capacity — what a real payment needs). A node already past the milestone returns immediately. timeout_s is the total wait budget (5–600 seconds, default 120); on timeout the tool errors with what to do next. A single call blocks for at most ~150 seconds, because desktop MCP clients abort calls held open past roughly 4 minutes: with a larger budget the tool returns a WAIT PAUSED result carrying the remaining seconds, and the agent re-calls with that budget to continue. The wait also stops early, loudly, when polling can't help: a terminal error state, or sitting in wallet_pending when only the user's browser step can advance it.

node_status({ node_id: "<node-id>", wait_for: "payable", timeout_s: 300 })

wallet_status

Inspect the spending wallet this session pays from: the payer-side counterpart to node_status, which covers receiving nodes. Reports balance, permissions (can this connection pay invoices at all?), identity, and connectivity, plus the session budget. Read-only, never moves funds, present whenever a wallet is configured. It is the first thing to run when a payment fails: an empty wallet, a receive-only NWC connection, and an unreachable wallet service each render differently here.

wallet_status()

Account tools (0.5.x): run your listing from the chat

Everything below acts on your bolthub account, so it needs the account token; discovering, calling, and paying for APIs never does. The design rule throughout: the agent does the assembly, the human does the deciding, and no secret ever enters the chat — wallet strings, macaroons, and VPS keys are entered in the browser only.

Connecting your account

Say "connect my bolthub account". connect_account returns an approval link plus a short confirmation code; check the code on the page matches the one in chat, click Approve, then connect_status finishes the pairing and stores a revocable 90-day token locally (never shown in chat). Alternative: mint a token at dashboard → Settings → MCP setup and set BOLTHUB_ACCOUNT_TOKEN in the connector env. Tokens can never read gateway secrets, change wallets, or touch VPS keys, and are revocable from the same dashboard page.

create_workspace

Creates a seller workspace (name, optional slug; taken slugs get a numbered variant). Free while empty: the 30-day trial only starts at first publish.

connect_wallet

Checks or sets up the payout wallet. A deployed bolthub node binds directly (node_id, server-side credential copy); other wallets connect in the browser, with in-chat guidance for self-hosted LND (invoice-only macaroon) and always-on NWC services. Reports connected yes/no and reachability only.

list_api

Turns an OpenAPI/Swagger/Postman spec (URL or inline, or a plain JSON list of endpoints) into a draft listing: endpoints parsed, per-request pricing proposed with a rationale, everything unlisted until you publish. Re-imports show a sync diff instead of duplicating.

analyze_listing

Audits a listing like a paying agent would: origin protection (live probe), honest status codes, docs and examples, uptime and latency, pricing fit. Returns a prioritized punch list with evidence and a fix per finding.

publish_listing

The only way anything goes live. Without confirm: true it is a dry run that shows exactly what would be published; with it, the listing goes live and the tool reports what changed.

get_earnings / usage_summary

Revenue (all-time and windowed, top endpoints) and operations (billing cycle, paid traffic, per-endpoint latency and errors), read from the same receipts a buyer could verify.

get_onboarding_state

One-look checklist: wallet connected and reachable, drafts vs published, origin-protection verdict, listing live, trial state, plus the single next step. Useful any time you ask "where was I?".

Wallets

The server needs a Lightning wallet to pay for tool calls. You only need one wallet type, set in the server's env.

No wallet is not an error: free tools and marketplace search keep working; paid calls return their payment challenge with a setup hint.

VariableDescription
LND_REST_HOSTLND REST API URL (bolthub Node Launcher or your own node). Fastest payment path (<200ms). Use a pay-scoped macaroon in production (see Agent wallet security).
LND_MACAROONHex-encoded macaroon for LND. Required with LND_REST_HOST.
PHOENIXD_URLPhoenixd HTTP API URL. Fast (<200ms), self-custodial, automatic channel management.
PHOENIXD_PASSWORDHTTP password for Phoenixd. Required with PHOENIXD_URL.
LNBITS_URLLNbits instance URL. Fast (<300ms). Accounts system built on any Lightning funding source. Use if you already run LNbits.
LNBITS_ADMIN_KEYAdmin API key for LNbits. Required with LNBITS_URL.
NWC_URIEasiest setup. Free start: CoinOS — no-KYC signup, copy the connection string, done. Set a per-connection budget and keep only a few thousand sats (custodial); its single relay refuses connections intermittently, so retry if a payment fails to connect. Reliability upgrade: Alby Hub (v1.21.5+, hosted or free self-hosted) puts two relays in every connection string and our client fails over automatically. Expect 1–3s per payment over NWC. Bundled: no extra packages needed.
BUDGET_SATSOptional. Seeds budget.sat (parity with the old bins); --budget and the config file override it. The startup log names which source won (--budget, config file, or BUDGET_SATS). A malformed value (anything but a plain non-negative integer) aborts startup rather than falling back to unlimited; 0 means free tools only.

Priority order: if multiple wallet types are configured, the first available wins: LND > LNbits > Phoenixd > NWC.

Which wallet should I use?

Pick by how your wallet will be used, not by brand:

  • Interactive use (you're present): Zeus, free, non-custodial, well-known. Great for trying APIs from the playground or manual CLI calls. Not for unattended agents: its wallet service runs on your phone, so payments only succeed while the app is running.
  • Unattended agents, free start: NWC with CoinOS, the only zero-cost always-on option; about 2 minutes to a working wallet. Set a per-connection budget and keep only a few thousand sats there (it's custodial). Its relay refuses connections intermittently; retry the call if it fails to connect (the client retries automatically).
  • Unattended agents, reliable: Alby Hub (v1.21.5+), non-custodial, two relays in every connection string with automatic failover in our client. Hosted plan or free self-hosted.
  • Production / power users: LND via the bolthub Node Launcher or your own node with a pay-scoped macaroon. Payments in under 200ms. Pairs naturally with Lightning Labs' agent stack (lnget, lightning-agent-tools), which speaks the same standard L402 as every bolthub API. Phoenixd is the equally fast self-custodial alternative if you already run it.
  • Whatever the wallet: always set a budget on connections an agent can spend from.

Payment receipts

Opt in with "receipts": true (or a path) in the config file, --receipts <path|default>, or the RECEIPTS_PATH env var. The server then records one proof-of-payment receipt per paid call to a JSONL ledger (default ~/.bolthub/receipts.jsonl) and exposes an export_receipts tool so the agent can produce its own expense report (JSON or CSV, optional redact to strip preimages). Off unless configured: nothing is ever written. Recording happens at payment time, so payments made before receipts were enabled are never backfilled. Export output names the ledger path, and if any receipt write failed during the session the export says so loudly instead of just looking empty. Verify a ledger offline with bolthub receipts verify from the CLI.

Budget: one pool, hard guarantee

budget.sat caps what the server can spend over its lifetime — across gateway calls, call_api, and paid downstream MCP tools combined. Reservations are synchronous, so concurrent calls on different sources can't jointly overspend. The agent can never lift the ceiling; refusals come back as clean "Payment refused" results instead of payments.

npx @bolthub/mcp --gateway https://btc-intel.gw.bolthub.ai --budget 1000
  • budget.sat: 0 is valid and means "free tools only".
  • Unset means no limit (the server warns at startup; pays any invoice as long as the wallet has funds).
  • maxPerCall.sat (or --max-per-call) caps each individual payment; max_cost_sats on call_api tightens it further for one call.
  • The budget lasts as long as the server process runs, typically the lifetime of your Cursor or Claude Desktop window.
BudgetUse case
100–500Testing and light use
1000–5000Typical daily development
10000+Heavy or production use
UnsetNo limit (pays any invoice as long as the wallet has funds)

Every payment logs one line to stderr (your local audit trail). The telemetry flag is reserved: v1 sends nothing anywhere, on or off; if a future version adds an opt-in ingest it will carry { scheme, asset, amount } only — no tool arguments, no resource identity.

Migrating from mcp-registry / mcp-bridge

BeforeAfter
npx @bolthub/mcp-registrynpx @bolthub/mcp (zero config = same behavior)
npx @bolthub/mcp-registry --api-url <url>npx @bolthub/mcp --api-url <url>
npx @bolthub/mcp-bridge --gateway <url>npx @bolthub/mcp --gateway <url>
--budget / BUDGET_SATSUnchanged (now a single pool across all sources)
Gateway tool btc-intel_get_v1_xbtc-intel__get_v1_x (double-underscore namespace)

The wallet env vars are unchanged. The old packages are deprecated on npm but not unpublished; existing configs keep working until you switch.

Remove the old entries from your client config rather than stacking them next to this one — a nested bolthub bin inside mcpServers would pay from its own wallet env, invisible to the shared budget (the server warns at startup if it spots this).

Notes & limits (v1)

  • Proxies MCP tools only — no resources, prompts, or sampling passthrough yet.
  • The downstream tool list is snapshotted at startup; hot add/remove needs a restart.
  • A downstream that fails to start is skipped with a stderr warning; the rest keep serving. If every source fails, the server exits.
  • Everything logs to stderr, never stdout (stdout is the MCP channel).

Alternatives

This server is one way to give agents L402 payment capabilities. There are also third-party MCPs that handle Lightning payments:

  • Alby MCP: Uses NWC under the hood. Works with any Alby Hub or CoinOS wallet.
  • Fewsats MCP: Zero-config custodial option. Single API key, no Lightning node needed.

For building custom agents without MCP, use @bolthub/pay (TypeScript) or bolthub (Python) directly.

Registry listing

@bolthub/mcp is published in the official MCP registry as ai.bolthub/mcp. The ai.bolthub/* namespace is verified via a DNS proof on the bolthub.ai apex, and each listing version maps to the matching @bolthub/mcp release on npm (which itself carries the mcpName field the registry validates against).