Skip to content

Configuration

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.

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.

Each variable also accepts the shorter alias shown under its name.

VariableRequiredDefaultMeaning
PIPRAIL_PRIVATE_KEY
aka PIPRAIL_WALLET_KEY · AGENT_KEY
only to payWallet 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_CHAIN
aka CHAIN
nobaseEVM preset name or non-EVM family — one wallet on one chain. (Single-chain mode.)
PIPRAIL_CHAINSnoMulti-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_AMOUNT
aka MAX_AMOUNT
no0.10Ceiling per payment, human units.
PIPRAIL_MAX_TOTAL
aka MAX_TOTAL
no10.00Lifetime ceiling per distinct token, human units.
PIPRAIL_MAX_TOTAL_DENOMno(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_PAYMENTSno(none)Lifetime cap on the number of settled payments, across every chain + token.
PIPRAIL_MAX_PAYMENTS_PER_WINDOWno(none)Rolling-window cap on the count of payments. Needs PIPRAIL_WINDOW_SECONDS.
PIPRAIL_TOKENS
aka TOKENS
noUSDC (USDT on any chain without native USDC — Tron, TON, Kaia)Comma-separated allowed token symbols, plus native for the chain’s coin.
PIPRAIL_SCHEMESnoonchain-proofComma-separated payment schemes (see below).
PIPRAIL_HOSTS
aka HOSTS
no(any)Comma-separated host allowlist — exact (api.example.com) or wildcard (*.example.com).
PIPRAIL_RPC_URL
aka RPC_URL
nochain defaultOverride the RPC endpoint; fold any API key into the URL.
PIPRAIL_ALLOW_UNKNOWN_TOKENSnofalsePay tokens the SDK can’t price? Keep false.
PIPRAIL_TTLno(none)Session deadline in seconds — terminal once past.
PIPRAIL_WINDOW_TOTALno(none)Rolling-window budget, human units. Needs PIPRAIL_WINDOW_SECONDS.
PIPRAIL_WINDOW_SECONDSno(none)Rolling-window width in seconds — pairs with PIPRAIL_WINDOW_TOTAL and/or PIPRAIL_MAX_PAYMENTS_PER_WINDOW.
PIPRAIL_WARN_AT_FRACTIONno(none)Emit a budget-threshold event the first time spend crosses this fraction (0,1] of any cap.
PIPRAIL_SPEND_LOGno(none)Path to a JSONL spend store — the budget survives a restart.
PIPRAIL_EVENT_LOGno(none)stderr or a file path — one-line-JSON sink for payment + budget events.
PIPRAIL_CONFIRMnofalseMode B — ask the human to approve each payment via elicitation.
PIPRAIL_CONFIRM_TIMEOUT_MSno55000Approval window in ms; keep below your client’s request timeout (≈60000).
PIPRAIL_GUIDEnotrueExpose the agent-guide prompt + the guide/budget resources.
PIPRAIL_NEAR_ACCOUNT_ID
aka NEAR_ACCOUNT_ID
only on NEARYour 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.

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>_KEY in 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_CHAINS with the single-chain PIPRAIL_CHAIN / PIPRAIL_PRIVATE_KEY / PIPRAIL_RPC_URL is 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.

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.25 is ~25¢ for USDC, but with native allowed it’s 0.25 of the chain’s coin (e.g. 0.25 ETH ≈ $1000s), since there’s no price oracle. Keep PIPRAIL_TOKENS to ≈$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.

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_DENOM folds the built-in stablecoins (USDC, USDT, USD1, FDUSD, U, RLUSDUSD; EURCEUR) 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 with PIPRAIL_MAX_TOTAL; the stricter cap wins.
  • PIPRAIL_MAX_PAYMENTS caps the lifetime number of settled payments (counts need no oracle, so they span every chain and token, native included).
  • PIPRAIL_MAX_PAYMENTS_PER_WINDOW is a count cap over the rolling window — it requires PIPRAIL_WINDOW_SECONDS (a count-only window is fine; it can share the same width as PIPRAIL_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.

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
}

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.

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_FRACTION is 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 a budget-threshold event — an early heads-up before a decline.
  • PIPRAIL_EVENT_LOG writes those events, plus every payment and decline, as one JSON object per line. Use stderr to fold them into the server’s log stream, or a path to keep a separate audit file.

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.

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
}

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, …), Tronprivate key — 0x… 32-byte hex
Suiprivate key — suiprivkey1… (bech32)
Aptosprivate key — ed25519-priv-0x… (AIP-80) or raw 0x… hex
Solanasecret key — base58
TONmnemonic — 24 words, space-separated
Algorandmnemonic — 25 words, space-separated
Stellarsecret seed — S…
XRPLseed — s…
NEARprivate key — ed25519:… plus PIPRAIL_NEAR_ACCOUNT_ID

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.

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.