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.
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 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.
# 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.// POST /api/v1/mcp/verify — 200 response
{
"data": {
"accountId": "acc_...",
"agentId": "agt_...",
"scopes": ["mcp:invoke"]
}
}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.
# 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)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.