@tongateway/mcp 0.14.0 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,7 +52,7 @@ openclaw config set --strict-json plugins.entries.acpx.config.mcpServers '{
52
52
  }'
53
53
  ```
54
54
 
55
- No token needed upfront — the agent authenticates via `request_auth` (generates a one-time link, user connects wallet). Token persists in `~/.tongateway/token` across restarts.
55
+ No token needed upfront — the agent authenticates via `auth.request` (generates a one-time link, user connects wallet). Token persists in `~/.tongateway/token` across restarts.
56
56
 
57
57
  ## Tools
58
58
 
@@ -60,48 +60,55 @@ No token needed upfront — the agent authenticates via `request_auth` (generate
60
60
 
61
61
  | Tool | Description |
62
62
  |------|-------------|
63
- | `request_auth` | Generate a one-time link for wallet connection |
64
- | `get_auth_token` | Retrieve token after user connects wallet |
63
+ | `auth.request` | Generate a one-time link for wallet connection |
64
+ | `auth.get_token` | Retrieve token after user connects wallet |
65
65
 
66
66
  ### Wallet
67
67
 
68
68
  | Tool | Description |
69
69
  |------|-------------|
70
- | `get_wallet_info` | Wallet address, TON balance, account status |
71
- | `get_jetton_balances` | All token balances (USDT, NOT, DOGS, etc.) |
72
- | `get_transactions` | Recent transaction history |
73
- | `get_nft_items` | NFTs owned by the wallet |
70
+ | `wallet.info` | Wallet address, TON balance, account status |
71
+ | `wallet.jettons` | All token balances (USDT, NOT, DOGS, etc.) |
72
+ | `wallet.transactions` | Recent transaction history |
73
+ | `wallet.nfts` | NFTs owned by the wallet |
74
74
 
75
75
  ### Transfers (Safe — requires wallet approval)
76
76
 
77
77
  | Tool | Description |
78
78
  |------|-------------|
79
- | `request_transfer` | Request a TON transfer (to, amountNano, payload?, stateInit?) |
80
- | `get_request_status` | Check transfer status by ID |
81
- | `list_pending_requests` | List all pending requests |
79
+ | `transfer.request` | Request a TON transfer (to, amountNano, payload?, stateInit?) |
80
+ | `transfer.status` | Check transfer status by ID |
81
+ | `transfer.pending` | List all pending requests |
82
82
 
83
83
  ### Lookup
84
84
 
85
85
  | Tool | Description |
86
86
  |------|-------------|
87
- | `resolve_name` | Resolve .ton domain to address |
88
- | `get_ton_price` | Current TON price in USD/EUR |
87
+ | `lookup.resolve_name` | Resolve .ton domain to address |
88
+ | `lookup.price` | Current TON price in USD/EUR |
89
+
90
+ ### DEX (open4dev order book)
91
+
92
+ | Tool | Description |
93
+ |------|-------------|
94
+ | `dex.create_order` | Place a limit order (fromToken, toToken, amount, price) |
95
+ | `dex.pairs` | List available trading pairs |
89
96
 
90
97
  ### Agent Wallet (Autonomous — no approval needed)
91
98
 
92
99
  | Tool | Description |
93
100
  |------|-------------|
94
- | `deploy_agent_wallet` | Deploy a dedicated wallet contract for the agent |
95
- | `execute_agent_wallet_transfer` | Send TON directly from agent wallet |
96
- | `get_agent_wallet_info` | Balance, seqno, agent key status |
101
+ | `agent_wallet.deploy` | Deploy a dedicated wallet contract for the agent |
102
+ | `agent_wallet.transfer` | Send TON directly from agent wallet |
103
+ | `agent_wallet.info` | Balance, seqno, agent key status |
97
104
 
98
105
  ## How it works
99
106
 
100
107
  ```
101
108
  You: "Send 1 TON to alice.ton"
102
109
 
103
- Agent: resolve_name("alice.ton") → 0:83df...
104
- request_transfer(to="0:83df...", amountNano="1000000000")
110
+ Agent: lookup.resolve_name("alice.ton") → 0:83df...
111
+ transfer.request(to="0:83df...", amountNano="1000000000")
105
112
  → Transfer request created. Approve in your wallet app.
106
113
  ```
107
114
 
@@ -110,10 +117,39 @@ For agent wallets (autonomous mode):
110
117
  ```
111
118
  You: "Send 0.5 TON from my agent wallet to 0:abc..."
112
119
 
113
- Agent: execute_agent_wallet_transfer(wallet, to, amount)
120
+ Agent: agent_wallet.transfer(wallet, to, amount)
114
121
  → Transfer executed. No approval needed.
115
122
  ```
116
123
 
124
+ ## Build from Source
125
+
126
+ If you prefer not to use `npx`, you can build and run locally:
127
+
128
+ ```bash
129
+ git clone https://github.com/tongateway/mcp
130
+ cd mcp
131
+ npm install
132
+ npm run build
133
+ ```
134
+
135
+ Then configure your MCP client to use the local build:
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "tongateway": {
141
+ "command": "node",
142
+ "args": ["/path/to/mcp/dist/index.js"],
143
+ "env": {
144
+ "AGENT_GATEWAY_API_URL": "https://api.tongateway.ai"
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ See [SECURITY.md](SECURITY.md) for the full security model.
152
+
117
153
  ## Links
118
154
 
119
155
  - [tongateway.ai](https://tongateway.ai) — landing page + install guides
@@ -121,6 +157,8 @@ Agent: execute_agent_wallet_transfer(wallet, to, amount)
121
157
  - [API Docs](https://api.tongateway.ai/docs) — Swagger UI
122
158
  - [Agent Wallet Contract](https://github.com/tongateway/ton-agent-gateway-contract) — FunC smart contract
123
159
  - [Skill File](https://tongateway.ai/agent-gateway.md) — context file for AI agents
160
+ - [Smithery](https://smithery.ai/servers/tongateway/agent) — MCP marketplace listing
161
+ - [MCP HTTP Endpoint](https://tongateway.run.tools) — remote MCP transport
124
162
 
125
163
  ## License
126
164
 
package/dist/index.js CHANGED
@@ -1,4 +1,33 @@
1
1
  #!/usr/bin/env node
2
+ // Handle --help and --version before importing anything
3
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
4
+ console.log(`@tongateway/mcp — TON blockchain gateway for AI agents
5
+
6
+ Usage:
7
+ npx @tongateway/mcp Start MCP server (stdio transport)
8
+ npx @tongateway/mcp --http Start HTTP server (port 3100)
9
+
10
+ Environment variables:
11
+ AGENT_GATEWAY_API_URL API base URL (default: https://api.tongateway.ai)
12
+ AGENT_GATEWAY_TOKEN Pre-configured auth token (optional)
13
+ MCP_HTTP_PORT HTTP server port (default: 3100 with --http)
14
+
15
+ Tools (16):
16
+ auth.request, auth.get_token
17
+ wallet.info, wallet.jettons, wallet.transactions, wallet.nfts
18
+ transfer.request, transfer.status, transfer.pending
19
+ lookup.resolve_name, lookup.price
20
+ dex.create_order, dex.pairs
21
+ agent_wallet.deploy, agent_wallet.transfer, agent_wallet.info
22
+
23
+ Docs: https://tongateway.ai/docs
24
+ GitHub: https://github.com/tongateway/mcp`);
25
+ process.exit(0);
26
+ }
27
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
28
+ console.log('0.15.0');
29
+ process.exit(0);
30
+ }
2
31
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
32
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
33
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -69,7 +98,7 @@ const server = new McpServer({
69
98
  name: 'agent-gateway',
70
99
  version: '0.1.0',
71
100
  });
72
- server.tool('auth.request', 'Authenticate with TON blockchain. Call this FIRST if you get "No token configured" errors. Generates a one-time link ask the user to open it and connect their wallet. Then call auth.get_token to complete. Token persists across restarts.', {
101
+ server.tool('auth.request', 'Authenticate with TON blockchain. Call this tool FIRST if you get "No token configured" errors. It returns a URL that you MUST display to the user as a clickable link. The user opens it in their browser to connect their wallet. After they confirm, call auth.get_token with the authId. Do NOT use curl or fetch — use this MCP tool. Do NOT poll in a loop — just call auth.get_token once after the user says they connected.', {
73
102
  label: z.string().optional().describe('Label for this agent session (e.g. "claude-agent")'),
74
103
  }, { title: 'Request Authentication', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ label }) => {
75
104
  try {
@@ -86,15 +115,15 @@ server.tool('auth.request', 'Authenticate with TON blockchain. Call this FIRST i
86
115
  {
87
116
  type: 'text',
88
117
  text: [
89
- `Authentication requested.`,
118
+ `IMPORTANT: Show this link to the user NOW:`,
90
119
  ``,
91
- `Ask the user to open this link:`,
92
- data.authUrl,
120
+ `👉 ${data.authUrl}`,
121
+ ``,
122
+ `Tell the user: "Open this link and connect your wallet. Let me know when done."`,
93
123
  ``,
94
124
  `Auth ID: ${data.authId}`,
95
- `Expires: ${new Date(data.expiresAt).toISOString()}`,
96
125
  ``,
97
- `After the user connects their wallet, call auth.get_token with this authId to get the token.`,
126
+ `When the user confirms they connected, call auth.get_token with authId "${data.authId}"`,
98
127
  ].join('\n'),
99
128
  },
100
129
  ],
@@ -165,12 +194,13 @@ server.tool('auth.get_token', 'Complete authentication after the user opened the
165
194
  };
166
195
  }
167
196
  });
168
- server.tool('transfer.request', 'Request a TON transfer. The user must approve it in their wallet app. Amount is in nanoTON (1 TON = 1000000000). Supports optional payload (BOC) and stateInit (for contract deployment). The transfer is queued use transfer.status to check if approved.', {
169
- to: z.string().describe('Destination TON address'),
170
- amountNano: z.string().describe('Amount in nanoTON (1 TON = 1000000000)'),
171
- payload: z.string().optional().describe('Optional BOC-encoded payload for the transaction'),
172
- stateInit: z.string().optional().describe('Optional stateInit BOC for deploying new contracts'),
173
- }, { title: 'Request Transfer', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ to, amountNano, payload, stateInit }) => {
197
+ server.tool('transfer.request', 'Send a TON transfer. The user approves in their wallet app. Use human-readable amounts (e.g. amount="1.5"). Supports .ton domain names directly (e.g. to="alice.ton"). The API handles name resolution and decimal conversion automatically.', {
198
+ to: z.string().describe('Destination: TON address or .ton domain (e.g. "alice.ton" or "EQD...abc")'),
199
+ amount: z.string().describe('Human-readable amount (e.g. "1.5" for 1.5 TON, "100" for 100 TON)'),
200
+ comment: z.string().optional().describe('Text comment/message to attach (e.g. "Payment for services")'),
201
+ payload: z.string().optional().describe('Optional BOC-encoded payload (advanced use comment for text)'),
202
+ stateInit: z.string().optional().describe('Optional stateInit BOC for contract deployment'),
203
+ }, { title: 'Request Transfer', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ to, amount, comment, payload, stateInit }) => {
174
204
  if (!TOKEN) {
175
205
  return {
176
206
  content: [{ type: 'text', text: 'No token configured. Use auth.request first to authenticate.' }],
@@ -178,7 +208,9 @@ server.tool('transfer.request', 'Request a TON transfer. The user must approve i
178
208
  };
179
209
  }
180
210
  try {
181
- const body = { to, amountNano };
211
+ const body = { to, amount };
212
+ if (comment)
213
+ body.comment = comment;
182
214
  if (payload)
183
215
  body.payload = payload;
184
216
  if (stateInit)
@@ -194,13 +226,13 @@ server.tool('transfer.request', 'Request a TON transfer. The user must approve i
194
226
  text: [
195
227
  `Transfer request created.`,
196
228
  `ID: ${result.id}`,
197
- `To: ${result.to}`,
198
- `Amount: ${result.amountNano} nanoTON`,
229
+ `To: ${result.resolved ? `${result.resolved.from} → ${result.resolved.to}` : result.to}`,
230
+ `Amount: ${amount} TON (${result.amountNano} nanoTON)`,
199
231
  `Status: ${result.status}`,
200
- `Expires: ${new Date(result.expiresAt).toISOString()}`,
232
+ comment ? `Comment: ${comment}` : null,
201
233
  ``,
202
- `The wallet owner must approve this in their TON Connect client.`,
203
- ].join('\n'),
234
+ `Approve in your wallet app.`,
235
+ ].filter(Boolean).join('\n'),
204
236
  },
205
237
  ],
206
238
  };
@@ -283,6 +315,56 @@ server.tool('transfer.pending', 'List all transfer requests waiting for wallet o
283
315
  };
284
316
  }
285
317
  });
318
+ server.tool('transfer.batch', 'Send multiple TON transfers in a SINGLE wallet approval. Up to 4 transfers per batch (v4 wallet). All transfers appear as one transaction to sign. Use for batch payments, multi-recipient sends, or multiple DEX orders at once.', {
319
+ transfers: z.string().describe('JSON array of transfers: [{"to":"addr","amountNano":"1000000000","comment":"optional text"},...]'),
320
+ }, { title: 'Batch Transfer', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ transfers: transfersJson }) => {
321
+ if (!TOKEN) {
322
+ return { content: [{ type: 'text', text: 'No token configured. Use auth.request first.' }], isError: true };
323
+ }
324
+ try {
325
+ const transfers = JSON.parse(transfersJson);
326
+ if (!transfers.length)
327
+ throw new Error('No transfers provided');
328
+ if (transfers.length > 4)
329
+ throw new Error('Max 4 transfers per batch (v4 wallet). Use agent_wallet.batch_transfer for more.');
330
+ // Encode comments as payloads
331
+ const processed = [];
332
+ for (const t of transfers) {
333
+ const entry = { to: t.to, amountNano: t.amountNano };
334
+ if (t.comment) {
335
+ const { beginCell } = await import('@ton/core');
336
+ const commentCell = beginCell().storeUint(0, 32).storeStringTail(t.comment).endCell();
337
+ entry.payload = commentCell.toBoc().toString('base64');
338
+ }
339
+ processed.push(entry);
340
+ }
341
+ const result = await apiCall('/v1/safe/tx/batch', {
342
+ method: 'POST',
343
+ body: JSON.stringify({ transfers: processed }),
344
+ });
345
+ const totalNano = BigInt(result.batch?.totalNano ?? '0');
346
+ const totalTon = (totalNano / 1000000000n).toString() + '.' +
347
+ (totalNano % 1000000000n).toString().padStart(9, '0').replace(/0+$/, '') || '0';
348
+ return {
349
+ content: [{
350
+ type: 'text',
351
+ text: [
352
+ `Batch transfer created! ${transfers.length} transfers in 1 approval.`,
353
+ '',
354
+ `Total: ${totalTon} TON`,
355
+ `Request ID: ${result.id}`,
356
+ '',
357
+ ...transfers.map((t, i) => ` ${i + 1}. ${t.amountNano} nanoTON → ${t.to.slice(0, 16)}...${t.comment ? ` "${t.comment}"` : ''}`),
358
+ '',
359
+ 'Approve in your wallet app — one signature for all transfers.',
360
+ ].join('\n'),
361
+ }],
362
+ };
363
+ }
364
+ catch (e) {
365
+ return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
366
+ }
367
+ });
286
368
  server.tool('wallet.info', 'Get the connected wallet address, TON balance (in nanoTON and TON), and account status. Use this to check how much TON the user has before sending transfers.', {}, { title: 'Wallet Info', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async () => {
287
369
  if (!TOKEN) {
288
370
  return { content: [{ type: 'text', text: 'No token configured. Use auth.request first.' }], isError: true };
@@ -413,11 +495,11 @@ server.tool('lookup.price', 'Get the current TON price in USD, EUR, or other cur
413
495
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
414
496
  }
415
497
  });
416
- server.tool('dex.create_order', 'Place a limit order on the open4dev DEX order book. Provide the token pair, amount in smallest units, and price in human-readable format (e.g. price=20 means "1 fromToken = 20 toToken"). The API handles all decimal conversions and slippage automatically. The order requires wallet approval.', {
498
+ server.tool('dex.create_order', 'Place a limit order on the open4dev DEX order book. Both amount and price are human-readable the API converts to raw units automatically. The order requires wallet approval. Slippage (4% including fees) is applied automatically.', {
417
499
  fromToken: z.string().describe('Token to sell, e.g. "NOT", "TON", "USDT"'),
418
500
  toToken: z.string().describe('Token to buy, e.g. "TON", "NOT", "AGNT"'),
419
- amount: z.string().describe('Amount to sell in smallest unit. TON/NOT/DOGS/BUILD/AGNT use 9 decimals (1 TON = 10^9). USDT/XAUT0 use 6 decimals (1 USDT = 10^6).'),
420
- price: z.number().describe('Human-readable price: how many toToken per 1 fromToken. E.g. price=20 means "1 USDT = 20 AGNT". price=0.05 means "1 AGNT = 0.05 USDT".'),
501
+ amount: z.string().describe('Human-readable amount to sell, e.g. "10000" for 10,000 NOT or "5" for 5 USDT'),
502
+ price: z.number().describe('Human-readable price: how many toToken per 1 fromToken. E.g. price=20 means "1 USDT = 20 AGNT". price=0.000289 means "1 NOT = 0.000289 TON".'),
421
503
  }, { title: 'Create DEX Order', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ fromToken, toToken, amount, price }) => {
422
504
  if (!TOKEN) {
423
505
  return { content: [{ type: 'text', text: 'No token configured. Use auth.request first.' }], isError: true };
@@ -644,6 +726,107 @@ server.tool('agent_wallet.transfer', 'Send TON directly from an Agent Wallet —
644
726
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
645
727
  }
646
728
  });
729
+ server.tool('agent_wallet.batch_transfer', 'Send multiple TON transfers in a SINGLE transaction from an Agent Wallet — NO approval needed. All transfers are executed atomically in one external message. Max 255 actions per transaction.', {
730
+ walletAddress: z.string().describe('The agent wallet contract address'),
731
+ transfers: z.string().describe('JSON array of transfers: [{"to":"addr","amountNano":"1000000000"},...]'),
732
+ }, { title: 'Batch Transfer from Agent Wallet', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }, async ({ walletAddress, transfers: transfersJson }) => {
733
+ if (!TOKEN) {
734
+ return { content: [{ type: 'text', text: 'No token configured. Use auth.request first.' }], isError: true };
735
+ }
736
+ try {
737
+ const { Cell, beginCell, Address, SendMode, external, storeMessage } = await import('@ton/core');
738
+ const { sign, keyPairFromSeed } = await import('@ton/crypto');
739
+ const transfers = JSON.parse(transfersJson);
740
+ if (!transfers.length)
741
+ throw new Error('No transfers provided');
742
+ if (transfers.length > 255)
743
+ throw new Error('Max 255 transfers per batch');
744
+ const localWallets = loadLocalWallets();
745
+ const localConfig = localWallets[walletAddress];
746
+ if (!localConfig)
747
+ throw new Error('Agent wallet secret key not found locally.');
748
+ const infoResult = await apiCall(`/v1/agent-wallet/${encodeURIComponent(walletAddress)}/info`);
749
+ const seqno = infoResult.seqno;
750
+ const walletId = localConfig.walletId;
751
+ const secretKeyBuf = Buffer.from(localConfig.agentSecretKey, 'hex');
752
+ let secretKey;
753
+ if (secretKeyBuf.length === 64) {
754
+ secretKey = secretKeyBuf;
755
+ }
756
+ else {
757
+ const kp = keyPairFromSeed(secretKeyBuf);
758
+ secretKey = kp.secretKey;
759
+ }
760
+ // Build actions list with all transfers
761
+ const sendMode = SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
762
+ let actionsList = beginCell().endCell(); // empty tail
763
+ for (const t of transfers) {
764
+ const destAddr = Address.parse(t.to);
765
+ const transferMsg = beginCell()
766
+ .storeUint(0x18, 6)
767
+ .storeAddress(destAddr)
768
+ .storeCoins(BigInt(t.amountNano))
769
+ .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
770
+ .endCell();
771
+ actionsList = beginCell()
772
+ .storeRef(actionsList)
773
+ .storeUint(0x0ec3c86d, 32)
774
+ .storeUint(sendMode, 8)
775
+ .storeRef(transferMsg)
776
+ .endCell();
777
+ }
778
+ // Build and sign external message
779
+ const validUntil = Math.floor(Date.now() / 1000) + 300;
780
+ const unsignedBody = beginCell()
781
+ .storeUint(0x7369676e, 32)
782
+ .storeUint(walletId, 32)
783
+ .storeUint(validUntil, 32)
784
+ .storeUint(seqno, 32)
785
+ .storeMaybeRef(actionsList)
786
+ .endCell();
787
+ const signature = sign(unsignedBody.hash(), secretKey);
788
+ const signedBody = beginCell()
789
+ .storeSlice(unsignedBody.beginParse())
790
+ .storeBuffer(signature)
791
+ .endCell();
792
+ const vaultAddr = Address.parse(walletAddress);
793
+ const extMsg = external({ to: vaultAddr, body: signedBody });
794
+ const boc = beginCell().store(storeMessage(extMsg)).endCell().toBoc().toString('base64');
795
+ // Broadcast
796
+ const broadcastRes = await fetch('https://toncenter.com/api/v2/sendBoc', {
797
+ method: 'POST',
798
+ headers: { 'Content-Type': 'application/json' },
799
+ body: JSON.stringify({ boc }),
800
+ });
801
+ const broadcastData = await broadcastRes.json();
802
+ if (!broadcastData.ok) {
803
+ throw new Error(`Broadcast failed: ${broadcastData.error || JSON.stringify(broadcastData)}`);
804
+ }
805
+ const totalNano = transfers.reduce((sum, t) => sum + BigInt(t.amountNano), 0n);
806
+ const totalTon = (totalNano / 1000000000n).toString() + '.' +
807
+ (totalNano % 1000000000n).toString().padStart(9, '0').replace(/0+$/, '');
808
+ return {
809
+ content: [{
810
+ type: 'text',
811
+ text: [
812
+ `Batch transfer executed! ${transfers.length} transfers in 1 transaction.`,
813
+ '',
814
+ `From: ${walletAddress}`,
815
+ `Total: ${totalTon} TON`,
816
+ `Transfers: ${transfers.length}`,
817
+ `Seqno: ${seqno}`,
818
+ '',
819
+ ...transfers.map((t, i) => ` ${i + 1}. ${t.amountNano} nanoTON → ${t.to.slice(0, 12)}...`),
820
+ '',
821
+ 'All broadcast successfully. No approval needed.',
822
+ ].join('\n'),
823
+ }],
824
+ };
825
+ }
826
+ catch (e) {
827
+ return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
828
+ }
829
+ });
647
830
  server.tool('agent_wallet.info', 'Get info about Agent Wallets — balance, seqno, agent key status. Pass a wallet address for details, or omit to list all agent wallets.', {
648
831
  walletAddress: z.string().optional().describe('Agent wallet address. If omitted, lists all your agent wallets.'),
649
832
  }, { title: 'Agent Wallet Info', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async ({ walletAddress }) => {
package/dist/worker.js CHANGED
@@ -32,10 +32,10 @@ function createMcpServer() {
32
32
  server.tool('lookup.resolve_name', 'Resolve a .ton domain name (e.g. "alice.ton") to a raw wallet address. Always use this before transfer.request when the user provides a .ton name.', { domain: z.string().describe('The .ton domain name to resolve, e.g. "alice.ton" or "foundation.ton"') }, { title: 'Resolve .ton Name', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async () => ({ content: [{ type: 'text', text: 'Use stdio transport for full tool execution.' }] }));
33
33
  server.tool('lookup.price', 'Get the current price of TON in USD, EUR, or other fiat currencies. Use to show users the value of their holdings.', { currencies: z.string().optional().describe('Comma-separated currency codes, e.g. "USD,EUR". Default: "USD"') }, { title: 'TON Price', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async () => ({ content: [{ type: 'text', text: 'Use stdio transport for full tool execution.' }] }));
34
34
  // --- DEX ---
35
- server.tool('dex.create_order', 'Place a limit order on the open4dev DEX order book. Provide token pair, amount in smallest units, and human-readable price. The API handles decimal conversion, slippage (4% including fees), and gas automatically.', {
35
+ server.tool('dex.create_order', 'Place a limit order on the open4dev DEX order book. Both amount and price are human-readable. The API converts to raw units, handles slippage (4% including fees), and gas automatically.', {
36
36
  fromToken: z.string().describe('Token to sell: TON, NOT, USDT, DOGS, BUILD, AGNT, CBBTC, PX, XAUT0'),
37
37
  toToken: z.string().describe('Token to buy: TON, NOT, USDT, DOGS, BUILD, AGNT, CBBTC, PX, XAUT0'),
38
- amount: z.string().describe('Amount to sell in smallest unit. TON/NOT/DOGS/BUILD/AGNT use 9 decimals. USDT/XAUT0 use 6 decimals.'),
38
+ amount: z.string().describe('Human-readable amount to sell, e.g. "10000" for 10,000 NOT or "5" for 5 USDT'),
39
39
  price: z.number().describe('Human-readable price: how many toToken per 1 fromToken. E.g. price=20 means "1 USDT = 20 AGNT".'),
40
40
  }, { title: 'Create DEX Order', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async () => ({ content: [{ type: 'text', text: 'Use stdio transport for full tool execution.' }] }));
41
41
  server.tool('dex.pairs', 'List all available trading pairs and tokens on the open4dev DEX. Returns supported tokens: TON, NOT, USDT, DOGS, BUILD, AGNT, CBBTC, PX, XAUT0.', {}, { title: 'DEX Pairs', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async () => ({ content: [{ type: 'text', text: 'Use stdio transport for full tool execution.' }] }));
@@ -53,16 +53,138 @@ function createMcpServer() {
53
53
  role: 'user',
54
54
  content: {
55
55
  type: 'text',
56
- text: 'Install: npx -y @tongateway/mcp\n\nClaude Code: claude mcp add-json tongateway \'{"command":"npx","args":["-y","@tongateway/mcp"],"env":{"AGENT_GATEWAY_API_URL":"https://api.tongateway.ai"}}\' --scope user\n\nThen say: "Send 1 TON to alice.ton"',
56
+ text: `# Quick Start
57
+
58
+ ## Install
59
+ \`\`\`bash
60
+ claude mcp add-json tongateway '{"command":"npx","args":["-y","@tongateway/mcp"],"env":{"AGENT_GATEWAY_API_URL":"https://api.tongateway.ai"}}' --scope user
61
+ \`\`\`
62
+
63
+ ## First use
64
+ The agent authenticates automatically — it generates a link, you open it and connect your wallet once. Token persists across restarts.
65
+
66
+ ## Try these commands:
67
+ - "What's my TON balance?"
68
+ - "Show my tokens"
69
+ - "Send 1 TON to alice.ton"
70
+ - "What's the current TON price?"
71
+ - "Show my NFTs"`,
57
72
  },
58
73
  }],
59
74
  }));
60
- server.prompt('token-reference', 'Amount conversion reference for TON blockchain', async () => ({
75
+ server.prompt('token-reference', 'Amount conversion and token decimals reference', async () => ({
61
76
  messages: [{
62
77
  role: 'user',
63
78
  content: {
64
79
  type: 'text',
65
- text: 'TON amounts are in nanoTON (1 TON = 10^9 nanoTON):\n0.1 TON = 100000000\n0.5 TON = 500000000\n1 TON = 1000000000\n10 TON = 10000000000\n\nUSDT/XAUT0 use 6 decimals. All other jettons use 9 decimals.',
80
+ text: `# Token Reference
81
+
82
+ ## Amount conversion (nanoTON)
83
+ | TON | nanoTON |
84
+ |-------|----------------|
85
+ | 0.1 | 100000000 |
86
+ | 0.5 | 500000000 |
87
+ | 1 | 1000000000 |
88
+ | 10 | 10000000000 |
89
+
90
+ ## Token decimals
91
+ - 9 decimals: TON, NOT, DOGS, BUILD, AGNT, PX, CBBTC
92
+ - 6 decimals: USDT, XAUT0
93
+
94
+ ## DEX price format
95
+ Price is human-readable: price=20 means "1 fromToken = 20 toToken"
96
+ Example: USDT→AGNT at price=20 means "1 USDT = 20 AGNT"`,
97
+ },
98
+ }],
99
+ }));
100
+ server.prompt('example-transfer', 'Example: Send TON to a .ton domain with price check', async () => ({
101
+ messages: [{
102
+ role: 'user',
103
+ content: {
104
+ type: 'text',
105
+ text: `# Example: Send TON to alice.ton
106
+
107
+ ## Step 1: Check balance
108
+ wallet.info()
109
+ → Address: 0:9d43...0c02, Balance: 823.18 TON, Status: active
110
+
111
+ ## Step 2: Check price
112
+ lookup.price({ currencies: "USD" })
113
+ → 1 TON = $2.45 USD
114
+
115
+ ## Step 3: Resolve .ton domain
116
+ lookup.resolve_name({ domain: "alice.ton" })
117
+ → alice.ton → 0:83df...31a8
118
+
119
+ ## Step 4: Send transfer
120
+ transfer.request({ to: "0:83df...31a8", amountNano: "500000000" })
121
+ → Transfer request created (ID: abc-123). Approve in your wallet app.
122
+
123
+ ## Step 5: Check status
124
+ transfer.status({ id: "abc-123" })
125
+ → Status: confirmed, Broadcast: success`,
126
+ },
127
+ }],
128
+ }));
129
+ server.prompt('example-dex-order', 'Example: Place a DEX order to swap tokens', async () => ({
130
+ messages: [{
131
+ role: 'user',
132
+ content: {
133
+ type: 'text',
134
+ text: `# Example: Swap 10,000 NOT for TON
135
+
136
+ ## Step 1: Check available tokens
137
+ dex.pairs()
138
+ → Available tokens: TON, NOT, USDT, DOGS, BUILD, AGNT, CBBTC, PX, XAUT0
139
+
140
+ ## Step 2: Check your NOT balance
141
+ wallet.jettons()
142
+ → NOT: 3,186,370.60, USDT: 107.79, BUILD: 45,277.57
143
+
144
+ ## Step 3: Get current price
145
+ lookup.price({ currencies: "USD" })
146
+ → 1 TON = $2.45 USD
147
+ (NOT ≈ 0.000289 TON per NOT)
148
+
149
+ ## Step 4: Place order
150
+ dex.create_order({
151
+ fromToken: "NOT",
152
+ toToken: "TON",
153
+ amount: "10000", // human-readable, API converts to raw
154
+ price: 0.000289 // TON per NOT
155
+ })
156
+ → Order placed! Approve in your wallet app.`,
157
+ },
158
+ }],
159
+ }));
160
+ server.prompt('example-agent-wallet', 'Example: Deploy and use an autonomous Agent Wallet', async () => ({
161
+ messages: [{
162
+ role: 'user',
163
+ content: {
164
+ type: 'text',
165
+ text: `# Example: Autonomous Agent Wallet
166
+
167
+ ⚠️ WARNING: Agent Wallet allows spending WITHOUT approval!
168
+
169
+ ## Step 1: Deploy
170
+ agent_wallet.deploy()
171
+ → Agent Wallet deployed at EQCT1... Approve 0.1 TON deploy fee in wallet.
172
+
173
+ ## Step 2: Top up
174
+ transfer.request({ to: "EQCT1...", amountNano: "1000000000" })
175
+ → Transfer 1 TON to agent wallet. Approve in wallet.
176
+
177
+ ## Step 3: Send from agent wallet (NO approval needed!)
178
+ agent_wallet.transfer({
179
+ walletAddress: "0:93d4...",
180
+ to: "0:abc...",
181
+ amountNano: "500000000"
182
+ })
183
+ → Transfer executed. No approval needed.
184
+
185
+ ## Check balance
186
+ agent_wallet.info({ walletAddress: "0:93d4..." })
187
+ → Balance: 0.5 TON, Seqno: 1, Status: active`,
66
188
  },
67
189
  }],
68
190
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tongateway/mcp",
3
- "version": "0.14.0",
3
+ "version": "0.19.2",
4
4
  "description": "TON blockchain gateway for AI agents — 16 MCP tools: wallet info, transfers, jettons, NFTs, DNS, prices, DEX orders, agent wallets",
5
5
  "homepage": "https://tongateway.ai",
6
6
  "license": "MIT",
@@ -25,7 +25,8 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "build": "tsc",
28
- "dev": "tsx src/index.ts"
28
+ "dev": "tsx src/index.ts",
29
+ "test": "vitest run"
29
30
  },
30
31
  "dependencies": {
31
32
  "@modelcontextprotocol/sdk": "^1.12.1",
@@ -38,6 +39,7 @@
38
39
  "@types/node": "^22.17.2",
39
40
  "tsx": "^4.20.5",
40
41
  "typescript": "^5.9.2",
42
+ "vitest": "^4.1.1",
41
43
  "wrangler": "^4.77.0"
42
44
  }
43
45
  }