Configuration
Introduction
Section titled “Introduction”The MCP server is configured entirely through environment variables — never CLI
arguments. A key in argv leaks in process listings and shell history, so PipRail won’t take
one there. Put every var in your MCP client’s env block (see Client
setup).
Canonical names are prefixed PIPRAIL_. The parser is strict: any unrecognized PIPRAIL_*
var is rejected as a typo at startup rather than silently ignored, so a misspelled name fails
loudly with the list of valid vars.
The one requirement
Section titled “The one requirement”Only the wallet secret is mandatory. Everything else has a safe default — base, USDC, 0.10
per payment, 10.00 lifetime per token.
"env": { "PIPRAIL_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY" // chain defaults to base, token to USDC}The value is read once, mapped to the chain’s wallet shape, and never logged. The startup
banner names which env var supplied it (PIPRAIL_PRIVATE_KEY / PIPRAIL_WALLET_KEY /
AGENT_KEY) but never the value.
Every variable
Section titled “Every variable”Each variable also accepts the shorter alias shown under its name.
| Variable | Required | Default | Meaning |
|---|---|---|---|
PIPRAIL_PRIVATE_KEYaka PIPRAIL_WALLET_KEY · AGENT_KEY | only to pay | — | Wallet key/seed/mnemonic in the chain’s native format (see below). Omit it to run read-only (discover/quote/register/budget/guide work; pay/plan need it). |
PIPRAIL_CHAINaka CHAIN | no | base | EVM preset name or non-EVM family — one wallet on one chain. (Single-chain mode.) |
PIPRAIL_CHAINS | no | — | Multi-chain mode — comma-separated chains (e.g. base,polygon,solana). Each takes its own PIPRAIL_<CHAIN>_KEY (+ optional PIPRAIL_<CHAIN>_RPC_URL); the server pays whichever chain a 402 asks for. Mutually exclusive with PIPRAIL_CHAIN/PIPRAIL_PRIVATE_KEY/PIPRAIL_RPC_URL. See below. |
PIPRAIL_MAX_AMOUNTaka MAX_AMOUNT | no | 0.10 | Ceiling per payment, human units. |
PIPRAIL_MAX_TOTALaka MAX_TOTAL | no | 10.00 | Lifetime ceiling per distinct token, human units. |
PIPRAIL_MAX_TOTAL_DENOM | no | (none) | Cross-token grand total per denomination, e.g. USD:20.00,EUR:5.00 — sums every token of that unit, across every chain. See Total budget. |
PIPRAIL_MAX_PAYMENTS | no | (none) | Lifetime cap on the number of settled payments, across every chain + token. |
PIPRAIL_MAX_PAYMENTS_PER_WINDOW | no | (none) | Rolling-window cap on the count of payments. Needs PIPRAIL_WINDOW_SECONDS. |
PIPRAIL_TOKENSaka TOKENS | no | USDC (USDT on any chain without native USDC — Tron, TON, Kaia) | Comma-separated allowed token symbols, plus native for the chain’s coin. |
PIPRAIL_SCHEMES | no | onchain-proof | Comma-separated payment schemes (see below). |
PIPRAIL_HOSTSaka HOSTS | no | (any) | Comma-separated host allowlist — exact (api.example.com) or wildcard (*.example.com). |
PIPRAIL_RPC_URLaka RPC_URL | no | chain default | Override the RPC endpoint; fold any API key into the URL. |
PIPRAIL_ALLOW_UNKNOWN_TOKENS | no | false | Pay tokens the SDK can’t price? Keep false. |
PIPRAIL_TTL | no | (none) | Session deadline in seconds — terminal once past. |
PIPRAIL_WINDOW_TOTAL | no | (none) | Rolling-window budget, human units. Needs PIPRAIL_WINDOW_SECONDS. |
PIPRAIL_WINDOW_SECONDS | no | (none) | Rolling-window width in seconds — pairs with PIPRAIL_WINDOW_TOTAL and/or PIPRAIL_MAX_PAYMENTS_PER_WINDOW. |
PIPRAIL_WARN_AT_FRACTION | no | (none) | Emit a budget-threshold event the first time spend crosses this fraction (0,1] of any cap. |
PIPRAIL_SPEND_LOG | no | (none) | Path to a JSONL spend store — the budget survives a restart. |
PIPRAIL_EVENT_LOG | no | (none) | stderr or a file path — one-line-JSON sink for payment + budget events. |
PIPRAIL_CONFIRM | no | false | Mode B — ask the human to approve each payment via elicitation. |
PIPRAIL_CONFIRM_TIMEOUT_MS | no | 55000 | Approval window in ms; keep below your client’s request timeout (≈60000). |
PIPRAIL_GUIDE | no | true | Expose the agent-guide prompt + the guide/budget resources. |
PIPRAIL_NEAR_ACCOUNT_IDaka NEAR_ACCOUNT_ID | only on NEAR | — | Your NEAR account id (e.g. you.near). |
Boolean knobs (PIPRAIL_ALLOW_UNKNOWN_TOKENS, PIPRAIL_CONFIRM, PIPRAIL_GUIDE) accept
1, true, or yes (case-insensitive) as true; anything else is false.
Pay on several chains from one server
Section titled “Pay on several chains from one server”Set PIPRAIL_CHAINS to a comma-separated list and give each chain its own key — the server
then pays whichever chain a 402 asks for, from one process, under one shared budget:
"env": { "PIPRAIL_CHAINS": "base,polygon,solana", "PIPRAIL_BASE_KEY": "0xYOUR_EVM_KEY", // one EVM key works on every EVM chain "PIPRAIL_POLYGON_KEY": "0xYOUR_EVM_KEY", "PIPRAIL_SOLANA_KEY": "YOUR_BASE58_SECRET", // needs the Solana peer libs (see Chains) "PIPRAIL_MAX_AMOUNT": "1.00"}- Per-chain key:
PIPRAIL_<CHAIN>_KEYin that chain’s native format (uppercase the chain; a hyphen becomes_). A chain with no key is read-only — it can plan/quote, not pay. - Per-chain RPC:
PIPRAIL_<CHAIN>_RPC_URL(optional, per chain). - NEAR still uses the single
PIPRAIL_NEAR_ACCOUNT_ID. - Shared budget:
PIPRAIL_MAX_AMOUNT/PIPRAIL_MAX_TOTAL/PIPRAIL_TOKENS(defaults to the union of each chain’s stablecoin) apply to every chain; each chain keeps its own per-token ledger (no cross-token sum). - One mode at a time: mixing
PIPRAIL_CHAINSwith the single-chainPIPRAIL_CHAIN/PIPRAIL_PRIVATE_KEY/PIPRAIL_RPC_URLis rejected at startup.
The tools, budget, and guide are identical to single-chain mode — piprail_pay_request just
gains the ability to route across your funded chains. It pays the first chain you list that
can settle the 402 (your preference order — there’s no oracle to compare gas across different
coins); within a chain it picks the cheapest-gas rail. List your preferred chain first.
The budget — the spend policy
Section titled “The budget — the spend policy”PIPRAIL_MAX_AMOUNT, PIPRAIL_MAX_TOTAL, PIPRAIL_TOKENS, and PIPRAIL_HOSTS become the
SDK’s spend policy — enforced before any on-chain send.
The model cannot exceed it even if it tries.
"env": { "PIPRAIL_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY", "PIPRAIL_MAX_AMOUNT": "0.25", // per payment, in the TOKEN's units "PIPRAIL_MAX_TOTAL": "5.00", // lifetime, per (chain, token) "PIPRAIL_TOKENS": "USDC,native", // USDC or the chain's coin "PIPRAIL_HOSTS": "*.example.com" // only this domain}The caps are in the paid token’s units — so
0.25is ~25¢ for USDC, but withnativeallowed it’s0.25of the chain’s coin (e.g. 0.25 ETH ≈ $1000s), since there’s no price oracle. KeepPIPRAIL_TOKENSto ≈$1 stablecoins if you want the cap to read as dollars.
PIPRAIL_TOKENS takes token symbols (USDC, USDT, EURC, …) plus the chain-agnostic
alias native — the chain’s own coin (ETH on Base, TRX on Tron, XLM on Stellar) without
naming the ticker. The default is data-driven — it tracks what actually exists on the chain: USDC
where it exists, else USDT on any chain without native USDC (Tron, TON, and the Kaia EVM preset),
so a USDC-only policy would never silently block every payment. See Chains for the full per-chain token story.
The grand total and the count caps
Section titled “The grand total and the count caps”PIPRAIL_MAX_TOTAL is per distinct token — across several stablecoins (or several chains) that
silently multiplies. For one number across everything, set PIPRAIL_MAX_TOTAL_DENOM: a
comma-separated list of UNIT:AMOUNT pairs that sums the human value of every token of that
unit. See Total budget.
"env": { "PIPRAIL_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY", "PIPRAIL_MAX_TOTAL_DENOM": "USD:20.00,EUR:5.00", // $20 across ALL USD stablecoins, €5 across EURC "PIPRAIL_MAX_PAYMENTS": "100", // …and at most 100 payments, ever "PIPRAIL_MAX_PAYMENTS_PER_WINDOW": "10", // …at most 10 within the rolling window "PIPRAIL_WINDOW_SECONDS": "3600"}PIPRAIL_MAX_TOTAL_DENOMfolds the built-in stablecoins (USDC,USDT,USD1,FDUSD,U,RLUSD→USD;EURC→EUR) into one unit-of-account total. It is not a price oracle — a token’s unit is a static, ship-time label, summed 1:1; the chain’s native coin and any unknown token have no denomination and are never summed. It coexists withPIPRAIL_MAX_TOTAL; the stricter cap wins.PIPRAIL_MAX_PAYMENTScaps the lifetime number of settled payments (counts need no oracle, so they span every chain and token, native included).PIPRAIL_MAX_PAYMENTS_PER_WINDOWis a count cap over the rolling window — it requiresPIPRAIL_WINDOW_SECONDS(a count-only window is fine; it can share the same width asPIPRAIL_WINDOW_TOTAL).
The piprail_budget tool and the piprail://budget resource report the grand total (grandTotal), the
payment counts, and the active policy alongside the per-asset rows.
The time envelope
Section titled “The time envelope”On top of the money caps you can add a time leash — see Time
envelope. PIPRAIL_TTL is a hard session deadline in seconds:
once past, every payment is refused (terminal — restart to reset). The rolling window
rate-limits spend over a sliding interval.
"env": { "PIPRAIL_TTL": "3600", // whole session expires after 1 hour "PIPRAIL_WINDOW_TOTAL": "1.00", // at most 1.00 per… "PIPRAIL_WINDOW_SECONDS": "60" // …rolling 60-second window}Survive a restart
Section titled “Survive a restart”By default the budget is in-memory — the session is the process, so a restart zeroes the lifetime
caps. Point PIPRAIL_SPEND_LOG at a file and the ledger hydrates from it at startup and appends each
settled payment, so PIPRAIL_MAX_TOTAL, PIPRAIL_MAX_TOTAL_DENOM, and PIPRAIL_MAX_PAYMENTS resume
where they left off. It’s a plain JSONL file you own — no backend, no database.
"env": { "PIPRAIL_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY", "PIPRAIL_MAX_TOTAL_DENOM": "USD:20.00", "PIPRAIL_SPEND_LOG": "/data/piprail-spend.jsonl"}In multi-chain mode the one file backs the single shared ledger, so the whole cross-chain grand
total is durable. The time envelope (PIPRAIL_TTL) stays process-scoped on purpose. See
Persistence.
Watch the spend
Section titled “Watch the spend”Two opt-in knobs surface what the wallet is doing without changing what it pays:
"env": { "PIPRAIL_WARN_AT_FRACTION": "0.8", // warn the first time spend crosses 80% of ANY cap "PIPRAIL_EVENT_LOG": "stderr" // or a file path}PIPRAIL_WARN_AT_FRACTIONis a number in(0,1]. The first time spend crosses that fraction of any cap (per-token, grand-total, count, or window), the server emits abudget-thresholdevent — an early heads-up before a decline.PIPRAIL_EVENT_LOGwrites those events, plus every payment and decline, as one JSON object per line. Usestderrto fold them into the server’s log stream, or a path to keep a separate audit file.
Schemes — opt-in exact
Section titled “Schemes — opt-in exact”PIPRAIL_SCHEMES chooses which payment schemes the wallet will settle. Absent, it stays on
PipRail’s backendless rail (onchain-proof) only — the zero-config default, where the wallet
broadcasts and pays gas. Add exact to also pay standard x402 servers gasless — the wallet
only signs, and the server (or a facilitator it chose, e.g. PayAI) broadcasts, so the wallet
spends zero gas on EVM, Solana, Algorand, Aptos, and NEAR. exact is one scheme; its on-chain method (EIP-3009
for USDC/EURC, Permit2 for other EVM ERC-20s, SVM for any Solana SPL token, or a fee-payer sponsored transfer on Algorand / Aptos) is auto-selected per
chain + token — you never name it. See the whole model in 30 seconds.
"env": { "PIPRAIL_CHAIN": "solana", // or an EVM chain like "base" "PIPRAIL_SCHEMES": "onchain-proof,exact" // also pay standard x402 servers (EVM + Solana + Algorand + Aptos + NEAR)}Add upto to also pay metered / variable-amount x402 servers on EVM (Permit2): the wallet signs
a maximum, the server meters real usage and settles the actual ≤ that max. The budget is
debited against the MAX, so a payable plan means the ceiling fits your leash.
Valid values are onchain-proof, exact, and upto; an empty or unrecognized list is rejected. See
the exact buyer rail for what the standard scheme settles.
Two modes — autonomous vs supervised
Section titled “Two modes — autonomous vs supervised”The wallet behaves the same in both; only the source of consent differs. See Modes for the full contract.
- Mode A — headless (default). The agent spends freely inside the budget and time envelope. The policy is the consent — no per-payment prompt.
- Mode B — supervised (
PIPRAIL_CONFIRM=1). On a client that can elicit, the human is asked to approve each spend at the moment it happens. Decline, cancel, timeout, or a dropped transport all fail-safe to not paying. On a client that can’t elicit, it silently degrades to Mode A.
"env": { "PIPRAIL_CONFIRM": "1", // ask before every payment "PIPRAIL_CONFIRM_TIMEOUT_MS": "45000" // approval window}Wallet key formats
Section titled “Wallet key formats”PIPRAIL_PRIVATE_KEY holds your secret in the chosen chain’s native form — the server
maps it to the right SDK wallet shape automatically. See Wallets by
family for details.
| Chain(s) | Format |
|---|---|
| EVM (base, ethereum, …), Tron | private key — 0x… 32-byte hex |
| Sui | private key — suiprivkey1… (bech32) |
| Aptos | private key — ed25519-priv-0x… (AIP-80) or raw 0x… hex |
| Solana | secret key — base58 |
| TON | mnemonic — 24 words, space-separated |
| Algorand | mnemonic — 25 words, space-separated |
| Stellar | secret seed — S… |
| XRPL | seed — s… |
| NEAR | private key — ed25519:… plus PIPRAIL_NEAR_ACCOUNT_ID |
RPC and API keys
Section titled “RPC and API keys”There is no separate API-key field — fold any key into PIPRAIL_RPC_URL. This is also how
you point a rate-limited chain at a real endpoint (TON’s keyless public RPC, for instance,
stalls verification at ~1 req/s).
"env": { "PIPRAIL_CHAIN": "ton", "PIPRAIL_RPC_URL": "https://toncenter.com/api/v2/jsonRPC?api_key=YOUR_KEY"}See Chains for the per-chain RPC and gas caveats.
Unknown tokens
Section titled “Unknown tokens”By default the wallet refuses any token the SDK can’t price, because an unpriceable token can’t
be checked against your caps. PIPRAIL_ALLOW_UNKNOWN_TOKENS=true lifts that guard — keep it
false unless you have a specific reason, since it weakens the budget boundary.