mercury_get_treasury
Retrieve Treasury account balance, yield, and eligibility to support cash management decisions or verify Treasury activation.
Instructions
Retrieve Mercury Treasury account information (balance, current yield, eligibility, etc.).
USE WHEN: checking treasury cash balance or yield for cash-management decisions, or to confirm the workspace has Treasury enabled.
DO NOT USE: for deposit accounts (use mercury_get_account). For Treasury transactions or statements, use the dedicated list tools.
RETURNS: { id, currentBalance, yield, eligibility, ... }.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/treasury.ts:20-23 (handler)Handler function for mercury_get_treasury — makes GET request to /treasury endpoint and returns sanitized JSON result
async () => { const data = await client.get("/treasury"); return textResult(data); }, - src/tools/treasury.ts:7-24 (registration)Registration of mercury_get_treasury tool via defineTool (which internally calls server.registerTool)
defineTool( server, "mercury_get_treasury", [ "Retrieve Mercury Treasury account information (balance, current yield, eligibility, etc.).", "", "USE WHEN: checking treasury cash balance or yield for cash-management decisions, or to confirm the workspace has Treasury enabled.", "", "DO NOT USE: for deposit accounts (use `mercury_get_account`). For Treasury transactions or statements, use the dedicated list tools.", "", "RETURNS: `{ id, currentBalance, yield, eligibility, ... }`.", ].join("\n"), {}, async () => { const data = await client.get("/treasury"); return textResult(data); }, ); - src/tools/_shared.ts:29-39 (helper)defineTool helper that wraps the handler with middleware (rate limiting, audit, dry-run) and registers it on the MCP server
export function defineTool<S extends ZodRawShape>( server: McpServer, name: string, description: string, inputSchema: S, handler: (args: z.infer<z.ZodObject<S>>) => Promise<ToolResult>, ): void { const wrapped = wrapToolHandler(name, handler); const strictSchema = z.object(inputSchema).strict(); server.registerTool(name, { description, inputSchema: strictSchema }, wrapped); } - src/middleware.ts:391-479 (helper)Middleware wrapper applied to all tools — adds rate limit enforcement, dry-run support, and audit logging
export function wrapToolHandler<TArgs>( toolName: string, handler: (args: TArgs) => Promise<ToolResult>, ): (args: TArgs) => Promise<ToolResult> { const isWriteOp = toolName in TOOL_BUCKET; return async (args: TArgs): Promise<ToolResult> => { if (isWriteOp) { try { enforceRateLimit(toolName); } catch (err) { if (err instanceof RateLimitError) { safeLogAudit(toolName, args, "error"); return { content: [{ type: "text", text: formatRateLimitError(err) }], isError: true, }; } // Non-RateLimitError: defensive path (enforceRateLimit only // throws RateLimitError today, but if a future regression // surfaces a different error here we still want the audit // trail to show it before the re-throw propagates). /* v8 ignore next 2 -- defensive: enforceRateLimit only throws RateLimitError today; this path guards against a future regression, not a runtime path we can exercise from a unit test. */ safeLogAudit(toolName, args, "error"); /* v8 ignore next */ throw err; } } if (isWriteOp && isDryRun()) { safeLogAudit(toolName, args, "dry-run"); return { content: [ { type: "text", text: JSON.stringify( { dryRun: true, tool: toolName, wouldCallWith: redactSensitive(args), note: "MERCURY_MCP_DRY_RUN=true; no actual Mercury API call was made. Sensitive fields are redacted.", }, null, 2, ), }, ], }; } try { const result = await handler(args); // Business errors returned via `isError: true` (vs thrown) are // audited as "error" so the audit log distinguishes a // successful call from one that surfaced a handler-side failure // through the MCP protocol's isError channel (Qodo finding // backported from klodr/gmail-mcp#48). if (isWriteOp) safeLogAudit(toolName, args, result.isError ? "error" : "ok"); return result; } catch (err) { safeLogAudit(toolName, args, "error"); if (err instanceof MercuryError) { const isAR = toolName.includes("invoice") || toolName.includes("customer"); const planHint = err.status === 403 && isAR ? " (Mercury's Invoicing/Customers API requires the Plus plan or higher.)" : ""; // err.message is plain text that can carry an upstream // reflection of attacker-supplied bytes. Route it through the // stripControl + fence pipeline so a prompt injection smuggled // via (say) an invoice memo cannot exit the error channel as // free-form instructions. return { content: [ { type: "text", text: sanitizeForLlm(`Mercury API error ${err.status}: ${err.message}${planHint}`), }, ], isError: true, }; } throw err; } }; } - src/tools/_shared.ts:17-27 (helper)textResult helper — sanitizes response data (strips control/invisible characters) and formats as MCP ToolResult
export function textResult(data: unknown): ToolResult { // Walk the payload once, reuse the sanitized value for both the // LLM-display JSON string and the `structuredContent` object. // Calling sanitizeJsonForLlm(data) + sanitizeJsonValues(data) // separately would run the walker twice on the same input. const sanitized = sanitizeJsonValues(data); return { content: [{ type: "text", text: JSON.stringify(sanitized, null, 2) }], structuredContent: (sanitized ?? {}) as Record<string, unknown>, }; }