@tongateway/mcp 0.6.0 → 0.8.0

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.
Files changed (3) hide show
  1. package/README.md +95 -13
  2. package/dist/index.js +249 -11
  3. package/package.json +3 -1
package/README.md CHANGED
@@ -1,24 +1,34 @@
1
1
  # @tongateway/mcp
2
2
 
3
- MCP server for [Agent Gateway](https://github.com/pewpewgogo/ton-agent-gateway) — lets AI agents request TON blockchain transfers via Model Context Protocol.
3
+ MCP server for [Agent Gateway](https://tongateway.ai) — gives AI agents full access to the TON blockchain via Model Context Protocol.
4
4
 
5
- ## Install
5
+ **14 tools:** wallet info, jettons, NFTs, transactions, transfers, .ton DNS, prices, agent wallets, and more.
6
+
7
+ ## Quick Start
8
+
9
+ ### Claude Code
6
10
 
7
11
  ```bash
8
- npm install -g @tongateway/mcp
12
+ claude mcp add-json tongateway '{
13
+ "command": "npx",
14
+ "args": ["-y", "@tongateway/mcp"],
15
+ "env": {
16
+ "AGENT_GATEWAY_API_URL": "https://api.tongateway.ai"
17
+ }
18
+ }' --scope user
9
19
  ```
10
20
 
11
- ## Configure
21
+ ### Cursor
12
22
 
13
- Add to your MCP client config (Claude Code, Cursor, etc.):
23
+ Add to Cursor Settings MCP Servers:
14
24
 
15
25
  ```json
16
26
  {
17
27
  "mcpServers": {
18
28
  "tongateway": {
19
- "command": "tongateway-mcp",
29
+ "command": "npx",
30
+ "args": ["-y", "@tongateway/mcp"],
20
31
  "env": {
21
- "AGENT_GATEWAY_TOKEN": "YOUR_TOKEN_HERE",
22
32
  "AGENT_GATEWAY_API_URL": "https://api.tongateway.ai"
23
33
  }
24
34
  }
@@ -26,18 +36,90 @@ Add to your MCP client config (Claude Code, Cursor, etc.):
26
36
  }
27
37
  ```
28
38
 
29
- Get your token at [tongateway.ai/app.html](https://tongateway.ai/app.html).
39
+ ### OpenClaw
40
+
41
+ ```bash
42
+ openclaw config set --strict-json plugins.entries.acpx.config.mcpServers '{
43
+ "tongateway": {
44
+ "command": "npx",
45
+ "args": ["-y", "@tongateway/mcp"],
46
+ "env": {
47
+ "AGENT_GATEWAY_API_URL": "https://api.tongateway.ai"
48
+ }
49
+ }
50
+ }'
51
+ ```
52
+
53
+ 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.
30
54
 
31
55
  ## Tools
32
56
 
57
+ ### Auth
58
+
59
+ | Tool | Description |
60
+ |------|-------------|
61
+ | `request_auth` | Generate a one-time link for wallet connection |
62
+ | `get_auth_token` | Retrieve token after user connects wallet |
63
+
64
+ ### Wallet
65
+
33
66
  | Tool | Description |
34
- |---|---|
35
- | `request_transfer` | Request a TON transfer (to, amountNano, payloadBoc?) |
36
- | `get_request_status` | Check status of a request by ID |
67
+ |------|-------------|
68
+ | `get_wallet_info` | Wallet address, TON balance, account status |
69
+ | `get_jetton_balances` | All token balances (USDT, NOT, DOGS, etc.) |
70
+ | `get_transactions` | Recent transaction history |
71
+ | `get_nft_items` | NFTs owned by the wallet |
72
+
73
+ ### Transfers (Safe — requires wallet approval)
74
+
75
+ | Tool | Description |
76
+ |------|-------------|
77
+ | `request_transfer` | Request a TON transfer (to, amountNano, payload?, stateInit?) |
78
+ | `get_request_status` | Check transfer status by ID |
37
79
  | `list_pending_requests` | List all pending requests |
38
80
 
81
+ ### Lookup
82
+
83
+ | Tool | Description |
84
+ |------|-------------|
85
+ | `resolve_name` | Resolve .ton domain to address |
86
+ | `get_ton_price` | Current TON price in USD/EUR |
87
+
88
+ ### Agent Wallet (Autonomous — no approval needed)
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `deploy_agent_wallet` | Deploy a dedicated wallet contract for the agent |
93
+ | `execute_agent_wallet_transfer` | Send TON directly from agent wallet |
94
+ | `get_agent_wallet_info` | Balance, seqno, agent key status |
95
+
96
+ ## How it works
97
+
98
+ ```
99
+ You: "Send 1 TON to alice.ton"
100
+
101
+ Agent: resolve_name("alice.ton") → 0:83df...
102
+ request_transfer(to="0:83df...", amountNano="1000000000")
103
+ → Transfer request created. Approve in your wallet app.
104
+ ```
105
+
106
+ For agent wallets (autonomous mode):
107
+
108
+ ```
109
+ You: "Send 0.5 TON from my agent wallet to 0:abc..."
110
+
111
+ Agent: execute_agent_wallet_transfer(wallet, to, amount)
112
+ → Transfer executed. No approval needed.
113
+ ```
114
+
39
115
  ## Links
40
116
 
41
- - [Agent Gateway](https://github.com/pewpewgogo/ton-agent-gateway) — main repo with all links
42
- - [Dashboard](https://tongateway.ai) — connect wallet & manage tokens
117
+ - [tongateway.ai](https://tongateway.ai) — landing page + install guides
118
+ - [Dashboard](https://tongateway.ai/app.html) — connect wallet & manage tokens
43
119
  - [API Docs](https://api.tongateway.ai/docs) — Swagger UI
120
+ - [Agent Wallet Contract](https://github.com/tongateway/ton-agent-gateway-contract) — FunC smart contract
121
+ - [Skill File](https://tongateway.ai/agent-gateway.md) — context file for AI agents
122
+
123
+ ## License
124
+
125
+ MIT
package/dist/index.js CHANGED
@@ -25,6 +25,24 @@ function saveToken(token) {
25
25
  catch { }
26
26
  }
27
27
  let TOKEN = loadToken();
28
+ // Compiled AgentVault contract code (embedded for security — no external dependency)
29
+ const AGENT_WALLET_CODE_HEX = 'b5ee9c7241020a01000210000114ff00f4a413f4bcf2c80b01020120020702014803060188d020d749c120915b8eb920d70b1f20821061677374bd2182106172766bbdb0218210616f776bbdb0925f03e0821005f5e10070fb0202d0d3030171b0925f03e0fa4030e20401a2ed44d0d200d31fd31fd3fffa40d3ffd31f305172c705f2e19c078020d7218040d72128821061677374ba8e2336363603d3ffd31f301036102506c8ca0015cb1f13cb1fcbff01cf16cbffcb1fc9ed54e30e0500b02882106172766bba8e2230353535702010365e22102306c8ca0015cb1f13cb1fcbff01cf16cbffcb1fc9ed54e032078210616f776bba8e1bd3ff30552306c8ca0015cb1f13cb1fcbff01cf16cbffcb1fc9ed54e05f07f2000017a0992fda89a0e3ae43ae163f0106f2db3c0801f620d70b1f82107369676ebaf2e195208308d722018308d723208020d721d31fd31fd31fed44d0d200d31fd31fd3fffa40d3ffd31f3026b3f2d1905185baf2e1915193baf2e19207f823bbf2d19408f901547098f9107029c300953051a8f91092323ae25290b1f2e19308b397f82324bcf2d19adef800a4506510470900e0470306c8ca0015cb1f13cb1fcbff01cf16cbffcb1fc9ed54f80ff40430206e91308e4c7f21d73930709421c700b38e2d01d72820761e436c20d749c008f2e19d20d74ac002f2e19d20d71d06c712c2005230b0f2d19ed74cd7393001a4e86c128407bbf2e19dd74ac000f2e19ded55e2c6472d0b';
30
+ // Local wallet storage — agent secret keys never leave the machine
31
+ const WALLETS_FILE = join(homedir(), '.tongateway', 'wallets.json');
32
+ function loadLocalWallets() {
33
+ try {
34
+ return JSON.parse(readFileSync(WALLETS_FILE, 'utf-8'));
35
+ }
36
+ catch {
37
+ return {};
38
+ }
39
+ }
40
+ function saveLocalWallet(address, agentSecretKey, walletId) {
41
+ const wallets = loadLocalWallets();
42
+ wallets[address] = { agentSecretKey, walletId };
43
+ mkdirSync(join(homedir(), '.tongateway'), { recursive: true });
44
+ writeFileSync(WALLETS_FILE, JSON.stringify(wallets, null, 2), 'utf-8');
45
+ }
28
46
  async function apiCall(path, options = {}) {
29
47
  const headers = {
30
48
  'Content-Type': 'application/json',
@@ -49,7 +67,7 @@ const server = new McpServer({
49
67
  name: 'agent-gateway',
50
68
  version: '0.1.0',
51
69
  });
52
- server.tool('request_auth', 'Request wallet authentication. Generates a one-time link for the user to connect their TON wallet. After the user connects, use get_auth_token to retrieve the token. Use this when no token is configured.', {
70
+ server.tool('request_auth', '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 get_auth_token to complete. Token persists across restarts.', {
53
71
  label: z.string().optional().describe('Label for this agent session (e.g. "claude-agent")'),
54
72
  }, async ({ label }) => {
55
73
  try {
@@ -87,7 +105,7 @@ server.tool('request_auth', 'Request wallet authentication. Generates a one-time
87
105
  };
88
106
  }
89
107
  });
90
- server.tool('get_auth_token', 'Check if the user has completed wallet authentication and retrieve the token. Call this after request_auth once the user has opened the link and connected their wallet.', {
108
+ server.tool('get_auth_token', 'Complete authentication after the user opened the link from request_auth. Pass the authId you received. Once successful, all other tools become available. You only need to authenticate once — the token is saved automatically.', {
91
109
  authId: z.string().describe('The authId returned by request_auth'),
92
110
  }, async ({ authId }) => {
93
111
  try {
@@ -137,7 +155,7 @@ server.tool('get_auth_token', 'Check if the user has completed wallet authentica
137
155
  };
138
156
  }
139
157
  });
140
- server.tool('request_transfer', 'Request a TON transfer from the wallet owner. The request will be queued and the owner must approve it via TON Connect.', {
158
+ server.tool('request_transfer', '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 get_request_status to check if approved.', {
141
159
  to: z.string().describe('Destination TON address'),
142
160
  amountNano: z.string().describe('Amount in nanoTON (1 TON = 1000000000)'),
143
161
  payload: z.string().optional().describe('Optional BOC-encoded payload for the transaction'),
@@ -184,7 +202,7 @@ server.tool('request_transfer', 'Request a TON transfer from the wallet owner. T
184
202
  };
185
203
  }
186
204
  });
187
- server.tool('get_request_status', 'Check the status of a previously submitted transfer request.', {
205
+ server.tool('get_request_status', 'Check the status of a transfer request. Statuses: pending (waiting for approval), confirmed (signed and broadcast), rejected (user declined), expired (5 min timeout). Also shows broadcast result if available.', {
188
206
  id: z.string().describe('The request ID returned by request_transfer'),
189
207
  }, async ({ id }) => {
190
208
  if (!TOKEN) {
@@ -223,7 +241,7 @@ server.tool('get_request_status', 'Check the status of a previously submitted tr
223
241
  };
224
242
  }
225
243
  });
226
- server.tool('list_pending_requests', 'List all pending transfer requests waiting for wallet owner approval.', {}, async () => {
244
+ server.tool('list_pending_requests', 'List all transfer requests waiting for wallet owner approval. Use to check if there are unfinished transfers.', {}, async () => {
227
245
  if (!TOKEN) {
228
246
  return {
229
247
  content: [{ type: 'text', text: 'No token configured. Use request_auth first to authenticate.' }],
@@ -255,7 +273,7 @@ server.tool('list_pending_requests', 'List all pending transfer requests waiting
255
273
  };
256
274
  }
257
275
  });
258
- server.tool('get_wallet_info', 'Get the connected wallet address, TON balance, and account status.', {}, async () => {
276
+ server.tool('get_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.', {}, async () => {
259
277
  if (!TOKEN) {
260
278
  return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
261
279
  }
@@ -278,7 +296,7 @@ server.tool('get_wallet_info', 'Get the connected wallet address, TON balance, a
278
296
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
279
297
  }
280
298
  });
281
- server.tool('get_jetton_balances', 'Get all jetton (token) balances in the connected wallet. Shows USDT, NOT, DOGS, and other tokens.', {}, async () => {
299
+ server.tool('get_jetton_balances', 'List all tokens (jettons) in the wallet USDT, NOT, DOGS, and others. Shows symbol, name, balance, and decimals for each. Use this when the user asks about their tokens or wants to know what they hold.', {}, async () => {
282
300
  if (!TOKEN) {
283
301
  return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
284
302
  }
@@ -303,7 +321,7 @@ server.tool('get_jetton_balances', 'Get all jetton (token) balances in the conne
303
321
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
304
322
  }
305
323
  });
306
- server.tool('get_transactions', 'Get recent transaction history for the connected wallet.', {
324
+ server.tool('get_transactions', 'Get recent transaction history. Shows timestamps, action types, and whether transactions were flagged as scam. Use when the user asks "what happened" or wants to review recent activity.', {
307
325
  limit: z.number().optional().describe('Number of transactions to return (default 10)'),
308
326
  }, async ({ limit }) => {
309
327
  if (!TOKEN) {
@@ -328,7 +346,7 @@ server.tool('get_transactions', 'Get recent transaction history for the connecte
328
346
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
329
347
  }
330
348
  });
331
- server.tool('get_nft_items', 'List NFTs owned by the connected wallet.', {}, async () => {
349
+ server.tool('get_nft_items', 'List all NFTs owned by the wallet — name, collection, and address for each. Use when the user asks about their NFTs or collectibles.', {}, async () => {
332
350
  if (!TOKEN) {
333
351
  return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
334
352
  }
@@ -347,7 +365,7 @@ server.tool('get_nft_items', 'List NFTs owned by the connected wallet.', {}, asy
347
365
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
348
366
  }
349
367
  });
350
- server.tool('resolve_name', 'Resolve a .ton domain name to a wallet address. Use this when the user says "send to alice.ton" instead of a raw address.', {
368
+ server.tool('resolve_name', 'Resolve a .ton domain name (like "alice.ton") to a raw wallet address. ALWAYS use this before request_transfer when the user gives a .ton name instead of a raw address.', {
351
369
  domain: z.string().describe('The .ton domain name to resolve (e.g. "alice.ton")'),
352
370
  }, async ({ domain }) => {
353
371
  try {
@@ -366,7 +384,7 @@ server.tool('resolve_name', 'Resolve a .ton domain name to a wallet address. Use
366
384
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
367
385
  }
368
386
  });
369
- server.tool('get_ton_price', 'Get the current price of TON in USD and other currencies.', {
387
+ server.tool('get_ton_price', 'Get the current TON price in USD, EUR, or other currencies. Use when the user asks "how much is my TON worth" or before transfers to show USD equivalents.', {
370
388
  currencies: z.string().optional().describe('Comma-separated currencies (default "USD")'),
371
389
  }, async ({ currencies }) => {
372
390
  try {
@@ -385,5 +403,225 @@ server.tool('get_ton_price', 'Get the current price of TON in USD and other curr
385
403
  return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
386
404
  }
387
405
  });
406
+ server.tool('deploy_agent_wallet', 'Deploy an Agent Wallet smart contract — a dedicated sub-wallet for autonomous operations. WARNING: The agent can spend funds from this wallet WITHOUT user approval. Only deploy if the user explicitly wants autonomous transfers. After deployment, top up the wallet with funds.', {}, async () => {
407
+ if (!TOKEN) {
408
+ return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
409
+ }
410
+ try {
411
+ // Get owner's public key
412
+ const meResult = await apiCall('/v1/auth/me');
413
+ const ownerAddress = meResult.address;
414
+ // Get owner public key from tonapi
415
+ const pubKeyRes = await fetch(`https://tonapi.io/v2/wallet/${encodeURIComponent(ownerAddress)}/get-account-public-key`);
416
+ const pubKeyData = await pubKeyRes.json();
417
+ if (!pubKeyData.public_key)
418
+ throw new Error('Could not get owner public key');
419
+ const ownerPublicKey = pubKeyData.public_key;
420
+ // Generate agent keypair on server
421
+ const deployResult = await apiCall('/v1/agent-wallet/deploy', {
422
+ method: 'POST',
423
+ body: JSON.stringify({ ownerPublicKey }),
424
+ });
425
+ const { agentPublicKey, agentSecretKey, walletId } = deployResult;
426
+ // Build stateInit using embedded compiled code
427
+ const { Cell, beginCell, Address, contractAddress, storeStateInit } = await import('@ton/core');
428
+ const code = Cell.fromBoc(Buffer.from(AGENT_WALLET_CODE_HEX, 'hex'))[0];
429
+ const ownerPubBuf = Buffer.from(ownerPublicKey, 'hex');
430
+ const agentPubBuf = Buffer.from(agentPublicKey, 'hex');
431
+ const adminAddr = Address.parse(ownerAddress);
432
+ const data = beginCell()
433
+ .storeBit(true) // signatureAllowed
434
+ .storeUint(0, 32) // seqno
435
+ .storeUint(walletId, 32) // walletId
436
+ .storeBuffer(ownerPubBuf, 32) // ownerPublicKey
437
+ .storeAddress(adminAddr) // adminAddress
438
+ .storeBuffer(agentPubBuf, 32) // agentPublicKey (set from the start)
439
+ .storeUint(Math.floor(Date.now() / 1000) + 10 * 365 * 24 * 3600, 32) // agentValidUntil (10 years)
440
+ .endCell();
441
+ const init = { code, data };
442
+ const address = contractAddress(0, init);
443
+ const stateInitCell = beginCell().store(storeStateInit(init)).endCell();
444
+ const stateInitBoc = stateInitCell.toBoc().toString('base64');
445
+ // Deploy via safe transfer (user approves)
446
+ const transferResult = await apiCall('/v1/safe/tx/transfer', {
447
+ method: 'POST',
448
+ body: JSON.stringify({
449
+ to: address.toRawString(),
450
+ amountNano: '100000000', // 0.1 TON for deployment
451
+ stateInit: stateInitBoc,
452
+ }),
453
+ });
454
+ // Register the wallet on the server
455
+ await apiCall('/v1/agent-wallet/register', {
456
+ method: 'POST',
457
+ body: JSON.stringify({
458
+ address: address.toRawString(),
459
+ agentSecretKey,
460
+ agentPublicKey,
461
+ ownerPublicKey,
462
+ walletId,
463
+ }),
464
+ });
465
+ // Save secret key locally — never leaves this machine
466
+ saveLocalWallet(address.toRawString(), agentSecretKey, walletId);
467
+ return {
468
+ content: [{
469
+ type: 'text',
470
+ text: [
471
+ 'Agent Wallet deployment requested!',
472
+ '',
473
+ `Address: ${address.toString()}`,
474
+ `Raw: ${address.toRawString()}`,
475
+ `Request ID: ${transferResult.id}`,
476
+ '',
477
+ 'Approve the deployment in your wallet app (0.1 TON).',
478
+ 'After approval, top up the wallet with funds the agent can spend.',
479
+ '',
480
+ 'WARNING: The agent can spend funds from this wallet without your approval.',
481
+ ].join('\n'),
482
+ }],
483
+ };
484
+ }
485
+ catch (e) {
486
+ return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
487
+ }
488
+ });
489
+ server.tool('execute_agent_wallet_transfer', 'Send TON directly from an Agent Wallet — NO approval needed. The agent signs and broadcasts immediately. Only works with deployed agent wallets. Use for automated/autonomous transfers where speed matters and the user has opted in.', {
490
+ walletAddress: z.string().describe('The agent wallet contract address'),
491
+ to: z.string().describe('Destination TON address'),
492
+ amountNano: z.string().describe('Amount in nanoTON'),
493
+ }, async ({ walletAddress, to, amountNano }) => {
494
+ if (!TOKEN) {
495
+ return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
496
+ }
497
+ try {
498
+ const { Cell, beginCell, Address, SendMode, external, storeMessage } = await import('@ton/core');
499
+ const { sign, keyPairFromSeed } = await import('@ton/crypto');
500
+ // Get wallet config from local storage
501
+ const localWallets = loadLocalWallets();
502
+ const localConfig = localWallets[walletAddress];
503
+ if (!localConfig)
504
+ throw new Error('Agent wallet secret key not found locally. Was it deployed from this machine?');
505
+ // Get current seqno from server
506
+ const infoResult = await apiCall(`/v1/agent-wallet/${encodeURIComponent(walletAddress)}/info`);
507
+ const seqno = infoResult.seqno;
508
+ const walletId = localConfig.walletId;
509
+ const secretKeyBuf = Buffer.from(localConfig.agentSecretKey, 'hex');
510
+ // Normalize: if 64 bytes use as-is, if 32 bytes derive from seed
511
+ let secretKey;
512
+ if (secretKeyBuf.length === 64) {
513
+ secretKey = secretKeyBuf;
514
+ }
515
+ else {
516
+ const kp = keyPairFromSeed(secretKeyBuf);
517
+ secretKey = kp.secretKey;
518
+ }
519
+ const vaultAddr = Address.parse(walletAddress);
520
+ const destAddr = Address.parse(to);
521
+ // Build transfer message
522
+ const transferMsg = beginCell()
523
+ .storeUint(0x18, 6)
524
+ .storeAddress(destAddr)
525
+ .storeCoins(BigInt(amountNano))
526
+ .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
527
+ .endCell();
528
+ // Build actions list
529
+ const sendMode = SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
530
+ const actionsList = beginCell()
531
+ .storeRef(beginCell().endCell()) // empty previous
532
+ .storeUint(0x0ec3c86d, 32) // action_send_msg prefix
533
+ .storeUint(sendMode, 8)
534
+ .storeRef(transferMsg)
535
+ .endCell();
536
+ // Build unsigned body
537
+ const validUntil = Math.floor(Date.now() / 1000) + 300;
538
+ const unsignedBody = beginCell()
539
+ .storeUint(0x7369676e, 32) // prefix::signed_external
540
+ .storeUint(walletId, 32)
541
+ .storeUint(validUntil, 32)
542
+ .storeUint(seqno, 32)
543
+ .storeMaybeRef(actionsList)
544
+ .endCell();
545
+ // Sign
546
+ const signature = sign(unsignedBody.hash(), secretKey);
547
+ const signedBody = beginCell()
548
+ .storeSlice(unsignedBody.beginParse())
549
+ .storeBuffer(signature)
550
+ .endCell();
551
+ // Build external message
552
+ const extMsg = external({ to: vaultAddr, body: signedBody });
553
+ const boc = beginCell().store(storeMessage(extMsg)).endCell().toBoc().toString('base64');
554
+ // Broadcast
555
+ const broadcastRes = await fetch('https://toncenter.com/api/v2/sendBoc', {
556
+ method: 'POST',
557
+ headers: { 'Content-Type': 'application/json' },
558
+ body: JSON.stringify({ boc }),
559
+ });
560
+ const broadcastData = await broadcastRes.json();
561
+ if (!broadcastData.ok) {
562
+ throw new Error(`Broadcast failed: ${broadcastData.error || JSON.stringify(broadcastData)}`);
563
+ }
564
+ const tonAmount = (BigInt(amountNano) / 1000000000n).toString() + '.' +
565
+ (BigInt(amountNano) % 1000000000n).toString().padStart(9, '0').replace(/0+$/, '');
566
+ return {
567
+ content: [{
568
+ type: 'text',
569
+ text: [
570
+ 'Transfer executed from Agent Wallet!',
571
+ '',
572
+ `From: ${walletAddress}`,
573
+ `To: ${to}`,
574
+ `Amount: ${tonAmount} TON`,
575
+ `Seqno: ${seqno}`,
576
+ '',
577
+ 'Transaction broadcast successfully. No approval was needed.',
578
+ ].join('\n'),
579
+ }],
580
+ };
581
+ }
582
+ catch (e) {
583
+ return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
584
+ }
585
+ });
586
+ server.tool('get_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.', {
587
+ walletAddress: z.string().optional().describe('Agent wallet address. If omitted, lists all your agent wallets.'),
588
+ }, async ({ walletAddress }) => {
589
+ if (!TOKEN) {
590
+ return { content: [{ type: 'text', text: 'No token configured. Use request_auth first.' }], isError: true };
591
+ }
592
+ try {
593
+ if (!walletAddress) {
594
+ // List all wallets
595
+ const result = await apiCall('/v1/agent-wallet/list');
596
+ const wallets = result.wallets || [];
597
+ if (!wallets.length) {
598
+ return { content: [{ type: 'text', text: 'No agent wallets found. Use deploy_agent_wallet to create one.' }] };
599
+ }
600
+ const lines = wallets.map((w) => `- ${w.address} (created ${new Date(w.createdAt).toISOString()})`);
601
+ return {
602
+ content: [{ type: 'text', text: `Agent wallets (${wallets.length}):\n${lines.join('\n')}` }],
603
+ };
604
+ }
605
+ const result = await apiCall(`/v1/agent-wallet/${encodeURIComponent(walletAddress)}/info`);
606
+ const balanceTon = (BigInt(result.balance) / 1000000000n).toString();
607
+ const balanceFrac = (BigInt(result.balance) % 1000000000n).toString().padStart(9, '0').replace(/0+$/, '') || '0';
608
+ return {
609
+ content: [{
610
+ type: 'text',
611
+ text: [
612
+ `Agent Wallet: ${result.address}`,
613
+ `Balance: ${balanceTon}.${balanceFrac} TON`,
614
+ `Status: ${result.status}`,
615
+ `Seqno: ${result.seqno}`,
616
+ `Agent Key: ${result.agentPublicKey}`,
617
+ `Created: ${new Date(result.createdAt).toISOString()}`,
618
+ ].join('\n'),
619
+ }],
620
+ };
621
+ }
622
+ catch (e) {
623
+ return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
624
+ }
625
+ });
388
626
  const transport = new StdioServerTransport();
389
627
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tongateway/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "MCP server for Agent Gateway — lets AI agents request TON blockchain transfers",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,6 +28,8 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@modelcontextprotocol/sdk": "^1.12.1",
31
+ "@ton/core": "^0.63.1",
32
+ "@ton/crypto": "^3.3.0",
31
33
  "zod": "^4.3.6"
32
34
  },
33
35
  "devDependencies": {