Tollwicket

← Back to blog

Paywalling Claude-callable tools: what works, what doesn't

How to add a paywall to MCP tools called from Claude, Cursor, Goose, and other LLM clients — the parts of MCP that help, the parts that hurt, and the patterns that convert.

A paywall in front of a Claude-callable tool is not the same as a paywall in front of a webpage. The user isn't looking at your page. They're looking at a Claude conversation. The "browser" is the LLM. The paywall has to work through the LLM.

This post is about what that looks like in practice — what works, what doesn't, and the patterns we've seen convert MCP tool users into paying customers.

The fundamental constraint

In a normal web paywall, you control the page. You can show a beautiful modal, a free-trial countdown, a sticky banner. In an MCP paywall, you control exactly one thing: the response body of a tool call.

That's it. That string is the paywall.

Everything else — does the user upgrade, do they give up, do they email support — is downstream of how good that string is.

What an effective MCP paywall string looks like

A good paywall response has three properties:

  1. It is unambiguously an error. The LLM must recognize that the tool failed. If your response is a successful tool result with "please pay" baked into it, the LLM will paste back to the user as if the tool worked, or worse, hallucinate a workaround.
  2. It contains an actionable URL. A bare URL the user (and the LLM) can click. Bare HTTPS URL, no markdown, no surrounding quote marks — the URL parser in most clients is rudimentary.
  3. It tells the LLM what to say. Claude is a good agent. If you tell it the tool failed because of quota and the user needs to upgrade at the URL, it will tell the user exactly that.

Here's a paywall response that works:

Quota exceeded for tool `search_papers`.
You've used 100 of 100 free daily calls.
Upgrade at https://yourtool.com/upgrade?token=ey... to continue.
The URL is single-use and pre-authenticated.

And here's one that doesn't:

{"error": "FORBIDDEN", "code": 403}

The second one is correct. It is also useless. Claude will say "I encountered an error" and move on, and your user will never know they could upgrade.

Pre-authenticated URLs are the single biggest conversion win

Imagine the user is mid-conversation with Claude. They asked Claude to find them five papers on a topic. Claude calls your search_papers tool. Your tool returns "quota exceeded, upgrade at https://yourtool.com/upgrade."

Two paths from here:

Path A: The user clicks the URL. The page says "Log in to upgrade." They have to find their credentials, log in, navigate to billing, pick a plan, enter their card, finish Checkout. By the time they're done, they have forgotten what they were doing with Claude.

Path B: The user clicks the URL. The page immediately opens Stripe Checkout for the recommended plan, pre-filled with their email. They tap their card on file. They go back to Claude and ask it to retry. The tool works.

Path B converts roughly 5x better than Path A in the conversion data we've seen. The trick is the single-use, pre-authenticated upgrade token — your tool generates it on the fly, embeds it in the error, the upgrade page exchanges it for a session, and there's no login step in between.

If you're building this yourself: generate a JWT signed by your server, with a 1-hour expiry and the user's ID as the subject. The upgrade page accepts the JWT in the URL and sets a session cookie. Done.

If you don't want to build this yourself, it's one of the things Tollwicket's billed_tool decorator does by default — every paywall error contains a pre-signed URL automatically.

The retry-and-loop problem

LLMs will sometimes retry failed tool calls. If your paywall is just a generic error, the LLM may retry several times, burning context and confusing the user. If your paywall is a clear "out of quota" message, modern Claude / GPT-4 / Cursor will not retry — they'll surface the message to the user.

This is one of the few areas where the LLM is actively helping you. Use it. Be specific.

If you observe LLMs retrying your paywall errors anyway, the most common culprit is using the wrong error type. Some MCP SDKs let you return a "tool error" or raise an exception. Use the tool error form — it's a structured signal to the LLM that the tool reported an error, not that the protocol broke.

What about the LLM telling the user "I can't help with that"?

You will occasionally see an LLM, faced with a quota-exceeded response, refuse to surface the upgrade URL to the user — instead saying something vague like "I'm not able to complete this task." This is usually a system-prompt artifact in the LLM client and there's nothing you can do about it directly.

What helps:

  • Use plain, friendly language in your paywall response. "Quota reached" beats "Authentication failure." LLMs surface friendly errors more readily.
  • Don't include scary tokens. A response with words like "blocked," "denied," "forbidden" sometimes triggers safety policies in the client.
  • Test in every client you care about. Behavior varies — Claude, Cursor, Goose, Continue all surface tool errors slightly differently.

Per-tool vs. per-server paywalls

A subtle decision: do you paywall individual tools, or the whole server?

Per-server: One subscription unlocks all tools on the server. Simpler for the user. Simpler for you. Best when all your tools have similar costs.

Per-tool: Each tool has its own quota or plan requirement. More flexible. Lets you charge differently for cheap and expensive tools. Adds support load ("why does search_papers work but analyze_pdf doesn't?").

We recommend per-server for almost everyone. The simplicity dominates. Move to per-tool only if you have a clear cost difference between tools that justifies separate billing.

The free-tier question

How much should the free tier give away?

Two answers depending on your goals:

  • If your goal is volume of paid conversions: generous free tier. Let users do real work, hit the quota mid-task, convert because they're already invested.
  • If your goal is high-LTV customers: stingy free tier. Filter for users who already know they want it. Lower conversion rate, higher value per converted user.

For most MCP tools targeting prosumers and devs, the first approach wins. Make the free tier good enough that users do real work on it. The conversion happens organically when they hit the cap.

How to know it's working

Three numbers tell you everything:

  1. Free signups per week. Top of funnel. If this is zero, your paywall is irrelevant — you have a marketing problem.
  2. Quota-exceeded events per free user. Are users actually using your tool enough to hit the cap? If not, raise the free quota or change your tool.
  3. Upgrade rate after quota-exceeded. Of users who hit the cap, what fraction upgrade within 24 hours? Healthy is 15–25%. Below 5%, your upgrade page is broken (likely missing pre-authenticated URLs).

Track all three. They tell you exactly which lever to pull.

What to do next

  • Write your paywall response string. Make it specific, friendly, and include a URL.
  • Build pre-authenticated upgrade tokens. (Or use a billing SDK that does.)
  • Pick per-server unless you have a strong reason to go per-tool.
  • Set a generous free tier and instrument the three metrics above.

Tollwicket ships pre-authenticated upgrade URLs, structured paywall errors, and per-server billing as defaults. If you'd rather skip the plumbing, that's the fastest path.

Related reading

Ship a paid MCP tool this weekend.

Drop one Python decorator. Tollwicket handles auth, quotas, and Stripe Checkout — on your own Stripe account. Free until you cross $500/mo of customer revenue.