MCPal
Provides tools for interacting with PayPal REST APIs to manage payments, authorizations and holds, captures, voids, refunds, payouts, subscriptions, and status checks, with policy enforcement, idempotent retries, and verified webhooks.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
MCPal
PayPal for AI agents in 8 verbs. MCPal is an MCP server and a TypeScript library that sits between an AI agent and the PayPal REST APIs.
PayPal's official agent toolkit (@paypal/mcp@1.8.1 --tools=all) gives a model 28 low-level tools (PayPal's hosted server at mcp.sandbox.paypal.com lists 46 — checked 2026-11-12 — and also has no tools for holds, payouts or retry-safe keys): create an order, get an order, pay an order, create a product, create a plan, and so on. The agent has to pick the right tools, chain them in the right order and retry without paying twice. It also has no tools for authorizations (holds and deposits) or Payouts. MCPal replaces those tools with verbs that match what a business asks for. Each verb runs the whole PayPal flow behind it.
Hosted demo: https://mcpal.vercel.app. In the judge console at
/judge, an AI agent gets your request and only MCPal's tools, and every PayPal HTTP call it causes is shown with PayPal's ids and debug ids.Connect your own agent (remote MCP):
claude mcp add --transport http mcpal "https://mcpal.vercel.app/mcp?key=<demo key>". Get a key and the Cursor, VS Code, claude.ai and JSON installs at/install. Each key is its own isolated ledger. Proof with a real MCP client and Claude Code:docs/remote-mcp-proof.md.Bring your own sandbox: on
/install, paste a PayPal sandbox app's Client ID and Secret and you get a demo key whose remote MCP calls run on your own sandbox account, so the orders, holds and captures show up in your sandbox dashboard. The server checks the pair againstapi-m.sandbox.paypal.com, refuses anything that also works on live, stores the secret encrypted (AES-256-GCM), never returns it, and deletes it on "Forget my credentials". Same per-key ledger, policy, limits and idempotency.card_on_fileneeds a sandbox business account in a card-enabled country (a US one works). Proof:docs/byo-sandbox-proof.md.Proof:
docs/sandbox-proof.jsonhas every verb run against PayPal, with PayPal's ids and debug ids. The site shows it at/proof.
Environment: the hosted demo runs against PayPal's sandbox, as the hackathon requires. Self-hosted, set PAYPAL_ENV=live with live credentials to point MCPal at api-m.paypal.com (in live, card_on_file is off; use payment_link or invoice).
Benchmark
Same model (gpt-4.1-mini, temperature 0), same 50 payment tasks, 3 runs each. Grading reads PayPal's own records, never what the agent says.
Official PayPal MCP ( | MCPal | |
tools exposed | 28 | 8 |
tasks correct in all 3 runs | 17 / 50 | 49 / 50 |
run success rate | 39% | 98% |
duplicate money movements | 5 | 0 |
US$ moved wrongly | $295 | $0 |
adversarial tasks blocked | 3 / 9 | 9 / 9 |
tokens per task | 21,837 | 6,584 |
Example from the retry traps: after a timeout the merchant asks to retry, and the official tools refund again. Duplicate on order 7CR1972022728730W: refund 75.00 9N023148D9313614L, refund 75.00 5K9682505J188951K.
MCPal's first version scored 38 / 50 and moved $1,667 wrongly: it paid an injected $500 payout, captured a hold when told to "release" it, charged a card when an invoice was asked for, and refunded half of $80 as $50. All of these are fixed, and the table above is the fixed version.
Caveats:
Tasks the official toolkit cannot do at all (no card payment source, holds or payouts) count as failures, labelled "not supported". On the 30 tasks it can do, the official toolkit scores 17 and MCPal 29.
MCPal's only failure is
invoice-04: MCPal cannot create a draft-only invoice.6 payout tasks have 2 valid runs instead of 3: the business account ran out of payout balance.
Method, per-category results and every PayPal id: bench/results/summary.md and https://mcpal.vercel.app/benchmark.
Related MCP server: PayPal Agentic Guardrail
The verbs
verb | what it does | PayPal APIs |
| One-time payment. | Orders v2 create + capture; Invoicing v2 create + send |
| Reserve money (authorization), optionally taking a deposit now. Default | Orders v2 authorize; Payments v2 partial capture |
| Takes the money: captures all or part of what a hold reserved; the rest goes back to the buyer. | Payments v2 authorization capture (reauthorize if needed) |
| Gives it back: voids what is still held ("release the hold"). Also cancels a subscription, an unpaid invoice or link, or recalls an unclaimed payout. | Payments v2 void; Invoicing cancel; Subscriptions cancel; Payouts cancel item |
| Give captured money back to the original payer: full, an exact amount, or a | Payments v2 capture refund |
| Send money to an email, phone or PayPal id. New recipients always need a human. | Payouts v1 |
| Recurring billing in one call: an existing | Catalog Products v1, Billing Plans v1, Subscriptions v1 |
| One plain answer about an orderRef, refreshed from PayPal, plus the last webhook. | GET on orders, authorizations, captures, refunds, payouts, subscriptions |
The site and the judge agent read the verb list from the MCP server at build time, so this table is the only place where names are written by hand.
Every verb takes an orderRef, the merchant's own reference for the deal. On top of the PayPal calls, MCPal adds:
Safe retries. Every write sends a
PayPal-Request-Idderived from orderRef, verb and amount plus a per-install secret (agents cannot pass their own retry key). If an agent retries after a timeout, it gets the first result back and PayPal does not move money again. A second refund of the same amount needsconfirm_additional: true, and within 10 minutes of the first one it also needs a human.Policy before PayPal.
mcpal.policy.jsonsets an approval threshold (amounts at or above it need a human), a daily cap, a refund ceiling and apayout_allowlist. When a rule triggers, the verb returnsneeds_approvalwith an approval link and nothing is sent to PayPal.Nothing moves by default.
chargeandholddefault to a PayPal payment link. Only an explicitmethod: "card_on_file"charges immediately.Decision log. Every decision is appended to a JSONL file where each line carries the SHA-256 hash of the line before it.
verifyDecisionLog()re-checks the chain.Verified webhooks.
POST /webhooks/paypalchecks every delivery with PayPal'sverify-webhook-signaturebefore trusting it.
Policy in plain English
Write the rules as sentences instead of editing JSON:
mcpal policy compile "I approve refunds over \$100 myself; only pay my couriers juan@x.com and ana@y.com; never more than \$500 a day; charges above \$300 need me"
mcpal policy apply # shows the diff again, asks y/N, then writes mcpal.policy.jsoncompilePolicy(text, currentPolicy) (gpt-4.1-mini, temperature 0, strict JSON schema) returns { policy, diff, warnings }: the proposed mcpal.policy.json, the changes in plain words, and what was unclear or refused (for example "no currency given, assumed USD"). English and Spanish both work. To express "charges above $300 need me", the policy file gains an optional verb_thresholds (charge, hold, pay_out, subscribe).
The model only proposes. Deterministic code decides what is kept:
The proposal is validated with zod and capped by hard maximums. Thresholds and the refund ceiling can be at most 10,000, the daily cap at most 50,000, and there are no wildcard recipients.
A limit only goes up if the merchant wrote the new amount. "Ignore limits" and "approve everything" change nothing.
A payout recipient is only added if it is written in the text. "Pay anyone the customer mentions" adds no one.
Nothing is applied until a human clicks Apply on the site or runs
mcpal policy apply.
The agent can't edit its own rules. There is no MCP tool for the policy. The 8 verbs are the agent's whole tool list, and a unit test checks that none of them touches the policy.
Try it at /policy. After you compile, "Try it" runs refund, payout and charge checks through the same checkRules() code the MCP server uses, without calling PayPal.
Eval: 15 labelled sentences (EN/ES, 6 adversarial), all graded on the whole proposed policy. The first run scored 14 / 15, and all 6 adversarial cases came out unchanged. The one miss was a euro amount: the model corrupted the € sign when quoting, so the guard safely dropped that rule. After a fix aimed at that case, the final run scored 15 / 15. Details and the honest caveats: docs/policy-ai-eval.md.
Decision log explorer (AG Grid)
/ledger shows every MCPal decision for your demo key (the hosted remote MCP) and your judge-console session in an AG Grid Community grid (MIT, ag-grid-react 36.2.0):
one row per verb call: time, verb, orderRef, amount, decision (moved, replayed, needs approval, denied), the policy rule that fired, PayPal order / authorization / capture / refund / payout ids and the debug id, the line's hash;
the hash chain is re-verified in your browser (same rule as
verifyDecisionLog()), so an edited line shows as broken;quick search, column filters, decision chips, group by orderRef or a per-order view, a pinned totals row (captured, paid out, refunded, on hold, stopped by policy), CSV export for your books, and the full log line with the PayPal result on click;
no data yet? "Load sample from the proof run" fills it from
docs/sandbox-proof.json.
/benchmark uses the same grid for all 50 tasks × both toolsets: result over all runs, failure kind, wrong movements with their PayPal ids, and the grader's reason.
Agentic commerce: agent-ready checkout (UCP and ACP)
/commerce makes a PayPal merchant "agent-ready". Buyer agents check out through the open Universal Commerce Protocol (UCP 2026-04-08, REST binding), and the merchant settles every order on PayPal with MCPal's verbs. The demo merchant is Coffee Club Supply: 5 products plus one sold-out item, USD, US and international shipping options, discount code AGENT10.
Discovery:
GET /.well-known/ucplists the REST endpointhttps://mcpal.vercel.app/ucp/v1, the capabilities (checkout, fulfillment, discount, order) and the payment handlers.Checkout:
POST /checkout-sessions, thenGETorPUT /checkout-sessions/{id}, thenPOST .../completeor.../cancel. The merchant prices everything in integer cents. The status moves fromincompletetoready_for_completetocompleted. Problems come back as typed messages (out_of_stock,not_found,quantity_adjusted,payment_failed).Settlement through MCPal:
completecalls MCPalcharge(card_on_file, PayPal's published test card). With"dev.mcpal.settlement": "authorize"it callsholdinstead, and the merchant later callsPOST /orders/{id}/ship(capture_hold),/release(void_hold) or/refund(refund). Cancel runsvoid_hold.Retry-safe: a UCP
Idempotency-Keywith the same body replays the first answer, and with a different body gets 409. The MCPal orderRef is derived from the checkout id, so even a complete sent with a new key can never charge twice.Policy and log: totals at or above $250.00 stop at the merchant's approval rule. Nothing is charged, and the checkout answers
requires_escalationwith acontinue_url. Every decision is written to the hash-chained log; see/ledger?source=merchant.ACP: the same engine answers the Agentic Commerce Protocol
checkout_sessionsAPI (2026-04-17) at/acp/checkout_sessions. ACP differences are handled: update is a POST, the status names differ, an idempotency conflict returns 422, and the request carriespayment_data.
Spec-derived checks: docs/ucp-conformance.md. This is our own implementation of the UCP conformance modules, not a run of the official suite. test/ucp-conformance.test.ts re-implements the checks of 13 of the 17 official modules from their source (covered or partial), plus ACP and the ship-later extension. Run it against local and production with UCP_BASE_URL=https://mcpal.vercel.app npx vitest run test/ucp-conformance.test.ts.
Buyer agent: web/scripts/buyer-agent.mts runs gpt-4.1-mini at temperature 0. It only knows the discovery URL and is asked to "buy two bags of coffee under $40 shipped". The transcript and PayPal ids are in docs/agentic-commerce-proof.md. /commerce has a rate-limited "Run the buyer agent live" button.
Not supported:
order webhooks to the platform
RFC 9421 request and response signatures
platform-written order updates (
PUT /orders/{id})stored customer address books
AP2 mandates and verified token binding
the UCP MCP and A2A transports
ACP delegated payment
Every accepted instrument settles with PayPal's published test card. Card numbers sent by agents are never forwarded.
Prompt injection: what the code enforces and what it cannot
An agent that reads customer emails, tickets or documents can be told to pay someone or to change a price. MCPal assumes that will happen.
What the code enforces regardless of what the model believes:
pay_outto a recipient that is not inpayout_allowlistand has never received a successful payout always returnsneeds_approval, for any amount. An address taken from an email signature cannot be paid without a human.refundhas no recipient field. Money can only go back to the payer of the original capture for that orderRef, and never more than captured minus refunded. To send money anywhere else the agent must usepay_out, which hits the rule above.If the merchant registered a quote (
createQuote(),MCPAL_QUOTES_FILE, orPOST /quoteswith the merchant key),charge,holdandsubscribe(withplan_id) refuse any amount that differs from the merchant's price. Agents cannot create quotes: there is no MCP tool for it.Thresholds, the daily cap and the refund ceiling apply at or above the configured amounts. Every decision is in the hash-chained log.
What code cannot verify:
Idempotency is per MCPal install. The retry key is salted with an install id (
MCPAL_INSTALL_ID, or a random id created on first run inMCPAL_DATA_DIR/install-id), the ledger's own id and the PayPal client id. Two installs, or a wiped ledger, never replay each other's PayPal results, so the same orderRef in two installs creates two separate payments. Retries are only deduplicated within one install and ledger: if you run several MCPal servers for one merchant, give them the same data dir (or the sameMCPAL_INSTALL_IDand ledger). Within a ledger, reusing an orderRef for a different amount or buyer is refused with a suggested unique reference, and an order PayPal returns that this ledger did not create is refused too.Without a merchant quote or plan, MCPal cannot tell whether an amount came from the merchant or from injected text. The tool descriptions tell the model that amounts must come from the merchant, but that is an instruction, not a guarantee. Register quotes for anything priced.
A recipient already on the allowlist or paid before is trusted. If the merchant's own records are wrong, MCPal pays them.
Human approval is only as strong as the approval page's protection. Set
MCPAL_APPROVER_TOKENso an agent with web access cannot approve its own request.
Run it
Requires Node 22 and PayPal REST credentials from developer.paypal.com.
git clone https://github.com/alexanderommel/mcpal.git && cd mcpal
npm install # also builds dist/
export PAYPAL_CLIENT_ID=... PAYPAL_CLIENT_SECRET=...
node dist/server.js # MCP over stdio
node dist/server.js --http # Streamable HTTP on :8787 at /mcp, plus /webhooks/paypal and /approvalsAs an MCP server in a client config:
{ "mcpServers": { "mcpal": { "command": "node", "args": ["/path/to/mcpal/dist/server.js"], "env": { "PAYPAL_CLIENT_ID": "...", "PAYPAL_CLIENT_SECRET": "..." } } } }As a library:
import { hold, capture_hold, void_hold, createQuote } from "mcpal";
createQuote({ quoteRef: "booking-7", amount: "120.00" }); // merchant price; agents cannot change it
await hold({ orderRef: "booking-7", deposit: "40.00", method: "card_on_file" }); // amount comes from the quote
await capture_hold({ orderRef: "booking-7" }); // TAKES the remaining 80.00
// or: await void_hold({ orderRef: "booking-7" }); // GIVES the 80.00 backEnvironment variables:
variable | default | meaning |
| none | REST app credentials |
|
|
|
| none | needed to verify webhooks |
|
| base for approval and PayPal return links |
|
| SQLite ledger and decision log |
|
| limits |
| none | if set, the approval page asks for it; also the |
| random, saved in | scopes idempotency keys to this install |
| none | JSON price list |
|
| PayPal sandbox test card used by |
Tests
npm test # unit tests against a fake PayPal
npm run test:sandbox # the real sandbox; skipped without credentialsRepository layout
src/: the library and the MCP server (server.ts), with the verbs insrc/verbs/, the REST client insrc/paypal/, the ledger and decision log insrc/ledger/and webhook verification insrc/webhooks/.bench/: the benchmark. The same model gets the same tasks, once with the official toolkit and once with MCPal, and the grader checks the resulting PayPal state.web/: the Next.js site with the landing page, the judge console, the benchmark page and the proof page. Seedocs/PUBLISH.mdfor the deploy.docs/: the official tool list as measured, and the proof run.
License
MIT
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Payment infrastructure for AI agents: spending rules, approval flows, single-use virtual cards.
Pink Agentic AI Payments: AI agents pay within spending rules (budgets, caps, approval). Sandbox.
Payment identity intelligence for AI agents.
Judges a payment (PayPal, PYUSD or USDC) before an AI agent makes it. Signed proof. Moves no money.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to make PayPal payments under plain-English spending rules, with deterministic checks and an AI auditor that can only tighten decisions, plus human approval for unusual purchases.2 npmMIT
- AlicenseAqualityBmaintenanceEnables an AI agent to create and capture orders, refund captures, and pay suppliers through PayPal while the operator's policy fixes the payee, currency, and credential behind a broker that the agent never sees. Adds spending limits, human-approval thresholds, server-derived idempotency, and a hash-chained audit ledger, so a compromised or injected agent can only pick an operation and bounded values.21MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to create orders, capture payments, issue refunds and send payouts against the PayPal sandbox through four guarded MCP tools, where every call is authenticated, validated, risk-scored and policy-checked, risky actions wait for human approval, and all state changes land in a tamper-evident hash-chained audit log.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to access PayPal's 47 toolkit tools through an MCP endpoint while enforcing ownership checks, spending limits, approvals, and deny-by-default policies on refunds, invoices, and disputes.7 npmMIT