call
attribute
UsageEvent
billable events → Stripe sink
REST / MCP call
+ bearer token
@agentback/metering · the “metered?” answer (also the rung-1
audit log)
principal
{user} / {client}
authentication-oauth2
= billable identity
MeteredRestServer
.dispatch (REST seam)
MeteredMCPServer
.dispatchTool (MCP seam)
Meter
observe()
status + latency + units
CompositeUsageSink
fan-out to N sinks
InMemory
dev/test
JSONL
audit log
Redis
shared
QuotaService
per-principal · metered? enforce
metering / seam
sink / quota
payment rail (gate)
identity
external
we don’t settle
@agentback/payments · the “paid?” answer · we orchestrate, we
don’t settle
paymentMiddleware
REST · PaymentRail.authorize
x402
HTTP 402
USDC · L2
MPP
pre-auth sessions
X-MPP-SESSION
PaidMCPServer
MCP · dispatchTool gate
tool error
_meta: challenge (no 402)
agent pays → retries
StripeMeterSink
a UsageSink — bills, does not gate
status:'ok' → meter event
Stripe Billing
meter events → invoice (fiat)
facilitator / processor (external)
verifies + settles — injected, not us
no/invalid payment → 402 + requirements
• MeteredRestServer / MeteredMCPServer wrap dispatch
• Meter.observe() times the call → a
UsageEvent
• Sinks: InMemory · JSONL (audit) · Redis · Composite fan-out
• QuotaService enforces per-principal limits
• Attributed to the auth principal — the billable identity
• x402 — paymentMiddleware → HTTP 402, USDC/L2
• MPP — pre-auth sessions (X-MPP-SESSION)
• PaidMCPServer — no 402 over JSON-RPC, so the challenge
rides back as a tool error in _meta; the agent pays
and retries
• Settlement is delegated to an external facilitator/processor
• StripeMeterSink is a UsageSink, not a rail
• Billable events → Stripe metered billing → invoice (fiat)
• Compose with the audit sink via CompositeUsageSink
• “Same units, different rail” — record once, bill from it