The Agent Can Ask, the Human Signs: What Mercury's MCP Write Tools Get Right About Money Movement


On October 15, 2026, Mercury’s release notes added an entry titled “Take action on your Mercury account from ChatGPT and Claude”: “nothing moves until you approve it.” Until then Mercury’s MCP server could only read. Now it has five write tools. Two of them can start money movement and three of them change bookkeeping data.

The interesting part is not that an AI tool can touch a bank account. It is how the line is drawn. The agent can propose a payment, but only to a payee a human already set up. A human then approves it in a different app, and the request expires if nobody does. Low-risk writes such as categorizing a transaction skip the approval and apply immediately. If you build AP automation, an order-to-cash back office, or any agent that touches money, this is a pattern worth copying on purpose.

As in When the OMS Speaks MCP and Shop MCP Without Webhooks, I keep what Mercury documents apart from my inference, and I label the inferences. My sources are the support article, the developer docs pages Supported tools, Connecting Mercury MCP, What is Mercury MCP? and Security best practices, and the release note. I read all of them on October 16, 2026.

What Mercury actually shipped

The docs list exactly five write tools, each behind its own OAuth scope:

ToolScopeDocumented behavior
requestSendMoneytransactions:requestCreates a payment request to an existing recipient. Needs approval in Mercury before money is sent.
requestTransferMoneytransfers:requestCreates a transfer request between your own Mercury accounts. Needs approval in Mercury.
createCategorycategories:createCreates a custom expense category immediately.
editCategorycategories:editEdits a custom expense category immediately.
updateTransactiontransactions:updateUpdates or clears a transaction’s note or category immediately.

The documented constraints:

  • Saved recipients only. The support article’s answer to “Can my AI tool add a new recipient?” is “No. You can only request payments to recipients you’ve already added in Mercury.” The tools page says the write tools “do not create recipients, delete categories, issue or manage cards, or create invoices.”
  • Approval happens in Mercury, not in chat. The user reviews the request in the chat and sends it to Mercury. Then they open the verification notification in the Mercury app on web or mobile, or find the request under “Needs approval” in Tasks or Payments, and approve or decline it there. The docs repeat it: “A confirmation in chat is not approval in Mercury, and ‘request created’ does not mean ‘payment sent.’”
  • Expiry. “Approval requests expire after approximately 5 minutes. If yours expires, start a new request through your AI tool.” That number appears in the support article. I did not find it in the developer docs pages.
  • Org policy still applies, and then some. “Your organization’s approval policies still apply. If those policies would otherwise allow the action without approval, the requester must approve it in Mercury.” So an agent request is never auto-approved, even when a human doing the same thing by hand would not need an approval.
  • Scopes are opt-in. Existing read-only connections “do not gain write access automatically”, so the user has to reauthorize and grant write permissions on the consent screen. “A write scope does not grant permission to approve a payment.”
  • One organization per connection. You can’t switch between organizations inside a connection.
  • Checking status. Read tools include listSendMoneyApprovalRequests, listTransferMoneyApprovalRequests and getTransferMoneyApprovalRequest. The docs advise: “Check for an existing request before creating another one after an uncertain result.”

The support article lists official integrations with ChatGPT, Claude, Cursor, Perplexity, Gemini Enterprise, Microsoft 365 Copilot, Grok Bot, Notion and Replit, plus manual setup for other MCP clients. The connection docs say Mercury “does not offer a non-hosted MCP at this time.”

A small wording oddity: the support article’s step list says to select “Approve on Mercury” in the AI tool. Its note says that selecting “Confirm Request” in the AI tool doesn’t authorize the payment. The button label probably differs by client. Either way, it only forwards the request.

The pattern: three controls that each do one job

Here is my reading of why the design works. It is inference, not Mercury’s stated rationale.

  1. Allowlisted payees bound the blast radius. The worst prompt-injected request can only send money to someone the business already decided to pay. Adding and verifying payees is the step where most payment fraud happens, and it stays in a human-only path. An agent that can create payees and also request payments to them can pay anyone. An agent that can only pick from a list cannot.
  2. Out-of-band approval separates the proposer from the authorizer. The approval happens in a different app and session from the one the agent controls. Text in the chat can’t approve anything, however convincing it is. This is the same reason AP teams split “enter bill” from “approve payment”. The difference is that the split is enforced by the bank, not by an ERP role setting someone can misconfigure.
  3. Short expiry stops stale intent. A request nobody approves within about five minutes dies. That means no backlog of agent-created requests that someone bulk-approves on Friday afternoon without reading them, and no request approved hours later against a balance or invoice that has since changed.

Mercury’s own security page is candid about what this does not cover. Prompt injection can still leak data, and with write scopes it can make “unwanted category or note changes, or create unwanted payment requests.” It adds: “The approval requirement for money movement does not protect against every data disclosure or immediate edit.” The approval gate protects money, not data.

Why categorization skips approval

The immediate-apply tools share three traits: they don’t move money, they don’t add a counterparty, and their effect can be reversed by another edit. A wrong category is a bookkeeping error you fix at month end. A wrong wire may not come back.

Mercury does flag the cost: these edits “can overwrite existing values; they do not wait in Mercury’s approval queue.” Inference: if you sync Mercury categories or notes into an accounting system, an agent edit is a real write into your books with no approval queue in front of it. You should be able to detect it after the fact and undo it, because nothing stops it beforehand.

A risk tiering for write tools

Generalizing beyond Mercury, this is the tiering I would apply to any finance or commerce MCP server. The Mercury column is documented; the rest is my proposal.

TierExamplesControlMercury today
0: readbalances, transactions, invoicesscope + auditread tools
1: reversible metadatacategory, note, tag, memoapply immediately, log before/after values, allow revertcreateCategory, editCategory, updateTransaction
2: money to known partypay saved vendor, move between own accountsout-of-band human approval, short expiry, org policyrequestSendMoney, requestTransferMoney
3: change who can be paidadd payee, edit bank detailshuman-only, outside the agent channelnot exposed
4: change controlsapproval rules, users, limits, cardshuman-onlynot exposed

The rule I take from Mercury: a tool’s tier is set by what it can do in the worst case, not by its average use. “Update a recipient’s bank account” sounds like metadata. It is really tier 3.

Brief context: three other answers from the same week

  • JPMorgan Chase (Oct 13) said on its Q3 earnings call that it blocks outside AI agents that log in with customers’ credentials to take actions such as moving money. Read-only access through aggregators remains, and the bank says it is working on “agentic connections” with no timeline (CNBC, American Banker). Mercury is a sanctioned version of what JPMorgan refuses: OAuth scopes instead of shared passwords, and an approval the bank controls.
  • American Express described an Intent Intelligence framework in which the cardholder authenticates a purchase intent (merchant, maximum budget, other constraints). The resulting Proof of Intent token is exchanged for a scoped payment credential (TechCrunch, Amex ACE). That is approval before the agent acts, with bounds. Mercury approves each instance after the agent proposes it.
  • x401 (spec) is an HTTP proof-requirement protocol: a verifier asks for a signed credential presentation, and payment stays with HTTP 402 and a separate protocol. In this comparison it is the portable, cryptographic version of “a verified human authorized this.”

Inference: these sit on a spectrum. Per-transaction human approval (Mercury) is the safest and the least autonomous. Pre-authorized bounded mandates (Amex intents, x401-style signed authorizations) let the agent run unattended inside limits. A checkout agent cannot wait for a phone tap every time, so it will drift toward mandates. Vendor payments, at the dollar amounts AP deals in, probably should not drift for a while.

What it means for AP and ERP

Say you build a flow where an agent reads approved bills from your ERP and calls requestSendMoney for each. Here is what Mercury’s docs give you and what you have to build yourself.

Idempotency: there is no documented key

I found no idempotency parameter documented for requestSendMoney, and MCP itself has none (the same gap as in the OMS post). Mercury’s advice is procedural: after an uncertain result, call listSendMoneyApprovalRequests and check before retrying. So the dedupe has to be yours:

create table agent_payment_intent (
  intent_id        uuid primary key,          -- minted when the bill is selected for payment
  erp_bill_ref     text not null,
  recipient_id     text not null,             -- Mercury recipient, must already exist
  amount           numeric not null,
  currency         text not null,
  state            text not null,             -- proposed | pending_approval | approved | declined | expired | unknown | paid
  mercury_request_ref text,                   -- whatever the request call returns, if anything
  attempt          int not null default 1,
  created_at       timestamptz not null default now(),
  unique (erp_bill_ref, attempt)
);
  • One live intent per bill. You may only create a new attempt once the previous one is terminal (declined or expired), never while it is unknown.
  • On a timeout, move to unknown, then list approval requests and match on recipient + amount + recent time. Matching on those fields is fuzzy, which is why it belongs in “What I could not settle.”
  • Put the bill reference in whatever memo or note field the request carries. Unverified: I did not confirm which fields requestSendMoney accepts.

Expired requests are a normal outcome

With a roughly five-minute window, expiry will be common: the approver is in a meeting, the push notification gets missed. Treat expired as a terminal state, not an error. The bill goes back to “ready to pay”, and the next proposal is a new attempt with a new request. Don’t have the agent re-request in a loop until someone happens to be looking at their phone. That trains people to approve by reflex, which removes the point of the control. Inference: batch the proposals into a time window when the approver has said they are available.

Reconcile agent-initiated vs human-initiated payments

At month end you want to answer “which outgoing payments did an agent propose?” Mercury’s docs don’t say whether the resulting transaction is marked as MCP-originated. So keep your own ledger: intent id → approval request → Mercury transaction → ERP payment record. Then reconcile three ways:

  • Mercury transactions with no matching intent: human-initiated (or something you missed).
  • Intents in approved with no settled transaction after the expected rail time: investigate.
  • ERP bills marked paid without a Mercury transaction: someone closed the bill on the agent’s word. This is the failure “request created ≠ payment sent” warns about.

Never mark an ERP bill paid when the request is created, or even when it is approved. Mark it from the bank transaction.

Audit trail

Per agent-proposed payment, record: the human who started the conversation, the AI client and connection, the tool arguments, the ERP document that justified it, the approval request reference, who approved in Mercury and when, and the final transaction id. Mercury holds the approval half. Your system holds the “why.” For tier-1 edits, keep before/after values, because Mercury says they overwrite.

Checklist

  • Grant only the write scopes you need. transactions:update without transactions:request is a valid choice.
  • Payee creation and bank-detail changes stay human-only, with their own review.
  • Never treat chat confirmation or “request created” as payment.
  • Mint an intent id per bill before calling requestSendMoney. One live request per bill.
  • After timeouts, check listSendMoneyApprovalRequests before retrying.
  • Handle expired as a normal terminal state. No auto-retry loops.
  • Mark ERP bills paid from the bank transaction, not from the approval.
  • Reconcile intents ↔ approval requests ↔ transactions ↔ ERP payments.
  • Log before/after values for category and note edits, and be able to revert them.
  • Classify every write tool you expose by worst-case tier, not typical use.
  • Treat any content the agent reads (vendor emails, invoices, PDFs) as a possible prompt-injection source.

What I could not settle

  • The request schema. The docs I read list tool names, scopes and behavior, but not the input and output fields of requestSendMoney. I don’t know whether it takes an idempotency key or a memo/external reference, or what identifier it returns.
  • The exact expiry. “Approximately 5 minutes” comes from the support article only. I don’t know whether it is configurable, or which status an expired request shows in listSendMoneyApprovalRequests.
  • The status vocabulary. The list tool supports filtering “by account and status”, but I did not find the status values.
  • Webhooks. MCP exposes getWebhooks. I did not check whether there are webhook events for approval-request state changes, which would replace polling.
  • Attribution. I found nothing on whether a settled transaction is marked as agent-originated in Mercury’s data or exports.
  • Approver rules. The docs say “eligible approver” and say the requester must approve when policy would otherwise skip approval. I didn’t settle whether a different user can approve a request the agent made on someone else’s connection, or how multi-approver policies combine with the five-minute window.
  • The October 15 date. It comes from Mercury’s release notes page. The support and docs pages carry no publish date I could see.