Tools reference
Introduction
Section titled “Introduction”The PipRail MCP server advertises eight tools, built by the SDK’s
paymentTools(client) and dropped straight onto the wire — the
SDK descriptors carry draft-07 JSON Schema, so the server forwards them untouched. Only
piprail_pay_request moves funds; piprail_register writes a listing to an external index
(so it’s not flagged read-only) but moves none; the other six are read-only. Every result is
emitted both as a text block and as structuredContent, so a client that ignores structured
output still reads the text.
// every tool, in advertised order"piprail_discover" · "piprail_quote_payment" · "piprail_plan_payment" ·"piprail_pay_request" · "piprail_register" · "piprail_budget" · "piprail_guide" ·"piprail_verify_receipt"Each tool carries advisory MCP annotations (readOnlyHint,
destructiveHint, openWorldHint, …). They are hints only — the real boundary is the
spend policy, enforced before any send.
piprail_discover
Section titled “piprail_discover”Find x402 payment-gated resources on the open indexes — a phone book of payable APIs — without paying. Use it to answer “what can I buy?”, then quote and pay a chosen one. All arguments are optional.
| Argument | Type | Meaning |
|---|---|---|
query | string | Free-text topic to search for. |
network | string | CAIP-2 id, 'self' (your chain — default), or 'any' (all chains). |
category | string | Keep only this category (strict — uncategorized results are dropped). |
asset | string | Keep only resources paying in this token symbol, e.g. 'USDC'. |
maxPrice | number | Drop results whose index-advertised price is above this number. |
minReliability | number | Drop results below this health score (0–100); unscored results pass through. |
verified | boolean | Prefer verified listings. |
sort | string | 'relevance' | 'reliability' | 'price' | 'uptime' | 'name'. |
limit | number | Max results per index (default 20). |
Returns { count, resources[] }, each resource carrying resource, name, description,
source, priceUsd, category, reliabilityScore, health, verified, and the distinct
networks it offers. priceUsd and maxPrice are the
index’s own advertised metadata — PipRail has no price oracle, so always
piprail_quote_payment the chosen resource for the live, true price before paying. It is
read-only and open-world.
piprail_quote_payment
Section titled “piprail_quote_payment”Price a gated URL without paying. Call it first to decide whether a resource is worth buying.
| Argument | Type | Meaning |
|---|---|---|
url | string (required) | Full URL of the gated resource. |
Returns the quote (gated: true plus amount, token, chain, recipient,
and whether it sits within your spend policy), or { gated: false, url } when the URL needs no
payment. Carries an open outputSchema so a strict client can validate structuredContent.
{ "gated": true, "amountFormatted": "0.10", "symbol": "USDC", "asset": "0x…", "network": "eip155:8453", "payTo": "0xYourWallet", "withinPolicy": true }piprail_plan_payment
Section titled “piprail_plan_payment”Check whether you can pay before committing — reads your wallet balance, native gas, and
recipient readiness across every rail the URL offers on your chain. Call it before
piprail_pay_request so you never start a payment you can’t finish.
| Argument | Type | Meaning |
|---|---|---|
url | string (required) | Full URL of the gated resource. |
Returns { gated, payable, status, fundingHint, summary, best, options[] } (and session when a
time policy is set). summary is one model-readable line distilling the whole
plan; each options[] entry carries state, blockers,
warnings, and recipientReady.
{ "gated": true, "payable": false, "status": "blocked", "fundingHint": "Can't settle on Base: top up 0.04 USDC (to pay 0.10 USDC).", "summary": "NOT payable: Can't settle on Base: top up 0.04 USDC (to pay 0.10 USDC).", "best": null, "options": [ { "network": "eip155:8453", "symbol": "USDC", "amount": "0.10", "state": "blocked", "blockers": ["INSUFFICIENT_TOKEN"], "warnings": [], "recipientReady": "n/a" } ] }The summary line comes verbatim from the SDK’s summarizePlan() — a payable plan instead reads
"Payable: 0.10 USDC on eip155:8453 (gas ~0.00002 ETH). 1 other rail(s) not settleable." Gas is
shown in the native coin only; there is no fiat figure.
piprail_pay_request
Section titled “piprail_pay_request”The one tool that moves funds. It fetches the URL and makes the required payment if needed,
subject to the spend policy and the approval hook. It pays whichever rail the
client is configured for — PipRail’s backendless on-chain rail, or the standard
exact rail when enabled. Its annotations mark it
readOnlyHint: false, destructiveHint: true, idempotentHint: false.
It always hands the model a structured outcome — success or failure — never an exception.
A settled fetch returns { status, ok, body, receipt, verifiableReceipt? }; anything that goes wrong (a refused
payment, a server rejection, a broadcast that didn’t confirm) comes back as a structured
{ ok: false, … } object the agent can branch on. The two shapes are below.
| Argument | Type | Meaning |
|---|---|---|
url | string (required) | Full URL to fetch. |
method | string | HTTP method, default GET. |
body | object | string | Request body for POST/PUT. An object is JSON-serialised and sent with content-type: application/json set automatically; a string is sent verbatim with no content-type set. |
On success it returns { status, ok, body, receipt, verifiableReceipt? }, where receipt is the
parsed payment receipt if one settled.
verifiableReceipt is present only when the gate emitted a verifiable-receipt extension — the
PipRailReceipt JSON ({ piprail, receipt, resource, … }, where piprail is the literal string
"1") stamped with the URL you fetched, which you keep and later re-check with
piprail_verify_receipt.
{ "status": 200, "ok": true, "body": { "...": "the resource you paid for" }, "receipt": { "transaction": "0x…", "network": "eip155:8453", "payer": "0xYourWallet" }, "verifiableReceipt": { "piprail": "1", "receipt": { "...": "…" }, "resource": "https://…" } }Every failure is structured — never a crash
Section titled “Every failure is structured — never a crash”This tool is the single funnel where every PipRailError reaches the
model as a structured object instead of a thrown error, so the agent reasons about it rather than
crashing. The fields are mutually contextual — they’re populated by the kind of failure, so
you’ll never see all of them on one object at once (a clean decline has no ref; a timeout has no
declined):
{ "ok": false, "code": "INSUFFICIENT_FUNDS", "reason": "…", "explain": "…", "ref": "0x…", // only on PAYMENT_TIMEOUT / MAX_RETRIES_EXCEEDED — the broadcast proof "reasonCode": "POLICY", // only on a decline "declined": true } // only on a policy/approval refusal| Field | When present | Meaning |
|---|---|---|
code | always | The stable PipRailError code — branch on this. |
reason | always | The error message. |
explain | always | A one-line human explanation (explainDecline). |
declined | policy / approval refusal | true — no funds moved. |
reasonCode | a decline | SESSION_EXPIRED, APPROVAL, OUTSIDE_WINDOW, POLICY, BUDGET — some are terminal. |
ref | PAYMENT_TIMEOUT / MAX_RETRIES_EXCEEDED | The on-chain proof of a broadcast-but-unconfirmed tx. |
A genuine, non-SDK bug is the only thing that still surfaces as an MCP isError result.
piprail_register
Section titled “piprail_register”List an x402 resource you run on the open indexes so other agents can find it. The default target is 402 Index — no auth, no signature, no payment. Moves no funds; nothing is PipRail-hosted.
| Argument | Type | Meaning |
|---|---|---|
url | string (required) | Full URL of the resource to list. |
name | string | Display name (defaults to the host). |
description | string | What the resource offers. |
category | string | The top findability lever — most listings have none. A real category ('ai', 'finance', …) makes a listing rank + filter. |
tags | string[] | Keywords — folded into the description for search and sent as a tags field. |
priceUsd | number | Advertised price (metadata). |
network | string | Network slug to advertise, e.g. 'base' (defaults to the paying chain) — set it when registering from a multi-chain (PIPRAIL_CHAINS) wallet. |
asset | string | Payment asset symbol, e.g. 'USDC' (metadata). |
provider | string | Who runs the resource (provider/org name). |
contactEmail | string | Contact email for the listing (also used by the domain claim). |
Returns { outcomes[] } — one { source, ok, detail, visibility, note } per index; a step the
chain can’t satisfy comes back ok: false with the reason.
piprail_budget
Section titled “piprail_budget”Read how much of your spend budget and time leash is left — per (network, asset) remaining, the
session time envelope, and your spend so far. Use it in Mode A (headless) to self-check before
paying, so you never discover the leash by hitting a decline. Read-only and idempotent; takes no
arguments.
Returns { spent, remaining, session, report, grandTotal, counts, policy }, where report is a
formatted line of the spend ledger and the last three mirror the
grand-total leash:
| Field | Meaning |
|---|---|
grandTotal | The per-denomination cross-token cap — one row per capped denomination ({ denom, spentFormatted, capFormatted, remainingFormatted, fraction }), present from the start. Empty when no maxTotalPerDenom is set. |
counts | The payment-count leash — { settled, lifetimeCap?, lifetimeRemaining?, windowCap?, windowSettled?, windowRemaining? }. settled is always present; the caps appear only when maxPayments / maxPaymentsPerWindow are set. |
policy | The configured spend policy read back, so the model sees the leash it’s bound by (undefined when none is set). |
{ "spent": "0.10", "remaining": "19.90", "report": "…", "session": { "start": "2026-06-10T00:00:00Z", "expiresAt": "2026-06-10T02:00:00Z", "secondsRemaining": 3500 }, "grandTotal": [ { "denom": "USD", "spentFormatted": "0.10", "capFormatted": "20.00", "remainingFormatted": "19.90", "fraction": 0.005 } ], "counts": { "settled": 1, "lifetimeCap": 100, "lifetimeRemaining": 99 }, "policy": { "maxTotalPerDenom": { "USD": "20.00" }, "maxPayments": 100 } }The grand total is a user-declared unit-of-account sum, not a price oracle: tokens you group as
one unit (USDC/USDT/… → USD) are summed 1:1; native and unknown tokens have no denomination and
are never summed. It still spans chains when the server runs in multi-chain mode, because all chains
share one ledger.
piprail_guide
Section titled “piprail_guide”Read the PipRail agent contract — the quote → plan → pay loop, how to read a refusal (and which
declines are terminal), the never-re-pay rule, and Mode A vs Mode B. Read-only and idempotent;
takes no arguments. Returns { guide }, the full PIPRAIL_AGENT_GUIDE string. Call it once if
you’re unsure how to use these tools.
piprail_verify_receipt
Section titled “piprail_verify_receipt”Re-verify a PipRail verifiable receipt against the chain — confirm a payment really settled
(the funds provably moved to payTo for at least the stated amount) without trusting whoever
handed you the receipt. Read-only and wallet-free: pass the PipRailReceipt JSON from a prior
piprail_pay_request verifiableReceipt, or any third party. Read-only and idempotent.
| Argument | Type | Meaning |
|---|---|---|
receipt | object (required) | The PipRailReceipt JSON ({ piprail, receipt, resource, decimals? }) to re-verify. |
rpcUrl | string | Optional RPC URL for the receipt’s chain (required for chains outside the common presets). |
Returns { ok, onChain: { payTo, asset, amount, payer }, matchesClaims, ageSeconds, error? }. ok
is true when the chain confirms the settlement; onChain.payer is re-derived from the
transaction, so matchesClaims: false means the receipt forged the payer; amount is a verified
lower bound. It is read-only and open-world (it reads the on-chain tx via RPC). Unlike most
tools it never throws — a chain or RPC problem comes back in the error field, never as an
exception. It calls the static PipRailClient.verifyReceipt,
so the same check is available wallet-free in code; see
Verifying receipts and
Verifiable receipts.
{ "ok": true, "onChain": { "payTo": "0xMerchant", "asset": "0x…", "amount": "100000", "payer": "0xBuyer" }, "matchesClaims": true, "ageSeconds": 42 }The guide prompt and resources
Section titled “The guide prompt and resources”When the server is built with guide on (the default — PIPRAIL_GUIDE off only suppresses it),
two extra MCP surfaces are exposed alongside the tools. They are purely additive: with guide
off, the tools path is byte-identical.
| Surface | Kind | Content |
|---|---|---|
piprail_agent_guide | prompt | The full agent contract — how to pay, reading a refusal, Mode A vs B. |
piprail://guide | resource (text/markdown) | The same PIPRAIL_AGENT_GUIDE text. |
piprail://budget | resource (application/json) | The live spend leash: { spent, remaining, grandTotal, counts, session, policy } — the same as the piprail_budget tool, minus the one-line report summary (the resource omits it). |
The piprail://budget resource mirrors the running client’s budget — including the
grand-total (grandTotal), payment-count leash (counts), and the
configured policy read back (policy) — so a client can poll the remaining leash as a resource read
rather than a tool call. See Modes for how the guide and the confirm hook fit
together.