npm packages are not installable yet: [email protected] can be published after 2026-09-16 10:30 UTC. Details
Tollstile

Sell an MCP server to people

Charge for MCP tools today, without waiting for clients to learn how to pay — identify the user, draw on what they bought, and send them somewhere to top up.

Public Beta · early access

No MCP client in wide use lets a model attach a payment to a tool call. Claude, ChatGPT, and Cursor give a model tools, not a wallet — by design, and it is the right design.

So do not ask the model to pay. Ask who is calling. Every remote MCP client signs its user in, which is all a merchant needs: the caller has an account, the account has credit or a subscription, and the call draws on it. Nothing in this guide waits on a payment protocol, a wallet, or a client feature that does not exist yet.

The finished example is examples/mcp/src/account-server.ts.

1. Identify the caller

A remote MCP server authenticates with OAuth, and the SDK hands the verified token to every tool call as extra.authInfo. Turn it into a Tollstile principal:

const principal = (extra) => {
  const account = accounts[extra.authInfo?.token ?? ""];   // your own table
  return account === undefined ? null : { id: account };
};

authInfo is set by whatever verified the request — the SDK's bearer-auth middleware on Express, or transport.handleRequest(request, { authInfo }) on a Web-standard server. If you have not wired auth yet, follow MCP's authorization spec first: without it, everyone is the same anonymous caller.

2. Charge the account, not the request

paidTool(
  server,
  "forecast",
  { description: "Tomorrow's weather. $0.01 from your balance.", inputSchema: { city: z.string() } },
  toll.price("$0.01", {
    access: [subscriber({ active: (user) => subscriptions.has(user.id) }), credits({ balance })],
  }),
  ({ city }) => ({ content: [{ type: "text", text: `Tomorrow in ${city}: clear` }] }),
  { principal },
);

Subscribers pass free; everyone else draws $0.01 from their balance, reserved before the handler and committed after. Your customers bought that balance the ordinary way — one Stripe checkout for $20 — so no card is charged per call and no fee is paid per call.

No payPerCall(), on purpose

Leaving payPerCall() out of the access list means there is no way into this tool except an account. A caller holding a perfectly valid payment proof is refused with access_denied before any rail is consulted. Add it back only when you do want unattended agents paying their own way.

3. Send them somewhere to pay

Out of credit is not a failure the model can fix. Left alone, it reaches the user as "the weather tool returned an error".

{
  principal,
  checkout: (denial) =>
    denial.error.code === "access_denied"
      ? { url: "https://weather.example/account/credits", message: "Add credit to keep using the weather tools." }
      : null,
}

A client that declared capabilities.elicitation.url shows its user that link and stops. Every other client gets the denial with { url, message } in _meta["tollstile/checkout"], so your own agent, a proxy, or a log can still find it. Details: Checkout.

4. Watch what the ledger says

const { count, total } = await ledger.spendSince("acct_amy", new Date(Date.now() - 86_400_000));

Every draw is a charge row: who, which tool, how much, when. That is the invoice line, the usage page, and the support answer to "what did I pay for", without a second metering system.

When to add per-call payment

Add payPerCall() and a rail when the caller is a program with its own money — your own agent fleet, a customer's backend, a marketplace client built to pay. Then a 402 is answered by the caller instead of by a person, and everything above still holds for the humans.

If those calls are expensive, ask before charging: Approval.

What this does not solve

  • A client that hides errors. If a client renders a denial as "tool failed" and drops _meta, the user sees less than you sent. Checkout is a request, not a guarantee.
  • Account-less callers. Anonymous access still needs a rail, and the client-side ecosystem for that is early. See Consent.
  • Sign-up. Tollstile never takes custody and sells nothing; the page you send people to is yours.

On this page