@suigar/mcp 0.1.1 → 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.mjs ADDED
@@ -0,0 +1,352 @@
1
+ import { buildTransactionResult, createSuigarClient, resolveDefaultCoinType, resolveOwnerAddress } from "./client.mjs";
2
+ import { GAMES } from "@suigar/sdk/games";
3
+ //#region src/tools.ts
4
+ const GAME_LABELS = {
5
+ coinflip: "Coinflip",
6
+ limbo: "Limbo",
7
+ plinko: "Plinko",
8
+ range: "Range",
9
+ wheel: "Wheel",
10
+ "pvp-coinflip": "PvP Coinflip"
11
+ };
12
+ const GAME_TO_PACKAGE_KEY = {
13
+ coinflip: "coinflip",
14
+ limbo: "limbo",
15
+ plinko: "plinko",
16
+ range: "range",
17
+ wheel: "wheel",
18
+ "pvp-coinflip": "pvpCoinflip"
19
+ };
20
+ const GAME_TO_TOOLS = {
21
+ coinflip: ["build_coinflip_transaction"],
22
+ limbo: ["build_limbo_transaction"],
23
+ plinko: ["build_plinko_transaction"],
24
+ range: ["build_range_transaction"],
25
+ wheel: ["build_wheel_transaction"],
26
+ "pvp-coinflip": [
27
+ "build_pvp_coinflip_create_transaction",
28
+ "build_pvp_coinflip_join_transaction",
29
+ "build_pvp_coinflip_cancel_transaction"
30
+ ]
31
+ };
32
+ const json = (value) => JSON.stringify(value, (_key, item) => typeof item === "bigint" ? item.toString() : item, 2);
33
+ const asTextResponse = (structuredContent) => ({
34
+ content: [{
35
+ type: "text",
36
+ text: json(structuredContent)
37
+ }],
38
+ structuredContent
39
+ });
40
+ const currencyAmountPattern = /^(?:\d+|\d+\.\d+|\.\d+)$/u;
41
+ const coinMetadataForAmount = (config, coinType) => {
42
+ const resolvedCoinType = resolveDefaultCoinType(config, coinType);
43
+ const coin = Object.values(config.sdk.coins).find((metadata) => resolveDefaultCoinType(config, metadata.coinType) === resolvedCoinType);
44
+ if (!coin) throw new RangeError(`Unable to resolve decimals for coin type ${resolvedCoinType}. Add the coin to config.coins before using currency-denominated amounts.`);
45
+ return {
46
+ coinType: resolvedCoinType,
47
+ decimals: coin.decimals
48
+ };
49
+ };
50
+ const toCurrencyAmountText = (value, fieldName) => {
51
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return String(value);
52
+ if (typeof value === "string" && currencyAmountPattern.test(value.trim())) return value.trim();
53
+ throw new TypeError(`Missing or invalid ${fieldName}. Provide a non-negative currency amount such as 1, 2, or 1.5.`);
54
+ };
55
+ const toBaseUnits = (value, fieldName, decimals) => {
56
+ const [rawWhole, rawFraction = ""] = toCurrencyAmountText(value, fieldName).split(".");
57
+ const whole = rawWhole === "" ? "0" : rawWhole;
58
+ const overflow = rawFraction.slice(decimals);
59
+ if (/[^0]/u.test(overflow)) throw new RangeError(`${fieldName} has more fractional digits than the configured coin decimals (${decimals}).`);
60
+ const fraction = rawFraction.slice(0, decimals).padEnd(decimals, "0");
61
+ return BigInt(whole) * 10n ** BigInt(decimals) + BigInt(fraction || "0");
62
+ };
63
+ const toPositiveInteger = (value, fieldName) => {
64
+ if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) return value;
65
+ if (typeof value === "string" && /^[1-9]\d*$/u.test(value)) return BigInt(value);
66
+ throw new TypeError(`Missing or invalid ${fieldName}. Provide a positive integer.`);
67
+ };
68
+ const requireString = (value, fieldName) => {
69
+ if (typeof value === "string" && value.trim()) return value.trim();
70
+ throw new TypeError(`Missing required field: ${fieldName}.`);
71
+ };
72
+ const requireNumber = (value, fieldName) => {
73
+ if (typeof value === "number" && Number.isFinite(value)) return value;
74
+ throw new TypeError(`Missing or invalid numeric field: ${fieldName}.`);
75
+ };
76
+ const getMode = (mode) => mode ?? "build";
77
+ const getConfigInput = (input) => ({
78
+ network: input.network,
79
+ providerUrl: input.providerUrl,
80
+ config: input.config,
81
+ partner: input.partner
82
+ });
83
+ const supportedGames = () => GAMES.map((id) => ({
84
+ id,
85
+ label: GAME_LABELS[id],
86
+ tools: [...GAME_TO_TOOLS[id]]
87
+ }));
88
+ const getPackageId = (config, game) => config.sdk.packageIds[GAME_TO_PACKAGE_KEY[game]];
89
+ const getTarget = (config, game, action) => {
90
+ const packageId = getPackageId(config, game);
91
+ if (game === "pvp-coinflip") return `${packageId}::pvp_coinflip::${action === "join" ? "join_game" : action === "cancel" ? "cancel_game" : "create_game"}`;
92
+ return `${packageId}::${game}::play`;
93
+ };
94
+ const readOnlyPlan = ({ input, game, action, requiredInputs, notes }) => {
95
+ const { config } = createSuigarClient(getConfigInput(input));
96
+ const coinType = resolveDefaultCoinType(config, input.coinType);
97
+ return {
98
+ mode: "read-only",
99
+ network: config.network,
100
+ game,
101
+ action,
102
+ config,
103
+ plan: {
104
+ target: getTarget(config, game, action),
105
+ typeArguments: [coinType],
106
+ requiredInputs,
107
+ notes
108
+ }
109
+ };
110
+ };
111
+ const commonOptions = async (input, bundle) => {
112
+ return {
113
+ owner: await resolveOwnerAddress(requireString(input.owner, "owner"), bundle),
114
+ coinType: resolveDefaultCoinType(bundle.config, input.coinType),
115
+ metadata: input.metadata,
116
+ gasBudget: input.gasBudget,
117
+ useGasCoin: input.useGasCoin
118
+ };
119
+ };
120
+ const stakeOptions = async (input, bundle) => {
121
+ const { decimals } = coinMetadataForAmount(bundle.config, input.coinType);
122
+ return {
123
+ ...await commonOptions(input, bundle),
124
+ stake: toBaseUnits(input.stake, "stake", decimals),
125
+ ...input.cashStake == null ? {} : { cashStake: toBaseUnits(input.cashStake, "cashStake", decimals) },
126
+ ...input.betCount == null ? {} : { betCount: toPositiveInteger(input.betCount, "betCount") }
127
+ };
128
+ };
129
+ const executeTransactionTool = async ({ input, game, action, createTransaction, stake, stakeDisplay, gameInputs }) => {
130
+ const mode = getMode(input.mode);
131
+ if (mode === "read-only") throw new Error("read-only mode must be handled before transaction execution.");
132
+ const bundle = createSuigarClient(getConfigInput(input));
133
+ const coin = coinMetadataForAmount(bundle.config, input.coinType);
134
+ const baseStake = stake ?? (stakeDisplay == null ? void 0 : toBaseUnits(stakeDisplay, "stake", coin.decimals));
135
+ return asTextResponse(await buildTransactionResult({
136
+ mode,
137
+ transaction: await createTransaction(bundle),
138
+ config: bundle.config,
139
+ client: bundle.client,
140
+ context: {
141
+ game,
142
+ action,
143
+ coinType: coin.coinType,
144
+ stake: baseStake,
145
+ stakeDisplay,
146
+ coinDecimals: coin.decimals,
147
+ gameInputs
148
+ }
149
+ }));
150
+ };
151
+ const readConfigTool = async (input = {}) => {
152
+ const { config } = createSuigarClient(getConfigInput(input));
153
+ return asTextResponse({
154
+ network: config.network,
155
+ config,
156
+ supportedGames: supportedGames()
157
+ });
158
+ };
159
+ const readGameMetadataTool = async (input = {}) => {
160
+ const { config } = createSuigarClient(getConfigInput(input));
161
+ const game = input.game ?? null;
162
+ const coinType = resolveDefaultCoinType(config, input.coinType);
163
+ return asTextResponse({
164
+ network: config.network,
165
+ config,
166
+ supportedGames: supportedGames(),
167
+ game: game ? {
168
+ id: game,
169
+ label: GAME_LABELS[game],
170
+ packageId: getPackageId(config, game),
171
+ coinType,
172
+ notes: [game === "pvp-coinflip" ? "PvP coinflip uses dedicated create, join, and cancel transaction builders." : "Standard games use client.suigar.tx.createBetTransaction().", "Transactions are unsigned and are never executed by the MCP server."]
173
+ } : null
174
+ });
175
+ };
176
+ const buildCoinflipTransactionTool = async (input = {}) => {
177
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
178
+ input,
179
+ game: "coinflip",
180
+ requiredInputs: [
181
+ "owner",
182
+ "stake",
183
+ "side"
184
+ ],
185
+ notes: ["Uses the configured SweetHouse object, Pyth price info, clock, and randomness objects."]
186
+ }));
187
+ const side = requireString(input.side, "side");
188
+ return executeTransactionTool({
189
+ input,
190
+ game: "coinflip",
191
+ stakeDisplay: toCurrencyAmountText(input.stake, "stake"),
192
+ gameInputs: { side },
193
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createBetTransaction("coinflip", {
194
+ ...await stakeOptions(input, bundle),
195
+ side
196
+ })
197
+ });
198
+ };
199
+ const buildLimboTransactionTool = async (input = {}) => {
200
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
201
+ input,
202
+ game: "limbo",
203
+ requiredInputs: [
204
+ "owner",
205
+ "stake",
206
+ "targetMultiplier"
207
+ ],
208
+ notes: ["Target multiplier is encoded by @suigar/sdk using the public fixed-point utility defaults."]
209
+ }));
210
+ const targetMultiplier = requireNumber(input.targetMultiplier, "targetMultiplier");
211
+ return executeTransactionTool({
212
+ input,
213
+ game: "limbo",
214
+ stakeDisplay: toCurrencyAmountText(input.stake, "stake"),
215
+ gameInputs: { targetMultiplier },
216
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createBetTransaction("limbo", {
217
+ ...await stakeOptions(input, bundle),
218
+ targetMultiplier
219
+ })
220
+ });
221
+ };
222
+ const buildConfigIdTransactionTool = async (input, game) => {
223
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
224
+ input,
225
+ game,
226
+ requiredInputs: [
227
+ "owner",
228
+ "stake",
229
+ "configId"
230
+ ],
231
+ notes: ["Config id selects the on-chain game configuration."]
232
+ }));
233
+ const configId = requireNumber(input.configId, "configId");
234
+ return executeTransactionTool({
235
+ input,
236
+ game,
237
+ stakeDisplay: toCurrencyAmountText(input.stake, "stake"),
238
+ gameInputs: { configId },
239
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createBetTransaction(game, {
240
+ ...await stakeOptions(input, bundle),
241
+ configId
242
+ })
243
+ });
244
+ };
245
+ const buildPlinkoTransactionTool = (input = {}) => buildConfigIdTransactionTool(input, "plinko");
246
+ const buildWheelTransactionTool = (input = {}) => buildConfigIdTransactionTool(input, "wheel");
247
+ const buildRangeTransactionTool = async (input = {}) => {
248
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
249
+ input,
250
+ game: "range",
251
+ requiredInputs: [
252
+ "owner",
253
+ "stake",
254
+ "leftPoint",
255
+ "rightPoint"
256
+ ],
257
+ notes: ["Range points are normalized by @suigar/sdk before Move call construction."]
258
+ }));
259
+ const leftPoint = requireNumber(input.leftPoint, "leftPoint");
260
+ const rightPoint = requireNumber(input.rightPoint, "rightPoint");
261
+ const outOfRange = Boolean(input.outOfRange);
262
+ return executeTransactionTool({
263
+ input,
264
+ game: "range",
265
+ stakeDisplay: toCurrencyAmountText(input.stake, "stake"),
266
+ gameInputs: {
267
+ leftPoint,
268
+ rightPoint,
269
+ outOfRange
270
+ },
271
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createBetTransaction("range", {
272
+ ...await stakeOptions(input, bundle),
273
+ leftPoint,
274
+ rightPoint,
275
+ outOfRange
276
+ })
277
+ });
278
+ };
279
+ const buildPvpCoinflipCreateTransactionTool = async (input = {}) => {
280
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
281
+ input,
282
+ game: "pvp-coinflip",
283
+ action: "create",
284
+ requiredInputs: [
285
+ "owner",
286
+ "stake",
287
+ "creatorSide"
288
+ ],
289
+ notes: ["Creates an unresolved PvP coinflip lobby without signing or executing the transaction."]
290
+ }));
291
+ const creatorSide = requireString(input.creatorSide, "creatorSide");
292
+ return executeTransactionTool({
293
+ input,
294
+ game: "pvp-coinflip",
295
+ action: "create",
296
+ stakeDisplay: toCurrencyAmountText(input.stake, "stake"),
297
+ gameInputs: {
298
+ creatorSide,
299
+ ...input.isPrivate == null ? {} : { isPrivate: input.isPrivate }
300
+ },
301
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createPvPCoinflipTransaction("create", {
302
+ ...await commonOptions(input, bundle),
303
+ stake: toBaseUnits(input.stake, "stake", coinMetadataForAmount(bundle.config, input.coinType).decimals),
304
+ side: creatorSide,
305
+ isPrivate: input.isPrivate
306
+ })
307
+ });
308
+ };
309
+ const buildPvpCoinflipJoinTransactionTool = async (input = {}) => {
310
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
311
+ input,
312
+ game: "pvp-coinflip",
313
+ action: "join",
314
+ requiredInputs: ["owner", "gameId"],
315
+ notes: ["Join resolves the live game object during transaction build so the SDK can source the matching stake."]
316
+ }));
317
+ const gameId = requireString(input.gameId, "gameId");
318
+ return executeTransactionTool({
319
+ input,
320
+ game: "pvp-coinflip",
321
+ action: "join",
322
+ gameInputs: { gameId },
323
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createPvPCoinflipTransaction("join", {
324
+ ...await commonOptions(input, bundle),
325
+ gameId
326
+ })
327
+ });
328
+ };
329
+ const buildPvpCoinflipCancelTransactionTool = async (input = {}) => {
330
+ if (getMode(input.mode) === "read-only") return asTextResponse(readOnlyPlan({
331
+ input,
332
+ game: "pvp-coinflip",
333
+ action: "cancel",
334
+ requiredInputs: ["owner", "gameId"],
335
+ notes: ["Cancel only prepares the unsigned cancellation transaction for the game creator."]
336
+ }));
337
+ const gameId = requireString(input.gameId, "gameId");
338
+ return executeTransactionTool({
339
+ input,
340
+ game: "pvp-coinflip",
341
+ action: "cancel",
342
+ gameInputs: { gameId },
343
+ createTransaction: async (bundle) => bundle.client.suigar.tx.createPvPCoinflipTransaction("cancel", {
344
+ ...await commonOptions(input, bundle),
345
+ gameId
346
+ })
347
+ });
348
+ };
349
+ //#endregion
350
+ export { buildCoinflipTransactionTool, buildLimboTransactionTool, buildPlinkoTransactionTool, buildPvpCoinflipCancelTransactionTool, buildPvpCoinflipCreateTransactionTool, buildPvpCoinflipJoinTransactionTool, buildRangeTransactionTool, buildWheelTransactionTool, readConfigTool, readGameMetadataTool };
351
+
352
+ //# sourceMappingURL=tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.mjs","names":[],"sources":["../src/tools.ts"],"sourcesContent":["// Copyright (c) Suigar\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Transaction } from '@mysten/sui/transactions';\nimport { GAMES } from '@suigar/sdk/games';\nimport type {\n\tCoinSide,\n\tGame,\n\tPvPCoinflipAction,\n\tStandardGame,\n} from '@suigar/sdk/games';\nimport {\n\tbuildTransactionResult,\n\tcreateSuigarClient,\n\tresolveDefaultCoinType,\n\tresolveOwnerAddress,\n} from './client.js';\nimport type { SuigarClientBundle } from './client.js';\nimport type {\n\tCoinflipInput,\n\tConfigIdInput,\n\tLimboInput,\n\tPvpCoinflipCancelInput,\n\tPvpCoinflipCreateInput,\n\tPvpCoinflipJoinInput,\n\tRangeInput,\n\tReadConfigInput,\n\tReadGameMetadataInput,\n} from './schemas.js';\nimport type {\n\tBuilderMode,\n\tJsonValue,\n\tReadConfigResult,\n\tReadGameMetadataResult,\n\tReadOnlyPlan,\n\tResolvedMcpConfig,\n\tToolTextResult,\n} from './types.js';\n\nconst GAME_LABELS = {\n\tcoinflip: 'Coinflip',\n\tlimbo: 'Limbo',\n\tplinko: 'Plinko',\n\trange: 'Range',\n\twheel: 'Wheel',\n\t'pvp-coinflip': 'PvP Coinflip',\n} as const satisfies Record<Game, string>;\n\nconst GAME_TO_PACKAGE_KEY = {\n\tcoinflip: 'coinflip',\n\tlimbo: 'limbo',\n\tplinko: 'plinko',\n\trange: 'range',\n\twheel: 'wheel',\n\t'pvp-coinflip': 'pvpCoinflip',\n} as const satisfies Record<Game, keyof ResolvedMcpConfig['sdk']['packageIds']>;\n\nconst GAME_TO_TOOLS = {\n\tcoinflip: ['build_coinflip_transaction'],\n\tlimbo: ['build_limbo_transaction'],\n\tplinko: ['build_plinko_transaction'],\n\trange: ['build_range_transaction'],\n\twheel: ['build_wheel_transaction'],\n\t'pvp-coinflip': [\n\t\t'build_pvp_coinflip_create_transaction',\n\t\t'build_pvp_coinflip_join_transaction',\n\t\t'build_pvp_coinflip_cancel_transaction',\n\t],\n} as const satisfies Record<Game, readonly string[]>;\n\nconst json = (value: unknown) =>\n\tJSON.stringify(\n\t\tvalue,\n\t\t(_key, item) => (typeof item === 'bigint' ? item.toString() : item),\n\t\t2,\n\t);\n\nconst asTextResponse = <T extends ToolTextResult['structuredContent']>(\n\tstructuredContent: T,\n): ToolTextResult => ({\n\tcontent: [{ type: 'text', text: json(structuredContent) }],\n\tstructuredContent,\n});\n\nconst currencyAmountPattern = /^(?:\\d+|\\d+\\.\\d+|\\.\\d+)$/u;\n\nconst coinMetadataForAmount = (\n\tconfig: ResolvedMcpConfig,\n\tcoinType?: string,\n) => {\n\tconst resolvedCoinType = resolveDefaultCoinType(config, coinType);\n\tconst coin = Object.values(config.sdk.coins).find(\n\t\t(metadata) =>\n\t\t\tresolveDefaultCoinType(config, metadata.coinType) === resolvedCoinType,\n\t);\n\n\tif (!coin) {\n\t\tthrow new RangeError(\n\t\t\t`Unable to resolve decimals for coin type ${resolvedCoinType}. Add the coin to config.coins before using currency-denominated amounts.`,\n\t\t);\n\t}\n\n\treturn {\n\t\tcoinType: resolvedCoinType,\n\t\tdecimals: coin.decimals,\n\t};\n};\n\nconst toCurrencyAmountText = (value: unknown, fieldName: string): string => {\n\tif (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n\t\treturn String(value);\n\t}\n\tif (typeof value === 'string' && currencyAmountPattern.test(value.trim())) {\n\t\treturn value.trim();\n\t}\n\tthrow new TypeError(\n\t\t`Missing or invalid ${fieldName}. Provide a non-negative currency amount such as 1, 2, or 1.5.`,\n\t);\n};\n\nconst toBaseUnits = (\n\tvalue: unknown,\n\tfieldName: string,\n\tdecimals: number,\n): bigint => {\n\tconst amount = toCurrencyAmountText(value, fieldName);\n\tconst [rawWhole, rawFraction = ''] = amount.split('.');\n\tconst whole = rawWhole === '' ? '0' : rawWhole;\n\tconst overflow = rawFraction.slice(decimals);\n\tif (/[^0]/u.test(overflow)) {\n\t\tthrow new RangeError(\n\t\t\t`${fieldName} has more fractional digits than the configured coin decimals (${decimals}).`,\n\t\t);\n\t}\n\tconst fraction = rawFraction.slice(0, decimals).padEnd(decimals, '0');\n\treturn BigInt(whole) * 10n ** BigInt(decimals) + BigInt(fraction || '0');\n};\n\nconst toPositiveInteger = (\n\tvalue: unknown,\n\tfieldName: string,\n): number | bigint => {\n\tif (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) {\n\t\treturn value;\n\t}\n\tif (typeof value === 'string' && /^[1-9]\\d*$/u.test(value)) {\n\t\treturn BigInt(value);\n\t}\n\tthrow new TypeError(\n\t\t`Missing or invalid ${fieldName}. Provide a positive integer.`,\n\t);\n};\n\nconst requireString = (value: unknown, fieldName: string): string => {\n\tif (typeof value === 'string' && value.trim()) {\n\t\treturn value.trim();\n\t}\n\tthrow new TypeError(`Missing required field: ${fieldName}.`);\n};\n\nconst requireNumber = (value: unknown, fieldName: string): number => {\n\tif (typeof value === 'number' && Number.isFinite(value)) {\n\t\treturn value;\n\t}\n\tthrow new TypeError(`Missing or invalid numeric field: ${fieldName}.`);\n};\n\nconst getMode = (mode: BuilderMode | undefined): BuilderMode => mode ?? 'build';\n\nconst getConfigInput = (input: ReadConfigInput) => ({\n\tnetwork: input.network,\n\tproviderUrl: input.providerUrl,\n\tconfig: input.config,\n\tpartner: input.partner,\n});\n\nconst supportedGames = () =>\n\tGAMES.map((id) => ({\n\t\tid,\n\t\tlabel: GAME_LABELS[id],\n\t\ttools: [...GAME_TO_TOOLS[id]],\n\t}));\n\nconst getPackageId = (config: ResolvedMcpConfig, game: Game) =>\n\tconfig.sdk.packageIds[GAME_TO_PACKAGE_KEY[game]];\n\nconst getTarget = (\n\tconfig: ResolvedMcpConfig,\n\tgame: Game,\n\taction?: PvPCoinflipAction,\n) => {\n\tconst packageId = getPackageId(config, game);\n\tif (game === 'pvp-coinflip') {\n\t\tconst functionName =\n\t\t\taction === 'join'\n\t\t\t\t? 'join_game'\n\t\t\t\t: action === 'cancel'\n\t\t\t\t\t? 'cancel_game'\n\t\t\t\t\t: 'create_game';\n\t\treturn `${packageId}::pvp_coinflip::${functionName}`;\n\t}\n\treturn `${packageId}::${game}::play`;\n};\n\nconst readOnlyPlan = ({\n\tinput,\n\tgame,\n\taction,\n\trequiredInputs,\n\tnotes,\n}: {\n\tinput:\n\t\t| CoinflipInput\n\t\t| LimboInput\n\t\t| ConfigIdInput\n\t\t| RangeInput\n\t\t| PvpCoinflipCreateInput\n\t\t| PvpCoinflipJoinInput\n\t\t| PvpCoinflipCancelInput;\n\tgame: Game;\n\taction?: PvPCoinflipAction;\n\trequiredInputs: string[];\n\tnotes: string[];\n}): ReadOnlyPlan => {\n\tconst { config } = createSuigarClient(getConfigInput(input));\n\tconst coinType = resolveDefaultCoinType(config, input.coinType);\n\treturn {\n\t\tmode: 'read-only',\n\t\tnetwork: config.network,\n\t\tgame,\n\t\taction,\n\t\tconfig,\n\t\tplan: {\n\t\t\ttarget: getTarget(config, game, action),\n\t\t\ttypeArguments: [coinType],\n\t\t\trequiredInputs,\n\t\t\tnotes,\n\t\t},\n\t};\n};\n\nconst commonOptions = async (\n\tinput:\n\t\t| CoinflipInput\n\t\t| LimboInput\n\t\t| ConfigIdInput\n\t\t| RangeInput\n\t\t| PvpCoinflipCreateInput\n\t\t| PvpCoinflipJoinInput\n\t\t| PvpCoinflipCancelInput,\n\tbundle: SuigarClientBundle,\n) => {\n\treturn {\n\t\towner: await resolveOwnerAddress(\n\t\t\trequireString(input.owner, 'owner'),\n\t\t\tbundle,\n\t\t),\n\t\tcoinType: resolveDefaultCoinType(bundle.config, input.coinType),\n\t\tmetadata: input.metadata,\n\t\tgasBudget: input.gasBudget,\n\t\tuseGasCoin: input.useGasCoin,\n\t};\n};\n\nconst stakeOptions = async (\n\tinput: CoinflipInput | LimboInput | ConfigIdInput | RangeInput,\n\tbundle: SuigarClientBundle,\n) => {\n\tconst { decimals } = coinMetadataForAmount(bundle.config, input.coinType);\n\treturn {\n\t\t...(await commonOptions(input, bundle)),\n\t\tstake: toBaseUnits(input.stake, 'stake', decimals),\n\t\t...(input.cashStake == null\n\t\t\t? {}\n\t\t\t: { cashStake: toBaseUnits(input.cashStake, 'cashStake', decimals) }),\n\t\t...(input.betCount == null\n\t\t\t? {}\n\t\t\t: { betCount: toPositiveInteger(input.betCount, 'betCount') }),\n\t};\n};\n\nconst executeTransactionTool = async ({\n\tinput,\n\tgame,\n\taction,\n\tcreateTransaction,\n\tstake,\n\tstakeDisplay,\n\tgameInputs,\n}: {\n\tinput:\n\t\t| CoinflipInput\n\t\t| LimboInput\n\t\t| ConfigIdInput\n\t\t| RangeInput\n\t\t| PvpCoinflipCreateInput\n\t\t| PvpCoinflipJoinInput\n\t\t| PvpCoinflipCancelInput;\n\tgame: Game;\n\taction?: PvPCoinflipAction;\n\tcreateTransaction: (bundle: SuigarClientBundle) => Promise<Transaction>;\n\tstake?: number | bigint;\n\tstakeDisplay?: string;\n\tgameInputs?: Record<string, JsonValue>;\n}) => {\n\tconst mode = getMode(input.mode);\n\tif (mode === 'read-only') {\n\t\tthrow new Error(\n\t\t\t'read-only mode must be handled before transaction execution.',\n\t\t);\n\t}\n\n\tconst bundle = createSuigarClient(getConfigInput(input));\n\tconst coin = coinMetadataForAmount(bundle.config, input.coinType);\n\tconst baseStake =\n\t\tstake ??\n\t\t(stakeDisplay == null\n\t\t\t? undefined\n\t\t\t: toBaseUnits(stakeDisplay, 'stake', coin.decimals));\n\tconst transaction = await createTransaction(bundle);\n\treturn asTextResponse(\n\t\tawait buildTransactionResult({\n\t\t\tmode,\n\t\t\ttransaction,\n\t\t\tconfig: bundle.config,\n\t\t\tclient: bundle.client,\n\t\t\tcontext: {\n\t\t\t\tgame,\n\t\t\t\taction,\n\t\t\t\tcoinType: coin.coinType,\n\t\t\t\tstake: baseStake,\n\t\t\t\tstakeDisplay,\n\t\t\t\tcoinDecimals: coin.decimals,\n\t\t\t\tgameInputs,\n\t\t\t},\n\t\t}),\n\t);\n};\n\nexport const readConfigTool = async (input: ReadConfigInput = {}) => {\n\tconst { config } = createSuigarClient(getConfigInput(input));\n\treturn asTextResponse({\n\t\tnetwork: config.network,\n\t\tconfig,\n\t\tsupportedGames: supportedGames(),\n\t} satisfies ReadConfigResult);\n};\n\nexport const readGameMetadataTool = async (\n\tinput: ReadGameMetadataInput = {},\n) => {\n\tconst { config } = createSuigarClient(getConfigInput(input));\n\tconst game = input.game ?? null;\n\tconst coinType = resolveDefaultCoinType(config, input.coinType);\n\treturn asTextResponse({\n\t\tnetwork: config.network,\n\t\tconfig,\n\t\tsupportedGames: supportedGames(),\n\t\tgame: game\n\t\t\t? {\n\t\t\t\t\tid: game,\n\t\t\t\t\tlabel: GAME_LABELS[game],\n\t\t\t\t\tpackageId: getPackageId(config, game),\n\t\t\t\t\tcoinType,\n\t\t\t\t\tnotes: [\n\t\t\t\t\t\tgame === 'pvp-coinflip'\n\t\t\t\t\t\t\t? 'PvP coinflip uses dedicated create, join, and cancel transaction builders.'\n\t\t\t\t\t\t\t: 'Standard games use client.suigar.tx.createBetTransaction().',\n\t\t\t\t\t\t'Transactions are unsigned and are never executed by the MCP server.',\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t: null,\n\t} satisfies ReadGameMetadataResult);\n};\n\nexport const buildCoinflipTransactionTool = async (\n\tinput: CoinflipInput = {},\n) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'coinflip',\n\t\t\t\trequiredInputs: ['owner', 'stake', 'side'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Uses the configured SweetHouse object, Pyth price info, clock, and randomness objects.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst side = requireString(input.side, 'side') as CoinSide;\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'coinflip',\n\t\tstakeDisplay: toCurrencyAmountText(input.stake, 'stake'),\n\t\tgameInputs: { side },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createBetTransaction('coinflip', {\n\t\t\t\t...(await stakeOptions(input, bundle)),\n\t\t\t\tside,\n\t\t\t}),\n\t});\n};\n\nexport const buildLimboTransactionTool = async (input: LimboInput = {}) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'limbo',\n\t\t\t\trequiredInputs: ['owner', 'stake', 'targetMultiplier'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Target multiplier is encoded by @suigar/sdk using the public fixed-point utility defaults.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst targetMultiplier = requireNumber(\n\t\tinput.targetMultiplier,\n\t\t'targetMultiplier',\n\t);\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'limbo',\n\t\tstakeDisplay: toCurrencyAmountText(input.stake, 'stake'),\n\t\tgameInputs: { targetMultiplier },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createBetTransaction('limbo', {\n\t\t\t\t...(await stakeOptions(input, bundle)),\n\t\t\t\ttargetMultiplier,\n\t\t\t}),\n\t});\n};\n\nconst buildConfigIdTransactionTool = async (\n\tinput: ConfigIdInput,\n\tgame: Extract<StandardGame, 'plinko' | 'wheel'>,\n) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame,\n\t\t\t\trequiredInputs: ['owner', 'stake', 'configId'],\n\t\t\t\tnotes: ['Config id selects the on-chain game configuration.'],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst configId = requireNumber(input.configId, 'configId');\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame,\n\t\tstakeDisplay: toCurrencyAmountText(input.stake, 'stake'),\n\t\tgameInputs: { configId },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createBetTransaction(game, {\n\t\t\t\t...(await stakeOptions(input, bundle)),\n\t\t\t\tconfigId,\n\t\t\t}),\n\t});\n};\n\nexport const buildPlinkoTransactionTool = (input: ConfigIdInput = {}) =>\n\tbuildConfigIdTransactionTool(input, 'plinko');\n\nexport const buildWheelTransactionTool = (input: ConfigIdInput = {}) =>\n\tbuildConfigIdTransactionTool(input, 'wheel');\n\nexport const buildRangeTransactionTool = async (input: RangeInput = {}) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'range',\n\t\t\t\trequiredInputs: ['owner', 'stake', 'leftPoint', 'rightPoint'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Range points are normalized by @suigar/sdk before Move call construction.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst leftPoint = requireNumber(input.leftPoint, 'leftPoint');\n\tconst rightPoint = requireNumber(input.rightPoint, 'rightPoint');\n\tconst outOfRange = Boolean(input.outOfRange);\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'range',\n\t\tstakeDisplay: toCurrencyAmountText(input.stake, 'stake'),\n\t\tgameInputs: { leftPoint, rightPoint, outOfRange },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createBetTransaction('range', {\n\t\t\t\t...(await stakeOptions(input, bundle)),\n\t\t\t\tleftPoint,\n\t\t\t\trightPoint,\n\t\t\t\toutOfRange,\n\t\t\t}),\n\t});\n};\n\nexport const buildPvpCoinflipCreateTransactionTool = async (\n\tinput: PvpCoinflipCreateInput = {},\n) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'pvp-coinflip',\n\t\t\t\taction: 'create',\n\t\t\t\trequiredInputs: ['owner', 'stake', 'creatorSide'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Creates an unresolved PvP coinflip lobby without signing or executing the transaction.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst creatorSide = requireString(\n\t\tinput.creatorSide,\n\t\t'creatorSide',\n\t) as CoinSide;\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'pvp-coinflip',\n\t\taction: 'create',\n\t\tstakeDisplay: toCurrencyAmountText(input.stake, 'stake'),\n\t\tgameInputs: {\n\t\t\tcreatorSide,\n\t\t\t...(input.isPrivate == null ? {} : { isPrivate: input.isPrivate }),\n\t\t},\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createPvPCoinflipTransaction('create', {\n\t\t\t\t...(await commonOptions(input, bundle)),\n\t\t\t\tstake: toBaseUnits(\n\t\t\t\t\tinput.stake,\n\t\t\t\t\t'stake',\n\t\t\t\t\tcoinMetadataForAmount(bundle.config, input.coinType).decimals,\n\t\t\t\t),\n\t\t\t\tside: creatorSide,\n\t\t\t\tisPrivate: input.isPrivate,\n\t\t\t}),\n\t});\n};\n\nexport const buildPvpCoinflipJoinTransactionTool = async (\n\tinput: PvpCoinflipJoinInput = {},\n) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'pvp-coinflip',\n\t\t\t\taction: 'join',\n\t\t\t\trequiredInputs: ['owner', 'gameId'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Join resolves the live game object during transaction build so the SDK can source the matching stake.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst gameId = requireString(input.gameId, 'gameId');\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'pvp-coinflip',\n\t\taction: 'join',\n\t\tgameInputs: { gameId },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createPvPCoinflipTransaction('join', {\n\t\t\t\t...(await commonOptions(input, bundle)),\n\t\t\t\tgameId,\n\t\t\t}),\n\t});\n};\n\nexport const buildPvpCoinflipCancelTransactionTool = async (\n\tinput: PvpCoinflipCancelInput = {},\n) => {\n\tif (getMode(input.mode) === 'read-only') {\n\t\treturn asTextResponse(\n\t\t\treadOnlyPlan({\n\t\t\t\tinput,\n\t\t\t\tgame: 'pvp-coinflip',\n\t\t\t\taction: 'cancel',\n\t\t\t\trequiredInputs: ['owner', 'gameId'],\n\t\t\t\tnotes: [\n\t\t\t\t\t'Cancel only prepares the unsigned cancellation transaction for the game creator.',\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst gameId = requireString(input.gameId, 'gameId');\n\treturn executeTransactionTool({\n\t\tinput,\n\t\tgame: 'pvp-coinflip',\n\t\taction: 'cancel',\n\t\tgameInputs: { gameId },\n\t\tcreateTransaction: async (bundle) =>\n\t\t\tbundle.client.suigar.tx.createPvPCoinflipTransaction('cancel', {\n\t\t\t\t...(await commonOptions(input, bundle)),\n\t\t\t\tgameId,\n\t\t\t}),\n\t});\n};\n"],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { SuigarClient, SuigarNetwork, suigar } from "@suigar/sdk";
2
+ import { Game, PvPCoinflipAction } from "@suigar/sdk/games";
3
+ import { SuiClientTypes } from "@mysten/sui/client";
4
+
5
+ //#region src/types.d.ts
6
+ type SuigarConfig = ReturnType<SuigarClient['getConfig']>;
7
+ type SuigarConfigOverrides = NonNullable<Parameters<typeof suigar>[0]>['config'];
8
+ type RawDryRunResult = SuiClientTypes.SimulateTransactionResult<{
9
+ effects: true;
10
+ events: true;
11
+ balanceChanges: true;
12
+ }>;
13
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
14
+ [key: string]: JsonValue;
15
+ };
16
+ type DryRunResult = {
17
+ [key: string]: JsonValue;
18
+ };
19
+ type BuilderMode = 'build' | 'dry-run' | 'read-only';
20
+ type SuigarMcpConfigInput = {
21
+ network?: SuigarNetwork;
22
+ providerUrl?: string;
23
+ config?: SuigarConfigOverrides;
24
+ partner?: string;
25
+ };
26
+ type ResolvedMcpConfig = {
27
+ network: SuigarNetwork;
28
+ providerUrl: string;
29
+ sdk: SuigarConfig;
30
+ };
31
+ type TransactionCommandSummary = {
32
+ kind: string;
33
+ target?: string;
34
+ typeArguments?: string[];
35
+ };
36
+ type TransactionSummary = {
37
+ sender: string | null;
38
+ gasBudget: string | null;
39
+ gasBudgetDisplay: string | null;
40
+ gasPrice: string | null;
41
+ commandCount: number;
42
+ commands: TransactionCommandSummary[];
43
+ inputs: number;
44
+ objectInputs: string[];
45
+ game?: Game;
46
+ action?: PvPCoinflipAction;
47
+ coinType?: string;
48
+ stake?: string;
49
+ stakeDisplay?: string;
50
+ coinDecimals?: number;
51
+ gameInputs?: {
52
+ [key: string]: JsonValue;
53
+ };
54
+ };
55
+ type FormattedAmount = {
56
+ raw: string;
57
+ display: string;
58
+ };
59
+ type DryRunEventSummary = {
60
+ type: string;
61
+ game?: Game;
62
+ eventName?: string;
63
+ fields: {
64
+ [key: string]: JsonValue;
65
+ };
66
+ };
67
+ type DryRunSummary = {
68
+ success: boolean;
69
+ error: string | null;
70
+ gasUsed: {
71
+ computation: FormattedAmount | null;
72
+ storage: FormattedAmount | null;
73
+ rebate: FormattedAmount | null;
74
+ nonRefundableStorageFee: FormattedAmount | null;
75
+ net: FormattedAmount | null;
76
+ };
77
+ balanceChanges: Array<{
78
+ address: string;
79
+ coinType: string;
80
+ amount: FormattedAmount;
81
+ }>;
82
+ events: DryRunEventSummary[];
83
+ };
84
+ type ReadOnlyPlan = {
85
+ mode: 'read-only';
86
+ network: SuigarNetwork;
87
+ game: Game;
88
+ action?: PvPCoinflipAction;
89
+ config: ResolvedMcpConfig;
90
+ plan: {
91
+ target: string | null;
92
+ typeArguments: string[];
93
+ requiredInputs: string[];
94
+ notes: string[];
95
+ };
96
+ };
97
+ type BuildTransactionResult = {
98
+ mode: 'build' | 'dry-run';
99
+ network: SuigarNetwork;
100
+ config: ResolvedMcpConfig;
101
+ summary: TransactionSummary;
102
+ transactionBytesBase64?: string;
103
+ dryRun?: DryRunResult;
104
+ dryRunSummary?: DryRunSummary;
105
+ errors?: string[];
106
+ };
107
+ type ReadConfigResult = {
108
+ network: SuigarNetwork;
109
+ config: ResolvedMcpConfig;
110
+ supportedGames: Array<{
111
+ id: Game;
112
+ label: string;
113
+ tools: string[];
114
+ }>;
115
+ };
116
+ type ReadGameMetadataResult = ReadConfigResult & {
117
+ game: {
118
+ id: Game;
119
+ label: string;
120
+ packageId: string;
121
+ coinType: string;
122
+ action?: PvPCoinflipAction;
123
+ notes: string[];
124
+ } | null;
125
+ };
126
+ type ToolStructuredResult = ReadConfigResult | ReadGameMetadataResult | ReadOnlyPlan | BuildTransactionResult;
127
+ type ToolTextResult = {
128
+ content: [{
129
+ type: 'text';
130
+ text: string;
131
+ }];
132
+ structuredContent: ToolStructuredResult;
133
+ _meta?: Record<string, unknown>;
134
+ };
135
+ //#endregion
136
+ export { BuildTransactionResult, BuilderMode, DryRunEventSummary, DryRunResult, DryRunSummary, FormattedAmount, JsonValue, RawDryRunResult, ReadConfigResult, ReadGameMetadataResult, ReadOnlyPlan, ResolvedMcpConfig, SuigarConfig, SuigarConfigOverrides, SuigarMcpConfigInput, ToolStructuredResult, ToolTextResult, TransactionCommandSummary, TransactionSummary };
137
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;KAOY,YAAA,GAAe,UAAU,CAAC,YAAA;AAAA,KAC1B,qBAAA,GAAwB,WAAA,CACnC,UAAA,QAAkB,MAAA;AAAA,KAEP,eAAA,GAAkB,cAAA,CAAe,yBAAyB;EACrE,OAAA;EACA,MAAA;EACA,cAAA;AAAA;AAAA,KAGW,SAAA,sCACwB,SAAA;EAAA,CAAiB,GAAA,WAAc,SAAS;AAAA;AAAA,KAEhE,YAAA;EAAA,CAAkB,GAAA,WAAc,SAAS;AAAA;AAAA,KAEzC,WAAA;AAAA,KAEA,oBAAA;EACX,OAAA,GAAU,aAAA;EACV,WAAA;EACA,MAAA,GAAS,qBAAqB;EAC9B,OAAA;AAAA;AAAA,KAGW,iBAAA;EACX,OAAA,EAAS,aAAA;EACT,WAAA;EACA,GAAA,EAAK,YAAY;AAAA;AAAA,KAGN,yBAAA;EACX,IAAA;EACA,MAAA;EACA,aAAA;AAAA;AAAA,KAGW,kBAAA;EACX,MAAA;EACA,SAAA;EACA,gBAAA;EACA,QAAA;EACA,YAAA;EACA,QAAA,EAAU,yBAAA;EACV,MAAA;EACA,YAAA;EACA,IAAA,GAAO,IAAA;EACP,MAAA,GAAS,iBAAA;EACT,QAAA;EACA,KAAA;EACA,YAAA;EACA,YAAA;EACA,UAAA;IAAA,CAAgB,GAAA,WAAc,SAAA;EAAA;AAAA;AAAA,KAGnB,eAAA;EACX,GAAA;EACA,OAAO;AAAA;AAAA,KAGI,kBAAA;EACX,IAAA;EACA,IAAA,GAAO,IAAA;EACP,SAAA;EACA,MAAA;IAAA,CAAW,GAAA,WAAc,SAAS;EAAA;AAAA;AAAA,KAGvB,aAAA;EACX,OAAA;EACA,KAAA;EACA,OAAA;IACC,WAAA,EAAa,eAAA;IACb,OAAA,EAAS,eAAA;IACT,MAAA,EAAQ,eAAA;IACR,uBAAA,EAAyB,eAAA;IACzB,GAAA,EAAK,eAAA;EAAA;EAEN,cAAA,EAAgB,KAAA;IACf,OAAA;IACA,QAAA;IACA,MAAA,EAAQ,eAAA;EAAA;EAET,MAAA,EAAQ,kBAAA;AAAA;AAAA,KAGG,YAAA;EACX,IAAA;EACA,OAAA,EAAS,aAAA;EACT,IAAA,EAAM,IAAA;EACN,MAAA,GAAS,iBAAA;EACT,MAAA,EAAQ,iBAAA;EACR,IAAA;IACC,MAAA;IACA,aAAA;IACA,cAAA;IACA,KAAA;EAAA;AAAA;AAAA,KAIU,sBAAA;EACX,IAAA;EACA,OAAA,EAAS,aAAA;EACT,MAAA,EAAQ,iBAAA;EACR,OAAA,EAAS,kBAAA;EACT,sBAAA;EACA,MAAA,GAAS,YAAA;EACT,aAAA,GAAgB,aAAA;EAChB,MAAA;AAAA;AAAA,KAGW,gBAAA;EACX,OAAA,EAAS,aAAA;EACT,MAAA,EAAQ,iBAAA;EACR,cAAA,EAAgB,KAAA;IACf,EAAA,EAAI,IAAA;IACJ,KAAA;IACA,KAAA;EAAA;AAAA;AAAA,KAIU,sBAAA,GAAyB,gBAAA;EACpC,IAAA;IACC,EAAA,EAAI,IAAA;IACJ,KAAA;IACA,SAAA;IACA,QAAA;IACA,MAAA,GAAS,iBAAA;IACT,KAAA;EAAA;AAAA;AAAA,KAIU,oBAAA,GACT,gBAAA,GACA,sBAAA,GACA,YAAA,GACA,sBAAA;AAAA,KAES,cAAA;EACX,OAAA;IAAY,IAAA;IAAc,IAAA;EAAA;EAC1B,iBAAA,EAAmB,oBAAA;EACnB,KAAA,GAAQ,MAAM;AAAA"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@suigar/mcp",
3
- "version": "0.1.1",
4
- "description": "Lightweight MCP tools and transaction builders for Suigar on Sui",
3
+ "version": "0.2.0-beta.0",
4
+ "description": "MCP server and MCP App for building and inspecting Suigar transactions on Sui.",
5
5
  "keywords": [
6
6
  "suigar",
7
7
  "sui",
8
8
  "mcp",
9
+ "mcp-app",
9
10
  "typescript",
10
11
  "move",
11
12
  "web3",
@@ -19,58 +20,65 @@
19
20
  "CHANGELOG.md",
20
21
  "README.md"
21
22
  ],
22
- "main": "./dist/index.cjs",
23
- "module": "./dist/index.mjs",
23
+ "main": "./dist/index.mjs",
24
24
  "types": "./dist/index.d.mts",
25
25
  "exports": {
26
26
  ".": {
27
27
  "types": "./dist/index.d.mts",
28
- "require": "./dist/index.cjs",
29
- "import": "./dist/index.mjs"
28
+ "import": "./dist/index.mjs",
29
+ "default": "./dist/index.mjs"
30
30
  },
31
31
  "./server": {
32
32
  "types": "./dist/server.d.mts",
33
- "require": "./dist/server.cjs",
34
- "import": "./dist/server.mjs"
33
+ "import": "./dist/server.mjs",
34
+ "default": "./dist/server.mjs"
35
35
  }
36
36
  },
37
37
  "bin": {
38
- "suigar": "dist/bin.cjs"
38
+ "suigar": "./dist/bin.mjs"
39
39
  },
40
40
  "engines": {
41
41
  "node": "^22.18.0 || >=24.0.0"
42
42
  },
43
43
  "repository": {
44
44
  "type": "git",
45
- "url": "git+https://github.com/Suigar-Gaming/suigar-skill.git"
45
+ "url": "git+https://github.com/Suigar-Gaming/sdk.git",
46
+ "directory": "packages/mcp"
46
47
  },
47
48
  "publishConfig": {
48
49
  "access": "public"
49
50
  },
50
51
  "bugs": {
51
- "url": "https://github.com/Suigar-Gaming/suigar-skill/issues"
52
+ "url": "https://github.com/Suigar-Gaming/sdk/issues"
52
53
  },
53
- "homepage": "https://docs.suigar.com",
54
+ "homepage": "https://docs.suigar.com/mcp",
54
55
  "dependencies": {
56
+ "@modelcontextprotocol/ext-apps": "1.7.4",
55
57
  "@modelcontextprotocol/sdk": "1.29.0",
56
- "@mysten/bcs": "2.1.0",
57
- "@mysten/sui": "2.20.1",
58
- "@suigar/sdk": "2.0.0-beta.21"
58
+ "@mysten/sui": "^2.20.1",
59
+ "zod": "4.4.3",
60
+ "@suigar/sdk": "^2.0.0-beta.22"
59
61
  },
60
62
  "devDependencies": {
61
- "@types/node": "24.13.2",
62
- "tsdown": "0.22.3",
63
- "typescript": "5.9.3",
64
- "vitest": "4.1.9",
65
- "@suigar/game-registry": "^0.0.0",
66
- "@suigar/currency-registry": "^0.0.0",
67
- "@suigar/sui-rpc-pool": "^0.0.0"
63
+ "@types/node": "^24.0.0",
64
+ "tsdown": "^0.22.3",
65
+ "typescript": "^6.0.3",
66
+ "vite": "^8.1.0",
67
+ "vite-plugin-singlefile": "^2.3.3",
68
+ "vitest": "^4.1.9"
68
69
  },
69
70
  "scripts": {
70
- "prebuild": "pnpm --filter @suigar/currency-registry build && pnpm --filter @suigar/game-registry build && pnpm --filter @suigar/sui-rpc-pool build",
71
- "build": "pnpm clean && tsdown",
71
+ "start:local": "pnpm --filter @suigar/sdk build:ci && pnpm build && node dist/bin.mjs",
72
+ "build": "pnpm clean && tsdown && vite build",
73
+ "build:ci": "pnpm build",
72
74
  "clean": "rm -rf dist",
75
+ "test": "vitest run",
73
76
  "typecheck": "tsc --noEmit",
74
- "test": "vitest run"
77
+ "eslint": "eslint .",
78
+ "eslint:fix": "eslint . --fix",
79
+ "prettier": "prettier --check --ignore-unknown .",
80
+ "prettier:fix": "prettier --write --ignore-unknown .",
81
+ "lint": "pnpm eslint && pnpm prettier",
82
+ "lint:fix": "pnpm eslint:fix && pnpm prettier:fix"
75
83
  }
76
84
  }
package/dist/bin.cjs DELETED
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- const require_server = require("./server-Cmo8UaHE.cjs");
3
- //#region src/bin.ts
4
- const main = async () => {
5
- await require_server.startSuigarMcpServer();
6
- };
7
- main().catch((error) => {
8
- console.error(error);
9
- process.exitCode = 1;
10
- });
11
- //#endregion
package/dist/bin.d.cts DELETED
@@ -1 +0,0 @@
1
- export { };