MAQPNADocs

Cookbook

Short, tested recipes for common developer tasks with MAQPNA, from read-only tools and approval thresholds to spend caps, DLP, revocations and evidence export.

Each recipe is one task: the goal, the commands or code, and what you see when it works. Policy recipes were checked with maqpna validate and maqpna policy test; the runtime recipes were run against a local MAQPNA (maqpna dev up). For the admin-API commands on a laptop, load the local admin token first with eval "$(maqpna dev env)"; in a cluster, pass --gateway "$GW" or use a context.

1. Allow one MCP server read-only#

Goal: the agent may read from github and nothing else.

apiVersion: maqpna.com/v1alpha1
kind: ToolPolicy
metadata: {name: triage-policy, namespace: team-a}
spec:
  agents: ["triage"]
  defaultAction: deny
  rules:
  - name: github-read
    servers: [github]
    tools: ["get_*", "list_*", "search_*"]
    action: allow
maqpna policy test --policy triage-policy.yaml --ns team-a --agent triage --server github --tool get_issue --expect allow
maqpna policy test --policy triage-policy.yaml --ns team-a --agent triage --server github --tool create_issue --expect deny

Result: both exit 0. create_issue is denied with reason default_action, and tools/list no longer shows it to the agent.

2. Require approval above an amount#

Goal: small refunds go through; refunds above 1000 wait for a person.

  rules:
  - name: big-refund-needs-approval
    servers: [payments]
    tools: [create_refund]
    when:
    - {arg: amount, op: gt, value: "1000"}
    action: require_approval
    reason: Refunds above 1000 need a human.
  - name: small-refund
    servers: [payments]
    tools: [create_refund]
    action: allow
maqpna policy test --policy payments.yaml --ns team-a --agent payer --server payments --tool create_refund --args '{"amount": 2500}'

Result: "action": "require_approval", rule big-refund-needs-approval; with {"amount": 20} it is allow, rule small-refund. Other operators: eq, neq, lt, lte, gte, in, nin, glob, regex, exists, absent, cidr, len_gt, len_lt. Two approvers: add approval: {minApprovers: 2}.

3. Keep an agent out of production namespaces#

Goal: deny any call whose namespace argument starts with prod-, whatever the tool.

  rules:
  - name: no-prod-namespaces
    servers: [kubernetes]
    tools: ["*"]
    argMatch:
      namespace: "prod-*"
    action: deny
    reason: Agents may not touch production namespaces.
  - name: k8s-read
    servers: [kubernetes]
    tools: ["get", "list", "describe", "logs"]
    action: allow

Result: get with {"namespace":"prod-eu"} is denied by no-prod-namespaces; with {"namespace":"staging"} it is allowed by k8s-read. If the deny rule came second, the allow rule would match first.

4. Freeze a tool at the weekend#

Goal: no payouts on Saturday or Sunday, Berlin time.

  - name: no-weekend-payouts
    servers: [payments]
    tools: [create_payout]
    schedule: {timezone: Europe/Berlin, windows: ["Sat,Sun"]}
    action: deny
    reason: No payouts at the weekend.
maqpna policy test --policy payments.yaml --ns team-a --agent payer --server payments --tool create_payout --at 2026-10-17T12:00:00Z

Result: on Saturday 17 October the decision is deny with rule no-weekend-payouts; with --at 2026-10-19T12:00:00Z (a Monday) the next rule decides.

5. Rate-limit a tool#

Goal: at most 10 calls a minute to a tool.

  - name: small-refund
    servers: [payments]
    tools: [create_refund]
    action: allow
    maxCallsPerMinute: 10

Result: the eleventh call in a minute gets -32001 with reason rate_limited. Back off and retry in your code (see Handle decisions).

6. Cap an agent's daily spend#

Goal: the agent may spend at most 2 per session and 50 per day.

apiVersion: maqpna.com/v1alpha1
kind: Agent
metadata: {name: triage, namespace: team-a}
spec:
  # ...
  budget:
    perSessionUSD: "2"
    perDayUSD: "50"
  model:
    # ...
    tokensPerMinute: 20000
    maxTokensPerRequest: 4096
maqpna apply -f manifests/agent.yaml -n team-a
maqpna budgets list --gateway "$GW"
maqpna budgets list --gateway "$GW" --exceeded -o json

Result: once spent, tool calls get budget_exceeded:agent/day and model calls get HTTP 402 maqpna_budget_exceeded until the window resets. The amounts are in the gateway's budget currency. Namespace and user limits go in a BudgetPolicy (see Budgets and cost limits).

7. Block secrets in prompts and tool arguments#

Goal: a call that contains an AWS access key or a GitHub token never leaves the gateway.

cat > dlp.json <<'EOF'
{"detectors": ["aws_access_key", "github_token", "email"], "requestAction": "deny", "responseAction": "redact"}
EOF
maqpna dev down && maqpna dev up --dlp-profile dlp.json
maqpna dev run -- maqpna call --server echo --tool echo --arg text="key AKIAIOSFODNN7EXAMPLE"

Result:

error -32001: denied by DLP profile dev: dlp:aws_access_key
data: {"domain":"maqpna.com","policy":"dlp","reason":"dlp:aws_access_key","rule":"dev"}

The same profile covers model calls. In a cluster, profiles are gateway configuration (Helm values), bound per server, namespace or model route (Agent.spec.model.dlpProfile); see DLP profiles.

8. Pin the tools you reviewed#

Goal: a tool whose definition changes later is hidden and denied.

maqpna mcp tools dev/echo
maqpna mcp pin dev/echo --mode enforce --out pins.yaml

Result: pins.yaml holds a toolPins block with one sha256: per tool; paste it into the ToolPolicy. After that, a drifted tool is denied with tool_definition_changed, and maqpna mcp tools dev/echo --drift-only lists it.

9. Kill a runaway session#

Goal: stop one session now.

maqpna kill session fix-4821 -n team-a --reason "INC-7: loop calling create_issue" --terminate --gateway "$GW"

Result: every further call of that session is denied within about a second with reason revoked; --terminate also ends its sandbox. Locally, without --terminate:

revoked (block, namespace dev): breakglass/6f5ce165d1dc82f5
error -32001: revoked: calls of this token are blocked by revocation/breakglass/6f5ce165d1dc82f5 (INC-1: test)

Use agent NAME or user NAME instead of session NAME to stop more. See Kill switch and revocations.

10. Revoke one leaked token#

Goal: a session token was printed in a CI log. Block that token only.

JTI=$(maqpna token inspect "$LEAKED_TOKEN" -o json | jq -r .claims.jti)
maqpna kill --gateway "$GW" --jti "$JTI" --reason "token leaked in CI log" --ttl 2h --yes

Result: revoked (block, all namespaces): breakglass/cb12701383deda31; calls with that token exit with status 3. The --ttl can be short, because session tokens live at most 15 minutes in a cluster.

11. Rotate a token#

Goal: replace credentials without downtime.

  • Session tokens rotate by themselves: the operator mints a new one at 80% of its lifetime into the same Secret, and the SDKs re-read the file on every request. Make sure your code does not cache the token (see Connect to the gateway).
  • Locally, a token lives as long as maqpna dev run --ttl (default 1h); start a new maqpna dev run for a new one.
  • The broker's signing key is rotated by the platform team with maqpna keys rotate identity -n maqpna-system, then --finish after the grace period.

Result: maqpna token inspect on the new token shows a new jti and exp; maqpna events SESSION -n NS shows TokenRefreshed.

12. Lift a revocation#

Goal: the incident is over; let the agent run again.

maqpna revocations list --gateway "$GW"
maqpna revocations delete --gateway "$GW" breakglass/cb12701383deda31 --yes

Result: deleted breakglass/cb12701383deda31. The next call is decided by policy again.

13. Export evidence for one session#

Goal: hand an auditor what a session did, in a form they can verify.

maqpna audit export .maqpna/audit.jsonl --session dev-curl2 --out evidence.json          # local ledger file
maqpna audit export --gateway "$GW" --session fix-4821 --out evidence.json               # cluster
maqpna audit verify .maqpna/audit.jsonl

Result: evidence.json (schema maqpna.io/audit-evidence/v1) holds the session's records, the record count and the chain head; audit verify prints OK: 21 records, hash chain intact. For signed, framework-mapped packs, use maqpna evidence generate (see Evidence packs).

14. Wait for the next approval in a script#

Goal: a script blocks until a call is held, then shows it.

maqpna approvals watch --exit-on-first
maqpna approvals list
maqpna approvals approve apr_31bc56b1ef96a592333824db --approver reviewer@localhost --note "checked"

Result: watch returns as soon as an approval is pending; after approve, the held call runs exactly once. A second use of the same approval is denied with approval_invalid.

15. Test as another user or group#

Goal: check a rule that depends on who the agent acts for.

maqpna dev run --user [email protected] --agent payer --namespace team-a -- python agent.py
maqpna policy test --policy payments.yaml --ns team-a --agent payer --user [email protected] \
  --group treasury --server payments --tool create_refund --args '{"amount": 2500}'

Result: the token's act.sub is user:[email protected], and rules with principals match on it. Approvers can never approve calls made on their own behalf.

Next steps#