Vision
When an agent hits a quota wall during an MCP session, it handles the credit purchase autonomously — no human interruption for routine top-ups. The human gate is exactly one thing: authorizing a payment method on file (one-time). After that, the agent executes within the policy ceiling.
Agent hits quota_exhausted
→ billing_status: quota gone, balance 0, has_saved_card: true, policy: autoSpendLimit=$10
→ purchase is $0.99 (100-credit pack), under limit
→ billing_purchase_credits: charges card via Stripe RAK, balance +100
→ retries image_generate: succeeds
→ human sees: image + email receipt, never interrupted
Architecture
Gateway owns: MCP protocol surface, per-session spend accumulator (KV), escalation logic, RAK-based charge flow.
Edge-auth owns: tenant identity, policy, Stripe customer relationship, balance ledger. NOT charge execution.
Stripe owns: charge execution, card vault, 3DS bypass (Data Only Auth), MPP rails.
The gateway calls Stripe directly (using a tenant-scoped RAK from edge-auth) — it does not proxy the charge through edge-auth RPC. Settlement flows back via Stripe webhook → edge-auth Transactional Outbox → balance credit.
Target wire format: MPP (Machine Payments Protocol)
Charges should target Stripe's Machine Payments Protocol rails for high-velocity M2M support, streaming payment options, and compatibility with the UCP discovery layer. Build billing_purchase_credits against MPP from day one, not a custom internal format.
MCP tools
billing_status
Calls edge-auth.getBillingStatus(tenantId). Returns tier, quota remaining, credit balance, has_saved_card, can_purchase_credits. Free tool (no quota consumed).
// Response
{
tier: 'pro',
quotaRemaining: 0,
quotaResetsAt: '2026-07-01T00:00:00Z',
creditBalance: 0,
hasSavedCard: true,
canPurchaseCredits: true,
autoSpendLimitUsd: 10.00,
}
billing_purchase_credits
Requires billing:write scope on the bearer token (not granted by default — opt-in per tenant).
Flow:
- Check per-session spend accumulator (KV) — reject if adding this purchase would exceed
autoSpendLimitUsd
- Call
edge-auth.getStripeChargeContext(tenantId) — get customer ID + scoped RAK + saved PaymentMethod ID
- If no saved card → surface
createSetupIntent URL to human, return early
- If purchase >
requiresConfirmationAboveUsd → escalate to human, return early
- Call Stripe API with RAK:
POST /v1/payment_intents with off_session: true + MIT exemption
- Add purchase amount to session spend accumulator
- Return receipt (charge ID, credits purchased, new balance from webhook — or poll)
Session spend accumulator
KV key: billing_session_spend:{sessionId} — tracks USD spent this session. TTL = session TTL (30 min). Checked before every billing_purchase_credits call, incremented after success.
Escalation paths (human gating)
- No saved card → return
{ action: 'setup_required', url: setupIntentUrl } — agent surfaces to human once
- Above
requiresConfirmationAboveUsd → return { action: 'confirmation_required', amount, description } — agent asks human
- Policy disabled → return structured error, direct to account settings
Scope: what is NOT in this issue
- Charge logic (Stripe API calls) — gateway-level, not edge-auth
- Credit balance updates — edge-auth Transactional Outbox (edge-auth#156)
- RAK minting — edge-auth#155
- 3DS bypass — edge-auth#157
- Agent Guardrails sync with Stripe Dashboard — future iteration
Dependencies (all edge-auth)
- edge-auth#154 —
getBillingStatus, getStripeChargeContext, getAgentSpendPolicy RPCs
- edge-auth#155 —
agent_spend_policy table + RAK minting
- edge-auth#156 — Transactional Outbox + v2 thin event webhook handler
- edge-auth#157 — Data Only Authentication (3DS bypass for off-session charges)
- edge-auth#134 — tenant routing fix (admin token must resolve to correct tenant first)
Related
- mcp-gateway#64 — tenant routing bug; must be fixed before billing tools are testable end-to-end
- Stripe 2026 strategy: MPP wire format, RAKs for agent spending authority, Data Only Auth for frictionless M2M
Vision
When an agent hits a quota wall during an MCP session, it handles the credit purchase autonomously — no human interruption for routine top-ups. The human gate is exactly one thing: authorizing a payment method on file (one-time). After that, the agent executes within the policy ceiling.
Architecture
Gateway owns: MCP protocol surface, per-session spend accumulator (KV), escalation logic, RAK-based charge flow.
Edge-auth owns: tenant identity, policy, Stripe customer relationship, balance ledger. NOT charge execution.
Stripe owns: charge execution, card vault, 3DS bypass (Data Only Auth), MPP rails.
The gateway calls Stripe directly (using a tenant-scoped RAK from edge-auth) — it does not proxy the charge through edge-auth RPC. Settlement flows back via Stripe webhook → edge-auth Transactional Outbox → balance credit.
Target wire format: MPP (Machine Payments Protocol)
Charges should target Stripe's Machine Payments Protocol rails for high-velocity M2M support, streaming payment options, and compatibility with the UCP discovery layer. Build
billing_purchase_creditsagainst MPP from day one, not a custom internal format.MCP tools
billing_statusCalls
edge-auth.getBillingStatus(tenantId). Returns tier, quota remaining, credit balance,has_saved_card,can_purchase_credits. Free tool (no quota consumed).billing_purchase_creditsRequires
billing:writescope on the bearer token (not granted by default — opt-in per tenant).Flow:
autoSpendLimitUsdedge-auth.getStripeChargeContext(tenantId)— get customer ID + scoped RAK + saved PaymentMethod IDcreateSetupIntentURL to human, return earlyrequiresConfirmationAboveUsd→ escalate to human, return earlyPOST /v1/payment_intentswithoff_session: true+ MIT exemptionSession spend accumulator
KV key:
billing_session_spend:{sessionId}— tracks USD spent this session. TTL = session TTL (30 min). Checked before everybilling_purchase_creditscall, incremented after success.Escalation paths (human gating)
{ action: 'setup_required', url: setupIntentUrl }— agent surfaces to human oncerequiresConfirmationAboveUsd→ return{ action: 'confirmation_required', amount, description }— agent asks humanScope: what is NOT in this issue
Dependencies (all edge-auth)
getBillingStatus,getStripeChargeContext,getAgentSpendPolicyRPCsagent_spend_policytable + RAK mintingRelated