Agentic APIs & MCP Server Monetization
Monetizing the Agentic Economy. Bill for tool calls, reasoning tokens, and long-running agent sessions in real time.
Monetizing the Agentic Economy#
Traditional billing counts API requests. The agentic era demands more. A single agent session may invoke dozens of tools, consume thousands of reasoning tokens, and run for minutes or hours. Aforo is the first monetization platform built for this reality.
Tool-Call Metering#
Instead of billing per HTTP request, Aforo meters at the tool execution level. When an AI agent calls tools/call via JSON-RPC, the Aforo gateway plugin (or SDK decorator) extracts the tool name, execution status, and duration:
This enables per-tool pricing. Charge $0.001 for a simple lookup, $0.05 for a database write, and $0.10 for an AI-powered analysis — all within the same MCP server subscription.
Reasoning-Token Aggregation#
For AI-powered tools that call LLMs internally, Aforo captures the token breakdown and maps it directly to your COGS:
provider, model, input_tokens and output_tokens from metadata, prices them with that model's provider rates, and records the result as the event's cost in COGS & Margins. quantity is what the customer is billed for.Session-Based Billing#
Agent sessions are long-running. A single session may span minutes, invoke 50+ tool calls, and consume thousands of tokens. Aforo tracks the full session lifecycle:
TIMED_OUT, COMPLETED, and FAILED are terminal. Each tool call is metered as it happens; the session record consolidates total tool calls, tokens, duration, and provider cost for full COGS attribution.
MCP Server Integration#
Here is how an MCP server integrates with Aforo. The entitlement check runs before every tool execution:
SDK Integration (Decorator Pattern)#
For MCP servers built with our SDKs, use the wrapToolHandler decorator to automatically meter every tool call:
Node.js
Python
Who is billed
From 2.0.0 the decorators need a customer source and fail when you wrap a handler without one (Node throws, Python raises ValueError). A call is never billed to its agent id or to a placeholder such as unknown.
customerResolver/customer_resolver— your server authenticates callers (OAuth token, API key, session) and returns the Aforo customer id.customerId(Node) — one fixed customer, for a server that serves one customer.trustCustomerHeader: true/trust_customer_header=True— billparams._meta.customer_idas the client sends it. Any MCP client can put any id there, so use it only when every client is yours or your transport sets the value itself.
When the resolver returns nothing for a call, the tool still runs; its event is dropped as invalid and counted. agentId is read from _meta.agent_id (Python: the agent_id kwarg) and identifies the MCP client, not the payer.
Proxy sidecar (no code change)
When you can't change the MCP server, run @aforoai/mcp-proxy in front of it over stdio, SSE or Streamable HTTP. From 2.0.0 it refuses to start without a customer source (--customer-id, a bearer-token-to-customer file, or the explicit --trust-client-customer-id):
- The HTTP listeners need
--auth-token(at least 16 characters),--auth-tokens-file, or--allow-unauthenticatedon a loopback--host. The token is never forwarded to the MCP server. - Requests with an unexpected
Hostget 421 and with an unlistedOriginget 403 (add them with--allowed-hosts/--allowed-origins); bodies over 4 MiB get 413. - The stdio child inherits only
PATH,HOME, locale, temp and proxy variables. Name anything else it needs with--pass-env;AFORO_*variables are never passed. - With
--quota-enforcement, each check has a 1 s deadline and fails open by default;--quota-fail-mode closedrefuses calls the check cannot answer (JSON-RPC-32000,QUOTA_CHECK_UNAVAILABLE).
What each tool call is billed as
The decorators and the @aforoai/mcp-proxy sidecar set executionStatus from what the tool did. Only Outcome-Based rate plans read it; every other model bills the call at its normal rate.
| What happened | executionStatus |
|---|---|
| Handler returned a result | SUCCESS |
| Handler returned a result with isError: true | ERROR |
| Handler threw a timeout (TimeoutError, or an MCP error with code -32001) | TIMEOUT |
| Handler threw anything else | ERROR |
| Python only: task cancelled or interrupted (asyncio.CancelledError, KeyboardInterrupt) | CANCELLED |
| Proxy only: still waiting when the proxy shuts down (SIGTERM/SIGINT) | CANCELLED |
| Proxy only: no response within AFORO_RESPONSE_TIMEOUT_MS (default 300000 — 5 minutes) | TIMEOUT |
statusResolver in Node and the proxy, status_resolver in Python). It must be synchronous and return one of the 11 statuses (SUCCESS, PARTIAL, TIMEOUT, ERROR, VALIDATION_FAILED, FAILED, FAILURE, CANCELLED, PENDING, BLOCKED, HITL_REQUIRED). A Promise or any other value is logged and the derived status is sent instead — never an unknown string, which the ingestor would reject, losing that tool call's usage. Raise AFORO_RESPONSE_TIMEOUT_MS for tools that legitimately run longer than 5 minutes: a response that arrives after the timeout is not metered again.Billing Hierarchy#
MCP Server billing follows the same hierarchy as all Aforo product types:
What this guide does NOT cover#
This page walks the on-the-wire side of MCP metering — how a tools/call becomes a billable event, how sessions are tracked, and how the gateway plugins + SDK decorators emit that data. It does not cover:
- Creating an MCP Server product in your catalog, wiring the tool registry, and choosing transport/integration mode — see MCP Server (Catalog).
- Pricing model choices per tool (per-tool multipliers via
dimensionPricing) — same page. - Which billable units to attach to an MCP Server product — see Billable Units.
- End-customer registration of agents and rotation of client credentials — see Customers.
- Operator-side MCP analytics dashboards. Those live inside the Aforo product console, not on this docs site.