Skip to main content
Glama
zeam-labs

@zeam-labs/x402-mcp-bridge

Official
by zeam-labs

x402-mcp-bridge

ZEAM Prism MCP MCP connector – tool definition quality and endpoint health on Glama

@zeam-labs/x402-mcp-bridge on npm. Source: https://github.com/zeam-labs/x402-mcp-bridge.

Published from this source at the tagged version. npm's integrity hash is of that build — verify it before you run anything against your key.

Put a wallet in front of a paid MCP server.

One command, no MCP client

A headless agent does not run a desktop MCP client. It runs a shell. So:

With your key already exported into the environment as X402_PRIVATE_KEY:

npx -y @zeam-labs/x402-mcp-bridge \
  --call call_rpc '{"chain":"base","method":"eth_blockNumber","params":[]}'

npx -y @zeam-labs/x402-mcp-bridge --tools

That pays for the call and prints the answer. Nothing else to write. With no arguments this is still an MCP stdio server, which is what an MCP client wants.

Related MCP server: x402 MCP Proxy

The line, and when this client drops it

While a line's meter is on, the server bills wall-clock time whether or not you call anything — that is what a line is, and there is no server-side idle timeout. This client does not touch the meter's switch; it drops its line after four tick intervals (1000ms) of no use, so a pause costs you a reopen rather than open-ended idle billing. Expect a line to reopen during a slow session. X402_LINE=off pays per call where the seller takes it. Prism serves a funded channel on a line, so there the bridge rides one for the call and lets it go.

Why you need it

A metered MCP endpoint takes payment inside the tool call's params._meta — a signed x402 payload the client builds per call against accumulating channel state. No stock MCP client does that, so adding a paid endpoint to your config gets you tools/list, the free tools, and 402 on everything else, with no API key to paste because there is no API key.

This is that client, wearing a stdio MCP server on the front. Your existing client talks to this; this talks money upstream.

{
  "mcpServers": {
    "prism": {
      "command": "npx",
      "args": ["-y", "@zeam-labs/x402-mcp-bridge"],
      "env": { "X402_PRIVATE_KEY": "0x..." }
    }
  }
}

Verify what you are about to run before you point a funded key at it; see below.

The key stays on your machine. It signs vouchers locally; it is never sent anywhere. The bridge holds no funds — your deposit sits in the upstream's settlement contract, withdrawable by your side of the channel alone: the payer, or the payerAuthorizer you named when the channel was opened, which for most clients is the same key but need not be.

That escrow is not the seller's. It is x402's own batch-settlement contract, hardcoded in @x402/evm, published by Coinbase. No owner, no pause, no upgrade, no sweep.

npm pack @x402/evm@2.12.0 && grep -rl 0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003 package/

Permit2 assets need one approval first

USDT, DAI and WETH settle through Permit2, so the wallet must approve the Permit2 contract once before its first payment:

approve(0x000000000022D473030F116dDEE9F6B43aC78BA3, amount)   // on the token

A bounded amount is enough. Without it the payment is refused with invalid_batch_settlement_evm_permit2_allowance_required. USDC and EURC also accept EIP-3009, which needs no approval at all.

Paying without asking first

The stock x402 flow sends every call unpaid, reads the 402 it comes back with, and then sends the same call again carrying payment. Two network round trips for one call. At a 130ms round trip that is 260ms instead of 130ms. Those probes are also unpaid calls, and a server may cap how many of those it will answer, which is how a funded wallet gets locked out of a channel it has money in.

The terms are static and published, so this bridge reads them once from /.well-known/x402 at connect and attaches payment to its first request — no unpaid probe, no extra round trip.

A refused payment re-reads the terms and retries once, because quotes for non-stable assets move with the oracle. Anything still failing falls back to the old probe-then-pay path rather than dropping your call.

Point a chain client at it

An agent that already has a viem client does not want an MCP tool called call_rpc. It wants its provider URL to be a wallet instead of an API key:

import { createPublicClient } from 'viem'
import { base } from 'viem/chains'
import { prism } from '@zeam-labs/x402-mcp-bridge/viem'

const client = createPublicClient({ chain: base, transport: prism({ key: process.env.X402_PRIVATE_KEY }) })
await client.getBlockNumber()      // paid from the wallet, served by an archive node

Nothing else in the agent changes. The transport speaks JSON-RPC to Prism's /rpc/base (or /rpc/eth with chain: 'eth') and x402 back. The first request funds a channel and buys one block. After that it holds a line: the meter is on while calls are flowing, off within a second of them stopping, and the line is let go after ten idle seconds. Vouchers cost no gas; only the deposit does.

Options, all optional: url (the Prism host), chain (base | eth), network, stateDir, depositMultiplier, asset, salt, rpcUrl (a node of your own for the payment client's chain reads), aheadMs (bought time to keep on the meter while calling, default 2000), idleMs (meter off after this long with no call, default 1000), dropAfterMs (let the line go, default 10000), log. Anything else is passed to viem's http(). The same X402_* environment variables the bridge reads are the defaults, and the channel state directory is shared, so a channel the bridge funded is the one the transport uses.

The transport carries three extra methods: state() reports the address, channel, whether a line is held, whether the meter is on and the milliseconds left; close() switches the meter off and drops the line; refund() returns the unspent collateral and the time bought and not burned to the wallet. Call close() or refund() before your process exits, or the open socket keeps it alive.

test/viem.mjs drives it against a live server with real money and checks each of those claims.

The same wallet behind the other two libraries agents are written against:

import { PrismProvider } from '@zeam-labs/x402-mcp-bridge/ethers'   // ethers v6
const provider = new PrismProvider({ key: process.env.X402_PRIVATE_KEY })
from zeam_prism import PrismProvider                                  # web3.py, see python/README.md
w3 = Web3(PrismProvider(key=os.environ["X402_PRIVATE_KEY"]))

Same options, same state(), close() and refund(), same channel state directory, so the three share one channel per key. ethers batches requests and the door answers a batch item by item. The Python package lives in python/, is built on the official x402 Python SDK, and installs from this repository at a commit, which cannot be altered: pip install 'git+https://github.com/zeam-labs/x402-mcp-bridge@<commit>#subdirectory=python'. It is not on PyPI. test/ethers.mjs and python/test/live.py prove each against a live server.

Holding a line

A server may sell time rather than calls, with a cheaper path than paying per call. The pricing is the server's — read it in its published terms — and this bridge drives it for you:

  1. Deposit once — your first paid call does it for you.

  2. Open a line on the endpoint's /pay websocket. If the server challenges, the bridge signs the challenge with your key to prove the channel is yours, and gets back a credential.

  3. Call the buy_time tool on a steady cadence, passing {line: "<credential>"}. That is an ordinary paid call and it pays the server for more time.

  4. Every other call carries only {line: "<credential>"} and no payment, and as many can be in flight at once as you like.

Stop ticking and the line lapses. What the server charges, when a line lapses, and whether unused time is kept are the server's to state, not this bridge's.

This bridge drives a line for you. X402_LINE controls it:

value

auto (default)

pay per call until calls come fast, then hold a line while they keep coming and let it lapse when they stop. Where the seller serves a funded channel only on a line (Prism does), a line_required answer makes the bridge ride a line for that call, so the first call of a session is served either way.

on

hold a line from startup and keep paying whether or not anyone calls.

off

per-call payment only. Works against any x402 endpoint.

If the server refuses a call because the line is gone — an ordinary rotate or idle close — the bridge reopens the line and retries; a line_required answer is answered by riding a line for the call, and only pays per call if that fails too. That ordering matters: falling straight through to per-call payment turns one closed line into a signed payment per in-flight call, serialized behind one channel, and when those run out of road they become unpaid requests that burn the hourly ceiling and lock a funded wallet out of its own channel.

Cold starts. A voucher signs a cumulative total, and that total is not on the chain — the escrow knows your balance and what has been claimed, not what has been metered. The only place it exists is the seller's 402. So on a cold start, or whenever a payment is refused as stale, the bridge spends one probing round trip to resync rather than handing you a failed call. Keep X402_STATE_DIR and it happens once; lose it, or run the same key on a second machine, and it happens again on the next call and then not after.

Two things worth knowing if you write your own client. Pay a tick against tickAccepts from /.well-known/x402, not accepts. And never send two ticks at once: a voucher signs a cumulative total, so a channel carries one payment at a time and an overlapping tick is refused as channel_busy.

Configuration

variable

default

X402_PRIVATE_KEY

Funds the channel and signs vouchers. Without it the bridge still serves the catalog and the free tools; a paid call returns the seller's quote.

X402_MCP_URL

https://mcp.zeamprism.com/mcp

any x402-paid MCP endpoint

X402_NETWORK

eip155:8453

CAIP-2

X402_LINE

auto

auto, on or off — see Holding a line above

X402_ASSET

first quoted

address or symbol, if you hold a specific token

X402_RPC_URL

the chain's own public RPC

chain reads. Point it at your own node — checking a seller's claims through the seller proves nothing.

X402_STATE_DIR

~/.x402-mcp-bridge/<host>/<address>

channel state

X402_SALT

scheme default

open a distinct channel. Any string; it is hashed to bytes32

X402_MAX_SPEND

10000000 (=$10)

ceiling on what this run may spend, in micro-USD. 0 removes it — see below

X402_DEPOSIT_MULTIPLIER

40

how much refundable collateral to lock, as a multiple of the seller's quote, on the first deposit and on every top-up. Every deposit is charged the seller's open fee (the gas of that deposit), so the multiplier sets the gas share of your bill: at Prism's quotes today, 5x buys ~7 s of metered time per ~1.5 k micro-USD of gas (~22% on top of the rate), 40x ~1.2 min per deposit (~2.5%). The x402 scheme's minimum is 3. A top-up is a deposit on the same funding quote, made when the collateral behind a tick is below one block.

It stops spending when you stop watching

This process holds your key and pays without asking, so two limits bound it.

It dies with its parent. npx is a wrapper, so a client killing its child kills npx and not this. A stdio server's parent going away closes stdin, and that is what this watches. X402_LINE=auto also lapses an unused line after four tick intervals; X402_LINE=on holds one regardless, so use it deliberately.

It will not spend past X402_MAX_SPEND (default 10,000,000 µUSD ≈ $10, about three hours of held line), counted from where the meter stood at startup. On reaching it the line drops and further calls return x402_bridge_spend_cap_reached with the numbers. X402_MAX_SPEND=0 removes it.

Channel state matters

Keep X402_STATE_DIR on disk. A client that reconnects to an existing channel with empty state pays a deposit it did not need, and can only recover if its signer can read the chain — so this bridge always gives the signer a reader. By default that reader is the upstream's own free /verify surface, which means recovery costs nothing and needs no RPC of your own.

The first call also makes the on-chain deposit, so it is slower than the rest; every call after it is fast. Losing state adds one more deposit-time call, then it is fast again.

What it does not do

It does not custody funds, meter you, or add a fee. It forwards tools/list and tools/call unchanged and attaches payment. If the upstream is free, you do not need this.

Why the versions are pinned exactly

Read this file — it is short on purpose — and you still cannot see what the dependencies do, and the signing happens inside them. Floating them on "*" means npx -y today and npx -y next month execute different code against your key. They are pinned to exact versions. Verify what you are about to run:

npm view @zeam-labs/x402-mcp-bridge version dist.integrity
npm pack @zeam-labs/x402-mcp-bridge
less package/index.mjs

npm's integrity hash proves the bytes you fetched are the bytes that were published — not that we are honest. The file is short on purpose: read it.

MIT.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    F
    maintenance
    Enables micropayments for MCP tool invocations using the X402 payment protocol, allowing servers to charge per tool usage in USDC and clients to automatically handle payments.
    13 npm
    4
    -
  • A
    license
    A
    quality
    F
    maintenance
    Enables MCP clients to access all endpoints of an x402 gateway by paying real-time microtransactions (USDC on Base) per API call, with automatic tool discovery and spend guardrails.
    23
    64 npm
    MIT