Fallback
Return a degraded result instead of forcing every unpaid call into a hard failure.
Tollgate is an open-source runtime for MCP and agent tools. It handles fallback, idempotent retries, metering, recovery, and execution traces so retries and edge cases do not double-charge or hard-fail.
Clone the repository to run the local demo. The first-run path uses the in-memory ledger and does not require Stripe, x402, MPP, wallets, webhooks, hosted APIs, or environment variables.
git clone https://github.com/niceberginc/tollgate.git
cd tollgate
npm install
npm run build
npm run example:local
Tollgate wraps the call boundary where payment, quota, retries, execution, and recovery meet.
Return a degraded result instead of forcing every unpaid call into a hard failure.
Replay completed results for duplicate request keys without charging twice.
Use minor-unit accounting through `Money`, `usd()`, and ledger adapters.
Record usage and optionally price after execution based on actual metrics.
Credit back prepaid calls when execution fails — and retry, queue, and reconcile uncertain on-chain settlements. More →
Inspect decisions, charge status, fallback usage, handler status, and recovery actions.
Start with `TollGate`, `InMemoryLedger`, and `usd()`; add rail adapters later.
import { TollGate, InMemoryLedger, usd } from "@niceberglabs/tollgate";
const ledger = new InMemoryLedger();
const gate = new TollGate({ publisherKey: "tg_local_demo", ledger });
const search = gate.paidTool({
name: "premium_search",
price: usd("0.05"),
onPaymentFailed: "fallback",
idempotencyKey: (input) => `premium_search:${input.requestId}`,
handler: async (input) => ({ tier: "premium", query: input.query }),
fallback: async (input) => ({ tier: "free", query: input.query }),
});
await ledger.credit("caller-1", usd("1.00"), {
source: "manual",
reference: "dev-credit",
});
const result = await search({ query: "vector dbs", requestId: "r1" }, "caller-1");
console.log(result.receipt);
Use `createMcpAdapter()` to expose a paid tool through an MCP server. `_meta.tollgate` carries receipt, fallback, and payment-required metadata.
import { TollGate, createMcpAdapter, usd } from "@niceberglabs/tollgate";
const gate = new TollGate({ publisherKey: "tg_local_demo" });
const mcp = createMcpAdapter(gate, {
getCallerId: (_args, extra) => extra?.sessionId ?? "demo-user",
});
mcp.paidTool("premium_search", {
price: usd("0.05"),
onPaymentFailed: "fallback",
inputSchema: { type: "object", properties: { query: { type: "string" } } },
handler: async (args) => ({ results: [`deep results for ${args.query}`] }),
fallback: async (args) => ({ results: [`basic result for ${args.query}`] }),
});
Tollgate is rail-agnostic: the runtime — fallback, idempotency, recovery, traces — stays identical while the rail is a pluggable adapter. x402 spans EVM and, now, Solana.
USDC micropayments via EIP-3009 transferWithAuthorization, validated and settled through a facilitator. Gasless — the facilitator pays gas.
Gasless: the agent partial-signs an SPL USDC transfer, the facilitator sponsors the fee and settles. Sub-cent, sub-second.
Top up with Stripe, run on a local prepaid ledger (in-memory / SQLite / D1), or settle through MPP — same runtime, same traces across every rail.
On-chain rails settle after execution — and settlement can fail on its own (RPC blip, facilitator timeout) while the payment already verified. Tollgate never lets that payment vanish.
Verified, executed, but settlement didn't confirm? Most setups drop it. Tollgate recovers it — and never double-settles a tx that already landed.
reconcileSettlements()`settlement_uncertain`, attempts, queued, and final settlement land in the execution trace — partial failure is explicit, never collapsed into one success bit.
Payment rails are optional adapters. Multi-instance production requires durable idempotency, which is future work.
| Area | Status |
|---|---|
| Core runtime | Developer preview |
| In-memory ledger and idempotency | Local development and single-process prototypes |
| SQLite / D1 ledger | Local and single-process paths |
| Stripe test mode | Validated with configured test credentials |
| Stripe production | Beta; validate your webhook and deployment path |
| x402 — EVM (Base, Polygon, Arbitrum, Optimism, Ethereum) | Beta; EIP-712 USDC domain auto-injected, verified on Base mainnet (gasless) |
| x402 — Solana / SVM | Beta; packaged signer + full MCP verify/settle e2e, verified on devnet and mainnet (gasless via x402 facilitators) |
| Settlement recovery | Retry + durable queue + reconcile + on-chain confirmation; in-memory or SQLite/D1 |
| MPP | Mocked / spec-path unless verified with real mppx integration |