# A stranger on Reddit suggested a guardrail for my payment MCP servers. It shipped to 30 countries in one day.

> Source: <https://dev.to/junter1989kai/a-stranger-on-reddit-suggested-a-guardrail-for-my-payment-mcp-servers-it-shipped-to-30-countries-26en>
> Published: 2026-07-08 22:04:56+00:00

Last week I posted about a family of MCP servers that let AI agents accept payments — Pix in Brazil, UPI in India, GCash in the Philippines, PromptPay in Thailand, one stateless server per country.

The first substantive reply came from someone who works on agent guardrails. Their point, paraphrased:

Trust boundaries around

money custodyare solved by your design (the server never holds funds). But there's a second boundary you haven't addressed:a well-formed request can still be a request the agent should never have made.Amount limits, allow-lists, human-approval thresholds — checked deterministicallybeforeanything is signed.

They were right, and the interesting part was figuring out how to do this **statelessly** — these servers have no database, no accounts, no config storage. Credentials ride on HTTP headers per request. Where does policy live?

The MCP client config (where a human pastes API keys) is something the **agent cannot touch**. Model output never edits `claude_desktop_config.json`

or a `.mcp.json`

. So policy set there has a property no in-band instruction has: **the model cannot relax its own limits.**

Two headers, checked before any signature is computed:

```
x-agentpay-max-amount: 1000
x-agentpay-approval-above: 100
```

`x-agentpay-max-amount`

`POLICY_BLOCKED`

error that tells the agent to ask the human, not to retry.`x-agentpay-approval-above`

`payment_url: null`

, `approval_required: true`

) for the human to review. There is deliberately A weak local model can't be prompt-injected past this, because the check isn't in the prompt. It's a deterministic `if`

before the crypto.

``` js
// before any PSP call or signature:
const gate = policy.enforce(headers, amountMajor);
if (gate.needsApproval) return policy.draftResult(field, amountMajor, description, gate.threshold);
```

The same ~40-line `policy.js`

dropped into every server, because they all share one shape: validate → policy gate → sign/call → return hosted checkout URL. The rollout:

Each server's e2e suite now asserts both branches with a fake key against the real gateway:

``` php
PASS policy cap -> POLICY_BLOCKED
PASS policy approval -> unsigned draft
```

And the daily fingerprint canary (a watchdog that hits every gateway with fake keys and asserts the error fingerprint hasn't changed) grew to 30 endpoints / 36 canaries.

The family hub with all 30 endpoints: [mcp.wishpool.app](https://mcp.wishpool.app/) — each country page's `llms.txt`

documents the two policy headers. All open source (MIT), all on the official MCP Registry under `app.wishpool/*`

.

If you're building agent guardrails and see a boundary I've missed — that's exactly the kind of comment that turned into this feature. 🙏
