@truecarry/mcp 0.1.3 → 0.1.5

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 (70) hide show
  1. package/dist/cli.js +654 -1579
  2. package/dist/factory.d.ts +35 -21
  3. package/dist/factory.d.ts.map +1 -1
  4. package/dist/index.cjs +781 -4180
  5. package/dist/index.d.ts +5 -16
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +781 -4167
  8. package/dist/services/McpWalletService.d.ts +74 -133
  9. package/dist/services/McpWalletService.d.ts.map +1 -1
  10. package/dist/tools/balance-tools.d.ts +61 -0
  11. package/dist/tools/balance-tools.d.ts.map +1 -0
  12. package/dist/tools/index.d.ts +5 -7
  13. package/dist/tools/index.d.ts.map +1 -1
  14. package/dist/tools/known-jettons-tools.d.ts +44 -0
  15. package/dist/tools/known-jettons-tools.d.ts.map +1 -0
  16. package/dist/tools/nft-tools.d.ts +85 -0
  17. package/dist/tools/nft-tools.d.ts.map +1 -0
  18. package/dist/tools/swap-tools.d.ts +49 -0
  19. package/dist/tools/swap-tools.d.ts.map +1 -0
  20. package/dist/tools/transfer-tools.d.ts +159 -0
  21. package/dist/tools/transfer-tools.d.ts.map +1 -0
  22. package/dist/tools/types.d.ts +21 -0
  23. package/dist/tools/types.d.ts.map +1 -0
  24. package/dist/types/config.d.ts +12 -36
  25. package/dist/types/config.d.ts.map +1 -1
  26. package/dist/types/index.d.ts +1 -4
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/dist/adapters/InMemoryStorageAdapter.d.ts +0 -49
  30. package/dist/adapters/InMemoryStorageAdapter.d.ts.map +0 -1
  31. package/dist/adapters/LocalSignerAdapter.d.ts +0 -107
  32. package/dist/adapters/LocalSignerAdapter.d.ts.map +0 -1
  33. package/dist/adapters/SqliteSignerAdapter.d.ts +0 -119
  34. package/dist/adapters/SqliteSignerAdapter.d.ts.map +0 -1
  35. package/dist/adapters/SqliteStorageAdapter.d.ts +0 -81
  36. package/dist/adapters/SqliteStorageAdapter.d.ts.map +0 -1
  37. package/dist/adapters/TelegramUserContextProvider.d.ts +0 -70
  38. package/dist/adapters/TelegramUserContextProvider.d.ts.map +0 -1
  39. package/dist/adapters/index.d.ts +0 -19
  40. package/dist/adapters/index.d.ts.map +0 -1
  41. package/dist/core/LimitsManager.d.ts +0 -59
  42. package/dist/core/LimitsManager.d.ts.map +0 -1
  43. package/dist/core/PendingTransactionManager.d.ts +0 -122
  44. package/dist/core/PendingTransactionManager.d.ts.map +0 -1
  45. package/dist/core/UserScopedSigner.d.ts +0 -96
  46. package/dist/core/UserScopedSigner.d.ts.map +0 -1
  47. package/dist/core/UserScopedStorage.d.ts +0 -59
  48. package/dist/core/UserScopedStorage.d.ts.map +0 -1
  49. package/dist/core/index.d.ts +0 -15
  50. package/dist/core/index.d.ts.map +0 -1
  51. package/dist/services/WalletService.d.ts +0 -144
  52. package/dist/services/WalletService.d.ts.map +0 -1
  53. package/dist/storage/SecureStorage.d.ts +0 -79
  54. package/dist/storage/SecureStorage.d.ts.map +0 -1
  55. package/dist/tools/balance.d.ts +0 -167
  56. package/dist/tools/balance.d.ts.map +0 -1
  57. package/dist/tools/mcp-tools.d.ts +0 -439
  58. package/dist/tools/mcp-tools.d.ts.map +0 -1
  59. package/dist/tools/swap.d.ts +0 -110
  60. package/dist/tools/swap.d.ts.map +0 -1
  61. package/dist/tools/transfer.d.ts +0 -146
  62. package/dist/tools/transfer.d.ts.map +0 -1
  63. package/dist/tools/wallet.d.ts +0 -138
  64. package/dist/tools/wallet.d.ts.map +0 -1
  65. package/dist/types/signer.d.ts +0 -120
  66. package/dist/types/signer.d.ts.map +0 -1
  67. package/dist/types/storage.d.ts +0 -41
  68. package/dist/types/storage.d.ts.map +0 -1
  69. package/dist/types/user-context.d.ts +0 -48
  70. package/dist/types/user-context.d.ts.map +0 -1
package/dist/cli.js CHANGED
@@ -21893,7 +21893,7 @@ var WalletKitError = class WalletKitError extends Error {
21893
21893
  function asBase64(data) {
21894
21894
  if (!/^[A-Za-z0-9+/]*={0,2}$/.test(data)) throw new Error("Not a valid base64");
21895
21895
  try {
21896
- if (Buffer.from(data, "base64").toString("base64") !== data) throw new Error("Not a valid base64");
21896
+ ParseBase64(data);
21897
21897
  } catch (_e) {
21898
21898
  throw new Error("Not a valid base64");
21899
21899
  }
@@ -23814,36 +23814,22 @@ var TONConnectStoredSessionManager = class {
23814
23814
  async getSession(sessionId) {
23815
23815
  return this.sessions.get(sessionId);
23816
23816
  }
23817
- async getSessionByDomain(domain) {
23818
- let host;
23819
- try {
23820
- host = new URL(domain).host;
23817
+ async getSessions(filter) {
23818
+ let sessions = Array.from(this.sessions.values());
23819
+ if (!filter) return sessions;
23820
+ let domain;
23821
+ if (filter.domain) try {
23822
+ domain = new URL(filter.domain).host;
23821
23823
  } catch {
23822
- return;
23823
- }
23824
- for (const session of this.sessions.values()) if (session.domain === host) return this.getSession(session.sessionId);
23825
- }
23826
- /**
23827
- * Get all sessions as array
23828
- */
23829
- async getSessions() {
23830
- return Array.from(this.sessions.values());
23831
- }
23832
- /**
23833
- * Get sessions for specific wallet by wallet ID
23834
- */
23835
- async getSessionsForWallet(walletId) {
23836
- return (await this.getSessions()).filter((session) => session.walletId === walletId);
23837
- }
23838
- /**
23839
- * Update session activity timestamp
23840
- */
23841
- async updateSessionActivity(sessionId) {
23842
- const session = this.sessions.get(sessionId);
23843
- if (session) {
23844
- session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
23845
- await this.persistSessions();
23846
- }
23824
+ domain = filter.domain;
23825
+ }
23826
+ return sessions.filter((session) => {
23827
+ let isIncluded = true;
23828
+ if (filter.walletId) isIncluded = isIncluded && session.walletId === filter.walletId;
23829
+ if (filter.domain) isIncluded = isIncluded && session.domain === domain;
23830
+ if (filter.isJsBridge !== void 0) isIncluded = isIncluded && session.isJsBridge === filter.isJsBridge;
23831
+ return isIncluded;
23832
+ });
23847
23833
  }
23848
23834
  /**
23849
23835
  * Remove session by ID
@@ -23851,11 +23837,8 @@ var TONConnectStoredSessionManager = class {
23851
23837
  async removeSession(sessionId) {
23852
23838
  if (this.sessions.delete(sessionId)) await this.persistSessions();
23853
23839
  }
23854
- /**
23855
- * Remove all sessions for a wallet by wallet ID or wallet adapter
23856
- */
23857
- async removeSessionsForWallet(walletId) {
23858
- const sessionsToRemove = await this.getSessionsForWallet(walletId);
23840
+ async removeSessions(filter) {
23841
+ const sessionsToRemove = await this.getSessions(filter);
23859
23842
  let removedCount = 0;
23860
23843
  for (const session of sessionsToRemove) if (this.sessions.delete(session.sessionId)) removedCount++;
23861
23844
  if (removedCount > 0) await this.persistSessions();
@@ -64265,13 +64248,15 @@ var BridgeManager = class {
64265
64248
  isJsBridge: true,
64266
64249
  tabId: messageInfo.tabId,
64267
64250
  domain: messageInfo.domain,
64268
- messageId: messageInfo.messageId
64251
+ messageId: messageInfo.messageId,
64252
+ walletId: messageInfo.walletId
64269
64253
  });
64270
64254
  else if (event.method == "restoreConnection") this.eventEmitter?.emit("restoreConnection", {
64271
64255
  ...event,
64272
64256
  tabId: messageInfo.tabId,
64273
64257
  domain: messageInfo.domain,
64274
- messageId: messageInfo.messageId
64258
+ messageId: messageInfo.messageId,
64259
+ walletId: messageInfo.walletId
64275
64260
  });
64276
64261
  else if (event.method == "send" && event?.params?.length === 1) this.eventQueue.push({
64277
64262
  ...event,
@@ -64279,7 +64264,8 @@ var BridgeManager = class {
64279
64264
  isJsBridge: true,
64280
64265
  tabId: messageInfo.tabId,
64281
64266
  domain: messageInfo.domain,
64282
- messageId: messageInfo.messageId
64267
+ messageId: messageInfo.messageId,
64268
+ walletId: messageInfo.walletId
64283
64269
  });
64284
64270
  this.processBridgeEvents().catch((error) => {
64285
64271
  log$21.error("Error in background event processing", { error });
@@ -64331,7 +64317,8 @@ var BridgeManager = class {
64331
64317
  isJsBridge: event?.isJsBridge,
64332
64318
  tabId: event?.tabId,
64333
64319
  messageId: event?.messageId,
64334
- traceId: event?.traceId
64320
+ traceId: event?.traceId,
64321
+ walletId: event?.walletId
64335
64322
  };
64336
64323
  if (!rawEvent.traceId) rawEvent.traceId = v7_default();
64337
64324
  await this.sessionManager.initialize();
@@ -64349,7 +64336,12 @@ var BridgeManager = class {
64349
64336
  };
64350
64337
  }
64351
64338
  } else if (rawEvent.domain) {
64352
- const session = await this.sessionManager.getSessionByDomain(rawEvent.domain);
64339
+ const sessions = await this.sessionManager.getSessions({
64340
+ walletId: event.walletId,
64341
+ domain: rawEvent.domain,
64342
+ isJsBridge: rawEvent.isJsBridge
64343
+ });
64344
+ const session = sessions.length > 0 ? sessions[0] : void 0;
64353
64345
  if (session?.walletId) rawEvent.walletId = session.walletId;
64354
64346
  if (session?.walletAddress) rawEvent.walletAddress = session.walletAddress;
64355
64347
  if (session?.sessionId) rawEvent.from = session.sessionId;
@@ -68159,9 +68151,6 @@ async function MnemonicToKeyPair(mnemonic, mnemonicType = "ton") {
68159
68151
  supportedTypes: ["ton", "bip39"]
68160
68152
  });
68161
68153
  }
68162
- async function CreateTonMnemonic() {
68163
- return (0, import_dist$1.mnemonicNew)(24);
68164
- }
68165
68154
 
68166
68155
  //#endregion
68167
68156
  //#region ../walletkit/dist/esm/utils/sign.js
@@ -70109,6 +70098,7 @@ var DefiManagerError = class extends Error {
70109
70098
  static NO_DEFAULT_PROVIDER = "NO_DEFAULT_PROVIDER";
70110
70099
  static NETWORK_ERROR = "NETWORK_ERROR";
70111
70100
  static INVALID_PARAMS = "INVALID_PARAMS";
70101
+ static INVALID_PROVIDER = "INVALID_PROVIDER";
70112
70102
  code;
70113
70103
  details;
70114
70104
  constructor(message, code, details) {
@@ -70150,36 +70140,42 @@ var SwapError = class extends DefiManagerError {
70150
70140
  */
70151
70141
  var DefiManager = class {
70152
70142
  providers = /* @__PURE__ */ new Map();
70153
- defaultProvider;
70143
+ defaultProviderId;
70154
70144
  /**
70155
70145
  * Register a swap provider
70156
70146
  * @param name - Unique name for the provider
70157
70147
  * @param provider - Provider instance
70158
70148
  */
70159
- registerProvider(name, provider) {
70160
- this.providers.set(name, provider);
70161
- if (!this.defaultProvider) this.defaultProvider = name;
70149
+ /**
70150
+ * Register a swap provider
70151
+ * @param provider - Provider instance
70152
+ */
70153
+ registerProvider(provider) {
70154
+ const providerId = provider.providerId;
70155
+ if (!providerId) throw this.createError("Provider must have a providerId", DefiManagerError.INVALID_PROVIDER);
70156
+ this.providers.set(providerId, provider);
70157
+ if (!this.defaultProviderId) this.defaultProviderId = providerId;
70162
70158
  }
70163
70159
  /**
70164
70160
  * Set the default provider to use when none is specified
70165
- * @param name - Provider name
70161
+ * @param providerId - Provider name
70166
70162
  * @throws DefiManagerError if provider not found
70167
70163
  */
70168
- setDefaultProvider(name) {
70169
- if (!this.providers.has(name)) throw this.createError(`Provider '${name}' not registered`, DefiManagerError.PROVIDER_NOT_FOUND, {
70170
- provider: name,
70164
+ setDefaultProvider(providerId) {
70165
+ if (!this.providers.has(providerId)) throw this.createError(`Provider '${providerId}' not registered`, DefiManagerError.PROVIDER_NOT_FOUND, {
70166
+ provider: providerId,
70171
70167
  registered: Array.from(this.providers.keys())
70172
70168
  });
70173
- this.defaultProvider = name;
70169
+ this.defaultProviderId = providerId;
70174
70170
  }
70175
70171
  /**
70176
70172
  * Get a provider by name, or the default provider
70177
- * @param name - Optional provider name
70173
+ * @param providerId - Optional provider name
70178
70174
  * @returns Provider instance
70179
70175
  * @throws DefiManagerError if provider not found or no default set
70180
70176
  */
70181
- getProvider(name) {
70182
- const providerName = name || this.defaultProvider;
70177
+ getProvider(providerId) {
70178
+ const providerName = providerId || this.defaultProviderId;
70183
70179
  if (!providerName) throw this.createError("No default provider set. Register a provider first.", DefiManagerError.NO_DEFAULT_PROVIDER);
70184
70180
  const provider = this.providers.get(providerName);
70185
70181
  if (!provider) throw this.createError(`Provider '${providerName}' not found`, DefiManagerError.PROVIDER_NOT_FOUND, {
@@ -70197,11 +70193,11 @@ var DefiManager = class {
70197
70193
  }
70198
70194
  /**
70199
70195
  * Check if a provider is registered
70200
- * @param name - Provider name
70196
+ * @param providerId - Provider id
70201
70197
  * @returns True if provider exists
70202
70198
  */
70203
- hasProvider(name) {
70204
- return this.providers.has(name);
70199
+ hasProvider(providerId) {
70200
+ return this.providers.has(providerId);
70205
70201
  }
70206
70202
  };
70207
70203
 
@@ -70229,14 +70225,12 @@ var SwapManager = class extends DefiManager {
70229
70225
  * @returns Promise resolving to swap quote
70230
70226
  */
70231
70227
  async getQuote(params, provider) {
70232
- if (params.amountFrom && params.amountTo) throw new SwapError("Cannot specify both amountFrom and amountTo", SwapError.INVALID_PARAMS);
70233
- if (!params.amountFrom && !params.amountTo) throw new SwapError("Must specify either amountFrom or amountTo", SwapError.INVALID_PARAMS);
70234
70228
  log$10.debug("Getting swap quote", {
70235
70229
  fromToken: params.fromToken,
70236
70230
  toToken: params.toToken,
70237
- amountFrom: params.amountFrom,
70238
- amountTo: params.amountTo,
70239
- provider: provider || this.defaultProvider
70231
+ amount: params.amount,
70232
+ isReverseSwap: params.isReverseSwap,
70233
+ provider: provider || this.defaultProviderId
70240
70234
  });
70241
70235
  try {
70242
70236
  const quote = await this.getProvider(provider).getQuote(params);
@@ -70263,7 +70257,7 @@ var SwapManager = class extends DefiManager {
70263
70257
  async buildSwapTransaction(params, provider) {
70264
70258
  log$10.debug("Building swap transaction", {
70265
70259
  userAddress: params.userAddress,
70266
- provider: provider || this.defaultProvider
70260
+ provider: provider || this.defaultProviderId
70267
70261
  });
70268
70262
  try {
70269
70263
  const transaction = await this.getProvider(provider).buildSwapTransaction(params);
@@ -71530,23 +71524,28 @@ var TonWalletKit = class {
71530
71524
  this.eventEmitter.on("restoreConnection", async (event) => {
71531
71525
  if (!event.domain) {
71532
71526
  log$5.error("Domain is required for restore connection");
71533
- return;
71527
+ return this.sendErrorConnectResponse(event);
71534
71528
  }
71535
- const session = await this.sessionManager.getSessionByDomain(event.domain);
71529
+ const sessions = await this.sessionManager.getSessions({
71530
+ walletId: event.walletId,
71531
+ domain: event.domain,
71532
+ isJsBridge: true
71533
+ });
71534
+ const session = sessions.length > 0 ? sessions[0] : void 0;
71536
71535
  if (!session) {
71537
71536
  log$5.error("Session not found for domain", { domain: event.domain });
71538
- return;
71537
+ return this.sendErrorConnectResponse(event);
71539
71538
  }
71540
71539
  const wallet = session.walletId ? this.walletManager?.getWallet(session.walletId) : void 0;
71541
71540
  if (!wallet) {
71542
71541
  log$5.error("Wallet not found for session", { walletId: session.walletId });
71543
- return;
71542
+ return this.sendErrorConnectResponse(event);
71544
71543
  }
71545
71544
  const walletAddress = wallet.getAddress();
71546
71545
  const walletStateInit = await wallet.getStateInit();
71547
71546
  const publicKey = wallet.getPublicKey().replace("0x", "");
71548
71547
  const deviceInfo = getDeviceInfoForWallet(wallet, this.config.deviceInfo);
71549
- const connectResponse = {
71548
+ const tonConnectEvent = {
71550
71549
  event: "connect",
71551
71550
  id: Date.now(),
71552
71551
  payload: {
@@ -71560,9 +71559,20 @@ var TonWalletKit = class {
71560
71559
  }]
71561
71560
  }
71562
71561
  };
71563
- this.bridgeManager.sendJsBridgeResponse(event?.tabId?.toString() || "", true, event?.id ?? event?.messageId, connectResponse);
71562
+ this.bridgeManager.sendJsBridgeResponse(event?.tabId?.toString() || "", true, event?.id ?? event?.messageId, tonConnectEvent);
71564
71563
  });
71565
71564
  }
71565
+ async sendErrorConnectResponse(event) {
71566
+ const tonConnectEvent = {
71567
+ event: "connect_error",
71568
+ id: Date.now(),
71569
+ payload: {
71570
+ code: CONNECT_EVENT_ERROR_CODES.UNKNOWN_APP_ERROR,
71571
+ message: ""
71572
+ }
71573
+ };
71574
+ await this.bridgeManager.sendJsBridgeResponse(event?.tabId?.toString() || "", true, event?.id ?? event?.messageId, tonConnectEvent);
71575
+ }
71566
71576
  /**
71567
71577
  * Initialize all components
71568
71578
  */
@@ -71667,7 +71677,7 @@ var TonWalletKit = class {
71667
71677
  if (!wallet) throw new WalletKitError(ERROR_CODES.WALLET_NOT_FOUND, "Wallet not found for removal", void 0, { walletId });
71668
71678
  await this.eventProcessor.stopProcessing(wallet.getAddress());
71669
71679
  await this.walletManager.removeWallet(walletId);
71670
- await this.sessionManager.removeSessionsForWallet(walletId);
71680
+ await this.sessionManager.removeSessions({ walletId });
71671
71681
  }
71672
71682
  async clearWallets() {
71673
71683
  await this.ensureInitialized();
@@ -87854,15 +87864,21 @@ function unwrapObservable(originalMethod) {
87854
87864
  *
87855
87865
  */
87856
87866
  const tokenToAddress = (token) => {
87857
- if (token === "TON") return "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
87858
- return import_dist$2.Address.parse(token).toRawString();
87867
+ if (token.type === "ton") return "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
87868
+ return import_dist$2.Address.parse(token.value).toRawString();
87859
87869
  };
87860
87870
  const addressToToken = (address) => {
87861
- if (address === "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c") return "TON";
87871
+ if (address === "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c") return { type: "ton" };
87862
87872
  try {
87863
- return import_dist$2.Address.parseRaw(address).toString();
87873
+ return {
87874
+ type: "jetton",
87875
+ value: import_dist$2.Address.parseRaw(address).toString()
87876
+ };
87864
87877
  } catch {
87865
- return address;
87878
+ return {
87879
+ type: "jetton",
87880
+ value: address
87881
+ };
87866
87882
  }
87867
87883
  };
87868
87884
  const toOmnistonAddress = (address, network) => {
@@ -87929,8 +87945,10 @@ var OmnistonSwapProvider = class extends SwapProvider {
87929
87945
  referrerFeeBps;
87930
87946
  flexibleReferrerFee;
87931
87947
  omniston$;
87948
+ providerId;
87932
87949
  constructor(config) {
87933
87950
  super();
87951
+ this.providerId = config?.providerId ?? "omniston";
87934
87952
  this.apiUrl = config?.apiUrl ?? "wss://omni-ws.ston.fi";
87935
87953
  this.defaultSlippageBps = config?.defaultSlippageBps ?? 100;
87936
87954
  this.quoteTimeoutMs = config?.quoteTimeoutMs ?? 1e4;
@@ -87950,8 +87968,8 @@ var OmnistonSwapProvider = class extends SwapProvider {
87950
87968
  log$1.debug("Getting Omniston quote", {
87951
87969
  fromToken: params.fromToken,
87952
87970
  toToken: params.toToken,
87953
- amountFrom: params.amountFrom,
87954
- amountTo: params.amountTo
87971
+ amount: params.amount,
87972
+ isReverseSwap: params.isReverseSwap
87955
87973
  });
87956
87974
  try {
87957
87975
  const bidAssetAddress = tokenToAddress(params.fromToken);
@@ -87961,7 +87979,7 @@ var OmnistonSwapProvider = class extends SwapProvider {
87961
87979
  const referrerFeeBps = params.providerOptions?.referrerFeeBps ?? this.referrerFeeBps;
87962
87980
  const flexibleReferrerFee = params.providerOptions?.flexibleReferrerFee ?? this.flexibleReferrerFee;
87963
87981
  const quoteRequest = {
87964
- amount: params.amountFrom ? { bidUnits: params.amountFrom } : { askUnits: params.amountTo },
87982
+ amount: params.isReverseSwap ? { askUnits: params.amount } : { bidUnits: params.amount },
87965
87983
  settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
87966
87984
  bidAssetAddress: toOmnistonAddress(bidAssetAddress, params.network),
87967
87985
  askAssetAddress: toOmnistonAddress(askAssetAddress, params.network),
@@ -88095,7 +88113,7 @@ var OmnistonSwapProvider = class extends SwapProvider {
88095
88113
  });
88096
88114
  return {
88097
88115
  metadata,
88098
- provider: "omniston",
88116
+ providerId: this.providerId,
88099
88117
  fromToken: params.fromToken,
88100
88118
  toToken: params.toToken,
88101
88119
  fromAmount: quote.bidUnits,
@@ -88108,388 +88126,6 @@ var OmnistonSwapProvider = class extends SwapProvider {
88108
88126
  }
88109
88127
  };
88110
88128
 
88111
- //#endregion
88112
- //#region src/core/UserScopedStorage.ts
88113
- /**
88114
- * User-scoped storage wrapper.
88115
- * Automatically prefixes all keys with user namespace.
88116
- */
88117
- var UserScopedStorage = class {
88118
- storage;
88119
- userId;
88120
- prefix;
88121
- constructor(storage, userId) {
88122
- this.storage = storage;
88123
- this.userId = userId;
88124
- this.prefix = `user:${userId}:`;
88125
- }
88126
- /**
88127
- * Get the user ID this storage is scoped to
88128
- */
88129
- getUserId() {
88130
- return this.userId;
88131
- }
88132
- /**
88133
- * Build full key with user prefix
88134
- */
88135
- buildKey(key) {
88136
- return `${this.prefix}${key}`;
88137
- }
88138
- /**
88139
- * Strip user prefix from a key
88140
- */
88141
- stripPrefix(fullKey) {
88142
- if (fullKey.startsWith(this.prefix)) return fullKey.slice(this.prefix.length);
88143
- return fullKey;
88144
- }
88145
- /**
88146
- * Get a value by key (user-scoped)
88147
- */
88148
- async get(key) {
88149
- return this.storage.get(this.buildKey(key));
88150
- }
88151
- /**
88152
- * Set a value (user-scoped)
88153
- */
88154
- async set(key, value, ttlSeconds) {
88155
- return this.storage.set(this.buildKey(key), value, ttlSeconds);
88156
- }
88157
- /**
88158
- * Delete a key (user-scoped)
88159
- */
88160
- async delete(key) {
88161
- return this.storage.delete(this.buildKey(key));
88162
- }
88163
- /**
88164
- * List keys matching prefix (user-scoped)
88165
- * Returns keys with user prefix stripped.
88166
- */
88167
- async list(subPrefix) {
88168
- const fullPrefix = this.buildKey(subPrefix);
88169
- return (await this.storage.list(fullPrefix)).map((k) => this.stripPrefix(k));
88170
- }
88171
- /**
88172
- * Get the underlying storage adapter.
88173
- * Use with caution - bypasses user isolation.
88174
- */
88175
- getUnderlyingStorage() {
88176
- return this.storage;
88177
- }
88178
- };
88179
-
88180
- //#endregion
88181
- //#region src/core/UserScopedSigner.ts
88182
- /**
88183
- * User-scoped signer wrapper.
88184
- * Ensures all operations are scoped to the authenticated user.
88185
- */
88186
- var UserScopedSigner = class {
88187
- signer;
88188
- userId;
88189
- userPrefix;
88190
- constructor(signer, userId) {
88191
- this.signer = signer;
88192
- this.userId = userId;
88193
- this.userPrefix = `${userId}:`;
88194
- }
88195
- /**
88196
- * Get the user ID this signer is scoped to
88197
- */
88198
- getUserId() {
88199
- return this.userId;
88200
- }
88201
- /**
88202
- * Build user-scoped wallet ID from wallet name
88203
- */
88204
- scopedId(walletName) {
88205
- return `${this.userPrefix}${walletName}`;
88206
- }
88207
- /**
88208
- * Extract wallet name from scoped ID
88209
- */
88210
- extractName(scopedId) {
88211
- if (scopedId.startsWith(this.userPrefix)) return scopedId.slice(this.userPrefix.length);
88212
- return scopedId;
88213
- }
88214
- /**
88215
- * Check if a wallet ID belongs to this user
88216
- */
88217
- isOwnedByUser(walletId) {
88218
- return walletId.startsWith(this.userPrefix);
88219
- }
88220
- /**
88221
- * Transform wallet info to expose wallet name instead of internal ID
88222
- */
88223
- transformWalletInfo(info) {
88224
- return {
88225
- ...info,
88226
- name: this.extractName(info.walletId)
88227
- };
88228
- }
88229
- /**
88230
- * Create a new wallet (user-scoped)
88231
- */
88232
- async createWallet(params) {
88233
- const result = await this.signer.createWallet({
88234
- walletId: this.scopedId(params.name),
88235
- version: params.version,
88236
- network: params.network
88237
- });
88238
- return this.transformWalletInfo(result);
88239
- }
88240
- /**
88241
- * Import a wallet from mnemonic (user-scoped)
88242
- * Note: Mnemonic is passed to signer and stored securely, never returned
88243
- */
88244
- async importWallet(params) {
88245
- const result = await this.signer.importWallet({
88246
- walletId: this.scopedId(params.name),
88247
- mnemonic: params.mnemonic,
88248
- version: params.version,
88249
- network: params.network
88250
- });
88251
- return this.transformWalletInfo(result);
88252
- }
88253
- /**
88254
- * Get wallet by name (user-scoped)
88255
- * Returns null for both "not found" and "not owned" to prevent enumeration
88256
- */
88257
- async getWallet(walletName) {
88258
- const walletId = this.scopedId(walletName);
88259
- const wallet = await this.signer.getWallet(walletId);
88260
- if (!wallet) return null;
88261
- if (!this.isOwnedByUser(wallet.walletId)) return null;
88262
- return this.transformWalletInfo(wallet);
88263
- }
88264
- /**
88265
- * List all wallets for this user
88266
- */
88267
- async listWallets() {
88268
- const userIds = (await this.signer.listWalletIds()).filter((id) => this.isOwnedByUser(id));
88269
- return (await Promise.all(userIds.map(async (id) => {
88270
- const wallet = await this.signer.getWallet(id);
88271
- return wallet ? this.transformWalletInfo(wallet) : null;
88272
- }))).filter((w) => w !== null);
88273
- }
88274
- /**
88275
- * Delete a wallet (user-scoped)
88276
- * Returns false for both "not found" and "not owned"
88277
- */
88278
- async deleteWallet(walletName) {
88279
- const walletId = this.scopedId(walletName);
88280
- const wallet = await this.signer.getWallet(walletId);
88281
- if (!wallet || !this.isOwnedByUser(wallet.walletId)) return false;
88282
- return this.signer.deleteWallet(walletId);
88283
- }
88284
- /**
88285
- * Sign a transaction (user-scoped)
88286
- * Verifies ownership before signing
88287
- */
88288
- async signTransaction(walletName, unsignedBoc) {
88289
- const walletId = this.scopedId(walletName);
88290
- const wallet = await this.signer.getWallet(walletId);
88291
- if (!wallet || !this.isOwnedByUser(wallet.walletId)) throw new Error("Wallet not found");
88292
- return this.signer.signTransaction(walletId, unsignedBoc);
88293
- }
88294
- /**
88295
- * Sign a message (user-scoped)
88296
- * Verifies ownership before signing
88297
- */
88298
- async signMessage(walletName, message) {
88299
- const walletId = this.scopedId(walletName);
88300
- const wallet = await this.signer.getWallet(walletId);
88301
- if (!wallet || !this.isOwnedByUser(wallet.walletId)) throw new Error("Wallet not found");
88302
- return this.signer.signMessage(walletId, message);
88303
- }
88304
- /**
88305
- * Get the underlying signer adapter.
88306
- * Use with caution - bypasses user isolation.
88307
- */
88308
- getUnderlyingSigner() {
88309
- return this.signer;
88310
- }
88311
- };
88312
-
88313
- //#endregion
88314
- //#region src/core/LimitsManager.ts
88315
- /** TON has 9 decimal places */
88316
- const TON_DECIMALS$1 = 9;
88317
- /**
88318
- * LimitsManager enforces safety limits on transactions and wallet counts.
88319
- */
88320
- var LimitsManager = class {
88321
- limits;
88322
- constructor(limits) {
88323
- this.limits = {
88324
- maxTransactionTon: limits?.maxTransactionTon ?? Infinity,
88325
- dailyLimitTon: limits?.dailyLimitTon ?? Infinity,
88326
- maxWalletsPerUser: limits?.maxWalletsPerUser ?? Infinity
88327
- };
88328
- }
88329
- /**
88330
- * Check if a transaction amount is within limits
88331
- */
88332
- async checkTransactionLimit(storage, amountTon) {
88333
- if (amountTon > this.limits.maxTransactionTon) return {
88334
- allowed: false,
88335
- reason: `Transaction amount ${amountTon} TON exceeds maximum allowed ${this.limits.maxTransactionTon} TON per transaction`
88336
- };
88337
- const today = this.getTodayDate();
88338
- const usage = await this.getDailyUsage(storage, today);
88339
- const currentSpentTon = this.nanoToTon(usage.totalSpentNano);
88340
- if (currentSpentTon + amountTon > this.limits.dailyLimitTon) {
88341
- const remainingTon = Math.max(0, this.limits.dailyLimitTon - currentSpentTon);
88342
- return {
88343
- allowed: false,
88344
- reason: `Transaction would exceed daily limit of ${this.limits.dailyLimitTon} TON. Already spent: ${currentSpentTon.toFixed(2)} TON. Remaining: ${remainingTon.toFixed(2)} TON`
88345
- };
88346
- }
88347
- return { allowed: true };
88348
- }
88349
- /**
88350
- * Record a transaction for daily limit tracking
88351
- */
88352
- async recordTransaction(storage, amountTon) {
88353
- const today = this.getTodayDate();
88354
- const usage = await this.getDailyUsage(storage, today);
88355
- const newUsage = {
88356
- date: today,
88357
- totalSpentNano: (BigInt(usage.totalSpentNano) + BigInt(this.tonToNano(amountTon))).toString()
88358
- };
88359
- await storage.set(`daily:${today}`, newUsage, 9e4);
88360
- }
88361
- /**
88362
- * Check if user can create another wallet
88363
- */
88364
- async checkWalletCountLimit(currentWalletCount) {
88365
- if (currentWalletCount >= this.limits.maxWalletsPerUser) return {
88366
- allowed: false,
88367
- reason: `Maximum wallet limit of ${this.limits.maxWalletsPerUser} reached`
88368
- };
88369
- return { allowed: true };
88370
- }
88371
- /**
88372
- * Get the configured limits
88373
- */
88374
- getLimits() {
88375
- return { ...this.limits };
88376
- }
88377
- /**
88378
- * Get today's date as ISO string (YYYY-MM-DD)
88379
- */
88380
- getTodayDate() {
88381
- return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
88382
- }
88383
- /**
88384
- * Get daily usage for a specific date
88385
- */
88386
- async getDailyUsage(storage, date) {
88387
- return await storage.get(`daily:${date}`) ?? {
88388
- date,
88389
- totalSpentNano: "0"
88390
- };
88391
- }
88392
- /**
88393
- * Convert TON to nanoTON string
88394
- */
88395
- tonToNano(ton) {
88396
- return BigInt(Math.floor(ton * Math.pow(10, TON_DECIMALS$1))).toString();
88397
- }
88398
- /**
88399
- * Convert nanoTON string to TON
88400
- */
88401
- nanoToTon(nano) {
88402
- return Number(BigInt(nano)) / Math.pow(10, TON_DECIMALS$1);
88403
- }
88404
- };
88405
-
88406
- //#endregion
88407
- //#region src/core/PendingTransactionManager.ts
88408
- /** Default pending transaction TTL in seconds (5 minutes) */
88409
- const DEFAULT_PENDING_TTL_SECONDS = 300;
88410
- /**
88411
- * PendingTransactionManager handles the confirmation flow for transactions.
88412
- */
88413
- var PendingTransactionManager = class {
88414
- ttlSeconds;
88415
- constructor(ttlSeconds = DEFAULT_PENDING_TTL_SECONDS) {
88416
- this.ttlSeconds = ttlSeconds;
88417
- }
88418
- /**
88419
- * Generate a unique transaction ID
88420
- */
88421
- generateId() {
88422
- return `tx_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
88423
- }
88424
- /**
88425
- * Create a pending transaction
88426
- */
88427
- async createPending(storage, params) {
88428
- const id = this.generateId();
88429
- const now = /* @__PURE__ */ new Date();
88430
- const expiresAt = new Date(now.getTime() + this.ttlSeconds * 1e3);
88431
- const pending = {
88432
- id,
88433
- type: params.type,
88434
- walletName: params.walletName,
88435
- createdAt: now.toISOString(),
88436
- expiresAt: expiresAt.toISOString(),
88437
- description: params.description,
88438
- data: params.data
88439
- };
88440
- await storage.set(`pending:${id}`, pending, this.ttlSeconds);
88441
- return pending;
88442
- }
88443
- /**
88444
- * Get a pending transaction by ID
88445
- */
88446
- async getPending(storage, transactionId) {
88447
- const pending = await storage.get(`pending:${transactionId}`);
88448
- if (!pending) return null;
88449
- if (new Date(pending.expiresAt) < /* @__PURE__ */ new Date()) {
88450
- await this.deletePending(storage, transactionId);
88451
- return null;
88452
- }
88453
- return pending;
88454
- }
88455
- /**
88456
- * List all pending transactions for the user
88457
- */
88458
- async listPending(storage) {
88459
- const keys = await storage.list("pending:");
88460
- const now = /* @__PURE__ */ new Date();
88461
- const transactions = [];
88462
- for (const key of keys) {
88463
- const pending = await storage.get(key);
88464
- if (pending && new Date(pending.expiresAt) >= now) transactions.push(pending);
88465
- }
88466
- return transactions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
88467
- }
88468
- /**
88469
- * Delete a pending transaction
88470
- */
88471
- async deletePending(storage, transactionId) {
88472
- return storage.delete(`pending:${transactionId}`);
88473
- }
88474
- /**
88475
- * Mark a pending transaction as confirmed (delete it)
88476
- * The actual execution is handled by the caller
88477
- */
88478
- async confirmPending(storage, transactionId) {
88479
- const pending = await this.getPending(storage, transactionId);
88480
- if (!pending) return null;
88481
- await this.deletePending(storage, transactionId);
88482
- return pending;
88483
- }
88484
- /**
88485
- * Cancel a pending transaction
88486
- */
88487
- async cancelPending(storage, transactionId) {
88488
- if (!await this.getPending(storage, transactionId)) return false;
88489
- return this.deletePending(storage, transactionId);
88490
- }
88491
- };
88492
-
88493
88129
  //#endregion
88494
88130
  //#region src/services/McpWalletService.ts
88495
88131
  /**
@@ -88500,38 +88136,46 @@ var PendingTransactionManager = class {
88500
88136
  *
88501
88137
  */
88502
88138
  /**
88503
- * McpWalletService - Multi-user wallet service with adapter support
88139
+ * McpWalletService - Simplified wallet service for MCP server
88504
88140
  *
88505
- * This service wraps wallet operations with:
88506
- * - User isolation via UserScopedSigner and UserScopedStorage
88507
- * - Transaction limits via LimitsManager
88508
- * - Confirmation flow via PendingTransactionManager
88141
+ * This service wraps a single Wallet instance for operations.
88142
+ * Wallet management (create/import/list/remove) is handled externally.
88509
88143
  *
88510
- * Unlike the standalone WalletService, this service is designed for
88511
- * multi-user environments like Telegram bots.
88144
+ * For multi-user scenarios (e.g., Telegram bots), use this service
88145
+ * with user-specific wallet instances.
88512
88146
  */
88513
88147
  /**
88514
- * McpWalletService manages wallet operations for multi-user MCP deployments.
88148
+ * McpWalletService manages wallet operations for a single wallet.
88515
88149
  */
88516
- var McpWalletService = class {
88150
+ var McpWalletService = class McpWalletService {
88517
88151
  config;
88518
- limitsManager;
88519
- pendingManager;
88152
+ wallet;
88520
88153
  kit = null;
88521
- loadedWallets = /* @__PURE__ */ new Map();
88522
88154
  constructor(config) {
88523
88155
  this.config = config;
88524
- this.limitsManager = new LimitsManager(config.limits);
88525
- this.pendingManager = new PendingTransactionManager();
88156
+ this.wallet = config.wallet;
88157
+ }
88158
+ static async create(config) {
88159
+ const wallet = await wrapWalletInterface(config.wallet);
88160
+ return new McpWalletService({
88161
+ ...config,
88162
+ wallet
88163
+ });
88526
88164
  }
88527
88165
  /**
88528
- * Get Network instance from network name
88166
+ * Get wallet address
88529
88167
  */
88530
- getNetwork(networkName) {
88531
- return networkName === "mainnet" ? Network.mainnet() : Network.testnet();
88168
+ getAddress() {
88169
+ return this.wallet.getAddress();
88532
88170
  }
88533
88171
  /**
88534
- * Initialize TonWalletKit
88172
+ * Get wallet network
88173
+ */
88174
+ getNetwork() {
88175
+ return this.wallet.getNetwork().chainId === Network.mainnet().chainId ? "mainnet" : "testnet";
88176
+ }
88177
+ /**
88178
+ * Initialize TonWalletKit (for swap operations)
88535
88179
  */
88536
88180
  async getKit() {
88537
88181
  if (!this.kit) {
@@ -88554,169 +88198,27 @@ var McpWalletService = class {
88554
88198
  });
88555
88199
  await this.kit.waitForReady();
88556
88200
  const omnistonProvider = new OmnistonSwapProvider({ defaultSlippageBps: 100 });
88557
- this.kit.swap.registerProvider("omniston", omnistonProvider);
88558
- this.kit.swap.setDefaultProvider("omniston");
88201
+ this.kit.swap.registerProvider(omnistonProvider);
88559
88202
  }
88560
88203
  return this.kit;
88561
88204
  }
88562
88205
  /**
88563
- * Create user-scoped storage wrapper
88564
- */
88565
- createUserStorage(userId) {
88566
- return new UserScopedStorage(this.config.storage, userId);
88567
- }
88568
- /**
88569
- * Create user-scoped signer wrapper
88570
- */
88571
- createUserSigner(userId) {
88572
- return new UserScopedSigner(this.config.signer, userId);
88573
- }
88574
- /**
88575
- * Get limits manager
88576
- */
88577
- getLimitsManager() {
88578
- return this.limitsManager;
88579
- }
88580
- /**
88581
- * Get pending transaction manager
88582
- */
88583
- getPendingManager() {
88584
- return this.pendingManager;
88585
- }
88586
- /**
88587
- * Check if confirmation is required
88588
- */
88589
- requiresConfirmation() {
88590
- return this.config.requireConfirmation ?? false;
88591
- }
88592
- /**
88593
- * Create a new wallet for a user
88594
- */
88595
- async createWallet(userSigner, userStorage, name, version = "v5r1", networkName = this.config.defaultNetwork ?? "mainnet") {
88596
- const existingWallets = await userSigner.listWallets();
88597
- const limitCheck = await this.limitsManager.checkWalletCountLimit(existingWallets.length);
88598
- if (!limitCheck.allowed) throw new Error(limitCheck.reason);
88599
- const walletInfo = await userSigner.createWallet({
88600
- name,
88601
- version,
88602
- network: networkName
88603
- });
88604
- const metadata = {
88605
- name: walletInfo.name,
88606
- address: walletInfo.address,
88607
- network: walletInfo.network,
88608
- version: walletInfo.version,
88609
- createdAt: walletInfo.createdAt
88610
- };
88611
- await userStorage.set(`wallet:${name}`, metadata);
88612
- return {
88613
- name: walletInfo.name,
88614
- address: walletInfo.address,
88615
- network: walletInfo.network
88616
- };
88617
- }
88618
- /**
88619
- * Import a wallet for a user
88620
- */
88621
- async importWallet(userSigner, userStorage, name, mnemonic, version = "v5r1", networkName = this.config.defaultNetwork ?? "mainnet") {
88622
- const existingWallets = await userSigner.listWallets();
88623
- const limitCheck = await this.limitsManager.checkWalletCountLimit(existingWallets.length);
88624
- if (!limitCheck.allowed) throw new Error(limitCheck.reason);
88625
- const walletInfo = await userSigner.importWallet({
88626
- name,
88627
- mnemonic,
88628
- version,
88629
- network: networkName
88630
- });
88631
- const metadata = {
88632
- name: walletInfo.name,
88633
- address: walletInfo.address,
88634
- network: walletInfo.network,
88635
- version: walletInfo.version,
88636
- createdAt: walletInfo.createdAt
88637
- };
88638
- await userStorage.set(`wallet:${name}`, metadata);
88639
- return {
88640
- name: walletInfo.name,
88641
- address: walletInfo.address,
88642
- network: walletInfo.network
88643
- };
88644
- }
88645
- /**
88646
- * List all wallets for a user
88647
- */
88648
- async listWallets(userSigner) {
88649
- return (await userSigner.listWallets()).map((w) => ({
88650
- name: w.name,
88651
- address: w.address,
88652
- network: w.network,
88653
- version: w.version,
88654
- createdAt: w.createdAt
88655
- }));
88656
- }
88657
- /**
88658
- * Remove a wallet for a user
88659
- */
88660
- async removeWallet(userSigner, userStorage, name) {
88661
- const deleted = await userSigner.deleteWallet(name);
88662
- if (deleted) await userStorage.delete(`wallet:${name}`);
88663
- return deleted;
88664
- }
88665
- /**
88666
- * Get or load a wallet for balance/transfer operations
88667
- */
88668
- async getWalletForOperations(userSigner, name) {
88669
- const walletInfo = await userSigner.getWallet(name);
88670
- if (!walletInfo) throw new Error("Wallet not found");
88671
- const network = this.getNetwork(walletInfo.network);
88672
- const walletId = createWalletId(network, walletInfo.address);
88673
- if (this.loadedWallets.has(walletId)) return this.loadedWallets.get(walletId);
88674
- const signer = userSigner.getUnderlyingSigner();
88675
- if (typeof signer.getLoadedWallet === "function") {
88676
- const scopedId = `${userSigner.getUserId()}:${name}`;
88677
- const wallet = await signer.getLoadedWallet(scopedId);
88678
- this.loadedWallets.set(walletId, wallet);
88679
- return wallet;
88680
- }
88681
- if (typeof signer.getStoredWallet === "function") {
88682
- const scopedId = `${userSigner.getUserId()}:${name}`;
88683
- const storedWallet = signer.getStoredWallet(scopedId);
88684
- if (storedWallet) {
88685
- const kit = await this.getKit();
88686
- const signerInstance = await Signer.fromMnemonic(storedWallet.mnemonic, { type: "ton" });
88687
- const walletAdapter = storedWallet.version === "v5r1" ? await WalletV5R1Adapter.create(signerInstance, {
88688
- client: kit.getApiClient(network),
88689
- network
88690
- }) : await WalletV4R2Adapter.create(signerInstance, {
88691
- client: kit.getApiClient(network),
88692
- network
88693
- });
88694
- let wallet = await kit.addWallet(walletAdapter);
88695
- if (!wallet) wallet = kit.getWallet(walletId);
88696
- if (!wallet) throw new Error("Failed to load wallet");
88697
- this.loadedWallets.set(walletId, wallet);
88698
- return wallet;
88699
- }
88700
- }
88701
- throw new Error("Unable to load wallet for operations");
88702
- }
88703
- /**
88704
88206
  * Get TON balance
88705
88207
  */
88706
- async getBalance(userSigner, walletName) {
88707
- return (await this.getWalletForOperations(userSigner, walletName)).getBalance();
88208
+ async getBalance() {
88209
+ return this.wallet.getBalance();
88708
88210
  }
88709
88211
  /**
88710
88212
  * Get Jetton balance
88711
88213
  */
88712
- async getJettonBalance(userSigner, walletName, jettonAddress) {
88713
- return (await this.getWalletForOperations(userSigner, walletName)).getJettonBalance(jettonAddress);
88214
+ async getJettonBalance(jettonAddress) {
88215
+ return this.wallet.getJettonBalance(jettonAddress);
88714
88216
  }
88715
88217
  /**
88716
88218
  * Get all Jettons
88717
88219
  */
88718
- async getJettons(userSigner, walletName) {
88719
- return (await (await this.getWalletForOperations(userSigner, walletName)).getJettons({ pagination: {
88220
+ async getJettons() {
88221
+ return (await this.wallet.getJettons({ pagination: {
88720
88222
  limit: 100,
88721
88223
  offset: 0
88722
88224
  } })).jettons.map((j) => ({
@@ -88728,15 +88230,11 @@ var McpWalletService = class {
88728
88230
  }));
88729
88231
  }
88730
88232
  /**
88731
- * Get transaction history for a wallet
88732
- */
88733
- /**
88734
- * Get transaction history using events API (like demo wallet)
88233
+ * Get transaction history using events API
88735
88234
  */
88736
- async getTransactions(userSigner, walletName, limit = 20) {
88737
- const wallet = await this.getWalletForOperations(userSigner, walletName);
88738
- const address = wallet.getAddress();
88739
- const response = await wallet.getClient().getEvents({
88235
+ async getTransactions(limit = 20) {
88236
+ const address = this.wallet.getAddress();
88237
+ const response = await this.wallet.getClient().getEvents({
88740
88238
  account: address,
88741
88239
  limit
88742
88240
  });
@@ -88781,49 +88279,16 @@ var McpWalletService = class {
88781
88279
  return results;
88782
88280
  }
88783
88281
  /**
88784
- * Send TON (with optional confirmation flow)
88785
- */
88786
- async sendTon(userSigner, userStorage, walletName, toAddress, amountNano, amountTon, comment) {
88787
- const limitCheck = await this.limitsManager.checkTransactionLimit(userStorage, parseFloat(amountTon));
88788
- if (!limitCheck.allowed) return {
88789
- success: false,
88790
- message: limitCheck.reason
88791
- };
88792
- if (this.requiresConfirmation()) {
88793
- const pending = await this.pendingManager.createPending(userStorage, {
88794
- type: "send_ton",
88795
- walletName,
88796
- description: `Send ${amountTon} TON to ${toAddress}${comment ? ` (${comment})` : ""}`,
88797
- data: {
88798
- type: "send_ton",
88799
- toAddress,
88800
- amountNano,
88801
- amountTon,
88802
- comment
88803
- }
88804
- });
88805
- return {
88806
- success: true,
88807
- message: `Transaction pending confirmation. ID: ${pending.id}`,
88808
- pendingTransactionId: pending.id
88809
- };
88810
- }
88811
- return this.executeTonTransfer(userSigner, userStorage, walletName, toAddress, amountNano, comment);
88812
- }
88813
- /**
88814
- * Execute TON transfer (internal)
88282
+ * Send TON
88815
88283
  */
88816
- async executeTonTransfer(userSigner, userStorage, walletName, toAddress, amountNano, comment) {
88284
+ async sendTon(toAddress, amountNano, comment) {
88817
88285
  try {
88818
- const wallet = await this.getWalletForOperations(userSigner, walletName);
88819
- const tx = await wallet.createTransferTonTransaction({
88286
+ const tx = await this.wallet.createTransferTonTransaction({
88820
88287
  recipientAddress: toAddress,
88821
88288
  transferAmount: amountNano,
88822
88289
  comment
88823
88290
  });
88824
- await wallet.sendTransaction(tx);
88825
- const amountTon = Number(BigInt(amountNano)) / 1e9;
88826
- await this.limitsManager.recordTransaction(userStorage, amountTon);
88291
+ await this.wallet.sendTransaction(tx);
88827
88292
  return {
88828
88293
  success: true,
88829
88294
  message: `Successfully sent ${amountNano} nanoTON to ${toAddress}`
@@ -88836,49 +88301,37 @@ var McpWalletService = class {
88836
88301
  }
88837
88302
  }
88838
88303
  /**
88839
- * Send Jetton (with optional confirmation flow)
88304
+ * Send Jetton
88840
88305
  */
88841
- async sendJetton(userSigner, userStorage, walletName, toAddress, jettonAddress, amountRaw, amountHuman, symbol, decimals, comment) {
88842
- if (this.requiresConfirmation()) {
88843
- const pending = await this.pendingManager.createPending(userStorage, {
88844
- type: "send_jetton",
88845
- walletName,
88846
- description: `Send ${amountHuman} ${symbol ?? "tokens"} to ${toAddress}${comment ? ` (${comment})` : ""}`,
88847
- data: {
88848
- type: "send_jetton",
88849
- toAddress,
88850
- jettonAddress,
88851
- amountRaw,
88852
- amountHuman,
88853
- symbol,
88854
- decimals,
88855
- comment
88856
- }
88306
+ async sendJetton(toAddress, jettonAddress, amountRaw, comment) {
88307
+ try {
88308
+ const tx = await this.wallet.createTransferJettonTransaction({
88309
+ recipientAddress: toAddress,
88310
+ jettonAddress,
88311
+ transferAmount: amountRaw,
88312
+ comment
88857
88313
  });
88314
+ await this.wallet.sendTransaction(tx);
88858
88315
  return {
88859
88316
  success: true,
88860
- message: `Transaction pending confirmation. ID: ${pending.id}`,
88861
- pendingTransactionId: pending.id
88317
+ message: `Successfully sent jettons to ${toAddress}`
88318
+ };
88319
+ } catch (error) {
88320
+ return {
88321
+ success: false,
88322
+ message: error instanceof Error ? error.message : "Unknown error"
88862
88323
  };
88863
88324
  }
88864
- return this.executeJettonTransfer(userSigner, walletName, toAddress, jettonAddress, amountRaw, comment);
88865
88325
  }
88866
88326
  /**
88867
- * Execute Jetton transfer (internal)
88327
+ * Send a raw transaction request directly
88868
88328
  */
88869
- async executeJettonTransfer(userSigner, walletName, toAddress, jettonAddress, amountRaw, comment) {
88329
+ async sendRawTransaction(request) {
88870
88330
  try {
88871
- const wallet = await this.getWalletForOperations(userSigner, walletName);
88872
- const tx = await wallet.createTransferJettonTransaction({
88873
- recipientAddress: toAddress,
88874
- jettonAddress,
88875
- transferAmount: amountRaw,
88876
- comment
88877
- });
88878
- await wallet.sendTransaction(tx);
88331
+ await this.wallet.sendTransaction(request);
88879
88332
  return {
88880
88333
  success: true,
88881
- message: `Successfully sent jettons to ${toAddress}`
88334
+ message: `Successfully sent transaction with ${request.messages.length} message(s)`
88882
88335
  };
88883
88336
  } catch (error) {
88884
88337
  return {
@@ -88888,80 +88341,114 @@ var McpWalletService = class {
88888
88341
  }
88889
88342
  }
88890
88343
  /**
88891
- * Get swap quote
88344
+ * Get swap quote with transaction params ready to execute
88892
88345
  */
88893
- async getSwapQuote(userSigner, walletName, fromToken, toToken, amount, slippageBps) {
88894
- const walletInfo = await userSigner.getWallet(walletName);
88895
- if (!walletInfo) throw new Error("Wallet not found");
88896
- const network = this.getNetwork(walletInfo.network);
88346
+ async getSwapQuote(fromToken, toToken, amount, slippageBps) {
88347
+ const network = this.wallet.getNetwork();
88897
88348
  const kit = await this.getKit();
88898
88349
  const params = {
88899
- fromToken: fromToken === "TON" ? "TON" : fromToken,
88900
- toToken: toToken === "TON" ? "TON" : toToken,
88901
- amountFrom: amount,
88350
+ fromToken: fromToken === "TON" ? { type: "ton" } : {
88351
+ type: "jetton",
88352
+ value: fromToken
88353
+ },
88354
+ toToken: toToken === "TON" ? { type: "ton" } : {
88355
+ type: "jetton",
88356
+ value: toToken
88357
+ },
88358
+ amount,
88902
88359
  network,
88903
88360
  slippageBps
88904
88361
  };
88905
88362
  const quote = await kit.swap.getQuote(params);
88906
- return {
88363
+ const swapParams = {
88907
88364
  quote,
88908
- fromToken: typeof quote.fromToken === "string" ? quote.fromToken : quote.fromToken,
88909
- toToken: typeof quote.toToken === "string" ? quote.toToken : quote.toToken,
88365
+ userAddress: this.wallet.getAddress()
88366
+ };
88367
+ const tx = await kit.swap.buildSwapTransaction(swapParams);
88368
+ return {
88369
+ fromToken: quote.fromToken.type === "ton" ? "TON" : quote.fromToken.value,
88370
+ toToken: quote.toToken.type === "ton" ? "TON" : quote.toToken.value,
88910
88371
  fromAmount: quote.fromAmount,
88911
88372
  toAmount: quote.toAmount,
88912
88373
  minReceived: quote.minReceived,
88913
- provider: quote.provider,
88914
- expiresAt: quote.expiresAt
88374
+ provider: quote.providerId,
88375
+ expiresAt: quote.expiresAt,
88376
+ transaction: {
88377
+ messages: tx.messages.map((m) => ({
88378
+ address: m.address,
88379
+ amount: m.amount.toString(),
88380
+ stateInit: m.stateInit,
88381
+ payload: m.payload
88382
+ })),
88383
+ validUntil: tx.validUntil
88384
+ }
88915
88385
  };
88916
88386
  }
88917
88387
  /**
88918
- * Execute swap (with optional confirmation flow)
88388
+ * Get all NFTs
88919
88389
  */
88920
- async executeSwap(userSigner, userStorage, walletName, quote) {
88921
- if (this.requiresConfirmation()) {
88922
- const pending = await this.pendingManager.createPending(userStorage, {
88923
- type: "swap",
88924
- walletName,
88925
- description: `Swap ${quote.fromAmount} ${quote.fromToken} for ${quote.toAmount} ${quote.toToken}`,
88926
- data: {
88927
- type: "swap",
88928
- fromToken: String(quote.fromToken),
88929
- toToken: String(quote.toToken),
88930
- fromAmount: quote.fromAmount,
88931
- toAmount: quote.toAmount,
88932
- minReceived: quote.minReceived,
88933
- provider: quote.provider,
88934
- quoteJson: JSON.stringify(quote)
88935
- }
88936
- });
88937
- return {
88938
- success: true,
88939
- message: `Swap pending confirmation. ID: ${pending.id}`,
88940
- pendingTransactionId: pending.id
88941
- };
88942
- }
88943
- return this.executeSwapInternal(userSigner, walletName, quote);
88390
+ async getNfts(limit = 20, offset = 0) {
88391
+ return (await this.wallet.getNfts({ pagination: {
88392
+ limit,
88393
+ offset
88394
+ } })).nfts.map((nft) => ({
88395
+ address: nft.address,
88396
+ name: nft.info?.name,
88397
+ description: nft.info?.description,
88398
+ image: typeof nft.info?.image === "string" ? nft.info.image : nft.info?.image?.url,
88399
+ collection: nft.collection ? {
88400
+ address: nft.collection.address,
88401
+ name: nft.collection.name
88402
+ } : void 0,
88403
+ attributes: nft.attributes?.map((attr) => ({
88404
+ trait_type: attr.traitType,
88405
+ value: attr.value
88406
+ })),
88407
+ ownerAddress: nft.ownerAddress,
88408
+ isOnSale: nft.isOnSale,
88409
+ isSoulbound: nft.isSoulbound,
88410
+ saleContractAddress: nft.saleContractAddress
88411
+ }));
88944
88412
  }
88945
88413
  /**
88946
- * Execute swap (internal)
88414
+ * Get a specific NFT by address
88947
88415
  */
88948
- async executeSwapInternal(userSigner, walletName, quote) {
88416
+ async getNft(nftAddress) {
88417
+ const nft = await this.wallet.getNft(nftAddress);
88418
+ if (!nft) return null;
88419
+ return {
88420
+ address: nft.address,
88421
+ name: nft.info?.name,
88422
+ description: nft.info?.description,
88423
+ image: typeof nft.info?.image === "string" ? nft.info.image : nft.info?.image?.url,
88424
+ collection: nft.collection ? {
88425
+ address: nft.collection.address,
88426
+ name: nft.collection.name
88427
+ } : void 0,
88428
+ attributes: nft.attributes?.map((attr) => ({
88429
+ trait_type: attr.traitType,
88430
+ value: attr.value
88431
+ })),
88432
+ ownerAddress: nft.ownerAddress,
88433
+ isOnSale: nft.isOnSale,
88434
+ isSoulbound: nft.isSoulbound,
88435
+ saleContractAddress: nft.saleContractAddress
88436
+ };
88437
+ }
88438
+ /**
88439
+ * Send NFT
88440
+ */
88441
+ async sendNft(nftAddress, toAddress, comment) {
88949
88442
  try {
88950
- const [wallet, kit, walletInfo] = await Promise.all([
88951
- this.getWalletForOperations(userSigner, walletName),
88952
- this.getKit(),
88953
- userSigner.getWallet(walletName)
88954
- ]);
88955
- if (!walletInfo) throw new Error("Wallet not found");
88956
- const params = {
88957
- quote,
88958
- userAddress: walletInfo.address
88959
- };
88960
- const tx = await kit.swap.buildSwapTransaction(params);
88961
- await wallet.sendTransaction(tx);
88443
+ const tx = await this.wallet.createTransferNftTransaction({
88444
+ nftAddress,
88445
+ recipientAddress: toAddress,
88446
+ comment
88447
+ });
88448
+ await this.wallet.sendTransaction(tx);
88962
88449
  return {
88963
88450
  success: true,
88964
- message: `Successfully swapped ${quote.fromAmount} ${quote.fromToken} for ${quote.toAmount} ${quote.toToken}`
88451
+ message: `Successfully sent NFT ${nftAddress} to ${toAddress}`
88965
88452
  };
88966
88453
  } catch (error) {
88967
88454
  return {
@@ -88971,52 +88458,11 @@ var McpWalletService = class {
88971
88458
  }
88972
88459
  }
88973
88460
  /**
88974
- * Confirm a pending transaction
88975
- */
88976
- async confirmTransaction(userSigner, userStorage, transactionId) {
88977
- const pending = await this.pendingManager.confirmPending(userStorage, transactionId);
88978
- if (!pending) return {
88979
- success: false,
88980
- message: "Transaction not found or expired"
88981
- };
88982
- switch (pending.data.type) {
88983
- case "send_ton": {
88984
- const data = pending.data;
88985
- return this.executeTonTransfer(userSigner, userStorage, pending.walletName, data.toAddress, data.amountNano, data.comment);
88986
- }
88987
- case "send_jetton": {
88988
- const data = pending.data;
88989
- return this.executeJettonTransfer(userSigner, pending.walletName, data.toAddress, data.jettonAddress, data.amountRaw, data.comment);
88990
- }
88991
- case "swap": {
88992
- const data = pending.data;
88993
- const quote = JSON.parse(data.quoteJson);
88994
- return this.executeSwapInternal(userSigner, pending.walletName, quote);
88995
- }
88996
- default: return {
88997
- success: false,
88998
- message: "Unknown transaction type"
88999
- };
89000
- }
89001
- }
89002
- /**
89003
- * Cancel a pending transaction
89004
- */
89005
- async cancelTransaction(userStorage, transactionId) {
89006
- return this.pendingManager.cancelPending(userStorage, transactionId);
89007
- }
89008
- /**
89009
- * List pending transactions
89010
- */
89011
- async listPendingTransactions(userStorage) {
89012
- return this.pendingManager.listPending(userStorage);
89013
- }
89014
- /**
89015
88461
  * Resolve contact name to address
89016
88462
  */
89017
- async resolveContact(userId, name) {
88463
+ async resolveContact(name) {
89018
88464
  if (!this.config.contacts) return null;
89019
- return this.config.contacts.resolve(userId, name);
88465
+ return this.config.contacts.resolve("default", name);
89020
88466
  }
89021
88467
  /**
89022
88468
  * Close and cleanup
@@ -89026,12 +88472,11 @@ var McpWalletService = class {
89026
88472
  await this.kit.close();
89027
88473
  this.kit = null;
89028
88474
  }
89029
- this.loadedWallets.clear();
89030
88475
  }
89031
88476
  };
89032
88477
 
89033
88478
  //#endregion
89034
- //#region src/tools/mcp-tools.ts
88479
+ //#region src/tools/balance-tools.ts
89035
88480
  /**
89036
88481
  * Copyright (c) TonTech.
89037
88482
  *
@@ -89039,185 +88484,24 @@ var McpWalletService = class {
89039
88484
  * LICENSE file in the root directory of this source tree.
89040
88485
  *
89041
88486
  */
89042
- /**
89043
- * MCP tools for multi-user deployments with authentication
89044
- *
89045
- * These tools wrap the McpWalletService with user isolation.
89046
- * They are used by the createTonWalletMCP factory.
89047
- */
89048
- /**
89049
- * Converts a human-readable amount to raw units.
89050
- */
89051
- function toRawAmount(amount, decimals) {
89052
- const [intPart, fracPart = ""] = amount.split(".");
89053
- return (intPart + fracPart.padEnd(decimals, "0").slice(0, decimals)).replace(/^0+/, "") || "0";
89054
- }
89055
- const TON_DECIMALS = 9;
89056
- const createWalletSchema = z.object({
89057
- name: z.string().min(1).describe("Unique name for the wallet"),
89058
- version: z.enum(["v5r1", "v4r2"]).optional().describe("Wallet version (v5r1 recommended, v4r2 for legacy compatibility). Defaults to v5r1."),
89059
- network: z.enum(["mainnet", "testnet"]).optional().describe("Network to create the wallet on. Defaults to the server configured network.")
89060
- });
89061
- const importWalletSchema = z.object({
89062
- name: z.string().min(1).describe("Unique name for the wallet"),
89063
- mnemonic: z.string().describe("24-word mnemonic phrase separated by spaces"),
89064
- version: z.enum(["v5r1", "v4r2"]).optional().describe("Wallet version (v5r1 recommended, v4r2 for legacy compatibility). Defaults to v5r1."),
89065
- network: z.enum(["mainnet", "testnet"]).optional().describe("Network to import the wallet on. Defaults to the server configured network.")
89066
- });
89067
- const removeWalletSchema = z.object({ name: z.string().min(1).describe("Name of the wallet to remove") });
89068
- function createMcpWalletTools(_walletService, wrapHandler) {
89069
- return {
89070
- create_wallet: {
89071
- description: "Create a new TON wallet. The wallet is created securely and you will receive the address. No seed phrase is exposed.",
89072
- inputSchema: createWalletSchema,
89073
- handler: wrapHandler(async (args, userId, service) => {
89074
- const userSigner = service.createUserSigner(userId);
89075
- const userStorage = service.createUserStorage(userId);
89076
- try {
89077
- const result = await service.createWallet(userSigner, userStorage, args.name, args.version, args.network);
89078
- return { content: [{
89079
- type: "text",
89080
- text: JSON.stringify({
89081
- success: true,
89082
- wallet: {
89083
- name: result.name,
89084
- address: result.address,
89085
- network: result.network
89086
- }
89087
- }, null, 2)
89088
- }] };
89089
- } catch (error) {
89090
- return {
89091
- content: [{
89092
- type: "text",
89093
- text: JSON.stringify({
89094
- success: false,
89095
- error: error instanceof Error ? error.message : "Unknown error"
89096
- })
89097
- }],
89098
- isError: true
89099
- };
89100
- }
89101
- })
89102
- },
89103
- import_wallet: {
89104
- description: "Import an existing TON wallet using a 24-word mnemonic phrase. The mnemonic is stored securely and never exposed.",
89105
- inputSchema: importWalletSchema,
89106
- handler: wrapHandler(async (args, userId, service) => {
89107
- const mnemonicWords = args.mnemonic.trim().split(/\s+/);
89108
- if (mnemonicWords.length !== 24) return {
89109
- content: [{
89110
- type: "text",
89111
- text: JSON.stringify({
89112
- success: false,
89113
- error: `Invalid mnemonic: expected 24 words, got ${mnemonicWords.length}`
89114
- })
89115
- }],
89116
- isError: true
89117
- };
89118
- const userSigner = service.createUserSigner(userId);
89119
- const userStorage = service.createUserStorage(userId);
89120
- try {
89121
- const result = await service.importWallet(userSigner, userStorage, args.name, mnemonicWords, args.version, args.network);
89122
- return { content: [{
89123
- type: "text",
89124
- text: JSON.stringify({
89125
- success: true,
89126
- wallet: {
89127
- name: result.name,
89128
- address: result.address,
89129
- network: result.network
89130
- }
89131
- }, null, 2)
89132
- }] };
89133
- } catch (error) {
89134
- return {
89135
- content: [{
89136
- type: "text",
89137
- text: JSON.stringify({
89138
- success: false,
89139
- error: error instanceof Error ? error.message : "Unknown error"
89140
- })
89141
- }],
89142
- isError: true
89143
- };
89144
- }
89145
- })
89146
- },
89147
- list_wallets: {
89148
- description: "List all your stored TON wallets with their addresses and metadata.",
89149
- inputSchema: z.object({}),
89150
- handler: wrapHandler(async (_args, userId, service) => {
89151
- const userSigner = service.createUserSigner(userId);
89152
- const wallets = await service.listWallets(userSigner);
89153
- return { content: [{
89154
- type: "text",
89155
- text: JSON.stringify({
89156
- success: true,
89157
- wallets: wallets.map((w) => ({
89158
- name: w.name,
89159
- address: w.address,
89160
- network: w.network,
89161
- version: w.version,
89162
- createdAt: w.createdAt
89163
- })),
89164
- count: wallets.length
89165
- }, null, 2)
89166
- }] };
89167
- })
89168
- },
89169
- remove_wallet: {
89170
- description: "Remove a wallet from storage. This action cannot be undone!",
89171
- inputSchema: removeWalletSchema,
89172
- handler: wrapHandler(async (args, userId, service) => {
89173
- const userSigner = service.createUserSigner(userId);
89174
- const userStorage = service.createUserStorage(userId);
89175
- if (!await service.removeWallet(userSigner, userStorage, args.name)) return {
89176
- content: [{
89177
- type: "text",
89178
- text: JSON.stringify({
89179
- success: false,
89180
- error: "Wallet not found"
89181
- })
89182
- }],
89183
- isError: true
89184
- };
89185
- return { content: [{
89186
- type: "text",
89187
- text: JSON.stringify({
89188
- success: true,
89189
- message: `Wallet "${args.name}" has been removed`
89190
- })
89191
- }] };
89192
- })
89193
- }
89194
- };
89195
- }
89196
- const getBalanceSchema = z.object({ wallet: z.string().min(1).describe("Name of the wallet to check balance") });
89197
- const getJettonBalanceSchema = z.object({
89198
- wallet: z.string().min(1).describe("Name of the wallet"),
89199
- jettonAddress: z.string().min(1).describe("Jetton master contract address")
89200
- });
89201
- const getJettonsSchema = z.object({ wallet: z.string().min(1).describe("Name of the wallet") });
89202
- const getTransactionsSchema = z.object({
89203
- wallet: z.string().min(1).describe("Name of the wallet to get transactions for"),
89204
- limit: z.number().min(1).max(100).optional().describe("Maximum number of transactions to return (default: 20, max: 100)")
89205
- });
89206
- function createMcpBalanceTools(_walletService, wrapHandler) {
88487
+ const getBalanceSchema = z.object({});
88488
+ const getJettonBalanceSchema = z.object({ jettonAddress: z.string().min(1).describe("Jetton master contract address") });
88489
+ const getJettonsSchema = z.object({});
88490
+ const getTransactionsSchema = z.object({ limit: z.number().min(1).max(100).optional().describe("Maximum number of transactions to return (default: 20, max: 100)") });
88491
+ function createMcpBalanceTools(service) {
89207
88492
  return {
89208
88493
  get_balance: {
89209
- description: "Get the TON balance of a wallet. Returns both human-readable and raw (nanoTON) amounts.",
88494
+ description: "Get the TON balance of the wallet. Returns both human-readable and raw (nanoTON) amounts.",
89210
88495
  inputSchema: getBalanceSchema,
89211
- handler: wrapHandler(async (args, userId, service) => {
89212
- const userSigner = service.createUserSigner(userId);
88496
+ handler: async () => {
89213
88497
  try {
89214
- const balance = await service.getBalance(userSigner, args.wallet);
88498
+ const balance = await service.getBalance();
89215
88499
  const balanceTon = Number(BigInt(balance)) / 1e9;
89216
88500
  return { content: [{
89217
88501
  type: "text",
89218
88502
  text: JSON.stringify({
89219
88503
  success: true,
89220
- wallet: args.wallet,
88504
+ address: service.getAddress(),
89221
88505
  balance: `${balanceTon} TON`,
89222
88506
  balanceNano: balance
89223
88507
  }, null, 2)
@@ -89234,20 +88518,18 @@ function createMcpBalanceTools(_walletService, wrapHandler) {
89234
88518
  isError: true
89235
88519
  };
89236
88520
  }
89237
- })
88521
+ }
89238
88522
  },
89239
88523
  get_jetton_balance: {
89240
- description: "Get the balance of a specific Jetton (token) in a wallet.",
88524
+ description: "Get the balance of a specific Jetton (token) in the wallet.",
89241
88525
  inputSchema: getJettonBalanceSchema,
89242
- handler: wrapHandler(async (args, userId, service) => {
89243
- const userSigner = service.createUserSigner(userId);
88526
+ handler: async (args) => {
89244
88527
  try {
89245
- const balance = await service.getJettonBalance(userSigner, args.wallet, args.jettonAddress);
88528
+ const balance = await service.getJettonBalance(args.jettonAddress);
89246
88529
  return { content: [{
89247
88530
  type: "text",
89248
88531
  text: JSON.stringify({
89249
88532
  success: true,
89250
- wallet: args.wallet,
89251
88533
  jettonAddress: args.jettonAddress,
89252
88534
  balance
89253
88535
  }, null, 2)
@@ -89264,20 +88546,18 @@ function createMcpBalanceTools(_walletService, wrapHandler) {
89264
88546
  isError: true
89265
88547
  };
89266
88548
  }
89267
- })
88549
+ }
89268
88550
  },
89269
88551
  get_jettons: {
89270
- description: "List all Jettons (tokens) in a wallet with their balances and metadata.",
88552
+ description: "List all Jettons (tokens) in the wallet with their balances and metadata.",
89271
88553
  inputSchema: getJettonsSchema,
89272
- handler: wrapHandler(async (args, userId, service) => {
89273
- const userSigner = service.createUserSigner(userId);
88554
+ handler: async () => {
89274
88555
  try {
89275
- const jettons = await service.getJettons(userSigner, args.wallet);
88556
+ const jettons = await service.getJettons();
89276
88557
  return { content: [{
89277
88558
  type: "text",
89278
88559
  text: JSON.stringify({
89279
88560
  success: true,
89280
- wallet: args.wallet,
89281
88561
  jettons,
89282
88562
  count: jettons.length
89283
88563
  }, null, 2)
@@ -89294,20 +88574,18 @@ function createMcpBalanceTools(_walletService, wrapHandler) {
89294
88574
  isError: true
89295
88575
  };
89296
88576
  }
89297
- })
88577
+ }
89298
88578
  },
89299
88579
  get_transactions: {
89300
- description: "Get recent transaction history for a wallet. Returns events with actions like TON transfers, Jetton transfers, swaps, and more.",
88580
+ description: "Get recent transaction history for the wallet. Returns events with actions like TON transfers, Jetton transfers, swaps, and more.",
89301
88581
  inputSchema: getTransactionsSchema,
89302
- handler: wrapHandler(async (args, userId, service) => {
89303
- const userSigner = service.createUserSigner(userId);
88582
+ handler: async (args) => {
89304
88583
  try {
89305
- const transactions = await service.getTransactions(userSigner, args.wallet, args.limit ?? 20);
88584
+ const transactions = await service.getTransactions(args.limit ?? 20);
89306
88585
  return { content: [{
89307
88586
  type: "text",
89308
88587
  text: JSON.stringify({
89309
88588
  success: true,
89310
- wallet: args.wallet,
89311
88589
  transactions: transactions.map((tx) => ({
89312
88590
  eventId: tx.eventId,
89313
88591
  timestamp: tx.timestamp,
@@ -89355,33 +88633,61 @@ function createMcpBalanceTools(_walletService, wrapHandler) {
89355
88633
  isError: true
89356
88634
  };
89357
88635
  }
89358
- })
88636
+ }
89359
88637
  }
89360
88638
  };
89361
88639
  }
88640
+
88641
+ //#endregion
88642
+ //#region src/tools/types.ts
88643
+ /**
88644
+ * Converts a human-readable amount to raw units.
88645
+ */
88646
+ function toRawAmount(amount, decimals) {
88647
+ const [intPart, fracPart = ""] = amount.split(".");
88648
+ return (intPart + fracPart.padEnd(decimals, "0").slice(0, decimals)).replace(/^0+/, "") || "0";
88649
+ }
88650
+ const TON_DECIMALS = 9;
88651
+
88652
+ //#endregion
88653
+ //#region src/tools/transfer-tools.ts
88654
+ /**
88655
+ * Copyright (c) TonTech.
88656
+ *
88657
+ * This source code is licensed under the MIT license found in the
88658
+ * LICENSE file in the root directory of this source tree.
88659
+ *
88660
+ */
89362
88661
  const sendTonSchema = z.object({
89363
- wallet: z.string().min(1).describe("Name of the wallet to send from"),
89364
88662
  toAddress: z.string().min(1).describe("Recipient TON address"),
89365
88663
  amount: z.string().min(1).describe("Amount of TON to send (e.g., \"1.5\" for 1.5 TON)"),
89366
88664
  comment: z.string().optional().describe("Optional comment/memo for the transaction")
89367
88665
  });
89368
88666
  const sendJettonSchema = z.object({
89369
- wallet: z.string().min(1).describe("Name of the wallet to send from"),
89370
88667
  toAddress: z.string().min(1).describe("Recipient TON address"),
89371
88668
  jettonAddress: z.string().min(1).describe("Jetton master contract address"),
89372
88669
  amount: z.string().min(1).describe("Amount of tokens to send in human-readable format"),
89373
88670
  comment: z.string().optional().describe("Optional comment/memo for the transaction")
89374
88671
  });
89375
- function createMcpTransferTools(_walletService, wrapHandler) {
88672
+ const transactionMessageSchema = z.object({
88673
+ address: z.string().min(1).describe("Recipient wallet address"),
88674
+ amount: z.string().min(1).describe("Amount to transfer in nanotons"),
88675
+ stateInit: z.string().optional().describe("Initial state for deploying a new contract (Base64)"),
88676
+ payload: z.string().optional().describe("Message payload data (Base64)")
88677
+ });
88678
+ const sendRawTransactionSchema = z.object({
88679
+ messages: z.array(transactionMessageSchema).min(1).describe("Array of messages to include in the transaction"),
88680
+ validUntil: z.number().optional().describe("Unix timestamp after which the transaction becomes invalid"),
88681
+ fromAddress: z.string().optional().describe("Sender wallet address")
88682
+ });
88683
+ function createMcpTransferTools(service) {
89376
88684
  return {
89377
88685
  send_ton: {
89378
- description: "Send TON from a wallet to an address. Amount is in TON (e.g., \"1.5\" means 1.5 TON). May require confirmation if enabled.",
88686
+ description: "Send TON from the wallet to an address. Amount is in TON (e.g., \"1.5\" means 1.5 TON).",
89379
88687
  inputSchema: sendTonSchema,
89380
- handler: wrapHandler(async (args, userId, service) => {
89381
- const userSigner = service.createUserSigner(userId);
89382
- const userStorage = service.createUserStorage(userId);
88688
+ handler: async (args) => {
89383
88689
  const rawAmount = toRawAmount(args.amount, TON_DECIMALS);
89384
- const result = await service.sendTon(userSigner, userStorage, args.wallet, args.toAddress, rawAmount, args.amount, args.comment);
88690
+ const result = await service.sendTon(args.toAddress, rawAmount, args.comment);
89385
88691
  if (!result.success) return {
89386
88692
  content: [{
89387
88693
  type: "text",
@@ -89398,26 +88704,22 @@ function createMcpTransferTools(_walletService, wrapHandler) {
89398
88704
  success: true,
89399
88705
  message: result.message,
89400
88706
  details: {
89401
- from: args.wallet,
89402
88707
  to: args.toAddress,
89403
88708
  amount: `${args.amount} TON`,
89404
- comment: args.comment || null,
89405
- pendingTransactionId: result.pendingTransactionId || null
88709
+ comment: args.comment || null
89406
88710
  }
89407
88711
  }, null, 2)
89408
88712
  }] };
89409
- })
88713
+ }
89410
88714
  },
89411
88715
  send_jetton: {
89412
- description: "Send Jettons (tokens) from a wallet to an address. Amount is in human-readable format. May require confirmation if enabled.",
88716
+ description: "Send Jettons (tokens) from the wallet to an address. Amount is in human-readable format.",
89413
88717
  inputSchema: sendJettonSchema,
89414
- handler: wrapHandler(async (args, userId, service) => {
89415
- const userSigner = service.createUserSigner(userId);
89416
- const userStorage = service.createUserStorage(userId);
88718
+ handler: async (args) => {
89417
88719
  let decimals;
89418
88720
  let symbol;
89419
88721
  try {
89420
- const jetton = (await service.getJettons(userSigner, args.wallet)).find((j) => j.address.toLowerCase() === args.jettonAddress.toLowerCase());
88722
+ const jetton = (await service.getJettons()).find((j) => j.address.toLowerCase() === args.jettonAddress.toLowerCase());
89421
88723
  if (jetton) {
89422
88724
  decimals = jetton.decimals;
89423
88725
  symbol = jetton.symbol;
@@ -89445,7 +88747,7 @@ function createMcpTransferTools(_walletService, wrapHandler) {
89445
88747
  isError: true
89446
88748
  };
89447
88749
  const rawAmount = toRawAmount(args.amount, decimals);
89448
- const result = await service.sendJetton(userSigner, userStorage, args.wallet, args.toAddress, args.jettonAddress, rawAmount, args.amount, symbol, decimals, args.comment);
88750
+ const result = await service.sendJetton(args.toAddress, args.jettonAddress, rawAmount, args.comment);
89449
88751
  if (!result.success) return {
89450
88752
  content: [{
89451
88753
  type: "text",
@@ -89462,68 +88764,209 @@ function createMcpTransferTools(_walletService, wrapHandler) {
89462
88764
  success: true,
89463
88765
  message: result.message,
89464
88766
  details: {
89465
- from: args.wallet,
89466
88767
  to: args.toAddress,
89467
88768
  jettonAddress: args.jettonAddress,
89468
88769
  amount: `${args.amount} ${symbol || "tokens"}`,
89469
- comment: args.comment || null,
89470
- pendingTransactionId: result.pendingTransactionId || null
88770
+ comment: args.comment || null
89471
88771
  }
89472
88772
  }, null, 2)
89473
88773
  }] };
89474
- })
88774
+ }
88775
+ },
88776
+ send_raw_transaction: {
88777
+ description: "Send a raw transaction with full control over messages. Amounts are in nanotons. Supports multiple messages in a single transaction.",
88778
+ inputSchema: sendRawTransactionSchema,
88779
+ handler: async (args) => {
88780
+ const result = await service.sendRawTransaction({
88781
+ messages: args.messages,
88782
+ validUntil: args.validUntil,
88783
+ fromAddress: args.fromAddress
88784
+ });
88785
+ if (!result.success) return {
88786
+ content: [{
88787
+ type: "text",
88788
+ text: JSON.stringify({
88789
+ success: false,
88790
+ error: result.message
88791
+ })
88792
+ }],
88793
+ isError: true
88794
+ };
88795
+ return { content: [{
88796
+ type: "text",
88797
+ text: JSON.stringify({
88798
+ success: true,
88799
+ message: result.message,
88800
+ details: {
88801
+ messageCount: args.messages.length,
88802
+ messages: args.messages.map((m) => ({
88803
+ to: m.address,
88804
+ amount: `${m.amount} nanoTON`
88805
+ }))
88806
+ }
88807
+ }, null, 2)
88808
+ }] };
88809
+ }
89475
88810
  }
89476
88811
  };
89477
88812
  }
89478
- const quoteCache = /* @__PURE__ */ new Map();
89479
- function generateQuoteId() {
89480
- return `quote_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
89481
- }
89482
- function cleanExpiredQuotes() {
89483
- const now = Date.now();
89484
- for (const [id, data] of quoteCache.entries()) if (data.expiresAt < now) quoteCache.delete(id);
89485
- }
88813
+
88814
+ //#endregion
88815
+ //#region src/tools/swap-tools.ts
88816
+ /**
88817
+ * Copyright (c) TonTech.
88818
+ *
88819
+ * This source code is licensed under the MIT license found in the
88820
+ * LICENSE file in the root directory of this source tree.
88821
+ *
88822
+ */
89486
88823
  const getSwapQuoteSchema = z.object({
89487
- wallet: z.string().min(1).describe("Name of the wallet to swap from"),
89488
88824
  fromToken: z.string().min(1).describe("Token to swap from (\"TON\" or jetton address)"),
89489
88825
  toToken: z.string().min(1).describe("Token to swap to (\"TON\" or jetton address)"),
89490
88826
  amount: z.string().min(1).describe("Amount to swap in raw units"),
89491
88827
  slippageBps: z.number().optional().describe("Slippage tolerance in basis points (default 100 = 1%)")
89492
88828
  });
89493
- const executeSwapSchema = z.object({
89494
- wallet: z.string().min(1).describe("Name of the wallet to execute swap from"),
89495
- quoteId: z.string().min(1).describe("Quote ID returned from get_swap_quote")
88829
+ function createMcpSwapTools(service) {
88830
+ return { get_swap_quote: {
88831
+ description: "Get a quote for swapping tokens. Returns quote details and transaction params. If user confirms, use send_raw_transaction to execute.",
88832
+ inputSchema: getSwapQuoteSchema,
88833
+ handler: async (args) => {
88834
+ try {
88835
+ const result = await service.getSwapQuote(args.fromToken, args.toToken, args.amount, args.slippageBps);
88836
+ return { content: [{
88837
+ type: "text",
88838
+ text: JSON.stringify({
88839
+ success: true,
88840
+ quote: {
88841
+ fromToken: result.fromToken,
88842
+ toToken: result.toToken,
88843
+ fromAmount: result.fromAmount,
88844
+ toAmount: result.toAmount,
88845
+ minReceived: result.minReceived,
88846
+ provider: result.provider,
88847
+ expiresAt: result.expiresAt ? (/* @__PURE__ */ new Date(result.expiresAt * 1e3)).toISOString() : null
88848
+ },
88849
+ transaction: result.transaction,
88850
+ note: "If user confirms, use send_raw_transaction with the transaction params to execute the swap."
88851
+ }, null, 2)
88852
+ }] };
88853
+ } catch (error) {
88854
+ return {
88855
+ content: [{
88856
+ type: "text",
88857
+ text: JSON.stringify({
88858
+ success: false,
88859
+ error: error instanceof Error ? error.message : "Unknown error"
88860
+ })
88861
+ }],
88862
+ isError: true
88863
+ };
88864
+ }
88865
+ }
88866
+ } };
88867
+ }
88868
+
88869
+ //#endregion
88870
+ //#region src/tools/known-jettons-tools.ts
88871
+ /**
88872
+ * Copyright (c) TonTech.
88873
+ *
88874
+ * This source code is licensed under the MIT license found in the
88875
+ * LICENSE file in the root directory of this source tree.
88876
+ *
88877
+ */
88878
+ const getKnownJettonsSchema = z.object({});
88879
+ const KNOWN_JETTONS = [
88880
+ {
88881
+ symbol: "USD₮",
88882
+ name: "Tether USD",
88883
+ address: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
88884
+ decimals: 6
88885
+ },
88886
+ {
88887
+ symbol: "NOT",
88888
+ name: "Notcoin",
88889
+ address: "EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT",
88890
+ decimals: 9
88891
+ },
88892
+ {
88893
+ symbol: "DOGS",
88894
+ name: "Dogs",
88895
+ address: "EQCvxJy4eG8hyHBFsZ7eePxrRsUQSFE_jpptRAYBmcG_DOGS",
88896
+ decimals: 9
88897
+ },
88898
+ {
88899
+ symbol: "DUST",
88900
+ name: "DeDust",
88901
+ address: "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE",
88902
+ decimals: 9
88903
+ },
88904
+ {
88905
+ symbol: "GRAM",
88906
+ name: "Gram",
88907
+ address: "EQC47093oX5Xhb0xuk2lCr2RhS8rj-vul61u4W2UH5ORmG_O",
88908
+ decimals: 9
88909
+ }
88910
+ ];
88911
+ function createMcpKnownJettonsTools() {
88912
+ return { get_known_jettons: {
88913
+ description: "Get a list of known/popular Jettons (tokens) on TON with their addresses and metadata. Useful for looking up token addresses for swaps or transfers.",
88914
+ inputSchema: getKnownJettonsSchema,
88915
+ handler: async () => {
88916
+ return { content: [{
88917
+ type: "text",
88918
+ text: JSON.stringify({
88919
+ success: true,
88920
+ jettons: KNOWN_JETTONS,
88921
+ count: KNOWN_JETTONS.length
88922
+ }, null, 2)
88923
+ }] };
88924
+ }
88925
+ } };
88926
+ }
88927
+
88928
+ //#endregion
88929
+ //#region src/tools/nft-tools.ts
88930
+ /**
88931
+ * Copyright (c) TonTech.
88932
+ *
88933
+ * This source code is licensed under the MIT license found in the
88934
+ * LICENSE file in the root directory of this source tree.
88935
+ *
88936
+ */
88937
+ const getNftsSchema = z.object({
88938
+ limit: z.number().min(1).max(100).optional().describe("Maximum number of NFTs to return (default: 20, max: 100)"),
88939
+ offset: z.number().min(0).optional().describe("Offset for pagination (default: 0)")
88940
+ });
88941
+ const getNftSchema = z.object({ nftAddress: z.string().min(1).describe("NFT item contract address") });
88942
+ const sendNftSchema = z.object({
88943
+ nftAddress: z.string().min(1).describe("NFT item contract address to transfer"),
88944
+ toAddress: z.string().min(1).describe("Recipient TON address"),
88945
+ comment: z.string().optional().describe("Optional comment/memo for the transaction")
89496
88946
  });
89497
- function createMcpSwapTools(_walletService, wrapHandler) {
88947
+ function createMcpNftTools(service) {
89498
88948
  return {
89499
- get_swap_quote: {
89500
- description: "Get a quote for swapping tokens. Returns a quote ID to use with execute_swap.",
89501
- inputSchema: getSwapQuoteSchema,
89502
- handler: wrapHandler(async (args, userId, service) => {
89503
- const userSigner = service.createUserSigner(userId);
88949
+ get_nfts: {
88950
+ description: "List all NFTs (non-fungible tokens) in the wallet with their metadata, collection info, and attributes.",
88951
+ inputSchema: getNftsSchema,
88952
+ handler: async (args) => {
89504
88953
  try {
89505
- cleanExpiredQuotes();
89506
- const result = await service.getSwapQuote(userSigner, args.wallet, args.fromToken, args.toToken, args.amount, args.slippageBps);
89507
- const quoteId = generateQuoteId();
89508
- const expiresAt = result.expiresAt ? result.expiresAt * 1e3 : Date.now() + 6e4;
89509
- quoteCache.set(quoteId, {
89510
- quote: result.quote,
89511
- expiresAt,
89512
- userId
89513
- });
88954
+ const nfts = await service.getNfts(args.limit ?? 20, args.offset ?? 0);
89514
88955
  return { content: [{
89515
88956
  type: "text",
89516
88957
  text: JSON.stringify({
89517
88958
  success: true,
89518
- quoteId,
89519
- fromToken: result.fromToken,
89520
- toToken: result.toToken,
89521
- fromAmount: result.fromAmount,
89522
- toAmount: result.toAmount,
89523
- minReceived: result.minReceived,
89524
- provider: result.provider,
89525
- expiresAt: result.expiresAt ? (/* @__PURE__ */ new Date(result.expiresAt * 1e3)).toISOString() : null,
89526
- note: "Use the quoteId with execute_swap to complete the swap."
88959
+ nfts: nfts.map((nft) => ({
88960
+ address: nft.address,
88961
+ name: nft.name,
88962
+ description: nft.description,
88963
+ image: nft.image,
88964
+ collection: nft.collection,
88965
+ attributes: nft.attributes,
88966
+ isOnSale: nft.isOnSale,
88967
+ isSoulbound: nft.isSoulbound
88968
+ })),
88969
+ count: nfts.length
89527
88970
  }, null, 2)
89528
88971
  }] };
89529
88972
  } catch (error) {
@@ -89538,156 +88981,87 @@ function createMcpSwapTools(_walletService, wrapHandler) {
89538
88981
  isError: true
89539
88982
  };
89540
88983
  }
89541
- })
88984
+ }
89542
88985
  },
89543
- execute_swap: {
89544
- description: "Execute a token swap using a quote. May require confirmation if enabled.",
89545
- inputSchema: executeSwapSchema,
89546
- handler: wrapHandler(async (args, userId, service) => {
89547
- const userSigner = service.createUserSigner(userId);
89548
- const userStorage = service.createUserStorage(userId);
89549
- cleanExpiredQuotes();
89550
- const cachedQuote = quoteCache.get(args.quoteId);
89551
- if (!cachedQuote) return {
89552
- content: [{
89553
- type: "text",
89554
- text: JSON.stringify({
89555
- success: false,
89556
- error: "Quote not found or expired. Please get a new quote."
89557
- })
89558
- }],
89559
- isError: true
89560
- };
89561
- if (cachedQuote.userId !== userId) return {
89562
- content: [{
88986
+ get_nft: {
88987
+ description: "Get detailed information about a specific NFT by its address.",
88988
+ inputSchema: getNftSchema,
88989
+ handler: async (args) => {
88990
+ try {
88991
+ const nft = await service.getNft(args.nftAddress);
88992
+ if (!nft) return {
88993
+ content: [{
88994
+ type: "text",
88995
+ text: JSON.stringify({
88996
+ success: false,
88997
+ error: "NFT not found"
88998
+ })
88999
+ }],
89000
+ isError: true
89001
+ };
89002
+ return { content: [{
89563
89003
  type: "text",
89564
89004
  text: JSON.stringify({
89565
- success: false,
89566
- error: "Quote not found or expired. Please get a new quote."
89567
- })
89568
- }],
89569
- isError: true
89570
- };
89571
- if (cachedQuote.expiresAt < Date.now()) {
89572
- quoteCache.delete(args.quoteId);
89005
+ success: true,
89006
+ nft: {
89007
+ address: nft.address,
89008
+ name: nft.name,
89009
+ description: nft.description,
89010
+ image: nft.image,
89011
+ collection: nft.collection,
89012
+ attributes: nft.attributes,
89013
+ ownerAddress: nft.ownerAddress,
89014
+ isOnSale: nft.isOnSale,
89015
+ isSoulbound: nft.isSoulbound,
89016
+ saleContractAddress: nft.saleContractAddress
89017
+ }
89018
+ }, null, 2)
89019
+ }] };
89020
+ } catch (error) {
89573
89021
  return {
89574
89022
  content: [{
89575
89023
  type: "text",
89576
89024
  text: JSON.stringify({
89577
89025
  success: false,
89578
- error: "Quote has expired. Please get a new quote."
89026
+ error: error instanceof Error ? error.message : "Unknown error"
89579
89027
  })
89580
89028
  }],
89581
89029
  isError: true
89582
89030
  };
89583
89031
  }
89584
- const result = await service.executeSwap(userSigner, userStorage, args.wallet, cachedQuote.quote);
89585
- quoteCache.delete(args.quoteId);
89586
- if (!result.success) return {
89587
- content: [{
89588
- type: "text",
89589
- text: JSON.stringify({
89590
- success: false,
89591
- error: result.message
89592
- })
89593
- }],
89594
- isError: true
89595
- };
89596
- return { content: [{
89597
- type: "text",
89598
- text: JSON.stringify({
89599
- success: true,
89600
- message: result.message,
89601
- details: {
89602
- fromToken: cachedQuote.quote.fromToken,
89603
- toToken: cachedQuote.quote.toToken,
89604
- fromAmount: cachedQuote.quote.fromAmount,
89605
- toAmount: cachedQuote.quote.toAmount,
89606
- provider: cachedQuote.quote.provider,
89607
- pendingTransactionId: result.pendingTransactionId || null
89608
- }
89609
- }, null, 2)
89610
- }] };
89611
- })
89612
- }
89613
- };
89614
- }
89615
- const confirmTransactionSchema = z.object({ transactionId: z.string().min(1).describe("ID of the pending transaction to confirm") });
89616
- const cancelTransactionSchema = z.object({ transactionId: z.string().min(1).describe("ID of the pending transaction to cancel") });
89617
- function createMcpPendingTools(_walletService, wrapHandler) {
89618
- return {
89619
- confirm_transaction: {
89620
- description: "Confirm and execute a pending transaction.",
89621
- inputSchema: confirmTransactionSchema,
89622
- handler: wrapHandler(async (args, userId, service) => {
89623
- const userSigner = service.createUserSigner(userId);
89624
- const userStorage = service.createUserStorage(userId);
89625
- const result = await service.confirmTransaction(userSigner, userStorage, args.transactionId);
89626
- if (!result.success) return {
89627
- content: [{
89628
- type: "text",
89629
- text: JSON.stringify({
89630
- success: false,
89631
- error: result.message
89632
- })
89633
- }],
89634
- isError: true
89635
- };
89636
- return { content: [{
89637
- type: "text",
89638
- text: JSON.stringify({
89639
- success: true,
89640
- message: result.message
89641
- }, null, 2)
89642
- }] };
89643
- })
89644
- },
89645
- cancel_transaction: {
89646
- description: "Cancel a pending transaction.",
89647
- inputSchema: cancelTransactionSchema,
89648
- handler: wrapHandler(async (args, userId, service) => {
89649
- const userStorage = service.createUserStorage(userId);
89650
- if (!await service.cancelTransaction(userStorage, args.transactionId)) return {
89651
- content: [{
89652
- type: "text",
89653
- text: JSON.stringify({
89654
- success: false,
89655
- error: "Transaction not found or already processed"
89656
- })
89657
- }],
89658
- isError: true
89659
- };
89660
- return { content: [{
89661
- type: "text",
89662
- text: JSON.stringify({
89663
- success: true,
89664
- message: "Transaction cancelled"
89665
- })
89666
- }] };
89667
- })
89032
+ }
89668
89033
  },
89669
- list_pending_transactions: {
89670
- description: "List all pending transactions awaiting confirmation.",
89671
- inputSchema: z.object({}),
89672
- handler: wrapHandler(async (_args, userId, service) => {
89673
- const userStorage = service.createUserStorage(userId);
89674
- const pending = await service.listPendingTransactions(userStorage);
89675
- return { content: [{
89676
- type: "text",
89677
- text: JSON.stringify({
89678
- success: true,
89679
- transactions: pending.map((tx) => ({
89680
- id: tx.id,
89681
- type: tx.type,
89682
- wallet: tx.walletName,
89683
- description: tx.description,
89684
- createdAt: tx.createdAt,
89685
- expiresAt: tx.expiresAt
89686
- })),
89687
- count: pending.length
89688
- }, null, 2)
89689
- }] };
89690
- })
89034
+ send_nft: {
89035
+ description: "Transfer an NFT from the wallet to another address.",
89036
+ inputSchema: sendNftSchema,
89037
+ handler: async (args) => {
89038
+ try {
89039
+ const result = await service.sendNft(args.nftAddress, args.toAddress, args.comment);
89040
+ return {
89041
+ content: [{
89042
+ type: "text",
89043
+ text: JSON.stringify({
89044
+ success: result.success,
89045
+ message: result.message,
89046
+ nftAddress: args.nftAddress,
89047
+ recipient: args.toAddress
89048
+ }, null, 2)
89049
+ }],
89050
+ isError: !result.success
89051
+ };
89052
+ } catch (error) {
89053
+ return {
89054
+ content: [{
89055
+ type: "text",
89056
+ text: JSON.stringify({
89057
+ success: false,
89058
+ error: error instanceof Error ? error.message : "Unknown error"
89059
+ })
89060
+ }],
89061
+ isError: true
89062
+ };
89063
+ }
89064
+ }
89691
89065
  }
89692
89066
  };
89693
89067
  }
@@ -89709,403 +89083,63 @@ const SERVER_VERSION = "0.1.0";
89709
89083
  /**
89710
89084
  * Create a configured TON Wallet MCP server
89711
89085
  *
89712
- * @param config - Configuration with adapters and limits
89086
+ * @param config - Configuration with wallet instance
89713
89087
  * @returns Configured McpServer instance
89714
89088
  *
89715
89089
  * @example
89716
89090
  * ```typescript
89717
- * import { createTonWalletMCP, InMemoryStorageAdapter, LocalSignerAdapter } from '@ton/mcp';
89091
+ * import { createTonWalletMCP } from '@ton/mcp';
89092
+ * import { Signer, WalletV5R1Adapter, TonWalletKit, Network } from '@ton/walletkit';
89718
89093
  *
89719
- * const server = createTonWalletMCP({
89720
- * storage: new InMemoryStorageAdapter(),
89721
- * signer: new LocalSignerAdapter(),
89722
- * userContext: {
89723
- * getUserId: async (ctx) => ctx.headers?.['x-user-id'] ?? null,
89724
- * },
89725
- * limits: {
89726
- * maxTransactionTon: 100,
89727
- * dailyLimitTon: 1000,
89728
- * maxWalletsPerUser: 10,
89729
- * },
89730
- * requireConfirmation: true,
89094
+ * // Create wallet adapter
89095
+ * const kit = new TonWalletKit({ ... });
89096
+ * const signer = await Signer.fromMnemonic(mnemonic, { type: 'ton' });
89097
+ * const walletAdapter = await WalletV5R1Adapter.create(signer, {
89098
+ * client: kit.getApiClient(Network.mainnet()),
89099
+ * network: Network.mainnet(),
89731
89100
  * });
89101
+ * const wallet = await kit.addWallet(walletAdapter);
89102
+ *
89103
+ * // Create MCP server
89104
+ * const server = createTonWalletMCP({ wallet });
89732
89105
  * ```
89733
89106
  */
89734
- function createTonWalletMCP(config) {
89735
- const walletService = new McpWalletService({
89736
- storage: config.storage,
89737
- signer: config.signer,
89107
+ async function createTonWalletMCP(config) {
89108
+ const walletService = await McpWalletService.create({
89109
+ wallet: config.wallet,
89738
89110
  contacts: config.contacts,
89739
- defaultNetwork: config.network,
89740
- limits: config.limits,
89741
- requireConfirmation: config.requireConfirmation
89111
+ networks: config.networks
89742
89112
  });
89743
89113
  const server = new McpServer({
89744
89114
  name: SERVER_NAME$1,
89745
89115
  version: SERVER_VERSION
89746
89116
  });
89747
- const authenticateUser = async (requestContext) => {
89748
- const userId = await config.userContext.getUserId(requestContext);
89749
- if (!userId) throw new Error("User authentication required");
89750
- return userId;
89751
- };
89752
- const createAuthenticatedHandler = (handler) => {
89753
- return async (args, extra) => {
89754
- const extraObj = extra;
89755
- return handler(args, await authenticateUser({
89756
- headers: extraObj?.headers,
89757
- metadata: extraObj?.meta
89758
- }), walletService);
89759
- };
89760
- };
89761
- const walletTools = createMcpWalletTools(walletService, createAuthenticatedHandler);
89762
- const balanceTools = createMcpBalanceTools(walletService, createAuthenticatedHandler);
89763
- const transferTools = createMcpTransferTools(walletService, createAuthenticatedHandler);
89764
- const swapTools = createMcpSwapTools(walletService, createAuthenticatedHandler);
89765
- const pendingTools = createMcpPendingTools(walletService, createAuthenticatedHandler);
89117
+ const balanceTools = createMcpBalanceTools(walletService);
89118
+ const transferTools = createMcpTransferTools(walletService);
89119
+ const swapTools = createMcpSwapTools(walletService);
89120
+ const knownJettonsTools = createMcpKnownJettonsTools();
89121
+ const nftTools = createMcpNftTools(walletService);
89766
89122
  const registerTool = (name, tool) => {
89767
89123
  server.registerTool(name, {
89768
89124
  description: tool.description,
89769
89125
  inputSchema: tool.inputSchema
89770
89126
  }, tool.handler);
89771
89127
  };
89772
- registerTool("create_wallet", walletTools.create_wallet);
89773
- registerTool("import_wallet", walletTools.import_wallet);
89774
- registerTool("list_wallets", walletTools.list_wallets);
89775
- registerTool("remove_wallet", walletTools.remove_wallet);
89776
89128
  registerTool("get_balance", balanceTools.get_balance);
89777
89129
  registerTool("get_jetton_balance", balanceTools.get_jetton_balance);
89778
89130
  registerTool("get_jettons", balanceTools.get_jettons);
89779
89131
  registerTool("get_transactions", balanceTools.get_transactions);
89780
89132
  registerTool("send_ton", transferTools.send_ton);
89781
89133
  registerTool("send_jetton", transferTools.send_jetton);
89134
+ registerTool("send_raw_transaction", transferTools.send_raw_transaction);
89782
89135
  registerTool("get_swap_quote", swapTools.get_swap_quote);
89783
- registerTool("execute_swap", swapTools.execute_swap);
89784
- if (config.requireConfirmation) {
89785
- registerTool("confirm_transaction", pendingTools.confirm_transaction);
89786
- registerTool("cancel_transaction", pendingTools.cancel_transaction);
89787
- registerTool("list_pending_transactions", pendingTools.list_pending_transactions);
89788
- }
89136
+ registerTool("get_known_jettons", knownJettonsTools.get_known_jettons);
89137
+ registerTool("get_nfts", nftTools.get_nfts);
89138
+ registerTool("get_nft", nftTools.get_nft);
89139
+ registerTool("send_nft", nftTools.send_nft);
89789
89140
  return server;
89790
89141
  }
89791
89142
 
89792
- //#endregion
89793
- //#region src/adapters/InMemoryStorageAdapter.ts
89794
- /**
89795
- * In-memory storage adapter for testing and development.
89796
- * Data is not persistent and will be lost on process restart.
89797
- */
89798
- var InMemoryStorageAdapter = class {
89799
- data = /* @__PURE__ */ new Map();
89800
- timers = /* @__PURE__ */ new Map();
89801
- /**
89802
- * Get a value by key
89803
- */
89804
- async get(key) {
89805
- return this.data.get(key) ?? null;
89806
- }
89807
- /**
89808
- * Set a value with optional TTL
89809
- */
89810
- async set(key, value, ttlSeconds) {
89811
- const existingTimer = this.timers.get(key);
89812
- if (existingTimer) {
89813
- clearTimeout(existingTimer);
89814
- this.timers.delete(key);
89815
- }
89816
- this.data.set(key, value);
89817
- if (ttlSeconds !== void 0 && ttlSeconds > 0) {
89818
- const timer = setTimeout(() => {
89819
- this.data.delete(key);
89820
- this.timers.delete(key);
89821
- }, ttlSeconds * 1e3);
89822
- this.timers.set(key, timer);
89823
- }
89824
- }
89825
- /**
89826
- * Delete a key
89827
- */
89828
- async delete(key) {
89829
- const timer = this.timers.get(key);
89830
- if (timer) {
89831
- clearTimeout(timer);
89832
- this.timers.delete(key);
89833
- }
89834
- return this.data.delete(key);
89835
- }
89836
- /**
89837
- * List keys matching prefix
89838
- */
89839
- async list(prefix) {
89840
- const keys = [];
89841
- for (const key of this.data.keys()) if (key.startsWith(prefix)) keys.push(key);
89842
- return keys;
89843
- }
89844
- /**
89845
- * Clear all data (useful for testing)
89846
- */
89847
- clear() {
89848
- for (const timer of this.timers.values()) clearTimeout(timer);
89849
- this.timers.clear();
89850
- this.data.clear();
89851
- }
89852
- /**
89853
- * Get the number of stored items (useful for testing)
89854
- */
89855
- size() {
89856
- return this.data.size;
89857
- }
89858
- };
89859
-
89860
- //#endregion
89861
- //#region src/adapters/LocalSignerAdapter.ts
89862
- /**
89863
- * Copyright (c) TonTech.
89864
- *
89865
- * This source code is licensed under the MIT license found in the
89866
- * LICENSE file in the root directory of this source tree.
89867
- *
89868
- */
89869
- /**
89870
- * LocalSignerAdapter - Local signer using @ton/walletkit
89871
- *
89872
- * This adapter uses TonWalletKit for wallet operations.
89873
- * For production use with encryption, extend this class or implement
89874
- * your own ISignerAdapter with proper key encryption.
89875
- *
89876
- * Note: This is a reference implementation. For production custody,
89877
- * consider using HSM, KMS, or Vault-based signers.
89878
- */
89879
- /**
89880
- * Local signer adapter using TonWalletKit.
89881
- *
89882
- * This implementation stores mnemonics in memory. For production use,
89883
- * implement encryption or use a secure key management system.
89884
- */
89885
- var LocalSignerAdapter = class {
89886
- wallets = /* @__PURE__ */ new Map();
89887
- kit = null;
89888
- loadedWallets = /* @__PURE__ */ new Map();
89889
- /**
89890
- * Get Network instance from network name
89891
- */
89892
- getNetwork(networkName) {
89893
- return networkName === "mainnet" ? Network.mainnet() : Network.testnet();
89894
- }
89895
- /**
89896
- * Initialize or get TonWalletKit instance
89897
- */
89898
- async getKit() {
89899
- if (!this.kit) {
89900
- this.kit = new TonWalletKit({
89901
- networks: {
89902
- [Network.mainnet().chainId]: {},
89903
- [Network.testnet().chainId]: {}
89904
- },
89905
- storage: new MemoryStorageAdapter()
89906
- });
89907
- await this.kit.waitForReady();
89908
- }
89909
- return this.kit;
89910
- }
89911
- /**
89912
- * Create wallet adapter from mnemonic
89913
- */
89914
- async createWalletAdapter(mnemonic, version, network) {
89915
- const kit = await this.getKit();
89916
- const signer = await Signer.fromMnemonic(mnemonic, { type: "ton" });
89917
- return {
89918
- adapter: version === "v5r1" ? await WalletV5R1Adapter.create(signer, {
89919
- client: kit.getApiClient(network),
89920
- network
89921
- }) : await WalletV4R2Adapter.create(signer, {
89922
- client: kit.getApiClient(network),
89923
- network
89924
- }),
89925
- publicKey: Buffer.from(signer.publicKey).toString("hex")
89926
- };
89927
- }
89928
- /**
89929
- * Create a new wallet with generated mnemonic
89930
- */
89931
- async createWallet(params) {
89932
- const { walletId, version, network: networkName } = params;
89933
- if (this.wallets.has(walletId)) throw new Error(`Wallet "${walletId}" already exists`);
89934
- const network = this.getNetwork(networkName);
89935
- const mnemonic = await CreateTonMnemonic();
89936
- const { adapter, publicKey } = await this.createWalletAdapter(mnemonic, version, network);
89937
- const address = adapter.getAddress();
89938
- const createdAt = (/* @__PURE__ */ new Date()).toISOString();
89939
- const storedWallet = {
89940
- walletId,
89941
- mnemonic,
89942
- publicKey,
89943
- address,
89944
- network: networkName,
89945
- version,
89946
- createdAt
89947
- };
89948
- this.wallets.set(walletId, storedWallet);
89949
- const wallet = await (await this.getKit()).addWallet(adapter);
89950
- if (wallet) this.loadedWallets.set(walletId, wallet);
89951
- return {
89952
- walletId,
89953
- publicKey,
89954
- address,
89955
- network: networkName,
89956
- version,
89957
- createdAt
89958
- };
89959
- }
89960
- /**
89961
- * Import a wallet from mnemonic
89962
- */
89963
- async importWallet(params) {
89964
- const { walletId, mnemonic, version, network: networkName } = params;
89965
- if (this.wallets.has(walletId)) throw new Error(`Wallet "${walletId}" already exists`);
89966
- if (mnemonic.length !== 24) throw new Error(`Invalid mnemonic: expected 24 words, got ${mnemonic.length}`);
89967
- const network = this.getNetwork(networkName);
89968
- const { adapter, publicKey } = await this.createWalletAdapter(mnemonic, version, network);
89969
- const address = adapter.getAddress();
89970
- const createdAt = (/* @__PURE__ */ new Date()).toISOString();
89971
- const storedWallet = {
89972
- walletId,
89973
- mnemonic,
89974
- publicKey,
89975
- address,
89976
- network: networkName,
89977
- version,
89978
- createdAt
89979
- };
89980
- this.wallets.set(walletId, storedWallet);
89981
- const wallet = await (await this.getKit()).addWallet(adapter);
89982
- if (wallet) this.loadedWallets.set(walletId, wallet);
89983
- return {
89984
- walletId,
89985
- publicKey,
89986
- address,
89987
- network: networkName,
89988
- version,
89989
- createdAt
89990
- };
89991
- }
89992
- /**
89993
- * Get wallet info by ID
89994
- */
89995
- async getWallet(walletId) {
89996
- const stored = this.wallets.get(walletId);
89997
- if (!stored) return null;
89998
- return {
89999
- walletId: stored.walletId,
90000
- publicKey: stored.publicKey,
90001
- address: stored.address,
90002
- network: stored.network,
90003
- version: stored.version,
90004
- createdAt: stored.createdAt
90005
- };
90006
- }
90007
- /**
90008
- * List all wallet IDs
90009
- */
90010
- async listWalletIds() {
90011
- return Array.from(this.wallets.keys());
90012
- }
90013
- /**
90014
- * Delete a wallet
90015
- */
90016
- async deleteWallet(walletId) {
90017
- if (!this.wallets.get(walletId)) return false;
90018
- this.loadedWallets.delete(walletId);
90019
- return this.wallets.delete(walletId);
90020
- }
90021
- /**
90022
- * Get or load a wallet for signing
90023
- */
90024
- async getWalletForSigning(walletId) {
90025
- if (this.loadedWallets.has(walletId)) return this.loadedWallets.get(walletId);
90026
- const stored = this.wallets.get(walletId);
90027
- if (!stored) throw new Error("Wallet not found");
90028
- const network = this.getNetwork(stored.network);
90029
- const { adapter } = await this.createWalletAdapter(stored.mnemonic, stored.version, network);
90030
- const kit = await this.getKit();
90031
- let wallet = await kit.addWallet(adapter);
90032
- if (!wallet) {
90033
- const kitWalletId = `${network.chainId}:${stored.address}`;
90034
- wallet = kit.getWallet(kitWalletId);
90035
- if (!wallet) throw new Error("Failed to load wallet");
90036
- }
90037
- this.loadedWallets.set(walletId, wallet);
90038
- return wallet;
90039
- }
90040
- /**
90041
- * Sign a transaction
90042
- */
90043
- async signTransaction(_walletId, _unsignedBoc) {
90044
- throw new Error("signTransaction with BOC not implemented. Use the wallet service for transaction signing.");
90045
- }
90046
- /**
90047
- * Sign a message
90048
- */
90049
- async signMessage(walletId, message) {
90050
- const stored = this.wallets.get(walletId);
90051
- if (!stored) throw new Error("Wallet not found");
90052
- const signature = await (await Signer.fromMnemonic(stored.mnemonic, { type: "ton" })).sign(message);
90053
- return Buffer.from(signature, "hex");
90054
- }
90055
- /**
90056
- * Close and cleanup
90057
- */
90058
- async close() {
90059
- if (this.kit) {
90060
- await this.kit.close();
90061
- this.kit = null;
90062
- }
90063
- this.loadedWallets.clear();
90064
- }
90065
- /**
90066
- * Get TonWalletKit instance for direct wallet operations
90067
- * Used by WalletService for balance/transfer operations
90068
- */
90069
- async getKitInstance() {
90070
- return this.getKit();
90071
- }
90072
- /**
90073
- * Get a loaded wallet by ID for direct operations
90074
- * Used by WalletService for balance/transfer operations
90075
- */
90076
- async getLoadedWallet(walletId) {
90077
- return this.getWalletForSigning(walletId);
90078
- }
90079
- /**
90080
- * Get stored wallet data (internal use only)
90081
- */
90082
- getStoredWallet(walletId) {
90083
- return this.wallets.get(walletId);
90084
- }
90085
- };
90086
-
90087
- //#endregion
90088
- //#region src/adapters/TelegramUserContextProvider.ts
90089
- /**
90090
- * Simple user context provider that always returns a fixed user ID.
90091
- * Useful for single-user CLI applications or testing.
90092
- */
90093
- var StaticUserContextProvider = class {
90094
- userId;
90095
- constructor(userId) {
90096
- this.userId = userId;
90097
- }
90098
- async getUserId() {
90099
- return this.userId;
90100
- }
90101
- async getUserMetadata() {
90102
- return {
90103
- provider: "static",
90104
- userId: this.userId
90105
- };
90106
- }
90107
- };
90108
-
90109
89143
  //#endregion
90110
89144
  //#region src/cli.ts
90111
89145
  /**
@@ -90122,12 +89156,21 @@ var StaticUserContextProvider = class {
90122
89156
  * TON wallet management tools for use with Claude Desktop or other MCP clients.
90123
89157
  *
90124
89158
  * Usage:
90125
- * npx @ton/mcp # stdio mode (default)
90126
- * npx @ton/mcp --http # HTTP server on port 3000
90127
- * npx @ton/mcp --http 8080 # HTTP server on custom port
89159
+ * npx @ton/mcp # stdio mode (default)
89160
+ * npx @ton/mcp --http # HTTP server on 0.0.0.0:3000
89161
+ * npx @ton/mcp --http 8080 # HTTP server on custom port
89162
+ * npx @ton/mcp --http --host 127.0.0.1 # HTTP server on custom host
89163
+ *
89164
+ * Environment variables:
89165
+ * NETWORK - Network to use (mainnet or testnet, default: mainnet)
89166
+ * MNEMONIC - 24-word mnemonic phrase for wallet (if not provided, a new wallet is created)
89167
+ * WALLET_VERSION - Wallet version (v5r1 or v4r2, default: v5r1)
90128
89168
  */
90129
89169
  const SERVER_NAME = "ton-mcp";
90130
89170
  const NETWORK = process.env.NETWORK || "mainnet";
89171
+ const MNEMONIC = process.env.MNEMONIC;
89172
+ const WALLET_VERSION = process.env.WALLET_VERSION || "v5r1";
89173
+ const TONCENTER_API_KEY = process.env.TONCENTER_API_KEY;
90131
89174
  function log(message) {
90132
89175
  console.error(`[${SERVER_NAME}] ${message}`);
90133
89176
  }
@@ -90136,33 +89179,66 @@ function parseArgs() {
90136
89179
  const httpIndex = args.indexOf("--http");
90137
89180
  if (httpIndex === -1) return { mode: "stdio" };
90138
89181
  const nextArg = args[httpIndex + 1];
89182
+ const port = nextArg && !nextArg.startsWith("-") ? parseInt(nextArg, 10) : 3e3;
89183
+ const hostIndex = args.indexOf("--host");
89184
+ const hostArg = hostIndex !== -1 ? args[hostIndex + 1] : void 0;
90139
89185
  return {
90140
89186
  mode: "http",
90141
- port: nextArg && !nextArg.startsWith("-") ? parseInt(nextArg, 10) : 3e3
89187
+ port,
89188
+ host: hostArg && !hostArg.startsWith("-") ? hostArg : "0.0.0.0"
90142
89189
  };
90143
89190
  }
90144
- function createAdaptersAndServer() {
90145
- const storage = new InMemoryStorageAdapter();
90146
- const signer = new LocalSignerAdapter();
89191
+ async function createWalletAndServer() {
89192
+ const network = NETWORK === "mainnet" ? Network.mainnet() : Network.testnet();
89193
+ const apiConfig = {};
89194
+ if (TONCENTER_API_KEY) {
89195
+ apiConfig.url = NETWORK === "mainnet" ? "https://toncenter.com" : "https://testnet.toncenter.com";
89196
+ apiConfig.key = TONCENTER_API_KEY;
89197
+ }
89198
+ const kit = new TonWalletKit({
89199
+ networks: { [network.chainId]: { apiClient: apiConfig } },
89200
+ storage: new MemoryStorageAdapter()
89201
+ });
89202
+ await kit.waitForReady();
89203
+ let mnemonic;
89204
+ if (MNEMONIC) {
89205
+ mnemonic = MNEMONIC.trim().split(/\s+/);
89206
+ if (mnemonic.length !== 24) throw new Error(`Invalid mnemonic: expected 24 words, got ${mnemonic.length}`);
89207
+ log("Using provided mnemonic");
89208
+ } else throw new Error("MNEMONIC is required");
89209
+ const signer = await Signer.fromMnemonic(mnemonic, { type: "ton" });
89210
+ const walletAdapter = WALLET_VERSION === "v5r1" ? await WalletV5R1Adapter.create(signer, {
89211
+ client: kit.getApiClient(network),
89212
+ network
89213
+ }) : await WalletV4R2Adapter.create(signer, {
89214
+ client: kit.getApiClient(network),
89215
+ network
89216
+ });
89217
+ let wallet = await kit.addWallet(walletAdapter);
89218
+ if (!wallet) wallet = kit.getWallet(walletAdapter.getWalletId());
89219
+ if (!wallet) throw new Error("Failed to create wallet");
89220
+ log(`Wallet address: ${wallet.getAddress()}`);
89221
+ log(`Network: ${NETWORK}`);
89222
+ log(`Version: ${WALLET_VERSION}`);
90147
89223
  return {
90148
- server: createTonWalletMCP({
90149
- storage,
90150
- signer,
90151
- userContext: new StaticUserContextProvider("cli-user"),
90152
- network: NETWORK,
90153
- requireConfirmation: false
89224
+ server: await createTonWalletMCP({
89225
+ wallet,
89226
+ networks: {
89227
+ mainnet: TONCENTER_API_KEY && NETWORK === "mainnet" ? { apiKey: TONCENTER_API_KEY } : void 0,
89228
+ testnet: TONCENTER_API_KEY && NETWORK === "testnet" ? { apiKey: TONCENTER_API_KEY } : void 0
89229
+ }
90154
89230
  }),
90155
- signer
89231
+ kit,
89232
+ wallet
90156
89233
  };
90157
89234
  }
90158
89235
  async function startStdio() {
90159
89236
  log("Starting in stdio mode...");
90160
- log(`Network: ${NETWORK}`);
90161
- const { server, signer } = createAdaptersAndServer();
89237
+ const { server, kit } = await createWalletAndServer();
90162
89238
  const transport = new StdioServerTransport();
90163
89239
  const shutdown = async () => {
90164
89240
  log("Shutting down...");
90165
- await signer.close();
89241
+ await kit.close();
90166
89242
  log("Shutdown complete");
90167
89243
  process.exit(0);
90168
89244
  };
@@ -90171,32 +89247,31 @@ async function startStdio() {
90171
89247
  await server.connect(transport);
90172
89248
  log("Server connected and ready to accept requests");
90173
89249
  }
90174
- async function startHttp(port) {
90175
- log(`Starting in HTTP mode on port ${port}...`);
90176
- log(`Network: ${NETWORK}`);
90177
- const { server, signer } = createAdaptersAndServer();
89250
+ async function startHttp(port, host) {
89251
+ log(`Starting in HTTP mode on ${host}:${port}...`);
89252
+ const { server, kit } = await createWalletAndServer();
90178
89253
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
90179
89254
  await server.connect(transport);
90180
89255
  const httpServer = createServer(async (req, res) => {
90181
- if (new URL(req.url ?? "/", `http://localhost:${port}`).pathname === "/mcp") await transport.handleRequest(req, res);
89256
+ if (new URL(req.url ?? "/", `http://${host}:${port}`).pathname === "/mcp") await transport.handleRequest(req, res);
90182
89257
  else res.writeHead(404).end("Not Found");
90183
89258
  });
90184
89259
  const shutdown = async () => {
90185
89260
  log("Shutting down...");
90186
89261
  httpServer.close();
90187
89262
  await transport.close();
90188
- await signer.close();
89263
+ await kit.close();
90189
89264
  log("Shutdown complete");
90190
89265
  process.exit(0);
90191
89266
  };
90192
89267
  process.on("SIGINT", shutdown);
90193
89268
  process.on("SIGTERM", shutdown);
90194
- httpServer.listen(port, () => {
90195
- log(`HTTP server listening on http://localhost:${port}/mcp`);
89269
+ httpServer.listen(port, host, () => {
89270
+ log(`HTTP server listening on http://${host}:${port}/mcp`);
90196
89271
  });
90197
89272
  }
90198
89273
  const config = parseArgs();
90199
- if (config.mode === "http") startHttp(config.port).catch((error) => {
89274
+ if (config.mode === "http") startHttp(config.port, config.host).catch((error) => {
90200
89275
  console.error(`[${SERVER_NAME}] Fatal error:`, error);
90201
89276
  process.exit(1);
90202
89277
  });