Guides
Recipe: Paid MCP server
Charge for an MCP server's tools per call with credits. A person buys a pack once; each call spends, then runs.
Agents should not pay on their own. A person buys a credit pack in their browser, then every tool call checks the balance, spends credits with an idempotency key, and runs. Out of credits, the tool answers with a top-up link the agent shows. No x402, no wallet inside the agent, nothing debited on its own.
paid-mcp-tools skill of the Perly Pay plugin for Claude Code does this recipe in your project — the plugin and its example, examples/paid-mcp-server (MIT), are on their way. Until then, any assistant can read the same skill from /.well-known/skills/perly-pay-paid-mcp-tools/SKILL.md.How it works
- 1
A credit pack
As in API credits. Give each tool a cost and put it in the tool's description.
- 2
Who is calling
A hosted server maps each caller's API key to a customer
external_id. Only your server holdsPERLY_PAY_SECRET_KEY. - 3
Charge, then run
credits.consumewith the tool's name asactionand a key made of the session id and the request id (JSON-RPC ids start again every session), then do the work. - 4
Short?
Return a tool result with
isError: trueand the top-up link, not a protocol error: clients show it. - 5
Free helpers
balanceandtop_uptools, so an agent can explain what is going on.
Run the example
- 1
Install
npm installinexamples/paid-mcp-server(Node 20.12+). - 2
Key
Copy
.env.exampleto.envand put a test key (sk_test_…) inPERLY_PAY_SECRET_KEY. - 3
Set up
npm run setupmakes the product and price with your key and prints their ids for.env. - 4
Run
npm start(ornpm run dev), as its README says. - 5
Pay without a wallet
Add it to Claude Code (
claude mcp add paid-tools -- npx tsx "$PWD/src/server.ts"), runword_stats: it answers with a top-up page. Pick the pack, press Simulate payment and run it again.
Pitfalls
- Putting
PERLY_PAY_SECRET_KEYin an MCP config other people copy. Host the server, or have it call your backend. - Charging after the work: a failed charge then means free work.
- Keying idempotency on the JSON-RPC id alone: a new session reuses it and replays an old charge.