Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.moneda.com/llms.txt

Use this file to discover all available pages before exploring further.

@moneda/services exports getAgentTools(userId, email?) — a ready-to-bind tool surface for LLM agents that need to read Moneda data. It packages all 29 read operations the MCP server exposes as provider-agnostic descriptors you wrap in your AI SDK’s tool factory (Anthropic, OpenAI, Gemini, Mastra, Vercel AI SDK, etc.). Use this when you’re building an in-process agent (support chatbot, internal assistant, background automation). It’s roughly 7–10× cheaper per query than hitting the MCP server over HTTP — the MCP copy stays for external third-party agents that need OAuth + scoped external access.
This is an SDK-level API, not an HTTP endpoint. Import it from @moneda/services in a Node process that already has a Moneda user ID — typically your support chatbot service or an internal agent worker.

Why is it cheaper than MCP?

LeverMCP-over-HTTPAgent Tools
Tool schema size37 multi-paragraph descriptions (~18k tokens) re-sent every turn29 terse descriptions (~1.1k tokens), can be prompt-cached
Response encodingJSON (verbose)TOON (~40% smaller)
Response shapeFull service shape with UI coaching fieldsLite projection — drops IDs, raw addresses, derived counts, UI hints
TransportHTTP round-trip per callIn-process function call
Rough per-query cost on Claude Sonnet 4.5:
PathCost
MCP-over-HTTP~$0.34
Agent Tools (no caching)~$0.03–0.05
Agent Tools (with cache_control on prefix)~$0.01–0.02

Quick start

import { tool } from "ai"; // or mastra, openai, @anthropic-ai/sdk, etc.
import { getAgentTools } from "@moneda/services";

// Build the descriptor set for an authenticated user.
// Email is only needed for referral tools; omit if not used.
const descriptors = getAgentTools(currentUser.id, currentUser.email);

// Wrap each descriptor in your AI SDK's tool() factory.
const tools = Object.fromEntries(
  Object.entries(descriptors).map(([name, d]) => [name, tool(d)]),
);

// Pass to your agent loop.
const response = await generateText({
  model: anthropic("claude-sonnet-4-5"),
  tools,
  messages,
});

What’s in the box

Every descriptor has { description, parameters, execute }:
  • description — terse (~30–50 tokens). Your caller is free to prepend agent-coaching on top.
  • parameters — a Zod schema with tight enums so the agent’s guess-space is narrow and tool calls validate more often.
  • execute(args) — returns a Promise<string>. The string is TOON-encoded, with the lite projection + pruneEmpty already applied.

Available tools

Account & balance:
  • get_balance{ currency?: "USD"|"EUR"|"CHF" }
  • get_wallet{}
  • get_transactions — rich filters, see below
  • get_spending_by_type_group{ period: "7d"|"30d"|"90d", currency? }
  • get_virtual_accounts{}
  • get_my_accounts{}
Rates & products:
  • get_exchange_rate{ from, to }
  • get_apy_rates{ currency? }
Rewards:
  • get_points_balance{}
  • get_points_activity{}
  • get_referral_code{} (requires email; lazily enrolls the user in GrowSurf on first call)
  • get_referees{} (requires email)
Contacts & security:
  • get_contacts{ search?, limit?, offset? }
  • get_recovery_contacts{}
  • list_recovery_emails{ accountId?: string } — ZK email-based account recovery
  • get_recovery_email{ id }
  • get_recovery_config{ accountId? } — accepted emails, total weight, threshold, fullySet flag
  • get_recovery_status{ accountId? } — most recent recovery snapshot; emails anonymized as a***@domain
  • get_passkeys{}
  • get_settings{}
Notifications:
  • list_notifications{ cursor?, limit?, filter?: "all" | "unread" }
  • get_unread_notification_count{} (cheaper than list_notifications when you only need the count)
Knowledge base (fallback — prefer embedding the FAQ in the system prompt directly):
  • search_knowledge{ query, category?, limit? }
  • get_faq_category{ slug }
  • get_faq_item{ id }
Sub-accounts (vaults):
  • list_sub_accounts{ includeClosed?: boolean }
  • get_sub_account{ id }
  • get_sub_account_balance{ id, currency?: "USDC"|"EURC"|"CHFAU" }
  • get_total_wealth{} (aggregates main account + every live vault)
AGENT_TOOL_NAMES is exported as a const array for allowlisting, analytics, or admin UIs that want to enumerate capabilities without building the full descriptor set.

get_transactions — lite by default, detail: true to opt into full

Returns the compact shape by default — hash, direction, amount, currency, status, date, trimmed counterparty. On a single-hash drill-down (e.g. “why did this transaction fail?”), pass detail: true along with transactionHash to get the full row (typeGroup, category, note, reference, raw counterparty routing, etc.):
// Common case — compact list
await tools.get_transactions.execute({ limit: 10, currency: "USD" });

// Drill-down — full row
await tools.get_transactions.execute({
  transactionHash: "0xabc...",
  detail: true,
});

Response shape — lite projections

Each tool’s execute runs the service output through a hand-picked field allowlist (project* in @moneda/services) and then through pruneEmpty before TOON-encoding.
  • get_wallet drops network, supportedTokens, warning, tip (~76% smaller)
  • get_virtual_accounts drops minimumTransfer, firstPartyPayments, thirdPartyPayments, capabilityInfo (~78% smaller)
  • get_balance drops source, notice, snapshotTimestamp; collapses snapshot metadata to an optional stale field on the fallback path (~22% live, ~65% on snapshot)
  • get_transactions drops typeGroup, type, category, note, reference, exchanged*, received*, raw counterparty routing (~27% list, ~48% single row)
  • get_my_accounts, get_contacts, get_recovery_contacts, get_passkeys, get_points_activity, get_referees, get_referral_code — see lite.ts for the exact allowlist.

Pair with prompt caching for the real win

The agent-tools descriptor set + your system prompt are cacheable. Wire cache_control on the prefix once and every subsequent conversation turn pays ~10% of the input cost on that portion. For a support chatbot with 3–5 turns, that typically compounds to another 3–5× savings on top of the lite projection.
// Anthropic Messages API example
messages: [
  { role: "system", content: systemPrompt },
],
tools, // descriptor set, 29 entries
// Add cache_control on the last system block + last tool definition to
// snapshot the prefix. The SDK handles refresh.
See packages/services/src/agent-tools.ts for the descriptor factory and packages/services/src/lite.ts for the projections.

When to use MCP instead

Use the MCP server when the agent runs in a third-party client (Claude Desktop, ChatGPT over MCP, Cursor) and needs OAuth + scope-gated external access with human-in-the-loop approval for writes. Use Agent Tools (this SDK) when the agent runs in your own process, you already have the authenticated userId, and you’re paying the tokens yourself. The support chatbot is the canonical use case.