satchel_move_money
The one write tool — refuses everywhere unless explicitly enabled.
satchel_move_money moves money between the customer's own Satchel accounts. It is the
only write tool Satchel MCP has, and it comes with the strictest defaults in the whole
server.
Where it's available
stdio only. The hosted remote MCP server never registers this tool — not gated behind a
flag or a permission, simply not present in that server's tool list at all. If you're
connected to the hosted server, satchel_move_money does not exist as far as your
assistant is concerned. It only exists when you run the local stdio server (mcp/)
yourself.
Off by default, everywhere
Even on the stdio server, and even with valid credentials, the tool refuses unless
SATCHEL_ALLOW_WRITES=true is set in the server's own environment — in every mode,
sandbox and live alike. Without it, calling the tool returns a polite refusal explaining
that writes are off by default and how to turn them on (set the env var and ask again) —
never a silent no-op and never an error that looks like something broke.
This is an explicit, opt-in gate on money movement, independent of read access: an assistant can always read your accounts (subject to the credentials it has), but it can only move money if a human deliberately flipped that specific switch in the server's environment ahead of time.
What it does
It calls the underlying POST /transfer/internal endpoint — an internal transfer between
two of the customer's own accounts. It cannot pay external recipients, and it cannot
initiate a payment to anyone else's account; account ids for both sides must belong to the
same authenticated customer (get them from satchel_list_accounts).
Parameters:
| Name | Type | Notes |
|---|---|---|
from_account_id | integer | Source account id. |
to_account_id | integer | Destination account id. |
amount | number, positive | Amount to move. |
currency | string, 3 letters | e.g. EUR. |
narrative | string, optional | Memo/description. Defaulted if omitted or too short (the real API requires 5+ characters). |
structuredContent (on success):
{
id: number;
amount: number;
currency: string;
narrative: string;
fee?: { amount: number; currency: string };
balances: Array<{ id: number; name: string; balance: { amount: number; currency: string } }>;
}The text response confirms the transfer and shows the updated balance of both accounts involved.
In the sandbox, it's real — the sandbox's ledger, at least
When writes are enabled against the sandbox, a successful transfer genuinely mutates
sandbox account balances and prepends a matching sender/recipient pair of transactions to
the ledger — a follow-up satchel_list_accounts or satchel_list_transactions reflects it
immediately. This is the sandbox's "your agent can act, with your consent" demonstration:
the mutation is real within the sandbox's own state, even though no real money exists
behind it.
In live mode
The same SATCHEL_ALLOW_WRITES=true gate applies, with the same refusal behaviour when
unset. Enabling it against live credentials is a deliberate choice to let this server
initiate a real internal transfer — treat it accordingly. (The hosted remote pilot sidesteps
this question entirely by never registering the tool in the first place; see
Authentication & security.)
Widget
Like satchel_financial_summary, this tool advertises a ui:// HTML widget (see
Widgets) — a confirmation/receipt view. The widget
triggers the transfer only on an explicit user click; it never fires the write as a side
effect of merely being displayed. As with every tool, the text + structuredContent result
is always present regardless of whether a client renders the widget.