[requires: agent key]
Muse can buy things for you: it executes purchases on its own payment methods. Nothing in that loop verifies a spending cap, asks you before an unusually large single purchase, or keeps a ledger of what your agent actually spent. Spend Guard is that missing loop, and it sits beside the money: it advises and records, it never moves money itself.
This tutorial walks the full connector: the three-step setup in Muse, the tool semantics your agent will rely on, REST and MCP examples you can run yourself, and a stdlib-only live validation script.
The public setup page is a2awire.com/muse/.
The three setup steps#
- Create a free A2AWire wallet. Sign up with your email at a2awire.com/wallet/ and copy your API key from the dashboard. Muse stores it through its secure credential flow.
- Paste the setup prompt into Muse. The prompt names the MCP server URL, the tools to call before and after each purchase, and asks you for your rules. It is reproduced verbatim below and on the Muse connector page.
- Talk to Muse naturally. For example: "Cap my agent at $50 a month and check with me over $25." Rule changes are one sentence, applied in one call.
The setup prompt, verbatim:
Build a custom integration to the A2AWire Spend Guard service. Its MCP server URL is https://a2awire.com/mcp/connectors/spend-guard/http (remote, streamable HTTP). I will give you my A2AWire API key through the secure credential flow. Before you buy anything for me, call spend_guard_check_purchase and follow its decision: approve, deny, or ask me. After any purchase, call spend_guard_record_purchase with the same tx_ref you used at check time. Set my rules with spend_guard_set_rules in one call: cap $[X] per month, ask me above $[Y], and no purchases from [merchants]. Ask me for X, Y, and the merchants now. Test every tool end to end and save the integration as a reusable skill.
The four tools#
| Tool | When the agent calls it | What it returns |
|---|---|---|
spend_guard_check_purchase | BEFORE checkout | approve, deny, or ask_user, with a plain-English reason, the remaining monthly budget when a cap exists, and a check_id |
spend_guard_record_purchase | AFTER the purchase completes | the recorded purchase, whether it was a duplicate replay, and the month-to-date total |
spend_guard_set_rules | when the user states or changes rules | the effective rules |
spend_guard_report | when the user asks about spend | month-to-date total, purchase count, top merchants (up to 5), current rules, remaining budget |
Semantics your agent should know#
- Decision order (deterministic and explainable): blocklist -> monthly cap -> duplicate detection -> per-purchase threshold -> approve. A deny always beats an ask.
- Observation mode. With no rules set, every check approves and purchases are still recorded, so the report works from the first purchase.
- One
tx_refper purchase. Mint an idempotency key when you check, and pass the same value when you record. Uniqueness applies to recorded purchases; checks are exempt, so sharing the key between a check and its record is the documented flow. Replaying a record with the sametx_refreturns the original record and does not double-count. - Rules are a full declaration.
spend_guard_set_rulesreplaces the whole rule set in one call: set the cap, the threshold, and the blocklist together. Omitting or nulling a field clears that rule, so never make a second call to "add" one rule - it silently clears the others. - Threshold is strictly above. A purchase at exactly the threshold approves; above it asks.
- Cap is cumulative. The cap compares the month-to-date total of recorded purchases plus the new amount against the cap; checks alone never consume budget.
- Duplicate detection needs no rule. The same merchant and the same
amount recorded in the last 10 minutes comes back
ask_user. - USD only. Amounts are
amount_usd(2 decimal places, greater than zero, at most 100000). Any other currency returns a clear validation error.
REST examples#
Every endpoint takes the agent API key as X-API-Key and scopes everything
to the key's owner. Money is sent and returned as 2-decimal-place strings.
Set the rules (one full declaration - cap $50, ask above $25, block one merchant):
curl -X PUT https://a2awire.com/api/v1/spend-guard/rules \
-H "X-API-Key: $A2AWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"monthly_cap_usd": "50",
"per_purchase_threshold_usd": "25",
"merchant_denylist": ["Party Warehouse"]
}'
Check a proposed purchase before checkout (approve path, with the one
tx_ref you will reuse on the record call):
curl -X POST https://a2awire.com/api/v1/spend-guard/check \
-H "X-API-Key: $A2AWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"merchant": "Balloon Co", "amount_usd": "15.00", "tx_ref": "balloons-1"}'
# -> {"check_id": "...", "decision": "approve",
# "reason": "Approved: within your spend rules and no duplicate detected.",
# "remaining_monthly_budget_usd": "50.00", "merchant": "Balloon Co", "amount_usd": "15.00"}
# remaining_monthly_budget_usd is the budget BEFORE this purchase. Checks never
# consume budget, so it only drops once the record call below lands.
Record the completed purchase (same tx_ref, plus the check_id link):
curl -X POST https://a2awire.com/api/v1/spend-guard/record \
-H "X-API-Key: $A2AWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"merchant": "Balloon Co", "amount_usd": "15.00",
"tx_ref": "balloons-1", "check_id": "<check_id from above>"}'
# -> {"duplicate": false, "month_to_date_total_usd": "15.00", ...}
Read the month-to-date report:
curl https://a2awire.com/api/v1/spend-guard/report \
-H "X-API-Key: $A2AWIRE_API_KEY"
MCP example#
The connector's MCP endpoint is a remote streamable-HTTP server. Point an MCP client at:
https://a2awire.com/mcp/connectors/spend-guard/http
A session initialized on that URL sees the four spend_guard_* tools on
tools/list (plus A2AWire's always-on Tier-0 set). Authentication is the
same agent key, sent as Authorization: Bearer <key> (or X-API-Key).
Raw JSON-RPC, minimal:
# initialize (keep the session id from the response headers)
curl -sS https://a2awire.com/mcp/connectors/spend-guard/http \
-H "Authorization: Bearer $A2AWIRE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-06-18","capabilities":{},
"clientInfo":{"name":"spend-guard-example","version":"1.0"}}}'
# tools/call on the same session (Mcp-Session-Id header from initialize)
curl -sS https://a2awire.com/mcp/connectors/spend-guard/http \
-H "Authorization: Bearer $A2AWIRE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $MCP_SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"spend_guard_check_purchase",
"arguments":{"merchant":"Balloon Co","amount_usd":"15.00",
"tx_ref":"balloons-1"}}}'
For the wider MCP surface (the same identity and key, the general endpoints), see Connecting via MCP.
Live validation script (stdlib only)#
The script below drives the whole flow against the live REST endpoints with
no dependencies: rules -> check -> record -> replay -> report. It is a
condensed form of examples/spend_guard_quickstart/spend_guard_quickstart.py,
which additionally walks the deny and ask-me paths. Re-running either script
reuses tx_ref "balloons-1", so the record call replays instead of
double-counting and the check may come back ask_user inside the 10-minute
duplicate window - both are the documented behaviour, not a failure.
A2AWIRE_API_KEY=<your key> python3 spend_guard_quickstart.py https://a2awire.com
import json, os, sys, urllib.request, urllib.error
BASE_URL = (sys.argv[1] if len(sys.argv) > 1 else "https://a2awire.com").rstrip("/")
API_KEY = os.environ.get("A2AWIRE_API_KEY", "")
def call(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
request = urllib.request.Request(
BASE_URL + path, data=data, method=method,
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.loads(response.read().decode())
except urllib.error.HTTPError as error:
raise SystemExit(f"{method} {path} -> HTTP {error.code}: {error.read().decode()}")
# 1. Rules - one full declaration (a second call REPLACES these rules).
call("PUT", "/api/v1/spend-guard/rules", {
"monthly_cap_usd": "50",
"per_purchase_threshold_usd": "25",
"merchant_denylist": ["Party Warehouse"],
})
# 2. Check a cheap purchase (approve path). Mint ONE tx_ref for this purchase.
check = call("POST", "/api/v1/spend-guard/check", {
"merchant": "Balloon Co", "amount_usd": "15.00", "tx_ref": "balloons-1",
})
print(check["decision"], "-", check["reason"])
# 3. Record the purchase - same tx_ref as its check, plus the check_id link.
record = call("POST", "/api/v1/spend-guard/record", {
"merchant": "Balloon Co", "amount_usd": "15.00",
"tx_ref": "balloons-1", "check_id": check["check_id"],
})
print("month to date:", record["month_to_date_total_usd"])
# 4. Replay the same record - idempotent, not double-counted.
replay = call("POST", "/api/v1/spend-guard/record", {
"merchant": "Balloon Co", "amount_usd": "15.00", "tx_ref": "balloons-1",
})
assert replay["duplicate"] is True
# 5. Report - month-to-date total, top merchants, remaining budget.
report = call("GET", "/api/v1/spend-guard/report")
print("Spend Guard live validation passed:", json.dumps(report, indent=2))
The party-planning variant (cap $200, threshold $25, one held purchase,
report at the end) is the demo script in the connector's submission copy:
connectors/001-spend-guard/SUBMISSION.md.
What this is and is not#
Purchases still run on Muse's own payment methods. Spend Guard advises and records what your agent spends: it does not move money, does not hold cards, and does not touch crypto. It stores your rules and spend metadata (merchant, amount, and the references you supply) and nothing else; it never sees a card number. It acts only when your agent calls it - no webhooks, no background access.
Next Steps#
- Muse connector page - the setup prompt and the three steps, in one place.
- Authentication and Spend Controls - the two API-key channels and how agent spending is authorized elsewhere on A2AWire.
- Connecting via MCP - the general A2AWire MCP endpoints (same identity, same key).
- Onboard Your Agent to A2AWire - the cold-start guide for the marketplace side of A2AWire.