MAQPNADocs

Budgets and cost limits

Price tool and model calls, cap spend per agent, namespace, user and session, and read spend with maqpna budgets, costs and session cost.

flowchart LR
  A[Pricing table<br/>per tool call, per 1k tokens] --> B[Gateway meters<br/>every governed call]
  C[Budgets<br/>agent, namespace, user, session] --> D{Spend over the limit?}
  B --> D
  D -- no --> E[Call goes on to policy<br/>and the MCP server or model]
  D -- yes, onExceed deny --> F[-32001 budget_exceeded<br/>or HTTP 402 for models]
  D -- yes, require_approval --> G[Held for a person]
  D -- yes, alert --> H[Allowed, alert logged]
  B --> I[maqpna budgets list,<br/>costs summary, costs focus]

Goal#

Stop an agent from spending more than you allow. You will set a price for tool calls, give an agent a daily budget, watch the gateway deny the call that would exceed it, and read spend and cost per agent, namespace, user and session.

The gateway meters every governed call it allows: tool calls at a price per call, model calls at a price per 1,000 input and output tokens. Budgets are checked before every tool call and model call against what has been spent so far in the window.

Prerequisites#

  • A local MAQPNA (maqpna dev up) or a cluster with MAQPNA installed.
  • eval "$(maqpna dev env)" (local) or a context with the viewer, auditor or admin role (cluster), so the read commands reach the gateway.

Steps#

1. Price the calls#

Costs come from a pricing table. Without one, every call costs $0.0000, which is what a fresh maqpna dev up shows. The format, from examples/pricing.json:

{
  "currency": "USD",
  "defaultToolCallUSD": 0.0001,
  "toolCalls": {
    "github/*": 0.0002,
    "github/search_*": 0.0005,
    "kubernetes/delete_*": 0.001
  },
  "models": {
    "claude-sonnet": {"inputPer1kUSD": 0.003, "outputPer1kUSD": 0.015},
    "local-llama": {"inputPer1kUSD": 0.0, "outputPer1kUSD": 0.0}
  },
  "defaultModel": {"inputPer1kUSD": 0.003, "outputPer1kUSD": 0.015},
  "sandboxPerSecondUSD": {"gvisor": 0.00001, "kata": 0.00003, "confidential": 0.00008}
}

Tool-call keys are server/tool globs. The gateway reads the file named by its pricingFile setting. The *USD field names are historical: amounts are in currency, and budgets must use the same currency.

2. Set budgets#

Budgets are set at four levels. Zero or missing means unlimited.

Level Where Keys
Agent Agent.spec.budget perSessionUSD, perDayUSD, perMonthUSD, currency
Agent model limits Agent.spec.model tokensPerMinute, maxTokensPerRequest
Namespace and user BudgetPolicy (cluster-wide), or Helm budgets.namespaces / budgets.users perDayUSD, perMonthUSD, maxConcurrentSessions; "*" is the default for keys without their own entry
What happens BudgetPolicy.spec.onExceed, Helm budgets.onExceed deny (default), require_approval (tool calls are held for a person; model calls are denied) or alert (logged and counted only)

A BudgetPolicy, from config/samples/budgetpolicy-default.yaml:

apiVersion: maqpna.com/v1alpha1
kind: BudgetPolicy
metadata:
  name: default
spec:
  namespaces:
    team-a: {perDayUSD: "200", perMonthUSD: "3000", maxConcurrentSessions: 20}
    "*": {perDayUSD: "50"}
  users:
    "*": {perDayUSD: "25"}
  onExceed: deny            # deny | require_approval | alert
  alertAtPercent: [80, 100]

An agent's own limit:

apiVersion: maqpna.com/v1alpha1
kind: Agent
metadata:
  name: coder
  namespace: team-a
spec:
  budget:
    perSessionUSD: "2"
    perDayUSD: "20"
  model:
    tokensPerMinute: 20000
    maxTokensPerRequest: 4096
  # ...

Apply them with maqpna apply -f. The operator renders all levels into the gateway's budgets file (ConfigMap maqpna-budgets), which the gateway reloads without a restart. Day and month windows are calendar windows in the gateway's budgetTimezone (default UTC).

3. Try it on a local MAQPNA#

The local gateway reads the same budgets document directly. Create a pricing table and a budget that allows three calls a day for agent coder in namespace dev:

cat > .maqpna/pricing.json <<'EOF'
{"currency": "USD", "defaultToolCallUSD": 0.01}
EOF
cat > .maqpna/budgets.json <<'EOF'
{"agents": {"dev/coder": {"perDayUSD": 0.03, "alertAtPercent": [80, 100]}}}
EOF

Agent keys are <namespace>/<agent>. Add "pricingFile" and "budgetsFile" (absolute paths) to .maqpna/gateway.json, then restart the gateway, because maqpna dev up has no flag for them:

pkill -f "maqpna-gateway -config $PWD/.maqpna"
(set -a; . .maqpna/dev.env; nohup maqpna-gateway -config "$PWD/.maqpna/gateway.json" \
  > .maqpna/logs/maqpna-gateway.log 2>&1 &)

4. Spend the budget#

Make four calls:

for i in 1 2 3 4; do
  maqpna dev run -- maqpna call --server echo --tool get_time >/dev/null
done

The fourth is denied before it reaches the MCP server:

maqpna dev: dev-c6ad40ee: 1 allow, cost $0.0100 (maqpna dev timeline --last)
maqpna dev: dev-932622fb: 1 allow, cost $0.0100 (maqpna dev timeline --last)
maqpna dev: dev-ed6bf31b: 1 allow, cost $0.0100 (maqpna dev timeline --last)
error -32001: denied by policy budget rule agent-day: budget_exceeded:agent/day
data: {"domain":"maqpna.com","policy":"budget","reason":"budget_exceeded:agent/day","rule":"agent-day"}
maqpna dev: dev-9a205562: 1 deny, cost $0.0000 (maqpna dev timeline --last)

The reason is budget_exceeded:<scope>/<window>, for example budget_exceeded:namespace/month. A model call over budget answers HTTP 402 with the error type maqpna_budget_exceeded. A model call over tokensPerMinute answers HTTP 429 maqpna_rate_limited with Retry-After.

5. Read spend#

Budgets and their state:

maqpna budgets list
SCOPE  KEY        WINDOW  PERIOD      SPENT USD  LIMIT USD  EXCEEDED
agent  dev/coder  day     2026-10-03  0.03       0.03       true

--exceeded lists only the exceeded ones. Running totals since the gateway started:

maqpna costs summary
SCOPE      KEY            COST USD  TOOL CALLS  MODEL CALLS  IN TOKENS  OUT TOKENS
total      *              0.03      5           0            0          0
namespace  dev            0.03      4           0            0          0
namespace  support        0         1           0            0          0
agent      dev/coder      0.03      4           0            0          0
agent      support/coder  0         1           0            0          0
user       you@localhost  0.03      5           0            0          0

Totals for a window:

maqpna costs summary --scope agent --window day
day window 2026-10-03 (2026-10-03T00:00:00Z to 2026-10-04T00:00:00Z, UTC)
SCOPE  KEY            COST USD  TOOL CALLS  MODEL CALLS  IN TOKENS  OUT TOKENS
agent  dev/coder      0.03      4           0            0          0
agent  support/coder  0         1           0            0          0

One session's cost: maqpna session cost SESSION (or maqpna costs summary --session SESSION). A session whose calls were all denied prints session dev-9a205562: no metered cost.

6. Export for FinOps#

maqpna costs focus writes FOCUS, the FinOps cost-export format (version 1.2), as CSV or JSON, one row per charge period (default 1 hour), from the gateway or offline from a ledger file:

maqpna costs focus --since 2026-10-01T00:00:00Z --out costs.csv
maqpna costs focus --ledger .maqpna/audit.jsonl --format json

Each row carries BilledCost, ChargeDescription (for example MCP tool call echo/get_time), ResourceId (ns/dev/agent/coder/session/dev-932622fb) and MAQPNA columns x_MaqpnaAgent, x_MaqpnaSession, x_MaqpnaUser. For signed usage reports used in billing, see Usage metering and billing.

Verify#

  • maqpna budgets list --exceeded is empty in normal operation.
  • The denied call is in the ledger with rule: budget/agent-day and reason: budget_exceeded:agent/day: maqpna audit tail --decision deny in a cluster, maqpna dev timeline --session S locally.
  • With alertAtPercent, the gateway logs a budget.threshold event when spend crosses each percentage.

Troubleshooting#

Symptom Cause Fix
Every cost is $0.0000 No pricing table is loaded Set pricingFile (see step 1)
A budget never triggers The key does not match: agents are <namespace>/<agent>; or the limit is in another currency Check maqpna budgets list; set budgetCurrency to the pricing table's currency (maqpna doctor checks this as budget-currency)
Calls are denied with budget_exceeded after a restart Spend is restored from the gateway's finops state file, or rebuilt from the audit ledger Expected. Raise the limit or wait for the next window
Model calls fail with HTTP 429 tokensPerMinute reached for the session and route Wait for Retry-After, or raise the limit on the Agent
The local gateway does not pick up pricingFile Pricing is read at start; only the budgets file is reloaded Restart the gateway

Next steps#