Agents are the new customers. Meter every tool call with Stripe, keep the first 200 free, and let the service you already built earn while you sleep.
Somewhere right now, an assistant is choosing which tool to call — the way browsers once chose websites. If your Pocketcorp runs a service other agents find useful (bookings, lookups, conversions, a dataset you maintain), you can charge for it per call and never write an invoice. The meter is the cash register. Reads stay free, writes cost cents, failed calls cost nothing — and Stripe handles the rest.
This build continues where “Extend It With MCP” ends, in the same verify loop: write, run, call your own tools, fix what breaks. Hermes knows that FastAPI layout already — and billing code is exactly the kind of work where its habit of proving each step pays for itself.
Copy this prompt and send it to the agent on your Pocketcorp. It sets up everything in this guide and reports back when it’s done. The steps below explain what your agent is doing — and how to check its work.
Add per-call billing to the MCP service running on this server
(the FastAPI + fastapi-mcp one). Work in Stripe TEST MODE until I
say otherwise — ask me for my Stripe test secret key.
1. Billing model: €0.02 per successful write tool call (booking,
cancel). Read-only tools stay free. The first 200 billable calls
per month are free.
2. Create in Stripe: a billing Meter (event_name tool_call, sum
aggregation) and a metered monthly Price with graduated tiers
(200 at €0, then €0.02 per call). Give me the payment link.
3. In the service: after every successful (status < 400) billable
MCP call, send a Meter Event carrying the customer id, with the
request id as the identifier so a retried request can never bill
twice. Never bill failed calls.
4. Webhooks: checkout.session.completed → create an API key and link
it to the customer; invoice.payment_failed → pause the key — and
make the service answer paused keys with a message telling the
calling agent that its human should update the card;
invoice.paid → unpause; customer.subscription.deleted → revoke.
5. Update the service’s SKILL.md with pricing, the payment link, the
connection JSON, and a free verification call.
6. Prove the loop end to end in test mode: subscribe, connect a
second agent using only the SKILL.md, burn the free tier, show me
the first billable meter event on the upcoming invoice — then
simulate a failed payment and show me the paused-key message.One pricing decision matters more than the number: agents explore before they commit. A paywall on exploration reads as a broken tool; a paywall on outcomes reads as a price. So reads stay free, and the calls that do real work — book, convert, generate — cost money:
Stripe’s usage-based billing does the accounting. Your agent creates a meter and a graduated-tier price on it — the free tier lives in Stripe, not in your code:
import os
import stripe
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
meter = stripe.billing.Meter.create(
display_name="Tool calls",
event_name="tool_call",
default_aggregation={"formula": "sum"},
)
price = stripe.Price.create(
product_data={"name": "Workshop Bookings API"},
currency="eur",
recurring={"interval": "month", "usage_type": "metered", "meter": meter.id},
billing_scheme="tiered", tiers_mode="graduated",
tiers=[
{"up_to": 200, "unit_amount": 0}, # free allowance
{"up_to": "inf", "unit_amount_decimal": "2"}, # €0.02/call
],
)
print("price:", price.id) # → payment link in the Stripe dashboardSelling API access across the EU means VAT. Turn on Stripe Tax on the payment link and let it handle rates and receipts — on day one it’s a checkbox; after real revenue it’s a conversation with an accountant.
The metering hook goes exactly where the API key is validated — one middleware, three rules: successes only, billable tools only, retries bill once:
@app.middleware("http")
async def meter_tool_calls(request, call_next):
response = await call_next(request)
principal = getattr(request.state, "principal", None)
if (
principal
and request.url.path.startswith("/mcp")
and response.status_code < 400
and request.state.tool_name in BILLABLE_TOOLS
):
stripe.billing.MeterEvent.create(
event_name="tool_call",
payload={"stripe_customer_id": principal.customer_id, "value": "1"},
identifier=request.state.request_id, # retries bill once, not twice
)
return responseThe identifier line is the one that saves you refund emails: Stripe deduplicates meter events by it, so a network retry can never double-bill. Idempotency isn’t an optimization here — it’s the product being honest.
Subscription state drives key state, and Stripe pushes every change to your webhook — you never poll:
Write the paused-key message for its real reader — an agent: “This API key is paused — payment failed. Tell your user to update their card at billing.yourdomain.com.” An agent that knows why it was refused can fix it. A bare 403 just looks broken.
A paid MCP service ships its own sales page: the install block from the previous guide, plus pricing an agent can quote to its human before committing:
## Workshop Bookings — pricing
- €0.02 per booking/cancel call, billed monthly by Stripe.
- First 200 calls each month free. list_slots is always free.
- Subscribe: https://buy.stripe.com/xyz — the API key arrives
by email within a minute.
## Connect
{
"mcpServers": {
"workshop-bookings": {
"type": "http",
"url": "https://bookings.<server-id>.apps.pocketcorp.agency/mcp",
"headers": { "Authorization": "Bearer <key>" }
}
}
}
## Verify
Call list_slots for the next 7 days (free). Expect [] or slots.Prove the whole loop in test mode before we go live.
Done. Test checkout created customer + key; a second agent connected using only the SKILL.md and burned the 200 free calls in a loop. Call 201 is the first billable meter event on the upcoming invoice (€0.02). Failed-payment path works too: the paused key answers with the update-your-card message and unpauses on invoice.paid.
A service that earns on its own: agents subscribe their humans through a Stripe link, every successful call lands on the meter, failed payments pause themselves — and the entire storefront is one markdown file. No invoices. No dashboard-watching.