> ## 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.

# Agent Tools (SDK)

> Provider-agnostic tool descriptors for binding Moneda's read-only services directly to an LLM agent — ~10× cheaper than MCP-over-HTTP

`@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.

<Info>
  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.
</Info>

## Why is it cheaper than MCP?

| Lever             | MCP-over-HTTP                                                     | Agent Tools                                                          |
| ----------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------- |
| Tool schema size  | 37 multi-paragraph descriptions (\~18k tokens) re-sent every turn | 29 terse descriptions (\~1.1k tokens), can be prompt-cached          |
| Response encoding | JSON (verbose)                                                    | TOON (\~40% smaller)                                                 |
| Response shape    | Full service shape with UI coaching fields                        | Lite projection — drops IDs, raw addresses, derived counts, UI hints |
| Transport         | HTTP round-trip per call                                          | In-process function call                                             |

Rough per-query cost on Claude Sonnet 4.5:

| Path                                         | Cost          |
| -------------------------------------------- | ------------- |
| 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

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

// Build the descriptor set for an authenticated user.
const descriptors = getAgentTools(currentUser.id);

// 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"|"CAD" }`
* `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` — `{}` (returns the user's referral code + referral count)
* `get_referees` — `{}` (lists users the caller has successfully referred)

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"|"CADD" }`
* `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.):

```ts theme={null}
// 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`](https://github.com/MonedaIO/app/blob/main/packages/services/src/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.

```ts theme={null}
// 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`](https://github.com/MonedaIO/app/blob/main/packages/services/src/agent-tools.ts) for the descriptor factory and [`packages/services/src/lite.ts`](https://github.com/MonedaIO/app/blob/main/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.
