Charge for Your MCP Server
Featured Stories Extend • Agent Built

Charge for Your MCP Server

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.

Agent Hermes Agent
Level Advanced
Setup ~1 hour

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.

Hermes Agent · Nous Research

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.

Start Here: One Prompt

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.

prompt — paste to your agent
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.

The Stack

ToolFastAPI + fastapi-mcp
ToolStripe metered billing
ToolBearer keys = accounts
ToolUsage middleware
ToolStripe webhooks
SkillSKILL.md with pricing
01

Price the work, not the browsing

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:

  • list_slots (read) — free, always. This is your storefront window.
  • book_slot / cancel_booking (write) — €0.02 per successful call.
  • First 200 billable calls per month — free. Trying your service costs nothing.
  • Failed calls — never billed. A tool that charges for its own errors gets uninstalled the same day.
02

One meter, one price, one link

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:

setup_billing.py (what your agent runs, test mode first)
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 dashboard
Heads up

Selling 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.

03

Bill where auth already lives

The metering hook goes exactly where the API key is validated — one middleware, three rules: successes only, billable tools only, retries bill once:

what the agent writes (backend/billing.py)
@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 response
Pro tip

The 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.

04

Keys pause themselves

Subscription state drives key state, and Stripe pushes every change to your webhook — you never poll:

  • checkout.session.completed → create the API key, email it to the buyer, link it to the customer
  • invoice.payment_failed → pause the key; the service answers with a clear message instead of silence
  • invoice.paid → unpause; nobody writes to support
  • customer.subscription.deleted → revoke, keep the usage history for the final invoice

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.

05

Your storefront is a SKILL.md

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:

SKILL.md — for the adopting agent
## 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.
You

Prove the whole loop in test mode before we go live.

Agent

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.

What you end up with

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.

Deploy your PocketcorpHermes Agent comes pre-installed. €15/month, or €12/month billed annually.

More stories to build