Skip to main content

Guides

Monetize an MCP Server

Gate your MCP server (or any agent endpoint) behind a per-agent paywall, meter every call, and turn usage into a Stripe invoice — using one API and one SDK.

The loop
Authorize an agent → verify + meter each tool call → close the billing period → generate an invoice. Each step is a single SDK call; the snippets below walk the whole loop end-to-end.

1. Authorize the Agent

Issue a short-lived, scoped credential for the agent. The returned AGT- token is the credential your MCP server checks on each call — it is shown only once, so store it securely.

authorize.py
# Authorize an agent for MCP access — returns a one-time bearer token.
# (Management API key, agents:write scope.)
from licentric import Licentric

client = Licentric(api_key="lk_test_...")
auth = client.mcp.authorize("<agent-id>", scopes=["mcp:invoke"])

# Hand 'auth.token' (an AGT-... credential) to the agent. Store it securely —
# it is shown only once.
print(auth.token)

2. Verify + Meter Each Call (the paywall)

On every protected tool call, your MCP server calls POST /api/v1/mcp/verify with the agent's bearer credential. An invalid or expired credential returns 401; a valid one returns the agent's account, id, and scopes so you can gate access. Pass an optional {meterName, quantity} to record a billable usage event in the same call.

mcp_server.py
# Inside your MCP server: gate each protected tool call behind /mcp/verify.
# The agent calls with its bearer credential; optionally meter the call.
import httpx

resp = httpx.post(
    "https://www.licentric.com/api/v1/mcp/verify",
    headers={"Authorization": f"Bearer {agent_token}"},
    json={"meterName": "api_calls", "quantity": 1},  # optional — records usage
)
if resp.status_code == 401:
    raise PermissionError("invalid or expired agent credential")

data = resp.json()["data"]
# {"accountId": "...", "agentId": "...", "scopes": ["mcp:invoke"]}
# Gate the tool on data["scopes"] as needed.
verify response (200)
// POST /api/v1/mcp/verify — 200 response
{
  "data": {
    "accountId": "acc_...",
    "agentId": "agt_...",
    "scopes": ["mcp:invoke"]
  }
}
Metering records usage — it does not enforce it
Recording a metering event always succeeds and is fire-and-forget: it never blocks the verify response. Token budgets soft-warn and hard-block the api_calls meter at the validation/API layer, but a budget being over limit does not stop the metering event from being recorded — metering is for tracking, enforcement happens separately. If you need to deny a call when a budget is exhausted, check the budget explicitly before serving the tool.

3. Close the Period & Invoice

Usage accrues into a monthly billing period. Closing the period aggregates the metered events into per-meter totals; generating the invoice creates a one-off Stripe (test-mode) invoice — one line item per meter — and returns the hosted invoice URL.

invoice.py
# Close the period (aggregates metered usage) and generate an invoice.
period = client.billing.get_current_period()
closed = client.billing.close_period(period.id)

invoice = client.billing.create_invoice(closed.id)
if invoice.stripe_configured:
    print(invoice.hosted_invoice_url)   # open this to pay (Stripe test-mode)
else:
    # No Stripe customer connected yet — degrades gracefully; you still get
    # the usage totals (the money-math) so you can wire billing later.
    print(invoice.total_usage)
No Stripe yet? It still works
If the account has no connected Stripe customer, create_invoice degrades gracefully: it returns stripeConfigured: false with no invoice, but still includes the computed usage totals so you can see the money-math before wiring billing.

A Note on Timing

Metering events are buffered and flushed to the database by a background job roughly once a minute. If you ingest usage and immediately close the period, wait for that flush (~60s) so the events are aggregated. In local development with the buffer disabled, writes go straight to the database and no wait is needed — see the demo script's DEMO_SKIP_FLUSH_WAIT note.