Skip to main content
Glama
klodr

mercury-invoicing-mcp

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

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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);
    },
  • 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);
      },
    );
  • 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);
    }
  • 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;
        }
      };
    }
  • 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>,
      };
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but description implies a safe read operation by returning treasury info. Mentions return fields. Could improve by clarifying permissions or idempotency, but sufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise, front-loaded with purpose, uses bullet-style sections for usage and returns. Every sentence adds value with zero waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no parameters and no output schema, the description is complete: explains return structure, usage context, and excludes alternatives. No gaps for agent execution.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 0 parameters, baseline is 4. Description adds no parameter details as none exist; no additional value needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states 'Retrieve Mercury Treasury account information (balance, current yield, eligibility, etc.)' with a specific verb and resource. Distinguishes from sibling 'mercury_get_account' for deposit accounts and list tools for transactions/statements.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (checking treasury cash balance/yield, confirming Treasury enabled) and when not to use (deposit accounts via mercury_get_account, transactions/statements via list tools).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/klodr/mercury-invoicing-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server