All posts
8 min read

TON Token Swaps for AI Agents: Non-Custodial by Design

How an AI agent swaps tokens on TON non-custodially: get_swap_quote and build_swap_tx via Omniston, with no private keys ever touching the server.

TONMCPtoken swapnon-custodialOmnistonAI agents

Who holds the key — the central question of agent-driven non-custodial token swaps on TON

Picture this: you task an AI agent with watching the market and buying USDT with GRAM whenever the price dips. The agent reads the market, picks its moment, prepares the trade — and then a question surfaces that people usually think about far too late. For the agent to actually execute the swap, someone has to sign the transaction with a private key. Who holds that key?

If the answer is "the service the agent works through," you have handed someone else's code the right to spend your money. One prompt injection, one leaked log — and the agent signs something you never intended. The real problem here isn't "how to call a DEX" — it's how to let the agent assemble the trade while keeping the signature strictly with the human's wallet. Below, we'll walk through how to do a non-custodial TON token swap with an agent.

The two halves of a swap: quoting is safe, signing is not

A DEX swap has two fundamentally different halves. The first is computational and safe: fetch the rate, calculate slippage, assemble the transaction body. The second is irreversible and dangerous: sign that body with a private key and broadcast it. That split forces a choice:

  • The custodial model. The agent (or the server behind it) holds the key and signs on its own. Convenient — the agent acts autonomously. But the signing key ends up sitting next to an LLM, and an LLM is a non-deterministic thing that can be talked into anything by untrusted text (jetton descriptions, other tools' outputs, user messages). This is how the official @ton/mcp from the TON Foundation works, for example: it's an agent wallet with an operator key that signs transfers and swaps by itself (a split-key scheme: the operator key lives with the agent, the owner key with the user). The model works, and it has real strengths — autonomous spending, NFT and DNS support, official-package status. But it still comes down to trusting whoever holds the key.
  • The non-custodial model. The server only prepares the transaction and returns it unsigned. The signature comes from the user's wallet via TonConnect. The server never sees or stores the private key.

For a trading agent that runs around the clock, the second model removes the biggest risk: even if the infrastructure is compromised, the worst that can go wrong is an unsigned draft — one you still confirm manually or via your wallet's own policy. This fork is covered in more depth in the honest comparison of TONNode vs the official @ton/mcp.

TONNode's non-custodial model: the server hands back an unsigned transaction

TONNode is a hosted MCP server for TON. MCP (Model Context Protocol) is the standard that AI agents (Claude, Cursor, ChatGPT/Codex, and any MCP client) use to call external tools. TONNode gives an agent 16 tools for working with TON, and the swap, cross-chain, and wallet-generation tools are strictly non-custodial.

The key invariant: the server NEVER signs transactions and never holds funds or private keys. For a swap, that means the final tool returns not an "executed trade" but an unsigned TonConnect message — an object ready for signing. The server physically cannot substitute a different recipient after you sign, because it never holds the signature.

A swap in TONNode is exactly two tools:

  • get_swap_quote — a firm quote,
  • build_swap_tx — an unsigned transaction, ready for TonConnect.

Both run on the Omniston protocol, which aggregates liquidity from STON.fi and DeDust at once — the quote is computed across both DEXes, and you get the best one available. Swaps run in GRAM⇄jetton pairs. GRAM is the renamed Toncoin (renamed in June 2026); the network itself is still called TON. So "GRAM" in a quote means the network's native coin.

get_swap_quote: a firm quote via Omniston (STON.fi + DeDust)

Step one is finding out what you'll actually receive. get_swap_quote returns a firm quote: the expected output amount and the rate. Since Omniston queries STON.fi and DeDust simultaneously under the hood, the agent doesn't need to crawl pools and compare them by hand — it sees an already-aggregated result.

Get a quote for swapping 100 USDT → GRAM.
Use get_swap_quote. Show the expected output and the rate.

One detail trips up quote math constantly: quotes operate in raw units (a token's smallest indivisible fractions), not "human" numbers. So before you can compute a jetton swap amount, you need to know its decimals.

get_jetton_info: why you need decimals before a swap (USDT = 6)

Here's the classic mistake that breaks swaps: the agent takes "100 USDT" and puts 100 in the amount field. But on-chain, a jetton has no concept of "100" — only raw units. How many raw units make up one token is defined by the decimals parameter, and the raw figure works out to amount × 10^decimals.

  • USDT on TON has decimals = 6 → 100 USDT is 100 × 10^6 = 100,000,000 raw units.
  • Most jettons have decimals = 9 → 100 tokens is 100,000,000,000 raw units.

If you naively append nine zeros to "100 USDT" (the way you would for GRAM), you request a swap 1000 times larger than intended. Being off by three orders of magnitude is remarkably easy here. That's why get_jetton_info is a mandatory step: it returns the jetton's name, symbol, decimals, and supply.

Before swapping, look up the input jetton's decimals via get_jetton_info,
and only then compute the amount in raw units.

There's a deeper dive on this in the article on connecting AI agents to TON via MCP.

Supporting tools: parse_address and get_jetton_balance

Two supporting tools are also worth calling before building the transaction:

  • parse_address — offline address validation and conversion (EQ/UQ/raw). Cheap insurance against a typo in a jetton or recipient address, before you assemble anything.
  • get_jetton_balance — a jetton balance by the wallet's main address. The jetton-wallet address for the balance is derived on-chain inside the tool, so you pass the main address, not the jetton-wallet one. A common mistake is feeding it a jetton-wallet address; don't — give it the main one.

build_swap_tx: an unsigned message for TonConnect

Once you have the quote and the amounts are computed in the right raw units, build_swap_tx assembles the transaction itself. And this is where non-custodial actually means something: the tool returns an UNSIGNED TonConnect message. It's not a submitted trade — it's a draft: contract address, amount, payload. The server hands you the envelope but leaves it unsealed — your wallet applies the seal (the signature).

From there the message goes to a TonConnect-compatible wallet (Tonkeeper and others), the user sees exactly what they're signing, and confirms. At no step does the private key leave the wallet or touch a TONNode server.

Build the swap transaction from the latest quote via build_swap_tx.
Return the unsigned TonConnect message — I'll sign it in my wallet.

The full flow: from quote to a signature in the wallet

A typical sequence for a jetton swap looks like this:

  1. parse_address — validate and normalize the jetton and recipient addresses (offline).
  2. get_jetton_info — look up decimals (6 for USDT, usually 9), name, symbol.
  3. get_jetton_balance — confirm there are enough funds (main address, not the jetton wallet).
  4. get_swap_quote — get a firm quote via Omniston (STON.fi + DeDust).
  5. build_swap_tx — assemble the unsigned TonConnect transaction.
  6. The user's wallet signs via TonConnect — the signature stays on the user's side.

At the prompt level, this reads naturally to an agent:

I want to swap 50 GRAM for USDT.
First verify the USDT jetton address via parse_address,
look up its decimals via get_jetton_info,
check the balance via get_jetton_balance,
get a quote with get_swap_quote and build the transaction with build_swap_tx.
Do not send the transaction — return the unsigned message for TonConnect.

The agent will break this down into tool calls on its own, and what you get back is a draft you sign in your own wallet. Note that the final step is not a TONNode tool. The wallet performs the signing, and that's exactly where the non-custodial boundary runs. If you're building a full trading agent, see the breakdown of latency and delays when trading on TON — it embeds this flow into a decision loop. And for trades beyond TON, TONNode ships five non-custodial cross-chain tools built on atomic HTLC escrow.

How to connect the swap tools to Claude, Cursor, or ChatGPT

The swap tools are available on every plan, including the free Hobby tier (60 requests/min) — Hobby is a hosted key. You only pay for throughput; the set of 16 tools is identical on every key.

Local and free — for reads. The @tonnode/mcp package is open source (MIT), speaks TON's native ADNL protocol with no HTTP middlemen, and starts with a single command. The public config gives you the full set of read tools:

{
  "mcpServers": {
    "ton": {
      "command": "npx",
      "args": ["-y", "@tonnode/mcp"]
    }
  }
}

Hosted endpoint — for swaps and everything else. Your own key (the free Hobby tier is enough), guaranteed throughput, and all 16 tools, including get_swap_quote and build_swap_tx:

{
  "mcpServers": {
    "ton": {
      "type": "http",
      "url": "https://mcp.tonnode.io/mcp",
      "headers": { "Authorization": "Bearer tn_live_…" }
    }
  }
}

This config goes into your client's MCP settings — Claude Desktop, Cursor, ChatGPT/Codex, or any other MCP-compatible agent. Once the hosted key is connected, get_swap_quote and build_swap_tx show up in the agent's toolset automatically. The tn_live_… key is issued right after sign-in, no card required.

The short version

  • A swap in TONNode is two tools: get_swap_quote (a firm quote) and build_swap_tx (an unsigned transaction for TonConnect).
  • Both run through Omniston — the liquidity aggregator over STON.fi and DeDust.
  • The signature always stays with the user. The server never stores keys and never signs — unlike the custodial official @ton/mcp.
  • Before swapping a jetton, look up its decimals via get_jetton_info (USDT = 6, most jettons = 9), or the raw-unit amount will come out wrong.
  • The swap tools work on any hosted key, including the free Hobby tier.

A non-custodial swap on TON for an agent comes down to a discipline of role separation: the server computes and assembles, the wallet signs. In TONNode that's exactly two tools on top of Omniston, plus get_jetton_info for correct decimals — and no private key ever leaves the user's side.

The easiest way to start is with a free key. Grab a Hobby key with no card and hook up the swap tools in a couple of minutes: tonnode.io/dashboard?plan=hobby. Want to browse the full toolset first? Head over to the tools page.

Give your agent TON access

16 MCP tools: reads, non-custodial swaps, cross-chain and wallets. Free tier — 60 req/min, no card required.