@mahesvara/discord-mcpserver 1.0.8 → 1.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/dist/index.js +4 -2833
- package/dist/schemas/channel.d.ts +176 -0
- package/dist/schemas/channel.js +146 -0
- package/dist/schemas/common.d.ts +23 -0
- package/dist/schemas/common.js +71 -0
- package/dist/schemas/community.d.ts +181 -0
- package/dist/schemas/community.js +60 -0
- package/dist/schemas/emoji.d.ts +59 -0
- package/dist/schemas/emoji.js +29 -0
- package/dist/schemas/event.d.ts +62 -0
- package/dist/schemas/event.js +33 -0
- package/dist/schemas/guild.d.ts +85 -0
- package/dist/schemas/guild.js +42 -0
- package/dist/schemas/index.d.ts +12 -855
- package/dist/schemas/index.js +13 -617
- package/dist/schemas/invite.d.ts +40 -0
- package/dist/schemas/invite.js +32 -0
- package/dist/schemas/member.d.ts +60 -0
- package/dist/schemas/member.js +33 -0
- package/dist/schemas/message.d.ts +121 -0
- package/dist/schemas/message.js +61 -0
- package/dist/schemas/moderation.d.ts +343 -0
- package/dist/schemas/moderation.js +152 -0
- package/dist/schemas/role.d.ts +126 -0
- package/dist/schemas/role.js +73 -0
- package/dist/schemas/webhook.d.ts +51 -0
- package/dist/schemas/webhook.js +33 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +6 -0
- package/dist/tools/channel.d.ts +2 -0
- package/dist/tools/channel.js +572 -0
- package/dist/tools/community.d.ts +2 -0
- package/dist/tools/community.js +268 -0
- package/dist/tools/emoji.d.ts +2 -0
- package/dist/tools/emoji.js +247 -0
- package/dist/tools/event.d.ts +2 -0
- package/dist/tools/event.js +170 -0
- package/dist/tools/guild.d.ts +2 -0
- package/dist/tools/guild.js +198 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +24 -0
- package/dist/tools/invite.d.ts +2 -0
- package/dist/tools/invite.js +143 -0
- package/dist/tools/member.d.ts +2 -0
- package/dist/tools/member.js +200 -0
- package/dist/tools/message.d.ts +2 -0
- package/dist/tools/message.js +386 -0
- package/dist/tools/moderation.d.ts +2 -0
- package/dist/tools/moderation.js +641 -0
- package/dist/tools/role.d.ts +2 -0
- package/dist/tools/role.js +420 -0
- package/dist/tools/webhook.d.ts +2 -0
- package/dist/tools/webhook.js +199 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,2841 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "dotenv/config";
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
5
|
import express from "express";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const server = new McpServer({
|
|
12
|
-
name: "discord-mcp-server",
|
|
13
|
-
version: "1.0.0",
|
|
14
|
-
});
|
|
15
|
-
// ============================================================================
|
|
16
|
-
// GUILD TOOLS
|
|
17
|
-
// ============================================================================
|
|
18
|
-
server.registerTool("discord_list_guilds", {
|
|
19
|
-
title: "List Discord Servers",
|
|
20
|
-
description: `List all Discord servers (guilds) the bot has access to.
|
|
21
|
-
|
|
22
|
-
Returns server names, IDs, member counts, and owner information.
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
List of guilds with id, name, memberCount, icon, ownerId`,
|
|
29
|
-
inputSchema: ListGuildsSchema,
|
|
30
|
-
annotations: {
|
|
31
|
-
readOnlyHint: true,
|
|
32
|
-
destructiveHint: false,
|
|
33
|
-
idempotentHint: true,
|
|
34
|
-
openWorldHint: true,
|
|
35
|
-
},
|
|
36
|
-
}, async (params) => {
|
|
37
|
-
try {
|
|
38
|
-
const client = await getClient();
|
|
39
|
-
const guilds = client.guilds.cache.map(formatGuild);
|
|
40
|
-
const text = formatResponse(guilds, params.response_format, (items) => items.map(guildToMarkdown).join("\n\n"));
|
|
41
|
-
return {
|
|
42
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
return {
|
|
47
|
-
isError: true,
|
|
48
|
-
content: [{ type: "text", text: `Error listing guilds: ${error.message}` }],
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
server.registerTool("discord_get_guild", {
|
|
53
|
-
title: "Get Discord Server Info",
|
|
54
|
-
description: `Get detailed information about a specific Discord server.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
- guild_id (string): Discord server/guild ID
|
|
58
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
Server details including name, member count, owner, and icon`,
|
|
62
|
-
inputSchema: GetGuildSchema,
|
|
63
|
-
annotations: {
|
|
64
|
-
readOnlyHint: true,
|
|
65
|
-
destructiveHint: false,
|
|
66
|
-
idempotentHint: true,
|
|
67
|
-
openWorldHint: true,
|
|
68
|
-
},
|
|
69
|
-
}, async (params) => {
|
|
70
|
-
try {
|
|
71
|
-
const client = await getClient();
|
|
72
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
73
|
-
if (!guild) {
|
|
74
|
-
return {
|
|
75
|
-
isError: true,
|
|
76
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}. Use discord_list_guilds to see available servers.` }],
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const formatted = formatGuild(guild);
|
|
80
|
-
const text = formatResponse(formatted, params.response_format, guildToMarkdown);
|
|
81
|
-
return {
|
|
82
|
-
content: [{ type: "text", text }],
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
return {
|
|
87
|
-
isError: true,
|
|
88
|
-
content: [{ type: "text", text: `Error getting guild: ${error.message}` }],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
server.registerTool("discord_leave_guild", {
|
|
93
|
-
title: "Leave Discord Server",
|
|
94
|
-
description: `Leave a Discord server (guild). The bot will no longer have access to this server.
|
|
95
|
-
|
|
96
|
-
WARNING: This is a destructive action. The bot will need to be re-invited to rejoin.
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
- guild_id (string): Discord server/guild ID to leave
|
|
100
|
-
- confirm (boolean): Must be set to true to confirm leaving
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
Confirmation message with server details`,
|
|
104
|
-
inputSchema: LeaveGuildSchema,
|
|
105
|
-
annotations: {
|
|
106
|
-
readOnlyHint: false,
|
|
107
|
-
destructiveHint: true,
|
|
108
|
-
idempotentHint: false,
|
|
109
|
-
openWorldHint: true,
|
|
110
|
-
},
|
|
111
|
-
}, async (params) => {
|
|
112
|
-
try {
|
|
113
|
-
if (!params.confirm) {
|
|
114
|
-
return {
|
|
115
|
-
isError: true,
|
|
116
|
-
content: [{ type: "text", text: "You must set 'confirm: true' to leave a server. This action cannot be undone - the bot will need to be re-invited to rejoin." }],
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const client = await getClient();
|
|
120
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
121
|
-
if (!guild) {
|
|
122
|
-
return {
|
|
123
|
-
isError: true,
|
|
124
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}. Use discord_list_guilds to see available servers.` }],
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
const guildName = guild.name;
|
|
128
|
-
const memberCount = guild.memberCount;
|
|
129
|
-
const ownerId = guild.ownerId;
|
|
130
|
-
await guild.leave();
|
|
131
|
-
return {
|
|
132
|
-
content: [{ type: "text", text: `Successfully left server "${guildName}" (ID: ${params.guild_id}, Members: ${memberCount}, Owner: ${ownerId}). The bot will need to be re-invited to rejoin.` }],
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
return {
|
|
137
|
-
isError: true,
|
|
138
|
-
content: [{ type: "text", text: `Error leaving guild: ${error.message}` }],
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
// ============================================================================
|
|
143
|
-
// CHANNEL TOOLS
|
|
144
|
-
// ============================================================================
|
|
145
|
-
server.registerTool("discord_list_channels", {
|
|
146
|
-
title: "List Discord Channels",
|
|
147
|
-
description: `List channels in a Discord server.
|
|
148
|
-
|
|
149
|
-
Args:
|
|
150
|
-
- guild_id (string): Discord server/guild ID
|
|
151
|
-
- type ('text' | 'voice' | 'category' | 'all'): Filter by channel type (default: 'all')
|
|
152
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
List of channels with id, name, type, topic, and category info`,
|
|
156
|
-
inputSchema: ListChannelsSchema,
|
|
157
|
-
annotations: {
|
|
158
|
-
readOnlyHint: true,
|
|
159
|
-
destructiveHint: false,
|
|
160
|
-
idempotentHint: true,
|
|
161
|
-
openWorldHint: true,
|
|
162
|
-
},
|
|
163
|
-
}, async (params) => {
|
|
164
|
-
try {
|
|
165
|
-
const client = await getClient();
|
|
166
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
167
|
-
if (!guild) {
|
|
168
|
-
return {
|
|
169
|
-
isError: true,
|
|
170
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
// Get all channels as array to avoid type predicate issues
|
|
174
|
-
const allChannels = Array.from(guild.channels.cache.values());
|
|
175
|
-
// Filter out threads and apply type filter
|
|
176
|
-
let filteredChannels = allChannels.filter(c => !c.isThread());
|
|
177
|
-
if (params.type !== "all") {
|
|
178
|
-
const targetType = getChannelType(params.type);
|
|
179
|
-
if (targetType !== null) {
|
|
180
|
-
filteredChannels = filteredChannels.filter(c => c.type === targetType);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
const formatted = filteredChannels.map(c => formatChannel(c));
|
|
184
|
-
const text = formatResponse(formatted, params.response_format, (items) => items.map(channelToMarkdown).join("\n\n"));
|
|
185
|
-
return {
|
|
186
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
return {
|
|
191
|
-
isError: true,
|
|
192
|
-
content: [{ type: "text", text: `Error listing channels: ${error.message}` }],
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
server.registerTool("discord_get_channel", {
|
|
197
|
-
title: "Get Discord Channel Info",
|
|
198
|
-
description: `Get detailed information about a specific channel.
|
|
199
|
-
|
|
200
|
-
Args:
|
|
201
|
-
- channel_id (string): Discord channel ID
|
|
202
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
203
|
-
|
|
204
|
-
Returns:
|
|
205
|
-
Channel details including name, type, topic, and category`,
|
|
206
|
-
inputSchema: GetChannelSchema,
|
|
207
|
-
annotations: {
|
|
208
|
-
readOnlyHint: true,
|
|
209
|
-
destructiveHint: false,
|
|
210
|
-
idempotentHint: true,
|
|
211
|
-
openWorldHint: true,
|
|
212
|
-
},
|
|
213
|
-
}, async (params) => {
|
|
214
|
-
try {
|
|
215
|
-
const client = await getClient();
|
|
216
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
217
|
-
if (!channel || !(channel instanceof GuildChannel)) {
|
|
218
|
-
return {
|
|
219
|
-
isError: true,
|
|
220
|
-
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
const formatted = formatChannel(channel);
|
|
224
|
-
const text = formatResponse(formatted, params.response_format, channelToMarkdown);
|
|
225
|
-
return {
|
|
226
|
-
content: [{ type: "text", text }],
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
return {
|
|
231
|
-
isError: true,
|
|
232
|
-
content: [{ type: "text", text: `Error getting channel: ${error.message}` }],
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
server.registerTool("discord_create_channel", {
|
|
237
|
-
title: "Create Discord Channel",
|
|
238
|
-
description: `Create a new channel or category in a Discord server.
|
|
239
|
-
|
|
240
|
-
Args:
|
|
241
|
-
- guild_id (string): Discord server/guild ID
|
|
242
|
-
- name (string): Channel name
|
|
243
|
-
- type ('text' | 'voice' | 'category'): Channel type (default: 'text')
|
|
244
|
-
- topic (string, optional): Channel topic (text channels only)
|
|
245
|
-
- parent_id (string, optional): Category ID to create channel under (not for categories)
|
|
246
|
-
- nsfw (boolean, optional): Whether the channel is NSFW (text channels only)
|
|
247
|
-
- bitrate (number, optional): Bitrate for voice channels (8000-384000)
|
|
248
|
-
- user_limit (number, optional): User limit for voice channels (0 = unlimited)
|
|
249
|
-
|
|
250
|
-
Returns:
|
|
251
|
-
The created channel's details`,
|
|
252
|
-
inputSchema: CreateChannelSchema,
|
|
253
|
-
annotations: {
|
|
254
|
-
readOnlyHint: false,
|
|
255
|
-
destructiveHint: false,
|
|
256
|
-
idempotentHint: false,
|
|
257
|
-
openWorldHint: true,
|
|
258
|
-
},
|
|
259
|
-
}, async (params) => {
|
|
260
|
-
try {
|
|
261
|
-
const client = await getClient();
|
|
262
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
263
|
-
if (!guild) {
|
|
264
|
-
return {
|
|
265
|
-
isError: true,
|
|
266
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
let channelType;
|
|
270
|
-
switch (params.type) {
|
|
271
|
-
case "voice":
|
|
272
|
-
channelType = ChannelType.GuildVoice;
|
|
273
|
-
break;
|
|
274
|
-
case "category":
|
|
275
|
-
channelType = ChannelType.GuildCategory;
|
|
276
|
-
break;
|
|
277
|
-
default:
|
|
278
|
-
channelType = ChannelType.GuildText;
|
|
279
|
-
}
|
|
280
|
-
const channelOptions = {
|
|
281
|
-
name: params.name,
|
|
282
|
-
type: channelType,
|
|
283
|
-
};
|
|
284
|
-
// Only add these for non-category channels
|
|
285
|
-
if (params.type !== "category") {
|
|
286
|
-
if (params.parent_id)
|
|
287
|
-
channelOptions.parent = params.parent_id;
|
|
288
|
-
}
|
|
289
|
-
// Text channel specific options
|
|
290
|
-
if (params.type === "text" || !params.type) {
|
|
291
|
-
if (params.topic)
|
|
292
|
-
channelOptions.topic = params.topic;
|
|
293
|
-
if (params.nsfw !== undefined)
|
|
294
|
-
channelOptions.nsfw = params.nsfw;
|
|
295
|
-
}
|
|
296
|
-
// Voice channel specific options
|
|
297
|
-
if (params.type === "voice") {
|
|
298
|
-
if (params.bitrate)
|
|
299
|
-
channelOptions.bitrate = params.bitrate;
|
|
300
|
-
if (params.user_limit !== undefined)
|
|
301
|
-
channelOptions.userLimit = params.user_limit;
|
|
302
|
-
}
|
|
303
|
-
const channel = await guild.channels.create(channelOptions);
|
|
304
|
-
const typeLabel = params.type === "category" ? "category" : "channel";
|
|
305
|
-
const prefix = params.type === "category" ? "📁" : "#";
|
|
306
|
-
return {
|
|
307
|
-
content: [{ type: "text", text: `Created ${typeLabel} ${prefix}${channel.name} (ID: ${channel.id})` }],
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
catch (error) {
|
|
311
|
-
return {
|
|
312
|
-
isError: true,
|
|
313
|
-
content: [{ type: "text", text: `Error creating channel: ${error.message}` }],
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
server.registerTool("discord_delete_channel", {
|
|
318
|
-
title: "Delete Discord Channel",
|
|
319
|
-
description: `Delete a channel from a Discord server. This action is permanent!
|
|
320
|
-
|
|
321
|
-
Args:
|
|
322
|
-
- channel_id (string): Discord channel ID to delete
|
|
323
|
-
|
|
324
|
-
Returns:
|
|
325
|
-
Confirmation of deletion`,
|
|
326
|
-
inputSchema: DeleteChannelSchema,
|
|
327
|
-
annotations: {
|
|
328
|
-
readOnlyHint: false,
|
|
329
|
-
destructiveHint: true,
|
|
330
|
-
idempotentHint: false,
|
|
331
|
-
openWorldHint: true,
|
|
332
|
-
},
|
|
333
|
-
}, async (params) => {
|
|
334
|
-
try {
|
|
335
|
-
const client = await getClient();
|
|
336
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
337
|
-
if (!channel || !('delete' in channel)) {
|
|
338
|
-
return {
|
|
339
|
-
isError: true,
|
|
340
|
-
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
const name = 'name' in channel ? channel.name : params.channel_id;
|
|
344
|
-
await channel.delete();
|
|
345
|
-
return {
|
|
346
|
-
content: [{ type: "text", text: `Deleted channel: ${name}` }],
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
return {
|
|
351
|
-
isError: true,
|
|
352
|
-
content: [{ type: "text", text: `Error deleting channel: ${error.message}` }],
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
server.registerTool("discord_edit_channel", {
|
|
357
|
-
title: "Edit Discord Channel",
|
|
358
|
-
description: `Edit a channel's properties including name, topic, category, position, and more.
|
|
359
|
-
|
|
360
|
-
Args:
|
|
361
|
-
- channel_id (string): Discord channel ID
|
|
362
|
-
- name (string, optional): New channel name
|
|
363
|
-
- topic (string, optional): New channel topic
|
|
364
|
-
- parent_id (string, optional): Move to a different category (or 'none' to remove from category)
|
|
365
|
-
- position (number, optional): New position of the channel
|
|
366
|
-
- nsfw (boolean, optional): Whether the channel is NSFW
|
|
367
|
-
- bitrate (number, optional): Bitrate for voice channels (8000-384000)
|
|
368
|
-
- user_limit (number, optional): User limit for voice channels
|
|
369
|
-
|
|
370
|
-
Returns:
|
|
371
|
-
Updated channel details`,
|
|
372
|
-
inputSchema: EditChannelSchema,
|
|
373
|
-
annotations: {
|
|
374
|
-
readOnlyHint: false,
|
|
375
|
-
destructiveHint: false,
|
|
376
|
-
idempotentHint: true,
|
|
377
|
-
openWorldHint: true,
|
|
378
|
-
},
|
|
379
|
-
}, async (params) => {
|
|
380
|
-
try {
|
|
381
|
-
const client = await getClient();
|
|
382
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
383
|
-
if (!channel || !('edit' in channel)) {
|
|
384
|
-
return {
|
|
385
|
-
isError: true,
|
|
386
|
-
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
const updates = {};
|
|
390
|
-
if (params.name)
|
|
391
|
-
updates.name = params.name;
|
|
392
|
-
if (params.topic !== undefined)
|
|
393
|
-
updates.topic = params.topic;
|
|
394
|
-
if (params.position !== undefined)
|
|
395
|
-
updates.position = params.position;
|
|
396
|
-
if (params.nsfw !== undefined)
|
|
397
|
-
updates.nsfw = params.nsfw;
|
|
398
|
-
if (params.bitrate !== undefined)
|
|
399
|
-
updates.bitrate = params.bitrate;
|
|
400
|
-
if (params.user_limit !== undefined)
|
|
401
|
-
updates.userLimit = params.user_limit;
|
|
402
|
-
if (params.parent_id !== undefined) {
|
|
403
|
-
updates.parent = params.parent_id === 'none' ? null : params.parent_id;
|
|
404
|
-
}
|
|
405
|
-
await channel.edit(updates);
|
|
406
|
-
return {
|
|
407
|
-
content: [{ type: "text", text: `Updated channel ${params.channel_id}` }],
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
catch (error) {
|
|
411
|
-
return {
|
|
412
|
-
isError: true,
|
|
413
|
-
content: [{ type: "text", text: `Error editing channel: ${error.message}` }],
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
// ============================================================================
|
|
418
|
-
// PERMISSION TOOLS
|
|
419
|
-
// ============================================================================
|
|
420
|
-
// Helper function to convert permission strings to bitfield
|
|
421
|
-
function parsePermissions(permissions) {
|
|
422
|
-
let bits = BigInt(0);
|
|
423
|
-
for (const perm of permissions) {
|
|
424
|
-
const flag = PermissionsBitField.Flags[perm];
|
|
425
|
-
if (flag) {
|
|
426
|
-
bits |= flag;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return bits;
|
|
430
|
-
}
|
|
431
|
-
// List of common permission names for reference
|
|
432
|
-
const PERMISSION_LIST = [
|
|
433
|
-
'ViewChannel', 'ManageChannels', 'ManageRoles', 'CreateInstantInvite',
|
|
434
|
-
'SendMessages', 'SendMessagesInThreads', 'CreatePublicThreads', 'CreatePrivateThreads',
|
|
435
|
-
'EmbedLinks', 'AttachFiles', 'AddReactions', 'UseExternalEmojis', 'UseExternalStickers',
|
|
436
|
-
'MentionEveryone', 'ManageMessages', 'ManageThreads', 'ReadMessageHistory',
|
|
437
|
-
'SendTTSMessages', 'UseApplicationCommands',
|
|
438
|
-
'Connect', 'Speak', 'Stream', 'UseEmbeddedActivities', 'UseSoundboard',
|
|
439
|
-
'UseExternalSounds', 'UseVAD', 'PrioritySpeaker', 'MuteMembers', 'DeafenMembers', 'MoveMembers',
|
|
440
|
-
'ManageEvents', 'ManageWebhooks'
|
|
441
|
-
];
|
|
442
|
-
server.registerTool("discord_set_channel_permissions", {
|
|
443
|
-
title: "Set Channel Permissions",
|
|
444
|
-
description: `Set permission overrides for a role or member on a channel.
|
|
445
|
-
|
|
446
|
-
Common permission names:
|
|
447
|
-
- View: ViewChannel
|
|
448
|
-
- Messages: SendMessages, ReadMessageHistory, ManageMessages, EmbedLinks, AttachFiles, AddReactions
|
|
449
|
-
- Voice: Connect, Speak, Stream, MuteMembers, DeafenMembers, MoveMembers
|
|
450
|
-
- Threads: CreatePublicThreads, CreatePrivateThreads, SendMessagesInThreads
|
|
451
|
-
- Management: ManageChannels, ManageRoles, ManageWebhooks
|
|
452
|
-
|
|
453
|
-
Args:
|
|
454
|
-
- channel_id (string): Discord channel ID
|
|
455
|
-
- target_id (string): Role or User ID to set permissions for
|
|
456
|
-
- target_type ('role' | 'member'): Whether target is a role or member
|
|
457
|
-
- allow (string[], optional): Permissions to allow (e.g., ['ViewChannel', 'SendMessages'])
|
|
458
|
-
- deny (string[], optional): Permissions to deny (e.g., ['SendMessages'])
|
|
459
|
-
|
|
460
|
-
Returns:
|
|
461
|
-
Confirmation of permission changes`,
|
|
462
|
-
inputSchema: SetChannelPermissionsSchema,
|
|
463
|
-
annotations: {
|
|
464
|
-
readOnlyHint: false,
|
|
465
|
-
destructiveHint: false,
|
|
466
|
-
idempotentHint: true,
|
|
467
|
-
openWorldHint: true,
|
|
468
|
-
},
|
|
469
|
-
}, async (params) => {
|
|
470
|
-
try {
|
|
471
|
-
const client = await getClient();
|
|
472
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
473
|
-
if (!channel || !('permissionOverwrites' in channel)) {
|
|
474
|
-
return {
|
|
475
|
-
isError: true,
|
|
476
|
-
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
const guildChannel = channel;
|
|
480
|
-
// Build permission overwrite object with individual permission keys
|
|
481
|
-
const permissionOverwrite = {};
|
|
482
|
-
// Set allowed permissions to true
|
|
483
|
-
if (params.allow) {
|
|
484
|
-
for (const perm of params.allow) {
|
|
485
|
-
permissionOverwrite[perm] = true;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
// Set denied permissions to false
|
|
489
|
-
if (params.deny) {
|
|
490
|
-
for (const perm of params.deny) {
|
|
491
|
-
permissionOverwrite[perm] = false;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
await guildChannel.permissionOverwrites.edit(params.target_id, permissionOverwrite, {
|
|
495
|
-
type: params.target_type === 'role' ? OverwriteType.Role : OverwriteType.Member,
|
|
496
|
-
});
|
|
497
|
-
const allowList = params.allow?.join(', ') || 'none';
|
|
498
|
-
const denyList = params.deny?.join(', ') || 'none';
|
|
499
|
-
return {
|
|
500
|
-
content: [{ type: "text", text: `Updated permissions for ${params.target_type} ${params.target_id} on channel ${params.channel_id}\nAllowed: ${allowList}\nDenied: ${denyList}` }],
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
catch (error) {
|
|
504
|
-
return {
|
|
505
|
-
isError: true,
|
|
506
|
-
content: [{ type: "text", text: `Error setting permissions: ${error.message}` }],
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
server.registerTool("discord_remove_channel_permissions", {
|
|
511
|
-
title: "Remove Channel Permission Overrides",
|
|
512
|
-
description: `Remove all permission overrides for a role or member on a channel.
|
|
513
|
-
|
|
514
|
-
Args:
|
|
515
|
-
- channel_id (string): Discord channel ID
|
|
516
|
-
- target_id (string): Role or User ID to remove permission overrides for
|
|
517
|
-
|
|
518
|
-
Returns:
|
|
519
|
-
Confirmation of removal`,
|
|
520
|
-
inputSchema: RemoveChannelPermissionsSchema,
|
|
521
|
-
annotations: {
|
|
522
|
-
readOnlyHint: false,
|
|
523
|
-
destructiveHint: true,
|
|
524
|
-
idempotentHint: true,
|
|
525
|
-
openWorldHint: true,
|
|
526
|
-
},
|
|
527
|
-
}, async (params) => {
|
|
528
|
-
try {
|
|
529
|
-
const client = await getClient();
|
|
530
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
531
|
-
if (!channel || !('permissionOverwrites' in channel)) {
|
|
532
|
-
return {
|
|
533
|
-
isError: true,
|
|
534
|
-
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
const guildChannel = channel;
|
|
538
|
-
await guildChannel.permissionOverwrites.delete(params.target_id);
|
|
539
|
-
return {
|
|
540
|
-
content: [{ type: "text", text: `Removed permission overrides for ${params.target_id} on channel ${params.channel_id}` }],
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
catch (error) {
|
|
544
|
-
return {
|
|
545
|
-
isError: true,
|
|
546
|
-
content: [{ type: "text", text: `Error removing permissions: ${error.message}` }],
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
server.registerTool("discord_get_channel_permissions", {
|
|
551
|
-
title: "Get Channel Permissions",
|
|
552
|
-
description: `Get all permission overrides for a channel.
|
|
553
|
-
|
|
554
|
-
Args:
|
|
555
|
-
- channel_id (string): Discord channel ID
|
|
556
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
557
|
-
|
|
558
|
-
Returns:
|
|
559
|
-
List of permission overrides with allow/deny for each role/member`,
|
|
560
|
-
inputSchema: GetChannelPermissionsSchema,
|
|
561
|
-
annotations: {
|
|
562
|
-
readOnlyHint: true,
|
|
563
|
-
destructiveHint: false,
|
|
564
|
-
idempotentHint: true,
|
|
565
|
-
openWorldHint: true,
|
|
566
|
-
},
|
|
567
|
-
}, async (params) => {
|
|
568
|
-
try {
|
|
569
|
-
const client = await getClient();
|
|
570
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
571
|
-
if (!channel || !('permissionOverwrites' in channel)) {
|
|
572
|
-
return {
|
|
573
|
-
isError: true,
|
|
574
|
-
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
const guildChannel = channel;
|
|
578
|
-
const overwrites = guildChannel.permissionOverwrites.cache;
|
|
579
|
-
const formatted = overwrites.map(ow => {
|
|
580
|
-
const allowedPerms = new PermissionsBitField(ow.allow).toArray();
|
|
581
|
-
const deniedPerms = new PermissionsBitField(ow.deny).toArray();
|
|
582
|
-
return {
|
|
583
|
-
id: ow.id,
|
|
584
|
-
type: ow.type === OverwriteType.Role ? 'role' : 'member',
|
|
585
|
-
allow: allowedPerms,
|
|
586
|
-
deny: deniedPerms,
|
|
587
|
-
};
|
|
588
|
-
});
|
|
589
|
-
if (params.response_format === 'json') {
|
|
590
|
-
return {
|
|
591
|
-
content: [{ type: "text", text: JSON.stringify(Array.from(formatted.values()), null, 2) }],
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
// Markdown format
|
|
595
|
-
const lines = Array.from(formatted.values()).map(ow => {
|
|
596
|
-
const allowStr = ow.allow.length > 0 ? ow.allow.join(', ') : 'none';
|
|
597
|
-
const denyStr = ow.deny.length > 0 ? ow.deny.join(', ') : 'none';
|
|
598
|
-
return `### ${ow.type}: ${ow.id}\n- **Allow**: ${allowStr}\n- **Deny**: ${denyStr}`;
|
|
599
|
-
});
|
|
600
|
-
const text = lines.length > 0 ? lines.join('\n\n') : 'No permission overrides on this channel.';
|
|
601
|
-
return {
|
|
602
|
-
content: [{ type: "text", text }],
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
catch (error) {
|
|
606
|
-
return {
|
|
607
|
-
isError: true,
|
|
608
|
-
content: [{ type: "text", text: `Error getting permissions: ${error.message}` }],
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
server.registerTool("discord_sync_channel_permissions", {
|
|
613
|
-
title: "Sync Channel Permissions with Category",
|
|
614
|
-
description: `Sync a channel's permissions with its parent category.
|
|
615
|
-
|
|
616
|
-
Args:
|
|
617
|
-
- channel_id (string): Discord channel ID
|
|
618
|
-
|
|
619
|
-
Returns:
|
|
620
|
-
Confirmation of sync`,
|
|
621
|
-
inputSchema: SyncChannelPermissionsSchema,
|
|
622
|
-
annotations: {
|
|
623
|
-
readOnlyHint: false,
|
|
624
|
-
destructiveHint: false,
|
|
625
|
-
idempotentHint: true,
|
|
626
|
-
openWorldHint: true,
|
|
627
|
-
},
|
|
628
|
-
}, async (params) => {
|
|
629
|
-
try {
|
|
630
|
-
const client = await getClient();
|
|
631
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
632
|
-
if (!channel || !('lockPermissions' in channel)) {
|
|
633
|
-
return {
|
|
634
|
-
isError: true,
|
|
635
|
-
content: [{ type: "text", text: `Channel not found or doesn't support permission sync: ${params.channel_id}` }],
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
await channel.lockPermissions();
|
|
639
|
-
return {
|
|
640
|
-
content: [{ type: "text", text: `Synced permissions for channel ${params.channel_id} with its parent category` }],
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
catch (error) {
|
|
644
|
-
return {
|
|
645
|
-
isError: true,
|
|
646
|
-
content: [{ type: "text", text: `Error syncing permissions: ${error.message}` }],
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
server.registerTool("discord_send_message", {
|
|
651
|
-
title: "Send Discord Message",
|
|
652
|
-
description: `Send a message to a Discord channel.
|
|
653
|
-
|
|
654
|
-
Args:
|
|
655
|
-
- channel_id (string): Discord channel ID
|
|
656
|
-
- content (string): Message content (max 2000 characters)
|
|
657
|
-
- reply_to (string, optional): Message ID to reply to
|
|
658
|
-
|
|
659
|
-
Returns:
|
|
660
|
-
The sent message details including ID`,
|
|
661
|
-
inputSchema: SendMessageSchema,
|
|
662
|
-
annotations: {
|
|
663
|
-
readOnlyHint: false,
|
|
664
|
-
destructiveHint: false,
|
|
665
|
-
idempotentHint: false,
|
|
666
|
-
openWorldHint: true,
|
|
667
|
-
},
|
|
668
|
-
}, async (params) => {
|
|
669
|
-
try {
|
|
670
|
-
const client = await getClient();
|
|
671
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
672
|
-
if (!channel || !('send' in channel)) {
|
|
673
|
-
return {
|
|
674
|
-
isError: true,
|
|
675
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
const options = { content: params.content };
|
|
679
|
-
if (params.reply_to) {
|
|
680
|
-
options.reply = { messageReference: params.reply_to };
|
|
681
|
-
}
|
|
682
|
-
const message = await channel.send(options);
|
|
683
|
-
return {
|
|
684
|
-
content: [{ type: "text", text: `Message sent (ID: ${message.id})` }],
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
catch (error) {
|
|
688
|
-
return {
|
|
689
|
-
isError: true,
|
|
690
|
-
content: [{ type: "text", text: `Error sending message: ${error.message}` }],
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
server.registerTool("discord_get_messages", {
|
|
695
|
-
title: "Get Discord Messages",
|
|
696
|
-
description: `Retrieve messages from a Discord channel.
|
|
697
|
-
|
|
698
|
-
Args:
|
|
699
|
-
- channel_id (string): Discord channel ID
|
|
700
|
-
- limit (number): Number of messages to retrieve (1-100, default: 20)
|
|
701
|
-
- before (string, optional): Get messages before this message ID
|
|
702
|
-
- after (string, optional): Get messages after this message ID
|
|
703
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
704
|
-
|
|
705
|
-
Returns:
|
|
706
|
-
List of messages with content, author, timestamp, attachments`,
|
|
707
|
-
inputSchema: GetMessagesSchema,
|
|
708
|
-
annotations: {
|
|
709
|
-
readOnlyHint: true,
|
|
710
|
-
destructiveHint: false,
|
|
711
|
-
idempotentHint: true,
|
|
712
|
-
openWorldHint: true,
|
|
713
|
-
},
|
|
714
|
-
}, async (params) => {
|
|
715
|
-
try {
|
|
716
|
-
const client = await getClient();
|
|
717
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
718
|
-
if (!channel || !('messages' in channel)) {
|
|
719
|
-
return {
|
|
720
|
-
isError: true,
|
|
721
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
const options = { limit: params.limit };
|
|
725
|
-
if (params.before)
|
|
726
|
-
options.before = params.before;
|
|
727
|
-
if (params.after)
|
|
728
|
-
options.after = params.after;
|
|
729
|
-
const messages = await channel.messages.fetch(options);
|
|
730
|
-
const formatted = messages.map((m) => formatMessage(m));
|
|
731
|
-
const text = formatResponse(Array.from(formatted.values()), params.response_format, (items) => items.map(messageToMarkdown).join("\n\n---\n\n"));
|
|
732
|
-
return {
|
|
733
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
catch (error) {
|
|
737
|
-
return {
|
|
738
|
-
isError: true,
|
|
739
|
-
content: [{ type: "text", text: `Error getting messages: ${error.message}` }],
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
});
|
|
743
|
-
server.registerTool("discord_delete_message", {
|
|
744
|
-
title: "Delete Discord Message",
|
|
745
|
-
description: `Delete a message from a Discord channel.
|
|
746
|
-
|
|
747
|
-
Args:
|
|
748
|
-
- channel_id (string): Discord channel ID
|
|
749
|
-
- message_id (string): Message ID to delete
|
|
750
|
-
|
|
751
|
-
Returns:
|
|
752
|
-
Confirmation of deletion`,
|
|
753
|
-
inputSchema: DeleteMessageSchema,
|
|
754
|
-
annotations: {
|
|
755
|
-
readOnlyHint: false,
|
|
756
|
-
destructiveHint: true,
|
|
757
|
-
idempotentHint: false,
|
|
758
|
-
openWorldHint: true,
|
|
759
|
-
},
|
|
760
|
-
}, async (params) => {
|
|
761
|
-
try {
|
|
762
|
-
const client = await getClient();
|
|
763
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
764
|
-
if (!channel || !('messages' in channel)) {
|
|
765
|
-
return {
|
|
766
|
-
isError: true,
|
|
767
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
771
|
-
await message.delete();
|
|
772
|
-
return {
|
|
773
|
-
content: [{ type: "text", text: `Deleted message ${params.message_id}` }],
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
catch (error) {
|
|
777
|
-
return {
|
|
778
|
-
isError: true,
|
|
779
|
-
content: [{ type: "text", text: `Error deleting message: ${error.message}` }],
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
server.registerTool("discord_edit_message", {
|
|
784
|
-
title: "Edit Discord Message",
|
|
785
|
-
description: `Edit a message sent by the bot.
|
|
786
|
-
|
|
787
|
-
Args:
|
|
788
|
-
- channel_id (string): Discord channel ID
|
|
789
|
-
- message_id (string): Message ID to edit
|
|
790
|
-
- content (string): New message content (max 2000 characters)
|
|
791
|
-
|
|
792
|
-
Returns:
|
|
793
|
-
Confirmation of edit`,
|
|
794
|
-
inputSchema: EditMessageSchema,
|
|
795
|
-
annotations: {
|
|
796
|
-
readOnlyHint: false,
|
|
797
|
-
destructiveHint: false,
|
|
798
|
-
idempotentHint: true,
|
|
799
|
-
openWorldHint: true,
|
|
800
|
-
},
|
|
801
|
-
}, async (params) => {
|
|
802
|
-
try {
|
|
803
|
-
const client = await getClient();
|
|
804
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
805
|
-
if (!channel || !('messages' in channel)) {
|
|
806
|
-
return {
|
|
807
|
-
isError: true,
|
|
808
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
812
|
-
await message.edit(params.content);
|
|
813
|
-
return {
|
|
814
|
-
content: [{ type: "text", text: `Edited message ${params.message_id}` }],
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
catch (error) {
|
|
818
|
-
return {
|
|
819
|
-
isError: true,
|
|
820
|
-
content: [{ type: "text", text: `Error editing message: ${error.message}` }],
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
|
-
server.registerTool("discord_add_reaction", {
|
|
825
|
-
title: "Add Reaction to Message",
|
|
826
|
-
description: `Add an emoji reaction to a message.
|
|
827
|
-
|
|
828
|
-
Args:
|
|
829
|
-
- channel_id (string): Discord channel ID
|
|
830
|
-
- message_id (string): Message ID to react to
|
|
831
|
-
- emoji (string): Emoji to react with (e.g., '👍' or custom emoji format)
|
|
832
|
-
|
|
833
|
-
Returns:
|
|
834
|
-
Confirmation of reaction added`,
|
|
835
|
-
inputSchema: AddReactionSchema,
|
|
836
|
-
annotations: {
|
|
837
|
-
readOnlyHint: false,
|
|
838
|
-
destructiveHint: false,
|
|
839
|
-
idempotentHint: true,
|
|
840
|
-
openWorldHint: true,
|
|
841
|
-
},
|
|
842
|
-
}, async (params) => {
|
|
843
|
-
try {
|
|
844
|
-
const client = await getClient();
|
|
845
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
846
|
-
if (!channel || !('messages' in channel)) {
|
|
847
|
-
return {
|
|
848
|
-
isError: true,
|
|
849
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
853
|
-
await message.react(params.emoji);
|
|
854
|
-
return {
|
|
855
|
-
content: [{ type: "text", text: `Added reaction ${params.emoji} to message ${params.message_id}` }],
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
catch (error) {
|
|
859
|
-
return {
|
|
860
|
-
isError: true,
|
|
861
|
-
content: [{ type: "text", text: `Error adding reaction: ${error.message}` }],
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
});
|
|
865
|
-
server.registerTool("discord_remove_reaction", {
|
|
866
|
-
title: "Remove Reaction from Message",
|
|
867
|
-
description: `Remove the bot's emoji reaction from a message.
|
|
868
|
-
|
|
869
|
-
Args:
|
|
870
|
-
- channel_id (string): Discord channel ID
|
|
871
|
-
- message_id (string): Message ID
|
|
872
|
-
- emoji (string): Emoji to remove
|
|
873
|
-
|
|
874
|
-
Returns:
|
|
875
|
-
Confirmation of reaction removed`,
|
|
876
|
-
inputSchema: RemoveReactionSchema,
|
|
877
|
-
annotations: {
|
|
878
|
-
readOnlyHint: false,
|
|
879
|
-
destructiveHint: false,
|
|
880
|
-
idempotentHint: true,
|
|
881
|
-
openWorldHint: true,
|
|
882
|
-
},
|
|
883
|
-
}, async (params) => {
|
|
884
|
-
try {
|
|
885
|
-
const client = await getClient();
|
|
886
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
887
|
-
if (!channel || !('messages' in channel)) {
|
|
888
|
-
return {
|
|
889
|
-
isError: true,
|
|
890
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
894
|
-
const reaction = message.reactions.cache.get(params.emoji);
|
|
895
|
-
if (reaction) {
|
|
896
|
-
await reaction.users.remove(client.user.id);
|
|
897
|
-
}
|
|
898
|
-
return {
|
|
899
|
-
content: [{ type: "text", text: `Removed reaction ${params.emoji} from message ${params.message_id}` }],
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
catch (error) {
|
|
903
|
-
return {
|
|
904
|
-
isError: true,
|
|
905
|
-
content: [{ type: "text", text: `Error removing reaction: ${error.message}` }],
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
server.registerTool("discord_pin_message", {
|
|
910
|
-
title: "Pin Discord Message",
|
|
911
|
-
description: `Pin a message in a channel.
|
|
912
|
-
|
|
913
|
-
Args:
|
|
914
|
-
- channel_id (string): Discord channel ID
|
|
915
|
-
- message_id (string): Message ID to pin
|
|
916
|
-
|
|
917
|
-
Returns:
|
|
918
|
-
Confirmation of message pinned`,
|
|
919
|
-
inputSchema: PinMessageSchema,
|
|
920
|
-
annotations: {
|
|
921
|
-
readOnlyHint: false,
|
|
922
|
-
destructiveHint: false,
|
|
923
|
-
idempotentHint: true,
|
|
924
|
-
openWorldHint: true,
|
|
925
|
-
},
|
|
926
|
-
}, async (params) => {
|
|
927
|
-
try {
|
|
928
|
-
const client = await getClient();
|
|
929
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
930
|
-
if (!channel || !('messages' in channel)) {
|
|
931
|
-
return {
|
|
932
|
-
isError: true,
|
|
933
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
937
|
-
await message.pin();
|
|
938
|
-
return {
|
|
939
|
-
content: [{ type: "text", text: `Pinned message ${params.message_id}` }],
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
catch (error) {
|
|
943
|
-
return {
|
|
944
|
-
isError: true,
|
|
945
|
-
content: [{ type: "text", text: `Error pinning message: ${error.message}` }],
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
server.registerTool("discord_unpin_message", {
|
|
950
|
-
title: "Unpin Discord Message",
|
|
951
|
-
description: `Unpin a message in a channel.
|
|
952
|
-
|
|
953
|
-
Args:
|
|
954
|
-
- channel_id (string): Discord channel ID
|
|
955
|
-
- message_id (string): Message ID to unpin
|
|
956
|
-
|
|
957
|
-
Returns:
|
|
958
|
-
Confirmation of message unpinned`,
|
|
959
|
-
inputSchema: UnpinMessageSchema,
|
|
960
|
-
annotations: {
|
|
961
|
-
readOnlyHint: false,
|
|
962
|
-
destructiveHint: false,
|
|
963
|
-
idempotentHint: true,
|
|
964
|
-
openWorldHint: true,
|
|
965
|
-
},
|
|
966
|
-
}, async (params) => {
|
|
967
|
-
try {
|
|
968
|
-
const client = await getClient();
|
|
969
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
970
|
-
if (!channel || !('messages' in channel)) {
|
|
971
|
-
return {
|
|
972
|
-
isError: true,
|
|
973
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
const message = await channel.messages.fetch(params.message_id);
|
|
977
|
-
await message.unpin();
|
|
978
|
-
return {
|
|
979
|
-
content: [{ type: "text", text: `Unpinned message ${params.message_id}` }],
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
catch (error) {
|
|
983
|
-
return {
|
|
984
|
-
isError: true,
|
|
985
|
-
content: [{ type: "text", text: `Error unpinning message: ${error.message}` }],
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
});
|
|
989
|
-
server.registerTool("discord_get_pinned_messages", {
|
|
990
|
-
title: "Get Pinned Messages",
|
|
991
|
-
description: `Get all pinned messages in a channel.
|
|
992
|
-
|
|
993
|
-
Args:
|
|
994
|
-
- channel_id (string): Discord channel ID
|
|
995
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
996
|
-
|
|
997
|
-
Returns:
|
|
998
|
-
List of pinned messages`,
|
|
999
|
-
inputSchema: GetPinnedMessagesSchema,
|
|
1000
|
-
annotations: {
|
|
1001
|
-
readOnlyHint: true,
|
|
1002
|
-
destructiveHint: false,
|
|
1003
|
-
idempotentHint: true,
|
|
1004
|
-
openWorldHint: true,
|
|
1005
|
-
},
|
|
1006
|
-
}, async (params) => {
|
|
1007
|
-
try {
|
|
1008
|
-
const client = await getClient();
|
|
1009
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
1010
|
-
if (!channel || !('messages' in channel)) {
|
|
1011
|
-
return {
|
|
1012
|
-
isError: true,
|
|
1013
|
-
content: [{ type: "text", text: `Text channel not found: ${params.channel_id}` }],
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
const messages = await channel.messages.fetchPinned();
|
|
1017
|
-
const formatted = messages.map((m) => formatMessage(m));
|
|
1018
|
-
const text = formatResponse(Array.from(formatted.values()), params.response_format, (items) => items.length > 0
|
|
1019
|
-
? items.map(messageToMarkdown).join("\n\n---\n\n")
|
|
1020
|
-
: "No pinned messages in this channel.");
|
|
1021
|
-
return {
|
|
1022
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
catch (error) {
|
|
1026
|
-
return {
|
|
1027
|
-
isError: true,
|
|
1028
|
-
content: [{ type: "text", text: `Error getting pinned messages: ${error.message}` }],
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1032
|
-
// ============================================================================
|
|
1033
|
-
// MEMBER TOOLS
|
|
1034
|
-
// ============================================================================
|
|
1035
|
-
server.registerTool("discord_list_members", {
|
|
1036
|
-
title: "List Discord Members",
|
|
1037
|
-
description: `List members in a Discord server.
|
|
1038
|
-
|
|
1039
|
-
Args:
|
|
1040
|
-
- guild_id (string): Discord server/guild ID
|
|
1041
|
-
- limit (number): Maximum number of members (1-100, default: 20)
|
|
1042
|
-
- after (string, optional): Get members after this user ID (for pagination)
|
|
1043
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
1044
|
-
|
|
1045
|
-
Returns:
|
|
1046
|
-
List of members with username, nickname, roles, join date`,
|
|
1047
|
-
inputSchema: ListMembersSchema,
|
|
1048
|
-
annotations: {
|
|
1049
|
-
readOnlyHint: true,
|
|
1050
|
-
destructiveHint: false,
|
|
1051
|
-
idempotentHint: true,
|
|
1052
|
-
openWorldHint: true,
|
|
1053
|
-
},
|
|
1054
|
-
}, async (params) => {
|
|
1055
|
-
try {
|
|
1056
|
-
const client = await getClient();
|
|
1057
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1058
|
-
if (!guild) {
|
|
1059
|
-
return {
|
|
1060
|
-
isError: true,
|
|
1061
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
const options = { limit: params.limit };
|
|
1065
|
-
if (params.after)
|
|
1066
|
-
options.after = params.after;
|
|
1067
|
-
// The fetch API returns a Collection when given limit, use 'as unknown as' to handle the union type
|
|
1068
|
-
const membersResult = (await guild.members.fetch(options));
|
|
1069
|
-
// Convert Collection to array
|
|
1070
|
-
const membersArray = Array.from(membersResult.values());
|
|
1071
|
-
const formatted = membersArray.map((m) => formatMember(m));
|
|
1072
|
-
const text = formatResponse(formatted, params.response_format, (items) => items.map(memberToMarkdown).join("\n\n"));
|
|
1073
|
-
return {
|
|
1074
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
catch (error) {
|
|
1078
|
-
return {
|
|
1079
|
-
isError: true,
|
|
1080
|
-
content: [{ type: "text", text: `Error listing members: ${error.message}` }],
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
server.registerTool("discord_get_member", {
|
|
1085
|
-
title: "Get Discord Member",
|
|
1086
|
-
description: `Get detailed information about a specific member.
|
|
1087
|
-
|
|
1088
|
-
Args:
|
|
1089
|
-
- guild_id (string): Discord server/guild ID
|
|
1090
|
-
- user_id (string): Discord user ID
|
|
1091
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
1092
|
-
|
|
1093
|
-
Returns:
|
|
1094
|
-
Member details including username, nickname, roles, join date`,
|
|
1095
|
-
inputSchema: GetMemberSchema,
|
|
1096
|
-
annotations: {
|
|
1097
|
-
readOnlyHint: true,
|
|
1098
|
-
destructiveHint: false,
|
|
1099
|
-
idempotentHint: true,
|
|
1100
|
-
openWorldHint: true,
|
|
1101
|
-
},
|
|
1102
|
-
}, async (params) => {
|
|
1103
|
-
try {
|
|
1104
|
-
const client = await getClient();
|
|
1105
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1106
|
-
if (!guild) {
|
|
1107
|
-
return {
|
|
1108
|
-
isError: true,
|
|
1109
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1113
|
-
const formatted = formatMember(member);
|
|
1114
|
-
const text = formatResponse(formatted, params.response_format, memberToMarkdown);
|
|
1115
|
-
return {
|
|
1116
|
-
content: [{ type: "text", text }],
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
catch (error) {
|
|
1120
|
-
return {
|
|
1121
|
-
isError: true,
|
|
1122
|
-
content: [{ type: "text", text: `Error getting member: ${error.message}` }],
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
});
|
|
1126
|
-
server.registerTool("discord_move_member", {
|
|
1127
|
-
title: "Move Member to Voice Channel",
|
|
1128
|
-
description: `Move a member to a different voice channel or disconnect them from voice.
|
|
1129
|
-
|
|
1130
|
-
The member must currently be connected to a voice channel to be moved.
|
|
1131
|
-
|
|
1132
|
-
Args:
|
|
1133
|
-
- guild_id (string): Discord server/guild ID
|
|
1134
|
-
- user_id (string): Discord user ID to move
|
|
1135
|
-
- channel_id (string | null): Target voice channel ID, or null to disconnect
|
|
1136
|
-
|
|
1137
|
-
Returns:
|
|
1138
|
-
Confirmation of move or disconnect`,
|
|
1139
|
-
inputSchema: MoveMemberSchema,
|
|
1140
|
-
annotations: {
|
|
1141
|
-
readOnlyHint: false,
|
|
1142
|
-
destructiveHint: false,
|
|
1143
|
-
idempotentHint: true,
|
|
1144
|
-
openWorldHint: true,
|
|
1145
|
-
},
|
|
1146
|
-
}, async (params) => {
|
|
1147
|
-
try {
|
|
1148
|
-
const client = await getClient();
|
|
1149
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1150
|
-
if (!guild) {
|
|
1151
|
-
return {
|
|
1152
|
-
isError: true,
|
|
1153
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1157
|
-
if (!member.voice.channel) {
|
|
1158
|
-
return {
|
|
1159
|
-
isError: true,
|
|
1160
|
-
content: [{ type: "text", text: `User ${params.user_id} is not connected to a voice channel` }],
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
const previousChannel = member.voice.channel.name;
|
|
1164
|
-
if (params.channel_id === null) {
|
|
1165
|
-
await member.voice.disconnect();
|
|
1166
|
-
return {
|
|
1167
|
-
content: [{ type: "text", text: `Disconnected user ${member.user.tag} from voice channel "${previousChannel}"` }],
|
|
1168
|
-
};
|
|
1169
|
-
}
|
|
1170
|
-
const targetChannel = guild.channels.cache.get(params.channel_id);
|
|
1171
|
-
if (!targetChannel || !targetChannel.isVoiceBased()) {
|
|
1172
|
-
return {
|
|
1173
|
-
isError: true,
|
|
1174
|
-
content: [{ type: "text", text: `Voice channel not found: ${params.channel_id}` }],
|
|
1175
|
-
};
|
|
1176
|
-
}
|
|
1177
|
-
await member.voice.setChannel(targetChannel);
|
|
1178
|
-
return {
|
|
1179
|
-
content: [{ type: "text", text: `Moved user ${member.user.tag} from "${previousChannel}" to "${targetChannel.name}"` }],
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
catch (error) {
|
|
1183
|
-
return {
|
|
1184
|
-
isError: true,
|
|
1185
|
-
content: [{ type: "text", text: `Error moving member: ${error.message}` }],
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
|
-
});
|
|
1189
|
-
server.registerTool("discord_kick_member", {
|
|
1190
|
-
title: "Kick Discord Member",
|
|
1191
|
-
description: `Kick a member from a Discord server.
|
|
1192
|
-
|
|
1193
|
-
Args:
|
|
1194
|
-
- guild_id (string): Discord server/guild ID
|
|
1195
|
-
- user_id (string): Discord user ID to kick
|
|
1196
|
-
- reason (string, optional): Reason for kick (visible in audit log)
|
|
1197
|
-
|
|
1198
|
-
Returns:
|
|
1199
|
-
Confirmation of kick`,
|
|
1200
|
-
inputSchema: KickMemberSchema,
|
|
1201
|
-
annotations: {
|
|
1202
|
-
readOnlyHint: false,
|
|
1203
|
-
destructiveHint: true,
|
|
1204
|
-
idempotentHint: false,
|
|
1205
|
-
openWorldHint: true,
|
|
1206
|
-
},
|
|
1207
|
-
}, async (params) => {
|
|
1208
|
-
try {
|
|
1209
|
-
const client = await getClient();
|
|
1210
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1211
|
-
if (!guild) {
|
|
1212
|
-
return {
|
|
1213
|
-
isError: true,
|
|
1214
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1218
|
-
await member.kick(params.reason);
|
|
1219
|
-
return {
|
|
1220
|
-
content: [{ type: "text", text: `Kicked user ${params.user_id} from ${guild.name}` }],
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
catch (error) {
|
|
1224
|
-
return {
|
|
1225
|
-
isError: true,
|
|
1226
|
-
content: [{ type: "text", text: `Error kicking member: ${error.message}` }],
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
});
|
|
1230
|
-
server.registerTool("discord_ban_member", {
|
|
1231
|
-
title: "Ban Discord Member",
|
|
1232
|
-
description: `Ban a member from a Discord server.
|
|
1233
|
-
|
|
1234
|
-
Args:
|
|
1235
|
-
- guild_id (string): Discord server/guild ID
|
|
1236
|
-
- user_id (string): Discord user ID to ban
|
|
1237
|
-
- reason (string, optional): Reason for ban (visible in audit log)
|
|
1238
|
-
- delete_message_days (number): Days of messages to delete (0-7, default: 0)
|
|
1239
|
-
|
|
1240
|
-
Returns:
|
|
1241
|
-
Confirmation of ban`,
|
|
1242
|
-
inputSchema: BanMemberSchema,
|
|
1243
|
-
annotations: {
|
|
1244
|
-
readOnlyHint: false,
|
|
1245
|
-
destructiveHint: true,
|
|
1246
|
-
idempotentHint: false,
|
|
1247
|
-
openWorldHint: true,
|
|
1248
|
-
},
|
|
1249
|
-
}, async (params) => {
|
|
1250
|
-
try {
|
|
1251
|
-
const client = await getClient();
|
|
1252
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1253
|
-
if (!guild) {
|
|
1254
|
-
return {
|
|
1255
|
-
isError: true,
|
|
1256
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
await guild.members.ban(params.user_id, {
|
|
1260
|
-
reason: params.reason,
|
|
1261
|
-
deleteMessageSeconds: params.delete_message_days * 86400,
|
|
1262
|
-
});
|
|
1263
|
-
return {
|
|
1264
|
-
content: [{ type: "text", text: `Banned user ${params.user_id} from ${guild.name}` }],
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
catch (error) {
|
|
1268
|
-
return {
|
|
1269
|
-
isError: true,
|
|
1270
|
-
content: [{ type: "text", text: `Error banning member: ${error.message}` }],
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
});
|
|
1274
|
-
server.registerTool("discord_unban_member", {
|
|
1275
|
-
title: "Unban Discord Member",
|
|
1276
|
-
description: `Remove a ban from a user.
|
|
1277
|
-
|
|
1278
|
-
Args:
|
|
1279
|
-
- guild_id (string): Discord server/guild ID
|
|
1280
|
-
- user_id (string): Discord user ID to unban
|
|
1281
|
-
|
|
1282
|
-
Returns:
|
|
1283
|
-
Confirmation of unban`,
|
|
1284
|
-
inputSchema: UnbanMemberSchema,
|
|
1285
|
-
annotations: {
|
|
1286
|
-
readOnlyHint: false,
|
|
1287
|
-
destructiveHint: false,
|
|
1288
|
-
idempotentHint: true,
|
|
1289
|
-
openWorldHint: true,
|
|
1290
|
-
},
|
|
1291
|
-
}, async (params) => {
|
|
1292
|
-
try {
|
|
1293
|
-
const client = await getClient();
|
|
1294
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1295
|
-
if (!guild) {
|
|
1296
|
-
return {
|
|
1297
|
-
isError: true,
|
|
1298
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
await guild.members.unban(params.user_id);
|
|
1302
|
-
return {
|
|
1303
|
-
content: [{ type: "text", text: `Unbanned user ${params.user_id} from ${guild.name}` }],
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
catch (error) {
|
|
1307
|
-
return {
|
|
1308
|
-
isError: true,
|
|
1309
|
-
content: [{ type: "text", text: `Error unbanning member: ${error.message}` }],
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
});
|
|
1313
|
-
server.registerTool("discord_set_nickname", {
|
|
1314
|
-
title: "Set Member Nickname",
|
|
1315
|
-
description: `Set or clear a member's nickname.
|
|
1316
|
-
|
|
1317
|
-
Args:
|
|
1318
|
-
- guild_id (string): Discord server/guild ID
|
|
1319
|
-
- user_id (string): Discord user ID
|
|
1320
|
-
- nickname (string, optional): New nickname (empty/null to reset)
|
|
1321
|
-
|
|
1322
|
-
Returns:
|
|
1323
|
-
Confirmation of nickname change`,
|
|
1324
|
-
inputSchema: SetNicknameSchema,
|
|
1325
|
-
annotations: {
|
|
1326
|
-
readOnlyHint: false,
|
|
1327
|
-
destructiveHint: false,
|
|
1328
|
-
idempotentHint: true,
|
|
1329
|
-
openWorldHint: true,
|
|
1330
|
-
},
|
|
1331
|
-
}, async (params) => {
|
|
1332
|
-
try {
|
|
1333
|
-
const client = await getClient();
|
|
1334
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1335
|
-
if (!guild) {
|
|
1336
|
-
return {
|
|
1337
|
-
isError: true,
|
|
1338
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1339
|
-
};
|
|
1340
|
-
}
|
|
1341
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1342
|
-
await member.setNickname(params.nickname ?? null);
|
|
1343
|
-
const action = params.nickname ? `set to "${params.nickname}"` : "reset";
|
|
1344
|
-
return {
|
|
1345
|
-
content: [{ type: "text", text: `Nickname ${action} for user ${params.user_id}` }],
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
catch (error) {
|
|
1349
|
-
return {
|
|
1350
|
-
isError: true,
|
|
1351
|
-
content: [{ type: "text", text: `Error setting nickname: ${error.message}` }],
|
|
1352
|
-
};
|
|
1353
|
-
}
|
|
1354
|
-
});
|
|
1355
|
-
// ============================================================================
|
|
1356
|
-
// ROLE TOOLS
|
|
1357
|
-
// ============================================================================
|
|
1358
|
-
server.registerTool("discord_list_roles", {
|
|
1359
|
-
title: "List Discord Roles",
|
|
1360
|
-
description: `List all roles in a Discord server.
|
|
1361
|
-
|
|
1362
|
-
Args:
|
|
1363
|
-
- guild_id (string): Discord server/guild ID
|
|
1364
|
-
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
1365
|
-
|
|
1366
|
-
Returns:
|
|
1367
|
-
List of roles with name, color, position, permissions`,
|
|
1368
|
-
inputSchema: ListRolesSchema,
|
|
1369
|
-
annotations: {
|
|
1370
|
-
readOnlyHint: true,
|
|
1371
|
-
destructiveHint: false,
|
|
1372
|
-
idempotentHint: true,
|
|
1373
|
-
openWorldHint: true,
|
|
1374
|
-
},
|
|
1375
|
-
}, async (params) => {
|
|
1376
|
-
try {
|
|
1377
|
-
const client = await getClient();
|
|
1378
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1379
|
-
if (!guild) {
|
|
1380
|
-
return {
|
|
1381
|
-
isError: true,
|
|
1382
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1383
|
-
};
|
|
1384
|
-
}
|
|
1385
|
-
const roles = guild.roles.cache
|
|
1386
|
-
.filter(r => r.name !== "@everyone")
|
|
1387
|
-
.sort((a, b) => b.position - a.position)
|
|
1388
|
-
.map(formatRole);
|
|
1389
|
-
const text = formatResponse(Array.from(roles.values()), params.response_format, (items) => items.map(roleToMarkdown).join("\n\n"));
|
|
1390
|
-
return {
|
|
1391
|
-
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
catch (error) {
|
|
1395
|
-
return {
|
|
1396
|
-
isError: true,
|
|
1397
|
-
content: [{ type: "text", text: `Error listing roles: ${error.message}` }],
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
});
|
|
1401
|
-
server.registerTool("discord_add_role", {
|
|
1402
|
-
title: "Add Role to Member",
|
|
1403
|
-
description: `Add a role to a Discord member.
|
|
1404
|
-
|
|
1405
|
-
Args:
|
|
1406
|
-
- guild_id (string): Discord server/guild ID
|
|
1407
|
-
- user_id (string): Discord user ID
|
|
1408
|
-
- role_id (string): Discord role ID to add
|
|
1409
|
-
|
|
1410
|
-
Returns:
|
|
1411
|
-
Confirmation of role added`,
|
|
1412
|
-
inputSchema: AddRoleSchema,
|
|
1413
|
-
annotations: {
|
|
1414
|
-
readOnlyHint: false,
|
|
1415
|
-
destructiveHint: false,
|
|
1416
|
-
idempotentHint: true,
|
|
1417
|
-
openWorldHint: true,
|
|
1418
|
-
},
|
|
1419
|
-
}, async (params) => {
|
|
1420
|
-
try {
|
|
1421
|
-
const client = await getClient();
|
|
1422
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1423
|
-
if (!guild) {
|
|
1424
|
-
return {
|
|
1425
|
-
isError: true,
|
|
1426
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1430
|
-
const role = guild.roles.cache.get(params.role_id);
|
|
1431
|
-
if (!role) {
|
|
1432
|
-
return {
|
|
1433
|
-
isError: true,
|
|
1434
|
-
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
await member.roles.add(role);
|
|
1438
|
-
return {
|
|
1439
|
-
content: [{ type: "text", text: `Added role "${role.name}" to user ${params.user_id}` }],
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
catch (error) {
|
|
1443
|
-
return {
|
|
1444
|
-
isError: true,
|
|
1445
|
-
content: [{ type: "text", text: `Error adding role: ${error.message}` }],
|
|
1446
|
-
};
|
|
1447
|
-
}
|
|
1448
|
-
});
|
|
1449
|
-
server.registerTool("discord_remove_role", {
|
|
1450
|
-
title: "Remove Role from Member",
|
|
1451
|
-
description: `Remove a role from a Discord member.
|
|
1452
|
-
|
|
1453
|
-
Args:
|
|
1454
|
-
- guild_id (string): Discord server/guild ID
|
|
1455
|
-
- user_id (string): Discord user ID
|
|
1456
|
-
- role_id (string): Discord role ID to remove
|
|
1457
|
-
|
|
1458
|
-
Returns:
|
|
1459
|
-
Confirmation of role removed`,
|
|
1460
|
-
inputSchema: RemoveRoleSchema,
|
|
1461
|
-
annotations: {
|
|
1462
|
-
readOnlyHint: false,
|
|
1463
|
-
destructiveHint: false,
|
|
1464
|
-
idempotentHint: true,
|
|
1465
|
-
openWorldHint: true,
|
|
1466
|
-
},
|
|
1467
|
-
}, async (params) => {
|
|
1468
|
-
try {
|
|
1469
|
-
const client = await getClient();
|
|
1470
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1471
|
-
if (!guild) {
|
|
1472
|
-
return {
|
|
1473
|
-
isError: true,
|
|
1474
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1475
|
-
};
|
|
1476
|
-
}
|
|
1477
|
-
const member = await guild.members.fetch(params.user_id);
|
|
1478
|
-
const role = guild.roles.cache.get(params.role_id);
|
|
1479
|
-
if (!role) {
|
|
1480
|
-
return {
|
|
1481
|
-
isError: true,
|
|
1482
|
-
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
await member.roles.remove(role);
|
|
1486
|
-
return {
|
|
1487
|
-
content: [{ type: "text", text: `Removed role "${role.name}" from user ${params.user_id}` }],
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
catch (error) {
|
|
1491
|
-
return {
|
|
1492
|
-
isError: true,
|
|
1493
|
-
content: [{ type: "text", text: `Error removing role: ${error.message}` }],
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
});
|
|
1497
|
-
server.registerTool("discord_create_role", {
|
|
1498
|
-
title: "Create Discord Role",
|
|
1499
|
-
description: `Create a new role in a Discord server.
|
|
1500
|
-
|
|
1501
|
-
Args:
|
|
1502
|
-
- guild_id (string): Discord server/guild ID
|
|
1503
|
-
- name (string): Role name
|
|
1504
|
-
- color (number, optional): Role color as decimal integer (e.g., 16711680 for red)
|
|
1505
|
-
- mentionable (boolean): Whether the role can be mentioned (default: false)
|
|
1506
|
-
- hoist (boolean): Whether to display role members separately (default: false)
|
|
1507
|
-
|
|
1508
|
-
Returns:
|
|
1509
|
-
The created role's details`,
|
|
1510
|
-
inputSchema: CreateRoleSchema,
|
|
1511
|
-
annotations: {
|
|
1512
|
-
readOnlyHint: false,
|
|
1513
|
-
destructiveHint: false,
|
|
1514
|
-
idempotentHint: false,
|
|
1515
|
-
openWorldHint: true,
|
|
1516
|
-
},
|
|
1517
|
-
}, async (params) => {
|
|
1518
|
-
try {
|
|
1519
|
-
const client = await getClient();
|
|
1520
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1521
|
-
if (!guild) {
|
|
1522
|
-
return {
|
|
1523
|
-
isError: true,
|
|
1524
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1525
|
-
};
|
|
1526
|
-
}
|
|
1527
|
-
const role = await guild.roles.create({
|
|
1528
|
-
name: params.name,
|
|
1529
|
-
color: params.color,
|
|
1530
|
-
mentionable: params.mentionable,
|
|
1531
|
-
hoist: params.hoist,
|
|
1532
|
-
});
|
|
1533
|
-
return {
|
|
1534
|
-
content: [{ type: "text", text: `Created role "${role.name}" (ID: ${role.id})` }],
|
|
1535
|
-
};
|
|
1536
|
-
}
|
|
1537
|
-
catch (error) {
|
|
1538
|
-
return {
|
|
1539
|
-
isError: true,
|
|
1540
|
-
content: [{ type: "text", text: `Error creating role: ${error.message}` }],
|
|
1541
|
-
};
|
|
1542
|
-
}
|
|
1543
|
-
});
|
|
1544
|
-
server.registerTool("discord_delete_role", {
|
|
1545
|
-
title: "Delete Discord Role",
|
|
1546
|
-
description: `Delete a role from a Discord server. This action is permanent!
|
|
1547
|
-
|
|
1548
|
-
Args:
|
|
1549
|
-
- guild_id (string): Discord server/guild ID
|
|
1550
|
-
- role_id (string): Discord role ID to delete
|
|
1551
|
-
|
|
1552
|
-
Returns:
|
|
1553
|
-
Confirmation of deletion`,
|
|
1554
|
-
inputSchema: DeleteRoleSchema,
|
|
1555
|
-
annotations: {
|
|
1556
|
-
readOnlyHint: false,
|
|
1557
|
-
destructiveHint: true,
|
|
1558
|
-
idempotentHint: false,
|
|
1559
|
-
openWorldHint: true,
|
|
1560
|
-
},
|
|
1561
|
-
}, async (params) => {
|
|
1562
|
-
try {
|
|
1563
|
-
const client = await getClient();
|
|
1564
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1565
|
-
if (!guild) {
|
|
1566
|
-
return {
|
|
1567
|
-
isError: true,
|
|
1568
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1569
|
-
};
|
|
1570
|
-
}
|
|
1571
|
-
const role = guild.roles.cache.get(params.role_id);
|
|
1572
|
-
if (!role) {
|
|
1573
|
-
return {
|
|
1574
|
-
isError: true,
|
|
1575
|
-
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
1576
|
-
};
|
|
1577
|
-
}
|
|
1578
|
-
const name = role.name;
|
|
1579
|
-
await role.delete();
|
|
1580
|
-
return {
|
|
1581
|
-
content: [{ type: "text", text: `Deleted role: ${name}` }],
|
|
1582
|
-
};
|
|
1583
|
-
}
|
|
1584
|
-
catch (error) {
|
|
1585
|
-
return {
|
|
1586
|
-
isError: true,
|
|
1587
|
-
content: [{ type: "text", text: `Error deleting role: ${error.message}` }],
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
});
|
|
1591
|
-
// Full list of role permissions for reference
|
|
1592
|
-
const ROLE_PERMISSION_LIST = [
|
|
1593
|
-
// General Server Permissions
|
|
1594
|
-
'Administrator', 'ViewAuditLog', 'ViewGuildInsights', 'ManageGuild', 'ManageRoles',
|
|
1595
|
-
'ManageChannels', 'KickMembers', 'BanMembers', 'CreateInstantInvite', 'ChangeNickname',
|
|
1596
|
-
'ManageNicknames', 'ManageEmojisAndStickers', 'ManageWebhooks', 'ManageGuildExpressions',
|
|
1597
|
-
'ViewCreatorMonetizationAnalytics', 'ModerateMembers',
|
|
1598
|
-
// Text Channel Permissions
|
|
1599
|
-
'ViewChannel', 'SendMessages', 'SendTTSMessages', 'ManageMessages', 'EmbedLinks',
|
|
1600
|
-
'AttachFiles', 'ReadMessageHistory', 'MentionEveryone', 'UseExternalEmojis',
|
|
1601
|
-
'AddReactions', 'UseApplicationCommands', 'ManageThreads', 'CreatePublicThreads',
|
|
1602
|
-
'CreatePrivateThreads', 'UseExternalStickers', 'SendMessagesInThreads', 'SendVoiceMessages',
|
|
1603
|
-
'SendPolls', 'UseExternalApps',
|
|
1604
|
-
// Voice Channel Permissions
|
|
1605
|
-
'Connect', 'Speak', 'MuteMembers', 'DeafenMembers', 'MoveMembers', 'UseVAD',
|
|
1606
|
-
'PrioritySpeaker', 'Stream', 'UseSoundboard', 'UseExternalSounds', 'UseEmbeddedActivities',
|
|
1607
|
-
// Stage Channel Permissions
|
|
1608
|
-
'RequestToSpeak',
|
|
1609
|
-
// Events Permissions
|
|
1610
|
-
'ManageEvents', 'CreateEvents'
|
|
1611
|
-
];
|
|
1612
|
-
server.registerTool("discord_edit_role", {
|
|
1613
|
-
title: "Edit Discord Role",
|
|
1614
|
-
description: `Edit a role's properties including name, color, and permissions.
|
|
1615
|
-
|
|
1616
|
-
Common permission names:
|
|
1617
|
-
- Admin: Administrator (grants all permissions)
|
|
1618
|
-
- General: ManageGuild, ManageRoles, ManageChannels, KickMembers, BanMembers, ModerateMembers
|
|
1619
|
-
- Messages: SendMessages, ManageMessages, EmbedLinks, AttachFiles, ReadMessageHistory, MentionEveryone
|
|
1620
|
-
- Voice: Connect, Speak, MuteMembers, DeafenMembers, MoveMembers, Stream
|
|
1621
|
-
- Other: ManageEmojisAndStickers, ManageWebhooks, ManageEvents, ViewAuditLog
|
|
1622
|
-
|
|
1623
|
-
Args:
|
|
1624
|
-
- guild_id (string): Discord server/guild ID
|
|
1625
|
-
- role_id (string): Discord role ID to edit
|
|
1626
|
-
- name (string, optional): New role name
|
|
1627
|
-
- color (number, optional): Role color as decimal integer (e.g., 16711680 for red)
|
|
1628
|
-
- mentionable (boolean, optional): Whether the role can be mentioned
|
|
1629
|
-
- hoist (boolean, optional): Whether to display role members separately
|
|
1630
|
-
- permissions (string[], optional): Permissions to grant (replaces existing). Examples: ['SendMessages', 'ViewChannel']
|
|
1631
|
-
|
|
1632
|
-
Returns:
|
|
1633
|
-
Updated role details`,
|
|
1634
|
-
inputSchema: EditRoleSchema,
|
|
1635
|
-
annotations: {
|
|
1636
|
-
readOnlyHint: false,
|
|
1637
|
-
destructiveHint: false,
|
|
1638
|
-
idempotentHint: true,
|
|
1639
|
-
openWorldHint: true,
|
|
1640
|
-
},
|
|
1641
|
-
}, async (params) => {
|
|
1642
|
-
try {
|
|
1643
|
-
const client = await getClient();
|
|
1644
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1645
|
-
if (!guild) {
|
|
1646
|
-
return {
|
|
1647
|
-
isError: true,
|
|
1648
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
const role = guild.roles.cache.get(params.role_id);
|
|
1652
|
-
if (!role) {
|
|
1653
|
-
return {
|
|
1654
|
-
isError: true,
|
|
1655
|
-
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
1656
|
-
};
|
|
1657
|
-
}
|
|
1658
|
-
// Build the edit options
|
|
1659
|
-
const editOptions = {};
|
|
1660
|
-
if (params.name !== undefined)
|
|
1661
|
-
editOptions.name = params.name;
|
|
1662
|
-
if (params.color !== undefined)
|
|
1663
|
-
editOptions.color = params.color;
|
|
1664
|
-
if (params.mentionable !== undefined)
|
|
1665
|
-
editOptions.mentionable = params.mentionable;
|
|
1666
|
-
if (params.hoist !== undefined)
|
|
1667
|
-
editOptions.hoist = params.hoist;
|
|
1668
|
-
// Handle permissions if provided
|
|
1669
|
-
if (params.permissions !== undefined) {
|
|
1670
|
-
const permissionBits = new PermissionsBitField();
|
|
1671
|
-
for (const perm of params.permissions) {
|
|
1672
|
-
try {
|
|
1673
|
-
permissionBits.add(perm);
|
|
1674
|
-
}
|
|
1675
|
-
catch {
|
|
1676
|
-
return {
|
|
1677
|
-
isError: true,
|
|
1678
|
-
content: [{ type: "text", text: `Invalid permission: ${perm}. Valid permissions include: ${ROLE_PERMISSION_LIST.slice(0, 10).join(', ')}, ...` }],
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
editOptions.permissions = permissionBits.bitfield;
|
|
1683
|
-
}
|
|
1684
|
-
const updatedRole = await role.edit(editOptions);
|
|
1685
|
-
const changes = [];
|
|
1686
|
-
if (params.name !== undefined)
|
|
1687
|
-
changes.push(`name: "${updatedRole.name}"`);
|
|
1688
|
-
if (params.color !== undefined)
|
|
1689
|
-
changes.push(`color: ${updatedRole.hexColor}`);
|
|
1690
|
-
if (params.mentionable !== undefined)
|
|
1691
|
-
changes.push(`mentionable: ${updatedRole.mentionable}`);
|
|
1692
|
-
if (params.hoist !== undefined)
|
|
1693
|
-
changes.push(`hoist: ${updatedRole.hoist}`);
|
|
1694
|
-
if (params.permissions !== undefined)
|
|
1695
|
-
changes.push(`permissions updated`);
|
|
1696
|
-
return {
|
|
1697
|
-
content: [{ type: "text", text: `Updated role "${updatedRole.name}" (ID: ${updatedRole.id})\nChanges: ${changes.join(', ')}` }],
|
|
1698
|
-
};
|
|
1699
|
-
}
|
|
1700
|
-
catch (error) {
|
|
1701
|
-
return {
|
|
1702
|
-
isError: true,
|
|
1703
|
-
content: [{ type: "text", text: `Error editing role: ${error.message}` }],
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
});
|
|
1707
|
-
server.registerTool("discord_set_role_positions", {
|
|
1708
|
-
title: "Set Role Positions",
|
|
1709
|
-
description: `Reorder role hierarchy by setting role positions.
|
|
1710
|
-
|
|
1711
|
-
Higher position = more authority in the hierarchy. The @everyone role is always at position 0.
|
|
1712
|
-
Roles can only manage other roles below them in the hierarchy.
|
|
1713
|
-
|
|
1714
|
-
IMPORTANT: The bot can only move roles that are below its own highest role in the hierarchy.
|
|
1715
|
-
|
|
1716
|
-
Args:
|
|
1717
|
-
- guild_id (string): Discord server/guild ID
|
|
1718
|
-
- positions (array): Array of objects with:
|
|
1719
|
-
- role_id (string): Discord role ID
|
|
1720
|
-
- position (number): New position for the role (higher = more authority)
|
|
1721
|
-
|
|
1722
|
-
Returns:
|
|
1723
|
-
Confirmation of position changes`,
|
|
1724
|
-
inputSchema: SetRolePositionsSchema,
|
|
1725
|
-
annotations: {
|
|
1726
|
-
readOnlyHint: false,
|
|
1727
|
-
destructiveHint: false,
|
|
1728
|
-
idempotentHint: true,
|
|
1729
|
-
openWorldHint: true,
|
|
1730
|
-
},
|
|
1731
|
-
}, async (params) => {
|
|
1732
|
-
try {
|
|
1733
|
-
const client = await getClient();
|
|
1734
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1735
|
-
if (!guild) {
|
|
1736
|
-
return {
|
|
1737
|
-
isError: true,
|
|
1738
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1739
|
-
};
|
|
1740
|
-
}
|
|
1741
|
-
// Validate all roles exist before making changes
|
|
1742
|
-
for (const pos of params.positions) {
|
|
1743
|
-
const role = guild.roles.cache.get(pos.role_id);
|
|
1744
|
-
if (!role) {
|
|
1745
|
-
return {
|
|
1746
|
-
isError: true,
|
|
1747
|
-
content: [{ type: "text", text: `Role not found: ${pos.role_id}` }],
|
|
1748
|
-
};
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
// Format positions for Discord API
|
|
1752
|
-
const positionUpdates = params.positions.map(pos => ({
|
|
1753
|
-
role: pos.role_id,
|
|
1754
|
-
position: pos.position,
|
|
1755
|
-
}));
|
|
1756
|
-
await guild.roles.setPositions(positionUpdates);
|
|
1757
|
-
// Build response with updated positions
|
|
1758
|
-
const updatedRoles = params.positions.map(pos => {
|
|
1759
|
-
const role = guild.roles.cache.get(pos.role_id);
|
|
1760
|
-
return `"${role?.name}" → position ${pos.position}`;
|
|
1761
|
-
});
|
|
1762
|
-
return {
|
|
1763
|
-
content: [{ type: "text", text: `Updated role positions:\n${updatedRoles.join('\n')}` }],
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
catch (error) {
|
|
1767
|
-
return {
|
|
1768
|
-
isError: true,
|
|
1769
|
-
content: [{ type: "text", text: `Error setting role positions: ${error.message}` }],
|
|
1770
|
-
};
|
|
1771
|
-
}
|
|
1772
|
-
});
|
|
1773
|
-
// ============================================================================
|
|
1774
|
-
// GUILD MANAGEMENT TOOLS
|
|
1775
|
-
// ============================================================================
|
|
1776
|
-
server.registerTool("discord_edit_guild", {
|
|
1777
|
-
title: "Edit Guild Settings",
|
|
1778
|
-
description: `Edit server settings like name, verification level, and system channels.
|
|
1779
|
-
|
|
1780
|
-
Verification Levels: 0=None, 1=Low (verified email), 2=Medium (5 min member), 3=High (10 min member), 4=Very High (verified phone)
|
|
1781
|
-
Content Filter: 0=Disabled, 1=Members without roles, 2=All members
|
|
1782
|
-
Notifications: 0=All messages, 1=Only mentions
|
|
1783
|
-
|
|
1784
|
-
Args:
|
|
1785
|
-
- guild_id (string): Discord server/guild ID
|
|
1786
|
-
- name (string, optional): New server name
|
|
1787
|
-
- description (string, optional): Server description (Community servers only)
|
|
1788
|
-
- afk_channel_id (string, optional): AFK voice channel ID
|
|
1789
|
-
- afk_timeout (number, optional): AFK timeout in seconds (60, 300, 900, 1800, 3600)
|
|
1790
|
-
- system_channel_id (string, optional): System message channel ID
|
|
1791
|
-
- rules_channel_id (string, optional): Rules channel ID (Community only)
|
|
1792
|
-
- public_updates_channel_id (string, optional): Public updates channel ID (Community only)
|
|
1793
|
-
- verification_level (number, optional): 0-4
|
|
1794
|
-
- explicit_content_filter (number, optional): 0-2
|
|
1795
|
-
- default_message_notifications (number, optional): 0-1
|
|
1796
|
-
|
|
1797
|
-
Returns:
|
|
1798
|
-
Updated guild settings`,
|
|
1799
|
-
inputSchema: EditGuildSchema,
|
|
1800
|
-
annotations: {
|
|
1801
|
-
readOnlyHint: false,
|
|
1802
|
-
destructiveHint: false,
|
|
1803
|
-
idempotentHint: true,
|
|
1804
|
-
openWorldHint: true,
|
|
1805
|
-
},
|
|
1806
|
-
}, async (params) => {
|
|
1807
|
-
try {
|
|
1808
|
-
const client = await getClient();
|
|
1809
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1810
|
-
if (!guild) {
|
|
1811
|
-
return {
|
|
1812
|
-
isError: true,
|
|
1813
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
const editOptions = {};
|
|
1817
|
-
if (params.name !== undefined)
|
|
1818
|
-
editOptions.name = params.name;
|
|
1819
|
-
if (params.description !== undefined)
|
|
1820
|
-
editOptions.description = params.description;
|
|
1821
|
-
if (params.afk_channel_id !== undefined)
|
|
1822
|
-
editOptions.afkChannel = params.afk_channel_id;
|
|
1823
|
-
if (params.afk_timeout !== undefined)
|
|
1824
|
-
editOptions.afkTimeout = params.afk_timeout;
|
|
1825
|
-
if (params.system_channel_id !== undefined)
|
|
1826
|
-
editOptions.systemChannel = params.system_channel_id;
|
|
1827
|
-
if (params.rules_channel_id !== undefined)
|
|
1828
|
-
editOptions.rulesChannel = params.rules_channel_id;
|
|
1829
|
-
if (params.public_updates_channel_id !== undefined)
|
|
1830
|
-
editOptions.publicUpdatesChannel = params.public_updates_channel_id;
|
|
1831
|
-
if (params.verification_level !== undefined)
|
|
1832
|
-
editOptions.verificationLevel = params.verification_level;
|
|
1833
|
-
if (params.explicit_content_filter !== undefined)
|
|
1834
|
-
editOptions.explicitContentFilter = params.explicit_content_filter;
|
|
1835
|
-
if (params.default_message_notifications !== undefined)
|
|
1836
|
-
editOptions.defaultMessageNotifications = params.default_message_notifications;
|
|
1837
|
-
await guild.edit(editOptions);
|
|
1838
|
-
return {
|
|
1839
|
-
content: [{ type: "text", text: `Updated guild settings for "${guild.name}"` }],
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
catch (error) {
|
|
1843
|
-
return {
|
|
1844
|
-
isError: true,
|
|
1845
|
-
content: [{ type: "text", text: `Error editing guild: ${error.message}` }],
|
|
1846
|
-
};
|
|
1847
|
-
}
|
|
1848
|
-
});
|
|
1849
|
-
// ============================================================================
|
|
1850
|
-
// MEMBER MODERATION TOOLS
|
|
1851
|
-
// ============================================================================
|
|
1852
|
-
server.registerTool("discord_timeout_member", {
|
|
1853
|
-
title: "Timeout Member",
|
|
1854
|
-
description: `Timeout (mute) a member for a specified duration. They cannot send messages, react, or join voice.
|
|
1855
|
-
|
|
1856
|
-
Args:
|
|
1857
|
-
- guild_id (string): Discord server/guild ID
|
|
1858
|
-
- user_id (string): Discord user ID to timeout
|
|
1859
|
-
- duration_minutes (number): Timeout duration in minutes (0 to remove, max 40320 = 28 days)
|
|
1860
|
-
- reason (string, optional): Reason for timeout (visible in audit log)
|
|
1861
|
-
|
|
1862
|
-
Returns:
|
|
1863
|
-
Confirmation of timeout`,
|
|
1864
|
-
inputSchema: TimeoutMemberSchema,
|
|
1865
|
-
annotations: {
|
|
1866
|
-
readOnlyHint: false,
|
|
1867
|
-
destructiveHint: false,
|
|
1868
|
-
idempotentHint: true,
|
|
1869
|
-
openWorldHint: true,
|
|
1870
|
-
},
|
|
1871
|
-
}, async (params) => {
|
|
1872
|
-
try {
|
|
1873
|
-
const client = await getClient();
|
|
1874
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1875
|
-
if (!guild) {
|
|
1876
|
-
return {
|
|
1877
|
-
isError: true,
|
|
1878
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
const member = await guild.members.fetch(params.user_id).catch(() => null);
|
|
1882
|
-
if (!member) {
|
|
1883
|
-
return {
|
|
1884
|
-
isError: true,
|
|
1885
|
-
content: [{ type: "text", text: `Member not found: ${params.user_id}` }],
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
if (params.duration_minutes === 0) {
|
|
1889
|
-
await member.timeout(null, params.reason);
|
|
1890
|
-
return {
|
|
1891
|
-
content: [{ type: "text", text: `Removed timeout from ${member.user.username}` }],
|
|
1892
|
-
};
|
|
1893
|
-
}
|
|
1894
|
-
const timeoutMs = params.duration_minutes * 60 * 1000;
|
|
1895
|
-
await member.timeout(timeoutMs, params.reason);
|
|
1896
|
-
return {
|
|
1897
|
-
content: [{ type: "text", text: `Timed out ${member.user.username} for ${params.duration_minutes} minutes` }],
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
catch (error) {
|
|
1901
|
-
return {
|
|
1902
|
-
isError: true,
|
|
1903
|
-
content: [{ type: "text", text: `Error timing out member: ${error.message}` }],
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
});
|
|
1907
|
-
server.registerTool("discord_list_bans", {
|
|
1908
|
-
title: "List Bans",
|
|
1909
|
-
description: `List all banned users in a server.
|
|
1910
|
-
|
|
1911
|
-
Args:
|
|
1912
|
-
- guild_id (string): Discord server/guild ID
|
|
1913
|
-
- limit (number): Number of bans to return (1-1000, default 100)
|
|
1914
|
-
- response_format ('json' | 'markdown'): Output format
|
|
1915
|
-
|
|
1916
|
-
Returns:
|
|
1917
|
-
List of banned users with reasons`,
|
|
1918
|
-
inputSchema: ListBansSchema,
|
|
1919
|
-
annotations: {
|
|
1920
|
-
readOnlyHint: true,
|
|
1921
|
-
destructiveHint: false,
|
|
1922
|
-
idempotentHint: true,
|
|
1923
|
-
openWorldHint: true,
|
|
1924
|
-
},
|
|
1925
|
-
}, async (params) => {
|
|
1926
|
-
try {
|
|
1927
|
-
const client = await getClient();
|
|
1928
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1929
|
-
if (!guild) {
|
|
1930
|
-
return {
|
|
1931
|
-
isError: true,
|
|
1932
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1933
|
-
};
|
|
1934
|
-
}
|
|
1935
|
-
const bans = await guild.bans.fetch({ limit: params.limit });
|
|
1936
|
-
const banList = bans.map(ban => ({
|
|
1937
|
-
userId: ban.user.id,
|
|
1938
|
-
username: ban.user.username,
|
|
1939
|
-
reason: ban.reason || 'No reason provided',
|
|
1940
|
-
}));
|
|
1941
|
-
const result = formatResponse(banList, params.response_format, (items) => items.map(b => `**${b.username}** (${b.userId})\nReason: ${b.reason}`).join('\n\n'));
|
|
1942
|
-
return {
|
|
1943
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
1944
|
-
};
|
|
1945
|
-
}
|
|
1946
|
-
catch (error) {
|
|
1947
|
-
return {
|
|
1948
|
-
isError: true,
|
|
1949
|
-
content: [{ type: "text", text: `Error listing bans: ${error.message}` }],
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
1953
|
-
// ============================================================================
|
|
1954
|
-
// EMOJI TOOLS
|
|
1955
|
-
// ============================================================================
|
|
1956
|
-
server.registerTool("discord_list_emojis", {
|
|
1957
|
-
title: "List Emojis",
|
|
1958
|
-
description: `List all custom emojis in a server.
|
|
1959
|
-
|
|
1960
|
-
Args:
|
|
1961
|
-
- guild_id (string): Discord server/guild ID
|
|
1962
|
-
- response_format ('json' | 'markdown'): Output format
|
|
1963
|
-
|
|
1964
|
-
Returns:
|
|
1965
|
-
List of emojis with name, ID, and whether animated`,
|
|
1966
|
-
inputSchema: ListEmojisSchema,
|
|
1967
|
-
annotations: {
|
|
1968
|
-
readOnlyHint: true,
|
|
1969
|
-
destructiveHint: false,
|
|
1970
|
-
idempotentHint: true,
|
|
1971
|
-
openWorldHint: true,
|
|
1972
|
-
},
|
|
1973
|
-
}, async (params) => {
|
|
1974
|
-
try {
|
|
1975
|
-
const client = await getClient();
|
|
1976
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
1977
|
-
if (!guild) {
|
|
1978
|
-
return {
|
|
1979
|
-
isError: true,
|
|
1980
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
1981
|
-
};
|
|
1982
|
-
}
|
|
1983
|
-
const emojis = guild.emojis.cache.map(emoji => ({
|
|
1984
|
-
id: emoji.id,
|
|
1985
|
-
name: emoji.name,
|
|
1986
|
-
animated: emoji.animated,
|
|
1987
|
-
available: emoji.available,
|
|
1988
|
-
managed: emoji.managed,
|
|
1989
|
-
requireColons: emoji.requiresColons,
|
|
1990
|
-
roles: emoji.roles.cache.map(r => r.name),
|
|
1991
|
-
url: emoji.url,
|
|
1992
|
-
}));
|
|
1993
|
-
const result = formatResponse(emojis, params.response_format, (items) => items.map(e => `${e.animated ? '(animated) ' : ''}**${e.name}** - ID: ${e.id}`).join('\n'));
|
|
1994
|
-
return {
|
|
1995
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
1998
|
-
catch (error) {
|
|
1999
|
-
return {
|
|
2000
|
-
isError: true,
|
|
2001
|
-
content: [{ type: "text", text: `Error listing emojis: ${error.message}` }],
|
|
2002
|
-
};
|
|
2003
|
-
}
|
|
2004
|
-
});
|
|
2005
|
-
server.registerTool("discord_create_emoji", {
|
|
2006
|
-
title: "Create Emoji",
|
|
2007
|
-
description: `Create a custom emoji in a server.
|
|
2008
|
-
|
|
2009
|
-
Args:
|
|
2010
|
-
- guild_id (string): Discord server/guild ID
|
|
2011
|
-
- name (string): Emoji name (2-32 chars, alphanumeric and underscores)
|
|
2012
|
-
- image_url (string): URL of image (PNG, JPG, GIF under 256KB)
|
|
2013
|
-
- roles (string[], optional): Role IDs that can use this emoji
|
|
2014
|
-
|
|
2015
|
-
Returns:
|
|
2016
|
-
Created emoji details`,
|
|
2017
|
-
inputSchema: CreateEmojiSchema,
|
|
2018
|
-
annotations: {
|
|
2019
|
-
readOnlyHint: false,
|
|
2020
|
-
destructiveHint: false,
|
|
2021
|
-
idempotentHint: false,
|
|
2022
|
-
openWorldHint: true,
|
|
2023
|
-
},
|
|
2024
|
-
}, async (params) => {
|
|
2025
|
-
try {
|
|
2026
|
-
const client = await getClient();
|
|
2027
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2028
|
-
if (!guild) {
|
|
2029
|
-
return {
|
|
2030
|
-
isError: true,
|
|
2031
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
const emoji = await guild.emojis.create({
|
|
2035
|
-
attachment: params.image_url,
|
|
2036
|
-
name: params.name,
|
|
2037
|
-
roles: params.roles,
|
|
2038
|
-
});
|
|
2039
|
-
return {
|
|
2040
|
-
content: [{ type: "text", text: `Created emoji :${emoji.name}: (ID: ${emoji.id})` }],
|
|
2041
|
-
};
|
|
2042
|
-
}
|
|
2043
|
-
catch (error) {
|
|
2044
|
-
return {
|
|
2045
|
-
isError: true,
|
|
2046
|
-
content: [{ type: "text", text: `Error creating emoji: ${error.message}` }],
|
|
2047
|
-
};
|
|
2048
|
-
}
|
|
2049
|
-
});
|
|
2050
|
-
server.registerTool("discord_delete_emoji", {
|
|
2051
|
-
title: "Delete Emoji",
|
|
2052
|
-
description: `Delete a custom emoji from a server.
|
|
2053
|
-
|
|
2054
|
-
Args:
|
|
2055
|
-
- guild_id (string): Discord server/guild ID
|
|
2056
|
-
- emoji_id (string): Discord emoji ID
|
|
2057
|
-
|
|
2058
|
-
Returns:
|
|
2059
|
-
Confirmation of deletion`,
|
|
2060
|
-
inputSchema: DeleteEmojiSchema,
|
|
2061
|
-
annotations: {
|
|
2062
|
-
readOnlyHint: false,
|
|
2063
|
-
destructiveHint: true,
|
|
2064
|
-
idempotentHint: true,
|
|
2065
|
-
openWorldHint: true,
|
|
2066
|
-
},
|
|
2067
|
-
}, async (params) => {
|
|
2068
|
-
try {
|
|
2069
|
-
const client = await getClient();
|
|
2070
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2071
|
-
if (!guild) {
|
|
2072
|
-
return {
|
|
2073
|
-
isError: true,
|
|
2074
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
const emoji = guild.emojis.cache.get(params.emoji_id);
|
|
2078
|
-
if (!emoji) {
|
|
2079
|
-
return {
|
|
2080
|
-
isError: true,
|
|
2081
|
-
content: [{ type: "text", text: `Emoji not found: ${params.emoji_id}` }],
|
|
2082
|
-
};
|
|
2083
|
-
}
|
|
2084
|
-
const name = emoji.name;
|
|
2085
|
-
await emoji.delete();
|
|
2086
|
-
return {
|
|
2087
|
-
content: [{ type: "text", text: `Deleted emoji :${name}:` }],
|
|
2088
|
-
};
|
|
2089
|
-
}
|
|
2090
|
-
catch (error) {
|
|
2091
|
-
return {
|
|
2092
|
-
isError: true,
|
|
2093
|
-
content: [{ type: "text", text: `Error deleting emoji: ${error.message}` }],
|
|
2094
|
-
};
|
|
2095
|
-
}
|
|
2096
|
-
});
|
|
2097
|
-
// ============================================================================
|
|
2098
|
-
// INVITE TOOLS
|
|
2099
|
-
// ============================================================================
|
|
2100
|
-
server.registerTool("discord_list_invites", {
|
|
2101
|
-
title: "List Invites",
|
|
2102
|
-
description: `List all active invites in a server.
|
|
2103
|
-
|
|
2104
|
-
Args:
|
|
2105
|
-
- guild_id (string): Discord server/guild ID
|
|
2106
|
-
- response_format ('json' | 'markdown'): Output format
|
|
2107
|
-
|
|
2108
|
-
Returns:
|
|
2109
|
-
List of invites with code, uses, max uses, expiration`,
|
|
2110
|
-
inputSchema: ListInvitesSchema,
|
|
2111
|
-
annotations: {
|
|
2112
|
-
readOnlyHint: true,
|
|
2113
|
-
destructiveHint: false,
|
|
2114
|
-
idempotentHint: true,
|
|
2115
|
-
openWorldHint: true,
|
|
2116
|
-
},
|
|
2117
|
-
}, async (params) => {
|
|
2118
|
-
try {
|
|
2119
|
-
const client = await getClient();
|
|
2120
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2121
|
-
if (!guild) {
|
|
2122
|
-
return {
|
|
2123
|
-
isError: true,
|
|
2124
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
const invites = await guild.invites.fetch();
|
|
2128
|
-
const inviteList = invites.map(invite => ({
|
|
2129
|
-
code: invite.code,
|
|
2130
|
-
url: invite.url,
|
|
2131
|
-
channel: invite.channel?.name || 'Unknown',
|
|
2132
|
-
channelId: invite.channel?.id,
|
|
2133
|
-
inviter: invite.inviter?.username || 'Unknown',
|
|
2134
|
-
uses: invite.uses,
|
|
2135
|
-
maxUses: invite.maxUses || 'Unlimited',
|
|
2136
|
-
maxAge: invite.maxAge === 0 ? 'Never' : `${invite.maxAge} seconds`,
|
|
2137
|
-
temporary: invite.temporary,
|
|
2138
|
-
createdAt: invite.createdAt?.toISOString(),
|
|
2139
|
-
expiresAt: invite.expiresAt?.toISOString() || 'Never',
|
|
2140
|
-
}));
|
|
2141
|
-
const result = formatResponse(inviteList, params.response_format, (items) => items.map(i => `**${i.code}** - #${i.channel}\nUses: ${i.uses}/${i.maxUses} | Expires: ${i.expiresAt}\nCreated by: ${i.inviter}`).join('\n\n'));
|
|
2142
|
-
return {
|
|
2143
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
2144
|
-
};
|
|
2145
|
-
}
|
|
2146
|
-
catch (error) {
|
|
2147
|
-
return {
|
|
2148
|
-
isError: true,
|
|
2149
|
-
content: [{ type: "text", text: `Error listing invites: ${error.message}` }],
|
|
2150
|
-
};
|
|
2151
|
-
}
|
|
2152
|
-
});
|
|
2153
|
-
server.registerTool("discord_create_invite", {
|
|
2154
|
-
title: "Create Invite",
|
|
2155
|
-
description: `Create an invite link for a channel.
|
|
2156
|
-
|
|
2157
|
-
Args:
|
|
2158
|
-
- channel_id (string): Discord channel ID
|
|
2159
|
-
- max_age (number): Duration in seconds (0 = never expires, max 604800 = 7 days)
|
|
2160
|
-
- max_uses (number): Max uses (0 = unlimited)
|
|
2161
|
-
- temporary (boolean): Kick members when they disconnect if not assigned a role
|
|
2162
|
-
- unique (boolean): Create new unique invite vs reuse existing
|
|
2163
|
-
|
|
2164
|
-
Returns:
|
|
2165
|
-
Created invite URL`,
|
|
2166
|
-
inputSchema: CreateInviteSchema,
|
|
2167
|
-
annotations: {
|
|
2168
|
-
readOnlyHint: false,
|
|
2169
|
-
destructiveHint: false,
|
|
2170
|
-
idempotentHint: false,
|
|
2171
|
-
openWorldHint: true,
|
|
2172
|
-
},
|
|
2173
|
-
}, async (params) => {
|
|
2174
|
-
try {
|
|
2175
|
-
const client = await getClient();
|
|
2176
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
2177
|
-
if (!channel || !('createInvite' in channel)) {
|
|
2178
|
-
return {
|
|
2179
|
-
isError: true,
|
|
2180
|
-
content: [{ type: "text", text: `Channel not found or cannot create invites: ${params.channel_id}` }],
|
|
2181
|
-
};
|
|
2182
|
-
}
|
|
2183
|
-
const textChannel = channel;
|
|
2184
|
-
const invite = await textChannel.createInvite({
|
|
2185
|
-
maxAge: params.max_age,
|
|
2186
|
-
maxUses: params.max_uses,
|
|
2187
|
-
temporary: params.temporary,
|
|
2188
|
-
unique: params.unique,
|
|
2189
|
-
});
|
|
2190
|
-
return {
|
|
2191
|
-
content: [{ type: "text", text: `Created invite: ${invite.url}\nCode: ${invite.code}\nMax uses: ${invite.maxUses || 'Unlimited'}\nExpires: ${invite.expiresAt?.toISOString() || 'Never'}` }],
|
|
2192
|
-
};
|
|
2193
|
-
}
|
|
2194
|
-
catch (error) {
|
|
2195
|
-
return {
|
|
2196
|
-
isError: true,
|
|
2197
|
-
content: [{ type: "text", text: `Error creating invite: ${error.message}` }],
|
|
2198
|
-
};
|
|
2199
|
-
}
|
|
2200
|
-
});
|
|
2201
|
-
server.registerTool("discord_delete_invite", {
|
|
2202
|
-
title: "Delete Invite",
|
|
2203
|
-
description: `Delete an invite by its code.
|
|
2204
|
-
|
|
2205
|
-
Args:
|
|
2206
|
-
- invite_code (string): The invite code to delete
|
|
2207
|
-
|
|
2208
|
-
Returns:
|
|
2209
|
-
Confirmation of deletion`,
|
|
2210
|
-
inputSchema: DeleteInviteSchema,
|
|
2211
|
-
annotations: {
|
|
2212
|
-
readOnlyHint: false,
|
|
2213
|
-
destructiveHint: true,
|
|
2214
|
-
idempotentHint: true,
|
|
2215
|
-
openWorldHint: true,
|
|
2216
|
-
},
|
|
2217
|
-
}, async (params) => {
|
|
2218
|
-
try {
|
|
2219
|
-
const client = await getClient();
|
|
2220
|
-
const invite = await client.fetchInvite(params.invite_code).catch(() => null);
|
|
2221
|
-
if (!invite) {
|
|
2222
|
-
return {
|
|
2223
|
-
isError: true,
|
|
2224
|
-
content: [{ type: "text", text: `Invite not found: ${params.invite_code}` }],
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
await invite.delete();
|
|
2228
|
-
return {
|
|
2229
|
-
content: [{ type: "text", text: `Deleted invite: ${params.invite_code}` }],
|
|
2230
|
-
};
|
|
2231
|
-
}
|
|
2232
|
-
catch (error) {
|
|
2233
|
-
return {
|
|
2234
|
-
isError: true,
|
|
2235
|
-
content: [{ type: "text", text: `Error deleting invite: ${error.message}` }],
|
|
2236
|
-
};
|
|
2237
|
-
}
|
|
2238
|
-
});
|
|
2239
|
-
// ============================================================================
|
|
2240
|
-
// WEBHOOK TOOLS
|
|
2241
|
-
// ============================================================================
|
|
2242
|
-
server.registerTool("discord_list_webhooks", {
|
|
2243
|
-
title: "List Webhooks",
|
|
2244
|
-
description: `List webhooks in a guild or channel.
|
|
2245
|
-
|
|
2246
|
-
Args:
|
|
2247
|
-
- guild_id (string, optional): List all webhooks in the server
|
|
2248
|
-
- channel_id (string, optional): List webhooks for a specific channel
|
|
2249
|
-
- response_format ('json' | 'markdown'): Output format
|
|
2250
|
-
|
|
2251
|
-
Returns:
|
|
2252
|
-
List of webhooks with name, channel, and URL`,
|
|
2253
|
-
inputSchema: ListWebhooksSchema,
|
|
2254
|
-
annotations: {
|
|
2255
|
-
readOnlyHint: true,
|
|
2256
|
-
destructiveHint: false,
|
|
2257
|
-
idempotentHint: true,
|
|
2258
|
-
openWorldHint: true,
|
|
2259
|
-
},
|
|
2260
|
-
}, async (params) => {
|
|
2261
|
-
try {
|
|
2262
|
-
const client = await getClient();
|
|
2263
|
-
let webhooks;
|
|
2264
|
-
if (params.channel_id) {
|
|
2265
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
2266
|
-
if (!channel || !('fetchWebhooks' in channel)) {
|
|
2267
|
-
return {
|
|
2268
|
-
isError: true,
|
|
2269
|
-
content: [{ type: "text", text: `Channel not found or doesn't support webhooks: ${params.channel_id}` }],
|
|
2270
|
-
};
|
|
2271
|
-
}
|
|
2272
|
-
webhooks = await channel.fetchWebhooks();
|
|
2273
|
-
}
|
|
2274
|
-
else if (params.guild_id) {
|
|
2275
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2276
|
-
if (!guild) {
|
|
2277
|
-
return {
|
|
2278
|
-
isError: true,
|
|
2279
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2280
|
-
};
|
|
2281
|
-
}
|
|
2282
|
-
webhooks = await guild.fetchWebhooks();
|
|
2283
|
-
}
|
|
2284
|
-
else {
|
|
2285
|
-
return {
|
|
2286
|
-
isError: true,
|
|
2287
|
-
content: [{ type: "text", text: `Must provide either guild_id or channel_id` }],
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2290
|
-
const webhookList = webhooks.map(wh => ({
|
|
2291
|
-
id: wh.id,
|
|
2292
|
-
name: wh.name,
|
|
2293
|
-
channel: wh.channel?.id,
|
|
2294
|
-
url: wh.url,
|
|
2295
|
-
owner: wh.owner?.username || 'Unknown',
|
|
2296
|
-
avatar: wh.avatarURL(),
|
|
2297
|
-
}));
|
|
2298
|
-
const result = formatResponse(webhookList, params.response_format, (items) => items.map(w => `**${w.name}** (ID: ${w.id})\nChannel: <#${w.channel}>\nOwner: ${w.owner}`).join('\n\n'));
|
|
2299
|
-
return {
|
|
2300
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
2301
|
-
};
|
|
2302
|
-
}
|
|
2303
|
-
catch (error) {
|
|
2304
|
-
return {
|
|
2305
|
-
isError: true,
|
|
2306
|
-
content: [{ type: "text", text: `Error listing webhooks: ${error.message}` }],
|
|
2307
|
-
};
|
|
2308
|
-
}
|
|
2309
|
-
});
|
|
2310
|
-
server.registerTool("discord_create_webhook", {
|
|
2311
|
-
title: "Create Webhook",
|
|
2312
|
-
description: `Create a webhook in a channel.
|
|
2313
|
-
|
|
2314
|
-
Args:
|
|
2315
|
-
- channel_id (string): Discord channel ID
|
|
2316
|
-
- name (string): Webhook name
|
|
2317
|
-
- avatar_url (string, optional): Avatar image URL
|
|
2318
|
-
|
|
2319
|
-
Returns:
|
|
2320
|
-
Created webhook details including URL`,
|
|
2321
|
-
inputSchema: CreateWebhookSchema,
|
|
2322
|
-
annotations: {
|
|
2323
|
-
readOnlyHint: false,
|
|
2324
|
-
destructiveHint: false,
|
|
2325
|
-
idempotentHint: false,
|
|
2326
|
-
openWorldHint: true,
|
|
2327
|
-
},
|
|
2328
|
-
}, async (params) => {
|
|
2329
|
-
try {
|
|
2330
|
-
const client = await getClient();
|
|
2331
|
-
const channel = client.channels.cache.get(params.channel_id);
|
|
2332
|
-
if (!channel || !('createWebhook' in channel)) {
|
|
2333
|
-
return {
|
|
2334
|
-
isError: true,
|
|
2335
|
-
content: [{ type: "text", text: `Channel not found or doesn't support webhooks: ${params.channel_id}` }],
|
|
2336
|
-
};
|
|
2337
|
-
}
|
|
2338
|
-
const webhook = await channel.createWebhook({
|
|
2339
|
-
name: params.name,
|
|
2340
|
-
avatar: params.avatar_url,
|
|
2341
|
-
});
|
|
2342
|
-
return {
|
|
2343
|
-
content: [{ type: "text", text: `Created webhook "${webhook.name}" (ID: ${webhook.id})\nURL: ${webhook.url}` }],
|
|
2344
|
-
};
|
|
2345
|
-
}
|
|
2346
|
-
catch (error) {
|
|
2347
|
-
return {
|
|
2348
|
-
isError: true,
|
|
2349
|
-
content: [{ type: "text", text: `Error creating webhook: ${error.message}` }],
|
|
2350
|
-
};
|
|
2351
|
-
}
|
|
2352
|
-
});
|
|
2353
|
-
server.registerTool("discord_edit_webhook", {
|
|
2354
|
-
title: "Edit Webhook",
|
|
2355
|
-
description: `Edit an existing webhook.
|
|
2356
|
-
|
|
2357
|
-
Args:
|
|
2358
|
-
- webhook_id (string): Discord webhook ID
|
|
2359
|
-
- name (string, optional): New webhook name
|
|
2360
|
-
- channel_id (string, optional): Move webhook to different channel
|
|
2361
|
-
|
|
2362
|
-
Returns:
|
|
2363
|
-
Updated webhook details`,
|
|
2364
|
-
inputSchema: EditWebhookSchema,
|
|
2365
|
-
annotations: {
|
|
2366
|
-
readOnlyHint: false,
|
|
2367
|
-
destructiveHint: false,
|
|
2368
|
-
idempotentHint: true,
|
|
2369
|
-
openWorldHint: true,
|
|
2370
|
-
},
|
|
2371
|
-
}, async (params) => {
|
|
2372
|
-
try {
|
|
2373
|
-
const client = await getClient();
|
|
2374
|
-
const webhook = await client.fetchWebhook(params.webhook_id).catch(() => null);
|
|
2375
|
-
if (!webhook) {
|
|
2376
|
-
return {
|
|
2377
|
-
isError: true,
|
|
2378
|
-
content: [{ type: "text", text: `Webhook not found: ${params.webhook_id}` }],
|
|
2379
|
-
};
|
|
2380
|
-
}
|
|
2381
|
-
const editOptions = {};
|
|
2382
|
-
if (params.name !== undefined)
|
|
2383
|
-
editOptions.name = params.name;
|
|
2384
|
-
if (params.channel_id !== undefined)
|
|
2385
|
-
editOptions.channel = params.channel_id;
|
|
2386
|
-
await webhook.edit(editOptions);
|
|
2387
|
-
return {
|
|
2388
|
-
content: [{ type: "text", text: `Updated webhook "${webhook.name}" (ID: ${webhook.id})` }],
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
catch (error) {
|
|
2392
|
-
return {
|
|
2393
|
-
isError: true,
|
|
2394
|
-
content: [{ type: "text", text: `Error editing webhook: ${error.message}` }],
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
|
-
});
|
|
2398
|
-
server.registerTool("discord_delete_webhook", {
|
|
2399
|
-
title: "Delete Webhook",
|
|
2400
|
-
description: `Delete a webhook.
|
|
2401
|
-
|
|
2402
|
-
Args:
|
|
2403
|
-
- webhook_id (string): Discord webhook ID
|
|
2404
|
-
|
|
2405
|
-
Returns:
|
|
2406
|
-
Confirmation of deletion`,
|
|
2407
|
-
inputSchema: DeleteWebhookSchema,
|
|
2408
|
-
annotations: {
|
|
2409
|
-
readOnlyHint: false,
|
|
2410
|
-
destructiveHint: true,
|
|
2411
|
-
idempotentHint: true,
|
|
2412
|
-
openWorldHint: true,
|
|
2413
|
-
},
|
|
2414
|
-
}, async (params) => {
|
|
2415
|
-
try {
|
|
2416
|
-
const client = await getClient();
|
|
2417
|
-
const webhook = await client.fetchWebhook(params.webhook_id).catch(() => null);
|
|
2418
|
-
if (!webhook) {
|
|
2419
|
-
return {
|
|
2420
|
-
isError: true,
|
|
2421
|
-
content: [{ type: "text", text: `Webhook not found: ${params.webhook_id}` }],
|
|
2422
|
-
};
|
|
2423
|
-
}
|
|
2424
|
-
const name = webhook.name;
|
|
2425
|
-
await webhook.delete();
|
|
2426
|
-
return {
|
|
2427
|
-
content: [{ type: "text", text: `Deleted webhook: ${name}` }],
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
catch (error) {
|
|
2431
|
-
return {
|
|
2432
|
-
isError: true,
|
|
2433
|
-
content: [{ type: "text", text: `Error deleting webhook: ${error.message}` }],
|
|
2434
|
-
};
|
|
2435
|
-
}
|
|
2436
|
-
});
|
|
2437
|
-
// ============================================================================
|
|
2438
|
-
// AUDIT LOG TOOL
|
|
2439
|
-
// ============================================================================
|
|
2440
|
-
server.registerTool("discord_get_audit_log", {
|
|
2441
|
-
title: "Get Audit Log",
|
|
2442
|
-
description: `Get the audit log for a server. Shows who did what actions.
|
|
2443
|
-
|
|
2444
|
-
Common action types:
|
|
2445
|
-
1=GuildUpdate, 10=ChannelCreate, 11=ChannelUpdate, 12=ChannelDelete,
|
|
2446
|
-
20=MemberKick, 22=MemberBanAdd, 23=MemberBanRemove, 24=MemberUpdate, 25=MemberRoleUpdate,
|
|
2447
|
-
30=RoleCreate, 31=RoleUpdate, 32=RoleDelete,
|
|
2448
|
-
72=MessageDelete, 73=MessageBulkDelete
|
|
2449
|
-
|
|
2450
|
-
Args:
|
|
2451
|
-
- guild_id (string): Discord server/guild ID
|
|
2452
|
-
- user_id (string, optional): Filter by user who performed action
|
|
2453
|
-
- action_type (number, optional): Filter by action type
|
|
2454
|
-
- limit (number): Number of entries (1-100, default 20)
|
|
2455
|
-
- response_format ('json' | 'markdown'): Output format
|
|
2456
|
-
|
|
2457
|
-
Returns:
|
|
2458
|
-
Audit log entries with action, user, target, and changes`,
|
|
2459
|
-
inputSchema: GetAuditLogSchema,
|
|
2460
|
-
annotations: {
|
|
2461
|
-
readOnlyHint: true,
|
|
2462
|
-
destructiveHint: false,
|
|
2463
|
-
idempotentHint: true,
|
|
2464
|
-
openWorldHint: true,
|
|
2465
|
-
},
|
|
2466
|
-
}, async (params) => {
|
|
2467
|
-
try {
|
|
2468
|
-
const client = await getClient();
|
|
2469
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2470
|
-
if (!guild) {
|
|
2471
|
-
return {
|
|
2472
|
-
isError: true,
|
|
2473
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2474
|
-
};
|
|
2475
|
-
}
|
|
2476
|
-
const fetchOptions = {
|
|
2477
|
-
limit: params.limit,
|
|
2478
|
-
};
|
|
2479
|
-
if (params.user_id)
|
|
2480
|
-
fetchOptions.user = params.user_id;
|
|
2481
|
-
if (params.action_type !== undefined)
|
|
2482
|
-
fetchOptions.type = params.action_type;
|
|
2483
|
-
const auditLogs = await guild.fetchAuditLogs(fetchOptions);
|
|
2484
|
-
const entries = auditLogs.entries.map(entry => ({
|
|
2485
|
-
id: entry.id,
|
|
2486
|
-
action: entry.action,
|
|
2487
|
-
actionType: entry.actionType,
|
|
2488
|
-
executor: entry.executor?.username || 'Unknown',
|
|
2489
|
-
executorId: entry.executor?.id,
|
|
2490
|
-
target: entry.target?.toString() || 'Unknown',
|
|
2491
|
-
reason: entry.reason || 'No reason',
|
|
2492
|
-
createdAt: entry.createdAt.toISOString(),
|
|
2493
|
-
changes: entry.changes.map(c => ({
|
|
2494
|
-
key: c.key,
|
|
2495
|
-
old: c.old,
|
|
2496
|
-
new: c.new,
|
|
2497
|
-
})),
|
|
2498
|
-
}));
|
|
2499
|
-
const result = formatResponse(entries, params.response_format, (items) => items.map(e => `**Action ${e.action}** by ${e.executor}\nTarget: ${e.target}\nReason: ${e.reason}\nTime: ${e.createdAt}`).join('\n\n'));
|
|
2500
|
-
return {
|
|
2501
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
2502
|
-
};
|
|
2503
|
-
}
|
|
2504
|
-
catch (error) {
|
|
2505
|
-
return {
|
|
2506
|
-
isError: true,
|
|
2507
|
-
content: [{ type: "text", text: `Error fetching audit log: ${error.message}` }],
|
|
2508
|
-
};
|
|
2509
|
-
}
|
|
2510
|
-
});
|
|
2511
|
-
// ============================================================================
|
|
2512
|
-
// PRUNE MEMBERS TOOL
|
|
2513
|
-
// ============================================================================
|
|
2514
|
-
server.registerTool("discord_prune_members", {
|
|
2515
|
-
title: "Prune Members",
|
|
2516
|
-
description: `Remove inactive members from the server.
|
|
2517
|
-
|
|
2518
|
-
Args:
|
|
2519
|
-
- guild_id (string): Discord server/guild ID
|
|
2520
|
-
- days (number): Days of inactivity required (1-30)
|
|
2521
|
-
- include_roles (string[], optional): Role IDs to include in prune (by default only members without roles)
|
|
2522
|
-
- dry_run (boolean): If true, returns count without actually pruning (default true)
|
|
2523
|
-
|
|
2524
|
-
Returns:
|
|
2525
|
-
Number of members pruned (or would be pruned if dry_run)`,
|
|
2526
|
-
inputSchema: PruneMembersSchema,
|
|
2527
|
-
annotations: {
|
|
2528
|
-
readOnlyHint: false,
|
|
2529
|
-
destructiveHint: true,
|
|
2530
|
-
idempotentHint: false,
|
|
2531
|
-
openWorldHint: true,
|
|
2532
|
-
},
|
|
2533
|
-
}, async (params) => {
|
|
2534
|
-
try {
|
|
2535
|
-
const client = await getClient();
|
|
2536
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2537
|
-
if (!guild) {
|
|
2538
|
-
return {
|
|
2539
|
-
isError: true,
|
|
2540
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
if (params.dry_run) {
|
|
2544
|
-
const count = await guild.members.prune({
|
|
2545
|
-
days: params.days,
|
|
2546
|
-
roles: params.include_roles,
|
|
2547
|
-
dry: true,
|
|
2548
|
-
});
|
|
2549
|
-
return {
|
|
2550
|
-
content: [{ type: "text", text: `Dry run: ${count} members would be pruned (inactive for ${params.days}+ days)` }],
|
|
2551
|
-
};
|
|
2552
|
-
}
|
|
2553
|
-
const pruned = await guild.members.prune({
|
|
2554
|
-
days: params.days,
|
|
2555
|
-
roles: params.include_roles,
|
|
2556
|
-
dry: false,
|
|
2557
|
-
});
|
|
2558
|
-
return {
|
|
2559
|
-
content: [{ type: "text", text: `Pruned ${pruned} members (inactive for ${params.days}+ days)` }],
|
|
2560
|
-
};
|
|
2561
|
-
}
|
|
2562
|
-
catch (error) {
|
|
2563
|
-
return {
|
|
2564
|
-
isError: true,
|
|
2565
|
-
content: [{ type: "text", text: `Error pruning members: ${error.message}` }],
|
|
2566
|
-
};
|
|
2567
|
-
}
|
|
2568
|
-
});
|
|
2569
|
-
// ============================================================================
|
|
2570
|
-
// STICKER TOOLS
|
|
2571
|
-
// ============================================================================
|
|
2572
|
-
server.registerTool("discord_list_stickers", {
|
|
2573
|
-
title: "List Stickers",
|
|
2574
|
-
description: `List all custom stickers in a server.
|
|
2575
|
-
|
|
2576
|
-
Args:
|
|
2577
|
-
- guild_id (string): Discord server/guild ID
|
|
2578
|
-
- response_format ('json' | 'markdown'): Output format
|
|
2579
|
-
|
|
2580
|
-
Returns:
|
|
2581
|
-
List of stickers with name, description, and format`,
|
|
2582
|
-
inputSchema: ListStickersSchema,
|
|
2583
|
-
annotations: {
|
|
2584
|
-
readOnlyHint: true,
|
|
2585
|
-
destructiveHint: false,
|
|
2586
|
-
idempotentHint: true,
|
|
2587
|
-
openWorldHint: true,
|
|
2588
|
-
},
|
|
2589
|
-
}, async (params) => {
|
|
2590
|
-
try {
|
|
2591
|
-
const client = await getClient();
|
|
2592
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2593
|
-
if (!guild) {
|
|
2594
|
-
return {
|
|
2595
|
-
isError: true,
|
|
2596
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2597
|
-
};
|
|
2598
|
-
}
|
|
2599
|
-
const stickers = await guild.stickers.fetch();
|
|
2600
|
-
const stickerList = stickers.map(sticker => ({
|
|
2601
|
-
id: sticker.id,
|
|
2602
|
-
name: sticker.name,
|
|
2603
|
-
description: sticker.description,
|
|
2604
|
-
tags: sticker.tags,
|
|
2605
|
-
format: sticker.format,
|
|
2606
|
-
available: sticker.available,
|
|
2607
|
-
url: sticker.url,
|
|
2608
|
-
}));
|
|
2609
|
-
const result = formatResponse(stickerList, params.response_format, (items) => items.map(s => `**${s.name}** (ID: ${s.id})\nDescription: ${s.description || 'None'}\nTags: ${s.tags || 'None'}`).join('\n\n'));
|
|
2610
|
-
return {
|
|
2611
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
2612
|
-
};
|
|
2613
|
-
}
|
|
2614
|
-
catch (error) {
|
|
2615
|
-
return {
|
|
2616
|
-
isError: true,
|
|
2617
|
-
content: [{ type: "text", text: `Error listing stickers: ${error.message}` }],
|
|
2618
|
-
};
|
|
2619
|
-
}
|
|
2620
|
-
});
|
|
2621
|
-
server.registerTool("discord_delete_sticker", {
|
|
2622
|
-
title: "Delete Sticker",
|
|
2623
|
-
description: `Delete a custom sticker from a server.
|
|
2624
|
-
|
|
2625
|
-
Args:
|
|
2626
|
-
- guild_id (string): Discord server/guild ID
|
|
2627
|
-
- sticker_id (string): Discord sticker ID
|
|
2628
|
-
|
|
2629
|
-
Returns:
|
|
2630
|
-
Confirmation of deletion`,
|
|
2631
|
-
inputSchema: DeleteStickerSchema,
|
|
2632
|
-
annotations: {
|
|
2633
|
-
readOnlyHint: false,
|
|
2634
|
-
destructiveHint: true,
|
|
2635
|
-
idempotentHint: true,
|
|
2636
|
-
openWorldHint: true,
|
|
2637
|
-
},
|
|
2638
|
-
}, async (params) => {
|
|
2639
|
-
try {
|
|
2640
|
-
const client = await getClient();
|
|
2641
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2642
|
-
if (!guild) {
|
|
2643
|
-
return {
|
|
2644
|
-
isError: true,
|
|
2645
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2646
|
-
};
|
|
2647
|
-
}
|
|
2648
|
-
const sticker = await guild.stickers.fetch(params.sticker_id).catch(() => null);
|
|
2649
|
-
if (!sticker) {
|
|
2650
|
-
return {
|
|
2651
|
-
isError: true,
|
|
2652
|
-
content: [{ type: "text", text: `Sticker not found: ${params.sticker_id}` }],
|
|
2653
|
-
};
|
|
2654
|
-
}
|
|
2655
|
-
const name = sticker.name;
|
|
2656
|
-
await sticker.delete();
|
|
2657
|
-
return {
|
|
2658
|
-
content: [{ type: "text", text: `Deleted sticker: ${name}` }],
|
|
2659
|
-
};
|
|
2660
|
-
}
|
|
2661
|
-
catch (error) {
|
|
2662
|
-
return {
|
|
2663
|
-
isError: true,
|
|
2664
|
-
content: [{ type: "text", text: `Error deleting sticker: ${error.message}` }],
|
|
2665
|
-
};
|
|
2666
|
-
}
|
|
2667
|
-
});
|
|
2668
|
-
// ============================================================================
|
|
2669
|
-
// SCHEDULED EVENT TOOLS
|
|
2670
|
-
// ============================================================================
|
|
2671
|
-
server.registerTool("discord_list_events", {
|
|
2672
|
-
title: "List Scheduled Events",
|
|
2673
|
-
description: `List all scheduled events in a server.
|
|
2674
|
-
|
|
2675
|
-
Args:
|
|
2676
|
-
- guild_id (string): Discord server/guild ID
|
|
2677
|
-
- response_format ('json' | 'markdown'): Output format
|
|
2678
|
-
|
|
2679
|
-
Returns:
|
|
2680
|
-
List of events with name, time, location, and status`,
|
|
2681
|
-
inputSchema: ListEventsSchema,
|
|
2682
|
-
annotations: {
|
|
2683
|
-
readOnlyHint: true,
|
|
2684
|
-
destructiveHint: false,
|
|
2685
|
-
idempotentHint: true,
|
|
2686
|
-
openWorldHint: true,
|
|
2687
|
-
},
|
|
2688
|
-
}, async (params) => {
|
|
2689
|
-
try {
|
|
2690
|
-
const client = await getClient();
|
|
2691
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2692
|
-
if (!guild) {
|
|
2693
|
-
return {
|
|
2694
|
-
isError: true,
|
|
2695
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2696
|
-
};
|
|
2697
|
-
}
|
|
2698
|
-
const events = await guild.scheduledEvents.fetch();
|
|
2699
|
-
const eventList = events.map(event => ({
|
|
2700
|
-
id: event.id,
|
|
2701
|
-
name: event.name,
|
|
2702
|
-
description: event.description,
|
|
2703
|
-
status: event.status,
|
|
2704
|
-
entityType: event.entityType,
|
|
2705
|
-
channel: event.channel?.name,
|
|
2706
|
-
channelId: event.channelId,
|
|
2707
|
-
location: event.entityMetadata?.location,
|
|
2708
|
-
scheduledStartTime: event.scheduledStartAt?.toISOString(),
|
|
2709
|
-
scheduledEndTime: event.scheduledEndAt?.toISOString(),
|
|
2710
|
-
userCount: event.userCount,
|
|
2711
|
-
creator: event.creator?.username,
|
|
2712
|
-
}));
|
|
2713
|
-
const result = formatResponse(eventList, params.response_format, (items) => items.map(e => `**${e.name}** (ID: ${e.id})\nStatus: ${e.status}\nStart: ${e.scheduledStartTime}\nLocation: ${e.location || e.channel || 'TBD'}\nAttending: ${e.userCount || 0}`).join('\n\n'));
|
|
2714
|
-
return {
|
|
2715
|
-
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
2716
|
-
};
|
|
2717
|
-
}
|
|
2718
|
-
catch (error) {
|
|
2719
|
-
return {
|
|
2720
|
-
isError: true,
|
|
2721
|
-
content: [{ type: "text", text: `Error listing events: ${error.message}` }],
|
|
2722
|
-
};
|
|
2723
|
-
}
|
|
2724
|
-
});
|
|
2725
|
-
server.registerTool("discord_create_event", {
|
|
2726
|
-
title: "Create Scheduled Event",
|
|
2727
|
-
description: `Create a scheduled event in a server.
|
|
2728
|
-
|
|
2729
|
-
Args:
|
|
2730
|
-
- guild_id (string): Discord server/guild ID
|
|
2731
|
-
- name (string): Event name
|
|
2732
|
-
- description (string, optional): Event description
|
|
2733
|
-
- scheduled_start_time (string): ISO8601 timestamp for start
|
|
2734
|
-
- scheduled_end_time (string, optional): ISO8601 timestamp for end
|
|
2735
|
-
- entity_type ('stage' | 'voice' | 'external'): Type of event
|
|
2736
|
-
- channel_id (string, optional): Channel ID for stage/voice events
|
|
2737
|
-
- location (string, optional): Location for external events
|
|
2738
|
-
|
|
2739
|
-
Returns:
|
|
2740
|
-
Created event details`,
|
|
2741
|
-
inputSchema: CreateEventSchema,
|
|
2742
|
-
annotations: {
|
|
2743
|
-
readOnlyHint: false,
|
|
2744
|
-
destructiveHint: false,
|
|
2745
|
-
idempotentHint: false,
|
|
2746
|
-
openWorldHint: true,
|
|
2747
|
-
},
|
|
2748
|
-
}, async (params) => {
|
|
2749
|
-
try {
|
|
2750
|
-
const client = await getClient();
|
|
2751
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2752
|
-
if (!guild) {
|
|
2753
|
-
return {
|
|
2754
|
-
isError: true,
|
|
2755
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2756
|
-
};
|
|
2757
|
-
}
|
|
2758
|
-
const entityTypeMap = {
|
|
2759
|
-
stage: GuildScheduledEventEntityType.StageInstance,
|
|
2760
|
-
voice: GuildScheduledEventEntityType.Voice,
|
|
2761
|
-
external: GuildScheduledEventEntityType.External,
|
|
2762
|
-
};
|
|
2763
|
-
const createOptions = {
|
|
2764
|
-
name: params.name,
|
|
2765
|
-
scheduledStartTime: new Date(params.scheduled_start_time),
|
|
2766
|
-
privacyLevel: GuildScheduledEventPrivacyLevel.GuildOnly,
|
|
2767
|
-
entityType: entityTypeMap[params.entity_type],
|
|
2768
|
-
};
|
|
2769
|
-
if (params.description)
|
|
2770
|
-
createOptions.description = params.description;
|
|
2771
|
-
if (params.scheduled_end_time)
|
|
2772
|
-
createOptions.scheduledEndTime = new Date(params.scheduled_end_time);
|
|
2773
|
-
if (params.channel_id)
|
|
2774
|
-
createOptions.channel = params.channel_id;
|
|
2775
|
-
if (params.location)
|
|
2776
|
-
createOptions.entityMetadata = { location: params.location };
|
|
2777
|
-
const event = await guild.scheduledEvents.create(createOptions);
|
|
2778
|
-
return {
|
|
2779
|
-
content: [{ type: "text", text: `Created event "${event.name}" (ID: ${event.id})\nStart: ${event.scheduledStartAt?.toISOString()}` }],
|
|
2780
|
-
};
|
|
2781
|
-
}
|
|
2782
|
-
catch (error) {
|
|
2783
|
-
return {
|
|
2784
|
-
isError: true,
|
|
2785
|
-
content: [{ type: "text", text: `Error creating event: ${error.message}` }],
|
|
2786
|
-
};
|
|
2787
|
-
}
|
|
2788
|
-
});
|
|
2789
|
-
server.registerTool("discord_delete_event", {
|
|
2790
|
-
title: "Delete Scheduled Event",
|
|
2791
|
-
description: `Delete a scheduled event from a server.
|
|
2792
|
-
|
|
2793
|
-
Args:
|
|
2794
|
-
- guild_id (string): Discord server/guild ID
|
|
2795
|
-
- event_id (string): Discord scheduled event ID
|
|
2796
|
-
|
|
2797
|
-
Returns:
|
|
2798
|
-
Confirmation of deletion`,
|
|
2799
|
-
inputSchema: DeleteEventSchema,
|
|
2800
|
-
annotations: {
|
|
2801
|
-
readOnlyHint: false,
|
|
2802
|
-
destructiveHint: true,
|
|
2803
|
-
idempotentHint: true,
|
|
2804
|
-
openWorldHint: true,
|
|
2805
|
-
},
|
|
2806
|
-
}, async (params) => {
|
|
2807
|
-
try {
|
|
2808
|
-
const client = await getClient();
|
|
2809
|
-
const guild = client.guilds.cache.get(params.guild_id);
|
|
2810
|
-
if (!guild) {
|
|
2811
|
-
return {
|
|
2812
|
-
isError: true,
|
|
2813
|
-
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
|
-
const event = await guild.scheduledEvents.fetch(params.event_id).catch(() => null);
|
|
2817
|
-
if (!event) {
|
|
2818
|
-
return {
|
|
2819
|
-
isError: true,
|
|
2820
|
-
content: [{ type: "text", text: `Event not found: ${params.event_id}` }],
|
|
2821
|
-
};
|
|
2822
|
-
}
|
|
2823
|
-
const name = event.name;
|
|
2824
|
-
await event.delete();
|
|
2825
|
-
return {
|
|
2826
|
-
content: [{ type: "text", text: `Deleted event: ${name}` }],
|
|
2827
|
-
};
|
|
2828
|
-
}
|
|
2829
|
-
catch (error) {
|
|
2830
|
-
return {
|
|
2831
|
-
isError: true,
|
|
2832
|
-
content: [{ type: "text", text: `Error deleting event: ${error.message}` }],
|
|
2833
|
-
};
|
|
2834
|
-
}
|
|
2835
|
-
});
|
|
2836
|
-
// ============================================================================
|
|
2837
|
-
// SERVER STARTUP
|
|
2838
|
-
// ============================================================================
|
|
6
|
+
import { server } from "./server.js";
|
|
7
|
+
import { registerAllTools } from "./tools/index.js";
|
|
8
|
+
// Register all tools
|
|
9
|
+
registerAllTools(server);
|
|
2839
10
|
async function runStdio() {
|
|
2840
11
|
const transport = new StdioServerTransport();
|
|
2841
12
|
await server.connect(transport);
|