Announcing CIMD support for MCP Client registration
Learn more

Should you use Mercury MCP or Mercury API for building AI Agents?

Varun Krishnan
Senior Content Marketer

TL;DR

  • Mercury's hosted MCP server is read-only by design. Every tool on it starts with get or list. Mercury's published table documents 31 tools; the Scalekit connector currently surfaces 35. None of them move money, create an invoice, or issue a card.
  • Mercury MCP authenticates with Authorization Code plus PKCE (S256 only) and supports Dynamic Client Registration (RFC 7591), so a client provisions its own client_id at runtime with no pre-approval.
  • The Mercury API is the opposite. OAuth access requires prior partner approval through an application form, and Mercury documents only two OAuth scopes: read and offline_access. Every documented write path is described in terms of API token scopes, not OAuth scopes.
  • Direct sends require an IP allowlist. Unused write permissions are downgraded after 45 days, and unused tokens are deleted after 45 days. Both rules will break a background agent that reads for a month and then tries to pay a vendor.
  • Scalekit's Mercury MCP connector handles the per-user OAuth flow, token vault, and refresh for the MCP path, and a custom connector plus Tool Proxy covers the REST write path, so the MCP versus API decision does not change your credential infrastructure.

Your agent needs to work with Mercury. It needs to answer "how much did we burn on infrastructure last month," or queue a vendor payment when an invoice clears approval. Mercury ships a hosted MCP server and a full REST API, and they are not two views of the same surface. One of them cannot move a cent, and that is the whole decision.

What Mercury MCP and the Mercury API actually are

These are two different products with two different auth stories. The reader has almost certainly used the REST API. The MCP server is newer and narrower than most people assume.

Mercury MCP

Mercury MCP is a Mercury-hosted, remote MCP server at https://mcp.mercury.com/mcp, served over streamable HTTP. It sits between an AI client and Mercury's public API, and Mercury restricts it to read-only actions on purpose. It is still labelled Beta in Mercury's documentation, and Mercury does not offer a local or self-hosted variant.

Auth is browser OAuth. Adding the server grants nothing until the user signs in and selects Allow, at which point Mercury issues that client a read-only token for the account the user signed in to.

The Mercury API

The Mercury API is a REST surface at https://api.mercury.com/api/v1 covering accounts, transactions, statements, treasury, recipients, send money, internal transfers, accounts receivable, cards, categories, SAFEs, and webhooks. A separate Vault host, vault-api.mercury.com, handles agent card credential reveal.

Two auth models exist. API tokens use HTTP Basic with the token as username and an empty password, or a bearer header. OAuth 2.0 with Authorization Code and PKCE exists for partner integrations.

What your agent can actually do

The capability gap here is not a matter of degree. It is a hard line drawn at the read and write boundary, and it holds across every Mercury product area.

The read surface is complete, the write surface is absent

Mercury MCP covers reads well. Transactions support 15 filter parameters including posted-date ranges, custom category, merchant search, card, and status. Statements, treasury, SAFEs, recipient tax attachments, and invoice attachments are all reachable. What is not reachable is any state change at all.

Capability
Mercury MCP
Mercury API
Accounts, balances, and credit lines
Read
Read
Transactions with date, category, and merchant filters
Read
Read
Account and treasury statements
Read
Read
Cards, recipients, users, and organization details
Read
Read
Invoices, customers, and invoice attachments
Read
Read and write
Send-money approval requests
Read
Read and write
Create or update a recipient
No
Yes
Send a payment directly (ACH, check, domestic wire)
No
Yes, IP allowlist required
Queue a payment or international wire for approval
No
Yes, no IP allowlist
Transfer between your own Mercury accounts
No
Yes
Issue, update, freeze, or cancel a card
No
Yes
Reveal agent card number, expiry, and CVC
No
Yes, separate Vault host

Where the MCP ceiling sits

The sharpest illustration is the approval queue. listSendMoneyApprovalRequests is on the MCP server, so an agent can tell you which payments are waiting for sign-off. It cannot put one there. Populating that queue requires POST /account/{accountId}/request-send-money on the REST API.

The same pattern repeats in accounts receivable. The MCP reads invoices, customers, and attachments. Creating, updating, or cancelling an invoice is REST-only, and invoicing is not available on Mercury's Free plan at all.

What only the API can do

Card operations are API-only across the board: issuance, spend-limit updates, freeze, unfreeze, and cancel. Note that getCard deliberately withholds full card numbers for PCI reasons.

Agent cards are the newest wrinkle. Since August 2026, a human can create a virtual card in the Mercury app, hand it to an agent, and the agent pulls the number, expiry, and CVC from GET https://vault-api.mercury.com/api/v1/cards/{cardId}/reveal. Mercury's changelog states this is not available via MCP. Agents also cannot create agent cards or lift their spend limits, which is the point.

The auth path each one puts you on

The two paths do not just differ in what they can do. They differ in who is allowed to build against them and how long it takes to get started.

Mercury MCP: OAuth with Dynamic Client Registration

Nothing is set up on Mercury's side in advance. Your client posts to https://mcp.mercury.com/register, stores the returned client_id, sends the user to https://mcp.mercury.com/authorize with a PKCE code_challenge and a resource indicator, then exchanges the code at https://mcp.mercury.com/token.

Mercury enforces PKCE with S256 and rejects plain. Token endpoint auth is client_secret_basic or none. Request read, and add offline_access if you want refresh tokens instead of sending the user back to a browser. One quirk worth knowing: client_name must not begin with the word "Mercury" or registration fails.

To understand how Dynamic Client Registration works in OAuth2 and its role in agentic auth, the mechanics are worth reviewing before you implement this path.

The API: token tiers, IP allowlists, and a partner form

API tokens come in three tiers. Read Only needs no IP allowlist. Read and Write requires one. Custom scopes to specific endpoints, and any write scope pulls in the allowlist requirement. Scopes cannot be edited after a Custom token is created; you issue a new token instead.

OAuth on the API is a different animal. Access requires prior approval through Mercury's integration application form, covering company details, use case, redirect URIs, and a GPG public key for credential delivery. Approval timelines vary.

Why headless Mercury agents cannot use OAuth

Here is the structural point. Mercury documents two OAuth scopes, read and offline_access. There is no documented OAuth scope that moves money. Sending payments, managing recipients, and creating invoices are all described in terms of API token scopes.

So a background agent that pays vendors on your customers' behalf cannot get there through delegated OAuth. It needs a Mercury API token issued inside each customer's Mercury organization, with a Custom scope such as Send Money with Approval, and possibly an allowlisted egress IP. That is a per-tenant onboarding step, not a consent screen.

What you own in production

Neither path removes operational ownership. It relocates it. The question is which failures land in your on-call rotation.

On the MCP path

Mercury handles hosting, tool schemas, pagination normalization, and permission enforcement. You still own token storage, refresh, revocation, and tenant isolation.

Two protocol gaps are worth planning for. A 401 from Mercury's MCP server carries no resource_metadata pointer in its WWW-Authenticate header, and the protected resource metadata document is served only at the root /.well-known/oauth-protected-resource path, not the per-resource variant. Generic MCP clients that rely on either will need hardcoded endpoints.

On the API path

You own everything: endpoint versioning, error handling, retries, pagination, and the token lifecycle. Money movement adds requirements that reads never had.

Every send and internal transfer takes an idempotencyKey, and createTransaction enforces a 24-hour duplicate guard that returns 400 for the same recipient, account, amount, and payment method even with a fresh key. Invoice creation has its own guard through a unique invoiceNumber. Approval requests have no webhook at all, so you poll GET /request-send-money/{requestId} until the status leaves pendingApproval.

The 45-day rules that quietly break write agents

Mercury automatically downgrades tokens holding permissions they have not exercised within a 45-day window, and deletes tokens that go unused for 45 days. Admins get an email seven days before either action.

Now picture the failure. Your agent reads Mercury daily for a quarter and queues its first payment in month two. The write scope is already gone. The warning email went to your customer's Mercury admins, not to your engineering team, and your agent gets a permission error on the one call that mattered. Exercise write scopes on a schedule, or monitor for the downgrade. This is exactly the kind of operational concern covered in secure token management for AI agents at scale.

When to use MCP, when to use the API

Both lists below assume a real Mercury agent, not a generic MCP preference. The split follows the read and write line almost exactly.

Use Mercury MCP when

  • Your agent answers finance questions in natural language: burn by category, runway against treasury balances, which merchants moved most last quarter
  • You are building an interactive assistant inside Claude, ChatGPT, Cursor, or Claude Code and want the user's own Mercury permissions to bound it
  • You want a hard architectural guarantee that the agent cannot initiate a transaction, which is a genuinely useful property when the tool sits next to a bank account
  • You need to ship without a partner approval cycle, because Dynamic Client Registration means Mercury has nothing to approve

Use the Mercury API when

  • Your agent pays vendors, queues international wires, or moves funds between accounts, since none of that exists on the MCP server
  • You are automating accounts receivable: creating customers, raising invoices, cancelling unpaid ones, and reconciling against GET /transactions
  • You manage cards programmatically, including agent cards where the Vault reveal endpoint is the only path to the credential
  • Your agent runs headless on a schedule with no browser and no user session, where a Custom API token is the only workable credential

The credential problem that exists on both paths

This is the part that does not change no matter which path you pick, and it is the part that decides whether your Mercury agent survives its second customer.

N tokens per tenant, two different shapes

The MCP path gives you an OAuth access token and, with offline_access, a refresh token per user. The API path gives you a static Mercury API token per organization. Different shapes, identical infrastructure problem.

In a multi-tenant B2B agent, that is N credentials to encrypt at rest, isolate per tenant, refresh proactively rather than on 401, and revoke on offboarding. Mercury enforces identity. It does not run your token vault. A Mercury MCP session in Claude also expires in roughly three days on the same chat thread, so reauthorization is an ongoing event, not a one-time setup. The challenges of handling token refresh for AI agents are real and worth planning for up front.

What Scalekit's Mercury connector handles

Scalekit's Mercury MCP connector runs the OAuth flow, stores the tokens in a vault outside your agent runtime, refreshes them, and scopes every call to the user who authorized it. For the REST write path, a custom connector plus Tool Proxy applies the same connected-account model to a Mercury API token. The MCP versus API choice stops being an auth decision.

Connecting a Mercury agent with Scalekit

The read path takes four steps: connect the account, authorize the user, retrieve the authorized tool surface, then run the loop. Prerequisites are a Scalekit account and a connection created under AgentKit in the dashboard.

Set up the connection and authorize a user

The connection_name string below must match the connection name configured in your Scalekit dashboard exactly. This is the single most common integration error.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions CONNECTION_NAME = "mercurymcp" # must match the dashboard connection name IDENTIFIER = "finance-ops@acme.com" account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) if account.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) print("Authorize Mercury:", link.link) input("Press Enter after authorizing...")

Retrieve the authorized tool surface

Before the agent loop runs, retrieve the tools this connected account is authorized to call. This is not a flat catalog of everything Mercury offers; list_scoped_tools returns what this specific user's Mercury grant permits.

from scalekit.v1.tools.tools_pb2 import ScopedToolFilter scoped = scalekit_client.tools.list_scoped_tools( IDENTIFIER, filter=ScopedToolFilter(connection_names=[CONNECTION_NAME]), page_size=50, ) for tool in scoped.tools: print(tool.name) # mercurymcp_getaccounts # mercurymcp_listtransactions # mercurymcp_listcategories # mercurymcp_getaccountstatements # ...

Run the agent loop with LangChain

actions.langchain.get_tools() returns native StructuredTool objects, so no schema reshaping is needed. Bind them and run the loop. This pattern reflects best practices for LangChain tool calling in production agentic systems.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tools = actions.langchain.get_tools( identifier=IDENTIFIER, connection_names=[CONNECTION_NAME], page_size=100, # Mercury exposes 35 tools; avoid truncation on the default page ) tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "Across all Mercury accounts, total the transactions posted in the last 30 days " "by category, and flag any category above 25000 USD." ) ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Scope the surface with a Virtual MCP server

Handing an agent all 35 Mercury tools costs context on every turn and gives it reach it does not need. A Virtual MCP server declares exactly which tools an agent role can see. Create it once per role, not once per user.

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = scalekit_client.actions.mcp.create_config( name="mercury-spend-reporter", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="mercurymcp", tools=[ "mercurymcp_getaccounts", "mercurymcp_listtransactions", "mercurymcp_listcategories", "mercurymcp_getaccountstatements", ], ), ], ) config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url

Consume the per-user MCP URL from Mastra

Mastra has native MCP support, so it discovers tools and Zod schemas straight from the URL. Generate the URL server-side for the authenticated user; a process-wide URL runs every request as one person.

# Backend (Python): one URL per user session inst = scalekit_client.actions.mcp.ensure_instance( config_name="mercury-spend-reporter", user_identifier="finance-ops@acme.com", ) mcp_url = inst.instance.url
import { Agent } from '@mastra/core/agent'; import { MCPClient } from '@mastra/mcp'; import { openai } from '@ai-sdk/openai'; // Resolved server-side for the authenticated user, never a shared constant const mcpUrl = await getMercuryMcpUrlForUser(currentUserId); const mcp = new MCPClient({ servers: { mercury: { url: new URL(mcpUrl) }, }, }); const tools = await mcp.getTools(); const agent = new Agent({ name: 'mercury_spend_reporter', instructions: 'You report on Mercury banking data. You can read accounts, transactions, ' + 'categories, and statements. You cannot move money.', model: openai('gpt-4o'), tools, }); const result = await agent.generate( 'Summarise last month spend by category and name the three largest merchants.', ); console.log(result.text); await mcp.disconnect();

Adding the Mercury write path through a custom connector

Scalekit ships one Mercury connector today, and it wraps the vendor MCP server. For payments and invoicing you register the REST API as your own connector and call it through Tool Proxy, keeping the same connected-account model.

Define the connector

Mercury accepts a bearer header, so a BEARER auth pattern works. The token value your customer pastes includes the secret-token: prefix Mercury issues.

{ "display_name": "Mercury API", "description": "Mercury banking REST API for recipients, payments, invoicing, and cards", "auth_patterns": [ { "type": "BEARER", "display_name": "Mercury API Token", "description": "Authenticate with a Mercury API token issued from Settings, Tokens", "fields": [ { "field_name": "token", "label": "Mercury API Token", "input_type": "password", "hint": "Paste the full token, including the secret-token: prefix", "required": true } ] } ], "proxy_url": "https://api.mercury.com/api/v1", "proxy_enabled": true }
curl --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ --header "Authorization: Bearer $ENV_ACCESS_TOKEN" \ --header "Content-Type: application/json" \ --data @mercury-api-connector.json

Full payload reference is in Create your own connector.

Queue a payment for approval

request-send-money is the right endpoint for an agent. It always parks the payment in Mercury's dashboard approval queue, and it needs no IP allowlist because human sign-off is the control.

import { ScalekitClient } from '@scalekit-sdk/node'; import { randomUUID } from 'node:crypto'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); // "mercury-api" must match the dashboard connection name for the custom connector const response = await scalekit.actions.request({ connectionName: 'mercury-api', identifier: 'finance-ops@acme.com', path: `/account/${accountId}/request-send-money`, method: 'POST', body: { recipientId, amount: 2500.0, paymentMethod: 'ach', idempotencyKey: randomUUID(), note: 'Q3 hosting invoice', }, }); console.log(response.data.requestId, response.data.status); // pendingApproval

The Mercury token never enters your agent runtime or the model context. Scalekit resolves it at request time from the vault.

Why the Scalekit path matters for multi-tenant Mercury agents

Banking data raises the stakes on the properties that are merely nice-to-have elsewhere. Three of them matter more here than for a Slack or Notion agent.

Per-call auth logs your auditor will ask for

A shared Mercury token makes every balance read and every queued payment look like one service account. When a finance lead asks who told the agent to pay that vendor, the log has no answer.

Scalekit resolves the credential of the user who triggered the run, so each entry carries the authorizing identity, the tool called, and the response. This connects directly to the broader need for audit trails for agent auth in B2B SaaS — logs export to your SIEM, with failures separated by source.

Virtual MCP servers for multi-tool, multi-tenant agents

Real finance agents rarely touch one system. A month-end close agent reads Mercury, reconciles against QuickBooks or Xero, checks payouts in Stripe, and posts a summary to Slack.

One Virtual MCP server definition spans those connections and exposes only the tools that role needs. Each run mints a short-lived session token bound to one user, so the same definition serves every tenant without credential sharing. See Set up and connect a Virtual MCP server.

One auth model across both Mercury paths

The read agent talks to Mercury MCP. The payment agent talks to the REST API through Tool Proxy. Both resolve credentials from the same vault, appear in the same logs, and revoke through the same call.

That is the practical payoff: you can start on MCP for reporting and add the write path later without rebuilding how credentials work. Understanding credential ownership across agent tool-calling patterns is key to designing a system that handles both paths cleanly.

Which one to build against

If your Mercury agent reports, reconciles, or answers questions, build on the MCP server. Read-only is a feature next to a bank account, Dynamic Client Registration removes the approval cycle, and Mercury's own permission model bounds what the agent sees.

If your agent moves money, raises invoices, or manages cards, the MCP server cannot help you, and delegated OAuth cannot either. You need per-tenant Mercury API tokens with the right Custom scopes, an idempotency strategy, and a plan for the 45-day downgrade.

Most production Mercury agents end up on both paths at once. The credential infrastructure underneath them should not care which call is which.

Talk to other Mercury agent builders

Browse the Scalekit Mercury MCP connector or the full connector catalog. Pricing, including the free tier, is on the pricing page.

Building something on Mercury and want a second opinion on the auth model? Talk to us if you need help.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.