mercury_add_recipient
Add a new payment recipient to send money via ACH, wire, or check. Returns a recipient ID used for sending payments.
Instructions
Add a new payment recipient (a counterparty you can later send money to via ACH/wire/check).
USE WHEN: onboarding a new vendor, contractor, or other payee before sending money. The returned id is what mercury_send_money and mercury_request_send_money expect as recipientId.
DO NOT USE: for AR customers (use mercury_create_customer — recipients receive money, customers pay invoices). Mercury enforces strict KYC/banking validation on bank fields — invalid routing numbers or account numbers are rejected at create time.
SIDE EFFECTS: writes a new recipient to Mercury. Persistent. Idempotent via idempotencyKey — the MCP auto-generates one if not provided, so repeated calls with the same generated key would not duplicate; pass an explicit idempotencyKey to make this stable across retries you control.
RETURNS: { id, name, status, defaultPaymentMethod, ... } — keep id for the send-money tools.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Recipient legal name | |
| emails | Yes | List of email addresses | |
| paymentMethod | Yes | Payment method to send to this recipient | |
| defaultPaymentMethod | No | Default payment method | |
| electronicRoutingInfo | No | Bank account info for ACH/wire | |
| idempotencyKey | No | Unique key to prevent duplicates |
Implementation Reference
- src/tools/recipients.ts:108-114 (handler)The handler function for mercury_add_recipient. Takes name, emails, paymentMethod, optional electronicRoutingInfo, and idempotencyKey; POSTs to /recipients on Mercury API.
async ({ idempotencyKey, ...body }) => { const idem = idempotencyKey ?? randomUUID(); const data = await client.post("/recipients", { ...body, idempotencyKey: idem }); return textResult(data); }, { title: "Add Recipient", destructiveHint: false, idempotentHint: true, openWorldHint: true }, ); - src/tools/recipients.ts:73-106 (schema)Zod input schema for mercury_add_recipient: validates name, emails, paymentMethod, defaultPaymentMethod, electronicRoutingInfo (bank details), and idempotencyKey.
{ name: z.string().describe("Recipient legal name"), emails: z.array(z.email()).describe("List of email addresses"), paymentMethod: z .enum(["domesticAch", "internationalWire", "domesticWire", "check"]) .describe("Payment method to send to this recipient"), defaultPaymentMethod: z .enum(["domesticAch", "internationalWire", "domesticWire", "check"]) .optional() .describe("Default payment method"), electronicRoutingInfo: z .object({ accountNumber: z.string(), routingNumber: z.string(), electronicAccountType: z.enum([ "businessChecking", "businessSavings", "personalChecking", "personalSavings", ]), address: z .object({ address1: z.string(), address2: z.string().optional(), city: z.string(), region: z.string(), postalCode: z.string(), country: z.string(), }) .optional(), }) .optional() .describe("Bank account info for ACH/wire"), idempotencyKey: z.string().optional().describe("Unique key to prevent duplicates"), - src/tools/recipients.ts:59-114 (registration)Registration: calls defineTool with name 'mercury_add_recipient', description, schema, handler, and annotations.
defineTool( server, "mercury_add_recipient", [ "Add a new payment recipient (a counterparty you can later send money to via ACH/wire/check).", "", "USE WHEN: onboarding a new vendor, contractor, or other payee before sending money. The returned `id` is what `mercury_send_money` and `mercury_request_send_money` expect as `recipientId`.", "", "DO NOT USE: for AR customers (use `mercury_create_customer` — recipients receive money, customers pay invoices). Mercury enforces strict KYC/banking validation on bank fields — invalid routing numbers or account numbers are rejected at create time.", "", "SIDE EFFECTS: writes a new recipient to Mercury. Persistent. **Idempotent via `idempotencyKey`** — the MCP auto-generates one if not provided, so repeated calls with the same generated key would not duplicate; pass an explicit `idempotencyKey` to make this stable across retries you control.", "", "RETURNS: `{ id, name, status, defaultPaymentMethod, ... }` — keep `id` for the send-money tools.", ].join("\n"), { name: z.string().describe("Recipient legal name"), emails: z.array(z.email()).describe("List of email addresses"), paymentMethod: z .enum(["domesticAch", "internationalWire", "domesticWire", "check"]) .describe("Payment method to send to this recipient"), defaultPaymentMethod: z .enum(["domesticAch", "internationalWire", "domesticWire", "check"]) .optional() .describe("Default payment method"), electronicRoutingInfo: z .object({ accountNumber: z.string(), routingNumber: z.string(), electronicAccountType: z.enum([ "businessChecking", "businessSavings", "personalChecking", "personalSavings", ]), address: z .object({ address1: z.string(), address2: z.string().optional(), city: z.string(), region: z.string(), postalCode: z.string(), country: z.string(), }) .optional(), }) .optional() .describe("Bank account info for ACH/wire"), idempotencyKey: z.string().optional().describe("Unique key to prevent duplicates"), }, async ({ idempotencyKey, ...body }) => { const idem = idempotencyKey ?? randomUUID(); const data = await client.post("/recipients", { ...body, idempotencyKey: idem }); return textResult(data); }, { title: "Add Recipient", destructiveHint: false, idempotentHint: true, openWorldHint: true }, ); - src/tools/_shared.ts:28-54 (registration)The defineTool helper that registers the tool on the McpServer via server.registerTool, wrapping the handler with rate-limit and audit middleware.
export function defineTool<S extends ZodRawShape>( server: McpServer, name: string, description: string, inputSchema: S, handler: (args: z.infer<z.ZodObject<S>>) => Promise<ToolResult>, annotations: ToolAnnotations, ): void { const wrapped = wrapToolHandler(name, handler); const strictSchema = z.object(inputSchema).strict(); // MCP behavioral annotations (readOnlyHint / destructiveHint / // idempotentHint / openWorldHint) — declared machine-readable so // hosts and rubrics (TDQS / Glama Behavior dimension) can detect // tool semantics without scraping the prose description. Required // (not optional) so every new tool ships with explicit semantics — // forgetting the annotation now fails typecheck instead of // silently shipping a tool with no hint set. // The MCP SDK overloads `registerTool` with shape narrowing the runtime // strict-schema and the wrapped callback can't satisfy through generics. // Both casts are runtime-safe — the signatures only diverge at the type // level. Asserted by the existing tool-registration tests. (server.registerTool as unknown as (...a: unknown[]) => unknown)( name, { description, inputSchema: strictSchema, annotations }, wrapped, ); } - src/tools/index.ts:20-38 (registration)registerAllTools calls registerRecipientTools which registers mercury_add_recipient among other recipient tools.
export function registerAllTools(server: McpServer, client: MercuryClient): void { // Banking registerAccountTools(server, client); registerCardTools(server, client); registerCreditTools(server, client); registerTransactionTools(server, client); registerRecipientTools(server, client); registerStatementTools(server, client); registerTreasuryTools(server, client); registerCategoryTools(server, client); registerOrganizationTools(server, client); // Accounts Receivable (Invoicing) — requires Mercury Plus registerInvoiceTools(server, client); registerCustomerTools(server, client); // Webhooks registerWebhookTools(server, client); }