Documentation
Toll Docs
Add payments to your MCP server. Earn USDC on every tool call. Settled on Stellar in seconds.
Make your first payment
Pay for a real MCP tool call on Stellar mainnet in under 5 minutes. No server setup required — use the live demo server at api.tollpay.xyz.
1. Install the SDK
npm install @rajkaria123/toll-sdk2. Create a wallet, fund it, call a tool
Run this once to create a Stellar keypair. Fund your address with at least 0.05 USDC on Stellar mainnet, then call any paid tool — payment is handled automatically.
// 1. Install
// npm install @rajkaria123/toll-sdk
// 2. Create a wallet (saved to ~/.toll/wallet.json)
import { WalletManager, TollClient } from "@rajkaria123/toll-sdk"
const wm = new WalletManager()
const wallet = wm.getOrCreate("mainnet")
console.log("Your address:", wallet.publicKey)
// → Fund this address with USDC on Stellar mainnet
// (send to USDC asset: CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI)
// Minimum: 0.05 USDC for 5 test calls at api.tollpay.xyz
// 3. Call a paid tool — auto-signs the 402 and pays
const toll = new TollClient({
serverUrl: "https://api.tollpay.xyz",
secretKey: wallet.secretKey,
budget: { maxPerCall: "0.05", maxDaily: "1.00" },
})
const result = await toll.callTool("search_competitors", { query: "project management" })
if (result.success) {
console.log(result.data) // tool output
console.log(result.paid) // true — 0.01 USDC paid on Stellar mainnet
}
console.log(toll.getSpending())
// { totalSpent: 0.01, callCount: 1, dailyRemaining: 0.99 }How to get USDC on Stellar mainnet
CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI. You also need ~1 XLM for the account reserve.Quick Start (server)
Three steps to start earning from your MCP tools.
1. Install
npm install @rajkaria123/toll-gateway @rajkaria123/toll-stellar2. Create toll.config.json
List your tools with prices. Tools with price "0" are free. Everything else charges USDC.
{
"network": "mainnet",
"payTo": "G...YOUR_STELLAR_ADDRESS",
"facilitatorUrl": "https://channels.openzeppelin.com/x402",
"tools": {
"my_free_tool": { "price": "0", "currency": "USDC" },
"my_paid_tool": { "price": "0.05", "currency": "USDC" }
}
}3. Add middleware to your server
Place tollMiddleware() before your MCP transport handler. It intercepts tools/call requests and enforces payment before execution.
import express from "express"
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
import { tollMiddleware, loadConfig, createHealthRoutes } from "@rajkaria123/toll-gateway"
const config = loadConfig("./toll.config.json")
const app = express()
app.use(express.json())
app.use("/mcp", tollMiddleware(config)) // paywall + spending policy
app.use(createHealthRoutes(config)) // /health, /health/tools, /cost
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })
await mcpServer.connect(transport)
await transport.handleRequest(req, res, req.body)
})
app.listen(3002)Mainnet deployment
payTo in the config. The x402 facilitator at channels.openzeppelin.com/x402 handles settlement.Connect an MCP client
Point any MCP client (Claude Desktop, Cursor, etc.) to your server.
// Claude Desktop / any MCP client config
{
"mcpServers": {
"toll-tools": {
"url": "https://proxy.tollpay.xyz/mcp?target=https://api.tollpay.xyz/mcp",
"transport": "streamable-http"
}
}
}How Toll Works
Toll is Express middleware. It sits between AI agents and your MCP tools. When an agent calls a paid tool, Toll intercepts the request, requires payment, verifies it on Stellar, and then lets the tool execute.
Request flow
x402 payment flow (per-call)
x402 is the default protocol. Each paid tool call is a separate USDC transaction verified on-chain.
- 1Agent calls tool— Standard MCP JSON-RPC POST to /mcp
- 2Toll checks for payment— Looks for payment-signature header
- 3Returns 402 if unpaid— Response includes price, payTo address, asset, network
- 4Agent signs USDC payment— Signs a Stellar transaction via x402 SDK
- 5Agent retries with proof— Sends payment-signature header
- 6Facilitator verifies on-chain— OpenZeppelin confirms the Stellar transaction
- 7Tool executes— Payment recorded, tool runs, result returned
MPP payment flow (session-based)
MPP (Machine Payments Protocol) uses Stellar payment channels for session-based billing. Set paymentMode: "mpp" on individual tools to use MPP instead of x402. MPP support is experimental.
Free tools and rate limiting
Tools with "price": "0" skip all payment checks. You can also set a free tier on paid tools — the first N calls per IP are free, then payment kicks in.
"search_competitors": {
"price": "0.01",
"currency": "USDC",
"rateLimit": {
"free": 5,
"perHour": true
}
}Configuration
All configuration lives in toll.config.json, validated at startup with Zod. Invalid configs fail fast with clear error messages.
| Field | Type | Description |
|---|---|---|
| network | "testnet" | "mainnet" | Stellar network to use |
| payTo | string | Your Stellar public address (G...) |
| facilitatorUrl | string | x402 facilitator URL |
| defaultPaymentMode | "x402" | "mpp" | Default protocol for all tools |
| tools | Record<string, ToolConfig> | Map of tool names to pricing config |
| tools[].price | string | USDC price per call (e.g. "0.01") |
| tools[].currency | "USDC" | Payment currency |
| tools[].paymentMode | "x402" | "mpp" | Override default per tool |
| tools[].rateLimit.free | number | Free calls before payment required |
| spendingPolicy | object | Global spending limits |
How to price your tools
There is no right answer, but here are guidelines based on what the tool does:
| Tool type | Suggested price | Why |
|---|---|---|
| Health check / status | Free ($0) | Builds trust, lets agents verify your server is up |
| Simple lookup / search | $0.001 - $0.01 | Low cost per call, high volume expected |
| Analysis / processing | $0.01 - $0.10 | More compute, more value to the caller |
| Complex multi-step | $0.10 - $1.00 | Significant processing, data access, or API costs |
Spending policies
Protect your server from runaway spending with global limits.
"spendingPolicy": {
"maxPerCall": "0.10", // No single call costs more than $0.10
"maxDailyPerCaller": "1.00", // Each caller capped at $1/day
"maxDailyGlobal": "10.00" // Total daily cap across all callers
}USDC decimal precision
USDC on Stellar uses 7 decimal places. Toll converts your human-readable prices internally:
"0.01" USDC → 100000 base units (stroops)
"0.001" USDC → 10000 base units
"1.00" USDC → 10000000 base unitsToll Proxy
The Toll Proxy lets any MCP client (Claude Desktop, Cursor, etc.) use paid tools without payment code. It sits between your client and the Toll-powered server, intercepting 402 responses, auto-signing USDC payments on Stellar, and retrying.
Start the Proxy
# Start the Toll Proxy locally — auto-creates a Stellar wallet
npx @rajkaria123/toll-proxy --target https://api.tollpay.xyz/mcp
# Or with custom budget limits
npx @rajkaria123/toll-proxy \
--target https://api.tollpay.xyz/mcp \
--budget-daily 5.00 \
--budget-per-call 0.50
# Or use the hosted proxy (no install needed):
# https://proxy.tollpay.xyz/mcp?target=https://api.tollpay.xyz/mcpConnect Your MCP Client
Add the proxy URL to your MCP client configuration:
// Add to Claude Desktop / Cursor MCP config:
{
"mcpServers": {
"my-paid-tools": {
"url": "https://proxy.tollpay.xyz/mcp?target=https://api.tollpay.xyz/mcp",
"transport": "streamable-http"
}
}
}
// Or use localhost if running the proxy locally:
// "url": "http://localhost:3010/mcp?target=https://api.tollpay.xyz/mcp"Auto Wallet
Budget Controls
The proxy enforces spending limits to prevent runaway costs:
--budget-daily 5.00 # Max $5 USDC per day (default)
--budget-per-call 0.50 # Max $0.50 per individual callHosted Proxy (no install)
Use the hosted proxy at proxy.tollpay.xyz — no install, no wallet setup. Just point your MCP client at:
https://proxy.tollpay.xyz/mcp?target=https://api.tollpay.xyz/mcpOr self-host with npx for full control over your wallet and budget. The proxy is open source and deployable on Railway/Docker.
Tool Registry
The Toll Registry is a public directory where Toll-powered MCP servers register their tools and agents discover them by capability, price, and quality score.
Register Your Server
From your MCP server project directory (with toll.config.json):
npx @rajkaria123/toll-cli register --url https://your-server.com/mcpRegistration is authenticated via Stellar keypair signature. The CLI fetches your server's tool manifest and registers all tools with the registry.
Discover Tools
Agents and developers can discover tools programmatically:
// Discover tools programmatically
const resp = await fetch(
"https://tollpay.xyz/api/registry/discover?q=search&maxPrice=0.05"
)
const { tools } = await resp.json()
// 18 tools across 5 servers — free and paid
// [{ name: "search_competitors", price: "0.01", server: { url: "..." }, ... }]Quality Scores
Each tool has a quality score (0-100) based on uptime, success rate, latency, and usage volume. The Toll Proxy automatically reports metrics to the registry. Higher quality scores appear first in discovery results.
Wallet Management
The Toll SDK auto-creates and manages Stellar Ed25519 keypairs for agents. No manual wallet setup required.
import { WalletManager } from "@rajkaria123/toll-sdk"
const wallet = new WalletManager()
// Auto-creates ~/.toll/wallet.json with a Stellar Ed25519 keypair
const { publicKey, secretKey } = wallet.getOrCreate("mainnet")
// Get funding instructions
console.log(wallet.fundingInstructions(publicKey))Wallet Storage
Wallets are stored at ~/.toll/wallet.json with 0600 file permissions (owner read/write only). The file contains the Stellar public key and secret key. Do not store large amounts — this is designed for micropayment budgets.
Funding
Fund your wallet with USDC on Stellar via LOBSTR, MoneyGram, or an exchange. See the Fund page for step-by-step instructions.
Agent SDK
For AI agents that need to discover and pay for tools automatically. The SDK handles 402 detection, USDC signing, request retry, and budget enforcement.
Install
npm install @rajkaria123/toll-sdkBasic usage
import { TollClient } from "@rajkaria123/toll-sdk"
const toll = new TollClient({
serverUrl: "https://your-server.com",
secretKey: "S...", // Stellar Ed25519 secret key
budget: {
maxPerCall: "0.10",
maxDaily: "5.00",
},
})
// Free tool — no payment
const health = await toll.callTool("health_check")
// Paid tool — auto-handles 402 → sign → retry
const result = await toll.callTool("search_competitors", {
query: "AI agent frameworks"
})Discover available tools
const manifest = await toll.discoverTools()
// Returns: { tools: [{ name, price, currency, paymentMode }], network }Track spending
const report = toll.getSpending()
// { totalSpent, callCount, byTool, dailyRemaining }Events
toll.on("payment", (ev, data) => console.log(`Paid ${data.amount} for ${data.tool}`))
toll.on("budget_warning", (ev, data) => console.log(`${data.remaining} remaining`))
toll.on("error", (ev, data) => console.error(data.error))Budget safety
Earnings & Dashboard
Every paid tool call is recorded to a local SQLite database. The dashboard reads this to show real-time earnings, per-tool revenue, and per-caller analytics.
Database schema
CREATE TABLE transactions (
id TEXT PRIMARY KEY, -- UUID
tool TEXT NOT NULL, -- tool name
caller TEXT, -- Stellar address or IP
amount_usdc REAL NOT NULL, -- payment amount
protocol TEXT NOT NULL, -- "x402" or "mpp"
tx_hash TEXT, -- on-chain transaction hash
created_at INTEGER NOT NULL -- Unix timestamp (ms)
);API Reference
tollMiddleware(config: TollConfig): RequestHandler
@rajkaria123/toll-gatewayExpress middleware. Place before your MCP transport handler. Intercepts tools/call requests, enforces payment.
import { tollMiddleware, loadConfig } from "@rajkaria123/toll-gateway"
const config = loadConfig("./toll.config.json")
app.use("/mcp", tollMiddleware(config))loadConfig(path: string): TollConfig
@rajkaria123/toll-gatewayReads and validates toll.config.json. Throws with clear errors on invalid config.
const config = loadConfig("./toll.config.json")
console.log(config.tools) // { tool_name: { price, currency } }new EarningsTracker(dataDir?: string)
@rajkaria123/toll-stellarSQLite-backed payment recorder. Auto-creates DB. Default directory: ~/.toll
const tracker = new EarningsTracker("./data")
tracker.record({ tool: "search", caller: "GABCD...", amountUsdc: 0.01, protocol: "x402", txHash: "abc..." })
tracker.getStats() // { totalEarnings, totalCalls }
tracker.getByTool() // [{ tool, calls, revenue }]new X402Verifier(config: TollConfig)
@rajkaria123/toll-stellarBuilds 402 responses and verifies payments via the facilitator.
const verifier = new X402Verifier(config)
const req = verifier.buildRequirements("tool", "0.01", resourceUrl)
const result = await verifier.settle(paymentHeader, req)Environment variables
| Variable | Description |
|---|---|
| PORT | Server port (default: 3002) |
| TOLL_SERVER_SECRET | Stellar secret key for payment signing |
| TOLL_SERVER_ADDRESS | Stellar public key (must match payTo) |
| TOLL_DATA_DIR | SQLite directory (default: ~/.toll) |
| X402_FACILITATOR_URL | x402 facilitator endpoint |
| TOLL_EARNINGS_API_URL | Remote earnings API for split deployments |
Security
On-chain payment verification
Every x402 payment is verified on Stellar mainnet via the OpenZeppelin facilitator. Toll never trusts client claims without on-chain proof.
Replay protection
Payment signatures are cached and rejected on reuse. Each signature has a 5-minute TTL. The same payment cannot be used twice.
Spending policies
Per-call caps, daily per-caller budgets, and global daily limits. Enforced before payment is even attempted.
Input validation
All config validated with Zod at startup. Tool names are validated against alphanumeric pattern. Invalid requests are rejected immediately.
Middleware ordering
tollMiddleware runs before the MCP transport. There is no way to bypass the payment check and reach your tool code without paying.
Key isolation
Server secret keys go in .env only. The config file contains only the public payTo address. No keys are logged or exposed.
FAQ & Troubleshooting
How do I get a Stellar address?
Generate a keypair using Stellar SDK or the Toll setup script. For mainnet, fund it with a small amount of XLM (2 XLM minimum) and add a USDC trustline. You can acquire XLM from any major exchange.
What if an agent doesn't have an x402-compatible wallet?
The agent will receive a 402 response but won't be able to pay. Free tools still work. For paid tools, the agent needs the @rajkaria123/toll-sdk or any x402-compatible client with a funded Stellar wallet.
Can I change prices after deployment?
Yes. Update toll.config.json and restart your server. Price changes take effect immediately. There is no migration needed.
How do I get my earnings out?
Earnings are USDC in your Stellar wallet. You can send USDC to any exchange that supports Stellar USDC (Coinbase, Kraken, etc.) and convert to fiat. The funds are yours immediately — there is no holding period.
Does Toll take a cut?
Currently, no. 100% of each payment goes to your payTo address. The only cost is the Stellar transaction fee (~$0.00001 per transaction), paid by the agent.
The playground shows 402 but my tool doesn't execute
This is expected. A 402 response means the payment gate is working correctly. The tool will only execute after the agent signs a USDC payment and retries with a valid payment-signature header.
Can I use Toll with a non-Express server?
Toll is currently Express middleware. For other frameworks (Hono, Fastify, etc.), you can adapt the middleware or use the withToll() wrapper which works with stdio-based MCP servers independent of HTTP framework.
Is MPP production-ready?
MPP integration is experimental. The x402 protocol is fully production-ready and recommended for most use cases. MPP support will be fully validated as the Stellar MPP SDK stabilizes.
How do I deploy?
Deploy your MCP server anywhere Node.js runs (Railway, Fly.io, AWS, etc.). The dashboard deploys to Vercel. Set TOLL_EARNINGS_API_URL on Vercel to connect the dashboard to your server's earnings API if they're on different hosts.
What are the known limitations?
- Rate limiter is in-memory (resets on server restart)
- SQLite earnings DB works for single-server deployments
- Streaming payment support is not yet available
- Express-only middleware (other frameworks need adapters)
On-Chain Transaction Proof
All transactions below are on Stellar Mainnet with real USDC. Every link goes to Stellar Expert, the public block explorer. Independently verifiable by anyone.
Summary
Wallets
| Role | Address | Balance | Explorer |
|---|---|---|---|
| Server | GDQRUDNV...F5HKOEUV | 1.24 USDC | View → |
| Agent 1 | GCW4WEKH...FMREJZK7 | 0.15 USDC | View → |
| Agent 2 | GDLX6OYX...44ZO3YHE | 0.14 USDC | View → |
What Was Achieved
End-to-End x402 Payment Flow on Mainnet
Agent calls tool -> Toll returns HTTP 402 -> Agent signs USDC payment -> Server settles via Soroban -> Tool executes. Real USDC, real blockchain.
Self-Hosted Settlement
Toll submits Soroban transactions directly using its own Stellar keypair. No dependency on external facilitators.
Multiple Agent Wallets
Two independent agent wallets successfully paid for tool calls, demonstrating multi-tenant capability.
USDC Trustline Auto-Setup
Full wallet lifecycle: account creation, USDC trustline, funding, payment signing, and settlement.
Sub-Second Settlement
All Soroban contract invocations settle in 3-5 seconds on Stellar mainnet.
x402 USDC Settlements (Soroban)
Each row is an invoke_host_function operation that transfers USDC via the Soroban USDC SAC contract.
| # | Time (UTC) | Amount | Agent | Tx Hash |
|---|---|---|---|---|
| 1 | Apr 11 07:23 | $0.01 | Agent 1 | e8f55b6814c9... |
| 2 | Apr 12 04:40 | $0.01 | Agent 1 | 75eb38cab324... |
| 3 | Apr 12 04:44 | $0.01 | Agent 1 | 82342ebec33f... |
| 4 | Apr 12 04:45 | $0.01 | Agent 1 | 737545dd0869... |
| 5 | Apr 12 04:46 | $0.01 | Agent 1 | 0e3ac945c9ec... |
| 6 | Apr 12 05:32 | $0.01 | Agent 2 | 25f3dfb9ffd7... |
| 7 | Apr 12 05:32 | $0.01 | Agent 2 | 2d6ea8477377... |
| 8 | Apr 12 05:32 | $0.01 | Agent 2 | 015ef6bacf05... |
| 9 | Apr 12 05:32 | $0.01 | Agent 2 | ff7902aa90af... |
| 10 | Apr 12 05:33 | $0.01 | Agent 2 | 8e707a0d4361... |
| 11 | Apr 12 05:33 | $0.01 | Agent 2 | 048fd865172d... |
USDC Payments & Account Setup
| Time (UTC) | Type | Details | Tx Hash |
|---|---|---|---|
| Apr 11 07:23 | create_account | Server wallet | 2f165f4ad7f9... |
| Apr 11 07:25 | trustline + fund | Server USDC setup | bbc1dda4971a... |
| Apr 12 03:08 | create_account | Agent 1 wallet | 66793cb66d8f... |
| Apr 12 03:08 | change_trust | Agent 1 USDC | c3a578eb3f04... |
| Apr 12 03:08 | payment | 0.20 USDC to Agent 1 | 26872672c277... |
| Apr 12 03:09 | payment | 0.01 USDC Agent 1 -> Server | d8b1c8ca5413... |
| Apr 12 05:26 | create_account | Agent 2 wallet | 3fe2eac0f11a... |
| Apr 12 05:29 | change_trust | Agent 2 USDC | 7e57a202e27b... |
| Apr 12 05:29 | payment | 0.20 USDC to Agent 2 | 0286dbb3987b... |
Timeline
| Apr 11 07:23 | Server wallet created and funded on Stellar mainnet |
| Apr 11 07:23 | First x402 USDC settlement on mainnet (Soroban) |
| Apr 12 03:08 | Agent 1 wallet created, trustline added, funded |
| Apr 12 03:09 | First agent-to-server USDC payment |
| Apr 12 04:40 | Self-hosted settlement working (no external facilitator) |
| Apr 12 05:26 | Agent 2 (proxy) wallet auto-created |
| Apr 12 05:32 | 6 consecutive x402 settlements from Agent 2 |
How to Verify
Click any transaction link to see it on Stellar Expert. All data is independently verifiable via the Stellar Horizon API.
# Check server account transactions
curl https://horizon.stellar.org/accounts/GDQRUDNV3D3DF3KMVPWHFW7Y676AEPL7U6CEXKCD2F7HLEPFF5HKOEUV/operations?order=desc&limit=20
# Or try a paid tool yourself (returns HTTP 402)
curl -X POST https://api.tollpay.xyz/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_competitors","arguments":{"query":"test"}}}'