@solworks/poll-mcp 0.1.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/README.md +121 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +97 -0
- package/dist/prompts/index.d.ts +19 -0
- package/dist/prompts/index.js +100 -0
- package/dist/resources/index.d.ts +9 -0
- package/dist/resources/index.js +83 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.js +686 -0
- package/package.json +28 -0
- package/tests/integration/server.test.ts +159 -0
- package/tests/security/excluded-endpoints.test.ts +25 -0
- package/tests/security/scope-enforcement.test.ts +66 -0
- package/tests/security/token-handling.test.ts +30 -0
- package/tests/unit/resources.test.ts +78 -0
- package/tests/unit/tools/bets.test.ts +116 -0
- package/tests/unit/tools/leaderboard.test.ts +45 -0
- package/tests/unit/tools/social.test.ts +47 -0
- package/tests/unit/tools/user.test.ts +49 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
function errorResult(message) {
|
|
2
|
+
return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
|
|
3
|
+
}
|
|
4
|
+
function textResult(text) {
|
|
5
|
+
return { content: [{ type: 'text', text }] };
|
|
6
|
+
}
|
|
7
|
+
export const TOOL_DEFINITIONS = {
|
|
8
|
+
// ── Read tools (scope: read) ───────────────────────────────────────
|
|
9
|
+
get_account: {
|
|
10
|
+
name: 'get_account',
|
|
11
|
+
description: 'Get the current user account information',
|
|
12
|
+
inputSchema: { type: 'object', properties: {} },
|
|
13
|
+
requiredScope: 'read',
|
|
14
|
+
async handler(_args, client) {
|
|
15
|
+
try {
|
|
16
|
+
const account = await client.getAccount();
|
|
17
|
+
const lines = [
|
|
18
|
+
`Account Information:`,
|
|
19
|
+
` Display Name: ${account.displayName ?? 'Not set'}`,
|
|
20
|
+
` UUID: ${account.uuid}`,
|
|
21
|
+
` ID: ${account.id}`,
|
|
22
|
+
` Email: ${account.email ?? 'Not set'}`,
|
|
23
|
+
];
|
|
24
|
+
return textResult(lines.join('\n'));
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return errorResult(err.message);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
get_profile: {
|
|
32
|
+
name: 'get_profile',
|
|
33
|
+
description: 'Get a user profile by UUID, or your own profile if no UUID is provided',
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
uuid: { type: 'string', description: 'User UUID to look up (optional)' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
requiredScope: 'read',
|
|
41
|
+
async handler(args, client) {
|
|
42
|
+
try {
|
|
43
|
+
const uuid = args.uuid;
|
|
44
|
+
const profile = await client.getProfile(uuid);
|
|
45
|
+
const lines = [
|
|
46
|
+
`User Profile:`,
|
|
47
|
+
` Display Name: ${profile.displayName ?? 'Not set'}`,
|
|
48
|
+
` UUID: ${profile.uuid}`,
|
|
49
|
+
];
|
|
50
|
+
return textResult(lines.join('\n'));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return errorResult(err.message);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
get_balance: {
|
|
58
|
+
name: 'get_balance',
|
|
59
|
+
description: 'Get both XP and USDC balances for the current user',
|
|
60
|
+
inputSchema: { type: 'object', properties: {} },
|
|
61
|
+
requiredScope: 'read',
|
|
62
|
+
async handler(_args, client) {
|
|
63
|
+
try {
|
|
64
|
+
const [xp, usdc] = await Promise.all([
|
|
65
|
+
client.getXpBalance(),
|
|
66
|
+
client.getUsdcBalance(),
|
|
67
|
+
]);
|
|
68
|
+
const lines = [
|
|
69
|
+
`Balances:`,
|
|
70
|
+
` XP: ${xp.xp}`,
|
|
71
|
+
` USDC: ${usdc.usdc ?? 'N/A'}`,
|
|
72
|
+
];
|
|
73
|
+
return textResult(lines.join('\n'));
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
return errorResult(err.message);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
list_public_bets: {
|
|
81
|
+
name: 'list_public_bets',
|
|
82
|
+
description: 'List public bets with optional pagination',
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
page: { type: 'number', description: 'Page number (optional)' },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
requiredScope: 'read',
|
|
90
|
+
async handler(args, client) {
|
|
91
|
+
try {
|
|
92
|
+
const page = args.page;
|
|
93
|
+
const bets = await client.listPublicBets({ page });
|
|
94
|
+
const display = bets.slice(0, 20);
|
|
95
|
+
const lines = display.map((b, i) => `${i + 1}. ${b.question} — Status: ${b.status}, Volume: ${b.totalVolume ?? 'N/A'}`);
|
|
96
|
+
let text = `Public Bets:\n${lines.join('\n')}`;
|
|
97
|
+
if (bets.length > 20)
|
|
98
|
+
text += `\n\n(Showing 20 of ${bets.length})`;
|
|
99
|
+
if (bets.length === 0)
|
|
100
|
+
text = 'No public bets found.';
|
|
101
|
+
return textResult(text);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
return errorResult(err.message);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
get_trending_bets: {
|
|
109
|
+
name: 'get_trending_bets',
|
|
110
|
+
description: 'Get currently trending bets on the platform',
|
|
111
|
+
inputSchema: { type: 'object', properties: {} },
|
|
112
|
+
requiredScope: 'read',
|
|
113
|
+
async handler(_args, client) {
|
|
114
|
+
try {
|
|
115
|
+
const bets = await client.getTrendingBets();
|
|
116
|
+
const display = bets.slice(0, 20);
|
|
117
|
+
const lines = display.map((b, i) => `${i + 1}. ${b.question} — Volume: ${b.totalVolume ?? 'N/A'}`);
|
|
118
|
+
let text = `Trending Bets:\n${lines.join('\n')}`;
|
|
119
|
+
if (bets.length > 20)
|
|
120
|
+
text += `\n\n(Showing 20 of ${bets.length})`;
|
|
121
|
+
if (bets.length === 0)
|
|
122
|
+
text = 'No trending bets found.';
|
|
123
|
+
return textResult(text);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return errorResult(err.message);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
get_bet: {
|
|
131
|
+
name: 'get_bet',
|
|
132
|
+
description: 'Get details of a specific bet by its ID or address',
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
id: { type: 'string', description: 'Bet ID or address' },
|
|
137
|
+
},
|
|
138
|
+
required: ['id'],
|
|
139
|
+
},
|
|
140
|
+
requiredScope: 'read',
|
|
141
|
+
async handler(args, client) {
|
|
142
|
+
const id = args.id;
|
|
143
|
+
if (!id)
|
|
144
|
+
return errorResult('Missing required parameter: id');
|
|
145
|
+
try {
|
|
146
|
+
const bet = await client.getBet(id);
|
|
147
|
+
const lines = [
|
|
148
|
+
`Bet Details:`,
|
|
149
|
+
` Question: ${bet.question}`,
|
|
150
|
+
` Status: ${bet.status}`,
|
|
151
|
+
` Options: ${bet.options.join(', ')}`,
|
|
152
|
+
` Volume: ${bet.totalVolume ?? 'N/A'}`,
|
|
153
|
+
` Created: ${bet.createdAt}`,
|
|
154
|
+
];
|
|
155
|
+
if (bet.betAddress)
|
|
156
|
+
lines.push(` Address: ${bet.betAddress}`);
|
|
157
|
+
return textResult(lines.join('\n'));
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
return errorResult(err.message);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
get_my_wagers: {
|
|
165
|
+
name: 'get_my_wagers',
|
|
166
|
+
description: 'Get your wagers, optionally filtered to only active ones',
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: 'object',
|
|
169
|
+
properties: {
|
|
170
|
+
active: { type: 'boolean', description: 'Filter to active wagers only (optional)' },
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
requiredScope: 'read',
|
|
174
|
+
async handler(args, client) {
|
|
175
|
+
try {
|
|
176
|
+
const active = args.active;
|
|
177
|
+
const wagers = await client.getMyWagers({ active });
|
|
178
|
+
if (wagers.length === 0)
|
|
179
|
+
return textResult('No wagers found.');
|
|
180
|
+
const lines = wagers.map((w, i) => `${i + 1}. Bet: ${w.betAddress} — Option: ${w.optionIndex}, Amount: ${w.amount}, Status: ${w.status}`);
|
|
181
|
+
return textResult(`Your Wagers:\n${lines.join('\n')}`);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
return errorResult(err.message);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
get_leaderboard: {
|
|
189
|
+
name: 'get_leaderboard',
|
|
190
|
+
description: 'Get the platform leaderboard rankings',
|
|
191
|
+
inputSchema: { type: 'object', properties: {} },
|
|
192
|
+
requiredScope: 'read',
|
|
193
|
+
async handler(_args, client) {
|
|
194
|
+
try {
|
|
195
|
+
const entries = await client.getLeaderboard();
|
|
196
|
+
const display = entries.slice(0, 25);
|
|
197
|
+
const lines = display.map((e) => `#${e.rank} ${e.displayName ?? 'Anonymous'} — ${e.points} pts`);
|
|
198
|
+
let text = `Leaderboard:\n${lines.join('\n')}`;
|
|
199
|
+
if (entries.length > 25)
|
|
200
|
+
text += `\n\n(Showing 25 of ${entries.length})`;
|
|
201
|
+
return textResult(text);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
return errorResult(err.message);
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
get_my_ranking: {
|
|
209
|
+
name: 'get_my_ranking',
|
|
210
|
+
description: 'Get your current leaderboard ranking',
|
|
211
|
+
inputSchema: { type: 'object', properties: {} },
|
|
212
|
+
requiredScope: 'read',
|
|
213
|
+
async handler(_args, client) {
|
|
214
|
+
try {
|
|
215
|
+
const ranking = await client.getMyRanking();
|
|
216
|
+
const lines = [
|
|
217
|
+
`Your Ranking:`,
|
|
218
|
+
` Rank: #${ranking.rank}`,
|
|
219
|
+
` Points: ${ranking.points}`,
|
|
220
|
+
` Display Name: ${ranking.displayName ?? 'Not set'}`,
|
|
221
|
+
];
|
|
222
|
+
return textResult(lines.join('\n'));
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
return errorResult(err.message);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
get_wallet_transactions: {
|
|
230
|
+
name: 'get_wallet_transactions',
|
|
231
|
+
description: 'Get your wallet transaction history',
|
|
232
|
+
inputSchema: { type: 'object', properties: {} },
|
|
233
|
+
requiredScope: 'read',
|
|
234
|
+
async handler(_args, client) {
|
|
235
|
+
try {
|
|
236
|
+
const txns = await client.getWalletTransactions();
|
|
237
|
+
if (txns.length === 0)
|
|
238
|
+
return textResult('No transactions found.');
|
|
239
|
+
const lines = txns.map((t, i) => `${i + 1}. ${t.type} — Amount: ${t.amount}, Date: ${t.createdAt}`);
|
|
240
|
+
return textResult(`Wallet Transactions:\n${lines.join('\n')}`);
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
return errorResult(err.message);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
get_friends: {
|
|
248
|
+
name: 'get_friends',
|
|
249
|
+
description: 'Get your friends list',
|
|
250
|
+
inputSchema: { type: 'object', properties: {} },
|
|
251
|
+
requiredScope: 'read',
|
|
252
|
+
async handler(_args, client) {
|
|
253
|
+
try {
|
|
254
|
+
const friends = await client.getFriends();
|
|
255
|
+
if (friends.length === 0)
|
|
256
|
+
return textResult('No friends found.');
|
|
257
|
+
const lines = friends.map((f, i) => `${i + 1}. ${f.displayName ?? 'Anonymous'} (${f.uuid})`);
|
|
258
|
+
return textResult(`Friends:\n${lines.join('\n')}`);
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
return errorResult(err.message);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
get_notifications: {
|
|
266
|
+
name: 'get_notifications',
|
|
267
|
+
description: 'Get your notifications with optional pagination',
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
properties: {
|
|
271
|
+
page: { type: 'number', description: 'Page number (optional)' },
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
requiredScope: 'read',
|
|
275
|
+
async handler(args, client) {
|
|
276
|
+
try {
|
|
277
|
+
const page = args.page;
|
|
278
|
+
const notifications = await client.getNotifications({ page });
|
|
279
|
+
const display = notifications.slice(0, 20);
|
|
280
|
+
if (display.length === 0)
|
|
281
|
+
return textResult('No notifications found.');
|
|
282
|
+
const lines = display.map((n, i) => `${i + 1}. [${n.read ? 'Read' : 'Unread'}] ${n.title}${n.body ? ` — ${n.body}` : ''} (${n.createdAt})`);
|
|
283
|
+
let text = `Notifications:\n${lines.join('\n')}`;
|
|
284
|
+
if (notifications.length > 20)
|
|
285
|
+
text += `\n\n(Showing 20 of ${notifications.length})`;
|
|
286
|
+
return textResult(text);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
return errorResult(err.message);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
get_streak: {
|
|
294
|
+
name: 'get_streak',
|
|
295
|
+
description: 'Get your current and longest betting streak',
|
|
296
|
+
inputSchema: { type: 'object', properties: {} },
|
|
297
|
+
requiredScope: 'read',
|
|
298
|
+
async handler(_args, client) {
|
|
299
|
+
try {
|
|
300
|
+
const streak = await client.getStreak();
|
|
301
|
+
const lines = [
|
|
302
|
+
`Streak Info:`,
|
|
303
|
+
` Current Streak: ${streak.currentStreak}`,
|
|
304
|
+
` Longest Streak: ${streak.longestStreak}`,
|
|
305
|
+
];
|
|
306
|
+
return textResult(lines.join('\n'));
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
return errorResult(err.message);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
get_favourite_bets: {
|
|
314
|
+
name: 'get_favourite_bets',
|
|
315
|
+
description: 'Get your list of favourite bets',
|
|
316
|
+
inputSchema: { type: 'object', properties: {} },
|
|
317
|
+
requiredScope: 'read',
|
|
318
|
+
async handler(_args, client) {
|
|
319
|
+
try {
|
|
320
|
+
const bets = await client.getFavouriteBets();
|
|
321
|
+
if (bets.length === 0)
|
|
322
|
+
return textResult('No favourite bets found.');
|
|
323
|
+
const lines = bets.map((b, i) => `${i + 1}. ${b.question} — Status: ${b.status}, Volume: ${b.totalVolume ?? 'N/A'}`);
|
|
324
|
+
return textResult(`Favourite Bets:\n${lines.join('\n')}`);
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
return errorResult(err.message);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
get_probability_history: {
|
|
332
|
+
name: 'get_probability_history',
|
|
333
|
+
description: 'Get the probability history for a specific bet',
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {
|
|
337
|
+
betAddress: { type: 'string', description: 'Bet address' },
|
|
338
|
+
},
|
|
339
|
+
required: ['betAddress'],
|
|
340
|
+
},
|
|
341
|
+
requiredScope: 'read',
|
|
342
|
+
async handler(args, client) {
|
|
343
|
+
const betAddress = args.betAddress;
|
|
344
|
+
if (!betAddress)
|
|
345
|
+
return errorResult('Missing required parameter: betAddress');
|
|
346
|
+
try {
|
|
347
|
+
const history = await client.getProbabilityHistory(betAddress);
|
|
348
|
+
if (history.length === 0)
|
|
349
|
+
return textResult('No probability history available.');
|
|
350
|
+
const lines = history.map((p) => ` ${p.timestamp}: ${(p.probability * 100).toFixed(1)}%`);
|
|
351
|
+
return textResult(`Probability History for ${betAddress}:\n${lines.join('\n')}`);
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
return errorResult(err.message);
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
// ── Bet write tools (scope: bet:write) ─────────────────────────────
|
|
359
|
+
create_bet: {
|
|
360
|
+
name: 'create_bet',
|
|
361
|
+
description: 'Create a new bet with a question and options',
|
|
362
|
+
inputSchema: {
|
|
363
|
+
type: 'object',
|
|
364
|
+
properties: {
|
|
365
|
+
question: { type: 'string', description: 'The bet question' },
|
|
366
|
+
options: {
|
|
367
|
+
type: 'array',
|
|
368
|
+
items: { type: 'string' },
|
|
369
|
+
description: 'List of answer options',
|
|
370
|
+
},
|
|
371
|
+
type: { type: 'string', description: 'Bet type (e.g. USDC, XP). Optional.' },
|
|
372
|
+
amount: { type: 'number', description: 'Initial wager amount (optional)' },
|
|
373
|
+
expiresAt: { type: 'string', description: 'Expiration date in ISO format (optional)' },
|
|
374
|
+
},
|
|
375
|
+
required: ['question', 'options'],
|
|
376
|
+
},
|
|
377
|
+
requiredScope: 'bet:write',
|
|
378
|
+
async handler(args, client) {
|
|
379
|
+
const question = args.question;
|
|
380
|
+
const options = args.options;
|
|
381
|
+
if (!question)
|
|
382
|
+
return errorResult('Missing required parameter: question');
|
|
383
|
+
if (!options || !Array.isArray(options) || options.length < 2)
|
|
384
|
+
return errorResult('Missing or invalid parameter: options (must be an array with at least 2 items)');
|
|
385
|
+
try {
|
|
386
|
+
const bet = await client.createBet({
|
|
387
|
+
question,
|
|
388
|
+
options,
|
|
389
|
+
type: args.type ?? 'USDC',
|
|
390
|
+
amount: args.amount,
|
|
391
|
+
expiresAt: args.expiresAt,
|
|
392
|
+
});
|
|
393
|
+
return textResult(`Bet created successfully!\n Question: ${bet.question}\n Options: ${bet.options.join(', ')}\n Status: ${bet.status}\n ID: ${bet.id}`);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
return errorResult(err.message);
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
join_bet: {
|
|
401
|
+
name: 'join_bet',
|
|
402
|
+
description: 'Join an existing bet',
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: 'object',
|
|
405
|
+
properties: {
|
|
406
|
+
betAddress: { type: 'string', description: 'Address of the bet to join' },
|
|
407
|
+
},
|
|
408
|
+
required: ['betAddress'],
|
|
409
|
+
},
|
|
410
|
+
requiredScope: 'bet:write',
|
|
411
|
+
async handler(args, client) {
|
|
412
|
+
const betAddress = args.betAddress;
|
|
413
|
+
if (!betAddress)
|
|
414
|
+
return errorResult('Missing required parameter: betAddress');
|
|
415
|
+
try {
|
|
416
|
+
await client.joinBet(betAddress);
|
|
417
|
+
return textResult(`Successfully joined bet: ${betAddress}`);
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
return errorResult(err.message);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
place_wager: {
|
|
425
|
+
name: 'place_wager',
|
|
426
|
+
description: 'Place a wager on a bet option',
|
|
427
|
+
inputSchema: {
|
|
428
|
+
type: 'object',
|
|
429
|
+
properties: {
|
|
430
|
+
betAddress: { type: 'string', description: 'Address of the bet' },
|
|
431
|
+
optionIndex: { type: 'number', description: 'Index of the option to bet on (0-based)' },
|
|
432
|
+
amount: { type: 'number', description: 'Amount to wager' },
|
|
433
|
+
},
|
|
434
|
+
required: ['betAddress', 'optionIndex', 'amount'],
|
|
435
|
+
},
|
|
436
|
+
requiredScope: 'bet:write',
|
|
437
|
+
async handler(args, client) {
|
|
438
|
+
const betAddress = args.betAddress;
|
|
439
|
+
const optionIndex = args.optionIndex;
|
|
440
|
+
const amount = args.amount;
|
|
441
|
+
if (!betAddress)
|
|
442
|
+
return errorResult('Missing required parameter: betAddress');
|
|
443
|
+
if (optionIndex === undefined || optionIndex === null)
|
|
444
|
+
return errorResult('Missing required parameter: optionIndex');
|
|
445
|
+
if (amount === undefined || amount === null)
|
|
446
|
+
return errorResult('Missing required parameter: amount');
|
|
447
|
+
try {
|
|
448
|
+
await client.placeWager({ betAddress, optionIndex, amount });
|
|
449
|
+
return textResult(`Wager placed successfully!\n Bet: ${betAddress}\n Option: ${optionIndex}\n Amount: ${amount}`);
|
|
450
|
+
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
return errorResult(err.message);
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
cancel_wager: {
|
|
457
|
+
name: 'cancel_wager',
|
|
458
|
+
description: 'Cancel a previously placed wager',
|
|
459
|
+
inputSchema: {
|
|
460
|
+
type: 'object',
|
|
461
|
+
properties: {
|
|
462
|
+
betAddress: { type: 'string', description: 'Address of the bet' },
|
|
463
|
+
wagerAddress: { type: 'string', description: 'Address of the wager to cancel' },
|
|
464
|
+
},
|
|
465
|
+
required: ['betAddress', 'wagerAddress'],
|
|
466
|
+
},
|
|
467
|
+
requiredScope: 'bet:write',
|
|
468
|
+
async handler(args, client) {
|
|
469
|
+
const betAddress = args.betAddress;
|
|
470
|
+
const wagerAddress = args.wagerAddress;
|
|
471
|
+
if (!betAddress)
|
|
472
|
+
return errorResult('Missing required parameter: betAddress');
|
|
473
|
+
if (!wagerAddress)
|
|
474
|
+
return errorResult('Missing required parameter: wagerAddress');
|
|
475
|
+
try {
|
|
476
|
+
await client.cancelWager({ betAddress, wagerAddress });
|
|
477
|
+
return textResult(`Wager cancelled successfully: ${wagerAddress}`);
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
return errorResult(err.message);
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
initiate_vote: {
|
|
485
|
+
name: 'initiate_vote',
|
|
486
|
+
description: 'Initiate the voting phase for a bet',
|
|
487
|
+
inputSchema: {
|
|
488
|
+
type: 'object',
|
|
489
|
+
properties: {
|
|
490
|
+
betAddress: { type: 'string', description: 'Address of the bet' },
|
|
491
|
+
},
|
|
492
|
+
required: ['betAddress'],
|
|
493
|
+
},
|
|
494
|
+
requiredScope: 'bet:write',
|
|
495
|
+
async handler(args, client) {
|
|
496
|
+
const betAddress = args.betAddress;
|
|
497
|
+
if (!betAddress)
|
|
498
|
+
return errorResult('Missing required parameter: betAddress');
|
|
499
|
+
try {
|
|
500
|
+
await client.initiateVote(betAddress);
|
|
501
|
+
return textResult(`Vote initiated for bet: ${betAddress}`);
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
return errorResult(err.message);
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
vote: {
|
|
509
|
+
name: 'vote',
|
|
510
|
+
description: 'Cast a vote on a bet outcome',
|
|
511
|
+
inputSchema: {
|
|
512
|
+
type: 'object',
|
|
513
|
+
properties: {
|
|
514
|
+
betAddress: { type: 'string', description: 'Address of the bet' },
|
|
515
|
+
optionIndex: { type: 'number', description: 'Index of the option to vote for (0-based)' },
|
|
516
|
+
},
|
|
517
|
+
required: ['betAddress', 'optionIndex'],
|
|
518
|
+
},
|
|
519
|
+
requiredScope: 'bet:write',
|
|
520
|
+
async handler(args, client) {
|
|
521
|
+
const betAddress = args.betAddress;
|
|
522
|
+
const optionIndex = args.optionIndex;
|
|
523
|
+
if (!betAddress)
|
|
524
|
+
return errorResult('Missing required parameter: betAddress');
|
|
525
|
+
if (optionIndex === undefined || optionIndex === null)
|
|
526
|
+
return errorResult('Missing required parameter: optionIndex');
|
|
527
|
+
try {
|
|
528
|
+
await client.vote({ betAddress, optionIndex });
|
|
529
|
+
return textResult(`Vote cast successfully on bet ${betAddress} for option ${optionIndex}`);
|
|
530
|
+
}
|
|
531
|
+
catch (err) {
|
|
532
|
+
return errorResult(err.message);
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
settle_bet: {
|
|
537
|
+
name: 'settle_bet',
|
|
538
|
+
description: 'Settle a bet to distribute winnings',
|
|
539
|
+
inputSchema: {
|
|
540
|
+
type: 'object',
|
|
541
|
+
properties: {
|
|
542
|
+
betAddress: { type: 'string', description: 'Address of the bet to settle' },
|
|
543
|
+
},
|
|
544
|
+
required: ['betAddress'],
|
|
545
|
+
},
|
|
546
|
+
requiredScope: 'bet:write',
|
|
547
|
+
async handler(args, client) {
|
|
548
|
+
const betAddress = args.betAddress;
|
|
549
|
+
if (!betAddress)
|
|
550
|
+
return errorResult('Missing required parameter: betAddress');
|
|
551
|
+
try {
|
|
552
|
+
await client.settleBet(betAddress);
|
|
553
|
+
return textResult(`Bet settled successfully: ${betAddress}`);
|
|
554
|
+
}
|
|
555
|
+
catch (err) {
|
|
556
|
+
return errorResult(err.message);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
// ── User write tools (scope: user:write) ───────────────────────────
|
|
561
|
+
update_display_name: {
|
|
562
|
+
name: 'update_display_name',
|
|
563
|
+
description: 'Update your display name',
|
|
564
|
+
inputSchema: {
|
|
565
|
+
type: 'object',
|
|
566
|
+
properties: {
|
|
567
|
+
name: { type: 'string', description: 'New display name' },
|
|
568
|
+
},
|
|
569
|
+
required: ['name'],
|
|
570
|
+
},
|
|
571
|
+
requiredScope: 'user:write',
|
|
572
|
+
async handler(args, client) {
|
|
573
|
+
const name = args.name;
|
|
574
|
+
if (!name)
|
|
575
|
+
return errorResult('Missing required parameter: name');
|
|
576
|
+
try {
|
|
577
|
+
await client.updateDisplayName(name);
|
|
578
|
+
return textResult(`Display name updated to: ${name}`);
|
|
579
|
+
}
|
|
580
|
+
catch (err) {
|
|
581
|
+
return errorResult(err.message);
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
// ── Social write tools (scope: social:write) ──────────────────────
|
|
586
|
+
send_friend_request: {
|
|
587
|
+
name: 'send_friend_request',
|
|
588
|
+
description: 'Send a friend request to another user',
|
|
589
|
+
inputSchema: {
|
|
590
|
+
type: 'object',
|
|
591
|
+
properties: {
|
|
592
|
+
uuid: { type: 'string', description: 'UUID of the user to send a request to' },
|
|
593
|
+
},
|
|
594
|
+
required: ['uuid'],
|
|
595
|
+
},
|
|
596
|
+
requiredScope: 'social:write',
|
|
597
|
+
async handler(args, client) {
|
|
598
|
+
const uuid = args.uuid;
|
|
599
|
+
if (!uuid)
|
|
600
|
+
return errorResult('Missing required parameter: uuid');
|
|
601
|
+
try {
|
|
602
|
+
await client.sendFriendRequest(uuid);
|
|
603
|
+
return textResult(`Friend request sent to: ${uuid}`);
|
|
604
|
+
}
|
|
605
|
+
catch (err) {
|
|
606
|
+
return errorResult(err.message);
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
respond_friend_request: {
|
|
611
|
+
name: 'respond_friend_request',
|
|
612
|
+
description: 'Accept or decline a friend request',
|
|
613
|
+
inputSchema: {
|
|
614
|
+
type: 'object',
|
|
615
|
+
properties: {
|
|
616
|
+
requestId: { type: 'string', description: 'ID of the friend request' },
|
|
617
|
+
accept: { type: 'boolean', description: 'True to accept, false to decline' },
|
|
618
|
+
},
|
|
619
|
+
required: ['requestId', 'accept'],
|
|
620
|
+
},
|
|
621
|
+
requiredScope: 'social:write',
|
|
622
|
+
async handler(args, client) {
|
|
623
|
+
const requestId = args.requestId;
|
|
624
|
+
const accept = args.accept;
|
|
625
|
+
if (!requestId)
|
|
626
|
+
return errorResult('Missing required parameter: requestId');
|
|
627
|
+
if (accept === undefined || accept === null)
|
|
628
|
+
return errorResult('Missing required parameter: accept');
|
|
629
|
+
try {
|
|
630
|
+
await client.respondFriendRequest(requestId, accept);
|
|
631
|
+
return textResult(`Friend request ${accept ? 'accepted' : 'declined'}: ${requestId}`);
|
|
632
|
+
}
|
|
633
|
+
catch (err) {
|
|
634
|
+
return errorResult(err.message);
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
favourite_bet: {
|
|
639
|
+
name: 'favourite_bet',
|
|
640
|
+
description: 'Add a bet to your favourites',
|
|
641
|
+
inputSchema: {
|
|
642
|
+
type: 'object',
|
|
643
|
+
properties: {
|
|
644
|
+
betAddress: { type: 'string', description: 'Address of the bet to favourite' },
|
|
645
|
+
},
|
|
646
|
+
required: ['betAddress'],
|
|
647
|
+
},
|
|
648
|
+
requiredScope: 'social:write',
|
|
649
|
+
async handler(args, client) {
|
|
650
|
+
const betAddress = args.betAddress;
|
|
651
|
+
if (!betAddress)
|
|
652
|
+
return errorResult('Missing required parameter: betAddress');
|
|
653
|
+
try {
|
|
654
|
+
await client.favouriteBet(betAddress);
|
|
655
|
+
return textResult(`Bet added to favourites: ${betAddress}`);
|
|
656
|
+
}
|
|
657
|
+
catch (err) {
|
|
658
|
+
return errorResult(err.message);
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
unfavourite_bet: {
|
|
663
|
+
name: 'unfavourite_bet',
|
|
664
|
+
description: 'Remove a bet from your favourites',
|
|
665
|
+
inputSchema: {
|
|
666
|
+
type: 'object',
|
|
667
|
+
properties: {
|
|
668
|
+
betAddress: { type: 'string', description: 'Address of the bet to unfavourite' },
|
|
669
|
+
},
|
|
670
|
+
required: ['betAddress'],
|
|
671
|
+
},
|
|
672
|
+
requiredScope: 'social:write',
|
|
673
|
+
async handler(args, client) {
|
|
674
|
+
const betAddress = args.betAddress;
|
|
675
|
+
if (!betAddress)
|
|
676
|
+
return errorResult('Missing required parameter: betAddress');
|
|
677
|
+
try {
|
|
678
|
+
await client.unfavouriteBet(betAddress);
|
|
679
|
+
return textResult(`Bet removed from favourites: ${betAddress}`);
|
|
680
|
+
}
|
|
681
|
+
catch (err) {
|
|
682
|
+
return errorResult(err.message);
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
};
|