@mahesvara/discord-mcpserver 1.0.8 → 1.0.9
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 +158 -0
- package/dist/schemas/channel.js +128 -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 +510 -0
- package/dist/tools/community.d.ts +2 -0
- package/dist/tools/community.js +259 -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
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { ListRolesSchema, AddRoleSchema, RemoveRoleSchema, CreateRoleSchema, DeleteRoleSchema, EditRoleSchema, SetRolePositionsSchema, } from "../schemas/index.js";
|
|
2
|
+
import { getClient, formatRole, roleToMarkdown, formatResponse, truncateIfNeeded, } from "../services/discord.js";
|
|
3
|
+
import { PermissionsBitField } from "discord.js";
|
|
4
|
+
// Full list of role permissions for reference
|
|
5
|
+
const ROLE_PERMISSION_LIST = [
|
|
6
|
+
// General Server Permissions
|
|
7
|
+
'Administrator', 'ViewAuditLog', 'ViewGuildInsights', 'ManageGuild', 'ManageRoles',
|
|
8
|
+
'ManageChannels', 'KickMembers', 'BanMembers', 'CreateInstantInvite', 'ChangeNickname',
|
|
9
|
+
'ManageNicknames', 'ManageEmojisAndStickers', 'ManageWebhooks', 'ManageGuildExpressions',
|
|
10
|
+
'ViewCreatorMonetizationAnalytics', 'ModerateMembers',
|
|
11
|
+
// Text Channel Permissions
|
|
12
|
+
'ViewChannel', 'SendMessages', 'SendTTSMessages', 'ManageMessages', 'EmbedLinks',
|
|
13
|
+
'AttachFiles', 'ReadMessageHistory', 'MentionEveryone', 'UseExternalEmojis',
|
|
14
|
+
'AddReactions', 'UseApplicationCommands', 'ManageThreads', 'CreatePublicThreads',
|
|
15
|
+
'CreatePrivateThreads', 'UseExternalStickers', 'SendMessagesInThreads', 'SendVoiceMessages',
|
|
16
|
+
'SendPolls', 'UseExternalApps',
|
|
17
|
+
// Voice Channel Permissions
|
|
18
|
+
'Connect', 'Speak', 'MuteMembers', 'DeafenMembers', 'MoveMembers', 'UseVAD',
|
|
19
|
+
'PrioritySpeaker', 'Stream', 'UseSoundboard', 'UseExternalSounds', 'UseEmbeddedActivities',
|
|
20
|
+
// Stage Channel Permissions
|
|
21
|
+
'RequestToSpeak',
|
|
22
|
+
// Events Permissions
|
|
23
|
+
'ManageEvents', 'CreateEvents'
|
|
24
|
+
];
|
|
25
|
+
export function registerRoleTools(server) {
|
|
26
|
+
server.registerTool("discord_list_roles", {
|
|
27
|
+
title: "List Discord Roles",
|
|
28
|
+
description: `List all roles in a Discord server.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
- guild_id (string): Discord server/guild ID
|
|
32
|
+
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of roles with name, color, position, permissions`,
|
|
36
|
+
inputSchema: ListRolesSchema,
|
|
37
|
+
annotations: {
|
|
38
|
+
readOnlyHint: true,
|
|
39
|
+
destructiveHint: false,
|
|
40
|
+
idempotentHint: true,
|
|
41
|
+
openWorldHint: true,
|
|
42
|
+
},
|
|
43
|
+
}, async (params) => {
|
|
44
|
+
try {
|
|
45
|
+
const client = await getClient();
|
|
46
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
47
|
+
if (!guild) {
|
|
48
|
+
return {
|
|
49
|
+
isError: true,
|
|
50
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const roles = guild.roles.cache
|
|
54
|
+
.filter(r => r.name !== "@everyone")
|
|
55
|
+
.sort((a, b) => b.position - a.position)
|
|
56
|
+
.map(formatRole);
|
|
57
|
+
const text = formatResponse(Array.from(roles.values()), params.response_format, (items) => items.map(roleToMarkdown).join("\n\n"));
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
isError: true,
|
|
65
|
+
content: [{ type: "text", text: `Error listing roles: ${error.message}` }],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
server.registerTool("discord_add_role", {
|
|
70
|
+
title: "Add Role to Member",
|
|
71
|
+
description: `Add a role to a Discord member.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
- guild_id (string): Discord server/guild ID
|
|
75
|
+
- user_id (string): Discord user ID
|
|
76
|
+
- role_id (string): Discord role ID to add
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Confirmation of role added`,
|
|
80
|
+
inputSchema: AddRoleSchema,
|
|
81
|
+
annotations: {
|
|
82
|
+
readOnlyHint: false,
|
|
83
|
+
destructiveHint: false,
|
|
84
|
+
idempotentHint: true,
|
|
85
|
+
openWorldHint: true,
|
|
86
|
+
},
|
|
87
|
+
}, async (params) => {
|
|
88
|
+
try {
|
|
89
|
+
const client = await getClient();
|
|
90
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
91
|
+
if (!guild) {
|
|
92
|
+
return {
|
|
93
|
+
isError: true,
|
|
94
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const member = await guild.members.fetch(params.user_id);
|
|
98
|
+
const role = guild.roles.cache.get(params.role_id);
|
|
99
|
+
if (!role) {
|
|
100
|
+
return {
|
|
101
|
+
isError: true,
|
|
102
|
+
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
await member.roles.add(role);
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: `Added role "${role.name}" to user ${params.user_id}` }],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
isError: true,
|
|
113
|
+
content: [{ type: "text", text: `Error adding role: ${error.message}` }],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
server.registerTool("discord_remove_role", {
|
|
118
|
+
title: "Remove Role from Member",
|
|
119
|
+
description: `Remove a role from a Discord member.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
- guild_id (string): Discord server/guild ID
|
|
123
|
+
- user_id (string): Discord user ID
|
|
124
|
+
- role_id (string): Discord role ID to remove
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Confirmation of role removed`,
|
|
128
|
+
inputSchema: RemoveRoleSchema,
|
|
129
|
+
annotations: {
|
|
130
|
+
readOnlyHint: false,
|
|
131
|
+
destructiveHint: false,
|
|
132
|
+
idempotentHint: true,
|
|
133
|
+
openWorldHint: true,
|
|
134
|
+
},
|
|
135
|
+
}, async (params) => {
|
|
136
|
+
try {
|
|
137
|
+
const client = await getClient();
|
|
138
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
139
|
+
if (!guild) {
|
|
140
|
+
return {
|
|
141
|
+
isError: true,
|
|
142
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const member = await guild.members.fetch(params.user_id);
|
|
146
|
+
const role = guild.roles.cache.get(params.role_id);
|
|
147
|
+
if (!role) {
|
|
148
|
+
return {
|
|
149
|
+
isError: true,
|
|
150
|
+
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
await member.roles.remove(role);
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: "text", text: `Removed role "${role.name}" from user ${params.user_id}` }],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
isError: true,
|
|
161
|
+
content: [{ type: "text", text: `Error removing role: ${error.message}` }],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
server.registerTool("discord_create_role", {
|
|
166
|
+
title: "Create Discord Role",
|
|
167
|
+
description: `Create a new role in a Discord server.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
- guild_id (string): Discord server/guild ID
|
|
171
|
+
- name (string): Role name
|
|
172
|
+
- color (number, optional): Role color as decimal integer (e.g., 16711680 for red)
|
|
173
|
+
- mentionable (boolean): Whether the role can be mentioned (default: false)
|
|
174
|
+
- hoist (boolean): Whether to display role members separately (default: false)
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
The created role's details`,
|
|
178
|
+
inputSchema: CreateRoleSchema,
|
|
179
|
+
annotations: {
|
|
180
|
+
readOnlyHint: false,
|
|
181
|
+
destructiveHint: false,
|
|
182
|
+
idempotentHint: false,
|
|
183
|
+
openWorldHint: true,
|
|
184
|
+
},
|
|
185
|
+
}, async (params) => {
|
|
186
|
+
try {
|
|
187
|
+
const client = await getClient();
|
|
188
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
189
|
+
if (!guild) {
|
|
190
|
+
return {
|
|
191
|
+
isError: true,
|
|
192
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const role = await guild.roles.create({
|
|
196
|
+
name: params.name,
|
|
197
|
+
color: params.color,
|
|
198
|
+
mentionable: params.mentionable,
|
|
199
|
+
hoist: params.hoist,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
content: [{ type: "text", text: `Created role "${role.name}" (ID: ${role.id})` }],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
return {
|
|
207
|
+
isError: true,
|
|
208
|
+
content: [{ type: "text", text: `Error creating role: ${error.message}` }],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
server.registerTool("discord_delete_role", {
|
|
213
|
+
title: "Delete Discord Role",
|
|
214
|
+
description: `Delete a role from a Discord server. This action is permanent!
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
- guild_id (string): Discord server/guild ID
|
|
218
|
+
- role_id (string): Discord role ID to delete
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Confirmation of deletion`,
|
|
222
|
+
inputSchema: DeleteRoleSchema,
|
|
223
|
+
annotations: {
|
|
224
|
+
readOnlyHint: false,
|
|
225
|
+
destructiveHint: true,
|
|
226
|
+
idempotentHint: false,
|
|
227
|
+
openWorldHint: true,
|
|
228
|
+
},
|
|
229
|
+
}, async (params) => {
|
|
230
|
+
try {
|
|
231
|
+
const client = await getClient();
|
|
232
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
233
|
+
if (!guild) {
|
|
234
|
+
return {
|
|
235
|
+
isError: true,
|
|
236
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const role = guild.roles.cache.get(params.role_id);
|
|
240
|
+
if (!role) {
|
|
241
|
+
return {
|
|
242
|
+
isError: true,
|
|
243
|
+
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const name = role.name;
|
|
247
|
+
await role.delete();
|
|
248
|
+
return {
|
|
249
|
+
content: [{ type: "text", text: `Deleted role: ${name}` }],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
return {
|
|
254
|
+
isError: true,
|
|
255
|
+
content: [{ type: "text", text: `Error deleting role: ${error.message}` }],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
server.registerTool("discord_edit_role", {
|
|
260
|
+
title: "Edit Discord Role",
|
|
261
|
+
description: `Edit a role's properties including name, color, and permissions.
|
|
262
|
+
|
|
263
|
+
Common permission names:
|
|
264
|
+
- Admin: Administrator (grants all permissions)
|
|
265
|
+
- General: ManageGuild, ManageRoles, ManageChannels, KickMembers, BanMembers, ModerateMembers
|
|
266
|
+
- Messages: SendMessages, ManageMessages, EmbedLinks, AttachFiles, ReadMessageHistory, MentionEveryone
|
|
267
|
+
- Voice: Connect, Speak, MuteMembers, DeafenMembers, MoveMembers, Stream
|
|
268
|
+
- Other: ManageEmojisAndStickers, ManageWebhooks, ManageEvents, ViewAuditLog
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
- guild_id (string): Discord server/guild ID
|
|
272
|
+
- role_id (string): Discord role ID to edit
|
|
273
|
+
- name (string, optional): New role name
|
|
274
|
+
- color (number, optional): Role color as decimal integer (e.g., 16711680 for red)
|
|
275
|
+
- mentionable (boolean, optional): Whether the role can be mentioned
|
|
276
|
+
- hoist (boolean, optional): Whether to display role members separately
|
|
277
|
+
- permissions (string[], optional): Permissions to grant (replaces existing). Examples: ['SendMessages', 'ViewChannel']
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Updated role details`,
|
|
281
|
+
inputSchema: EditRoleSchema,
|
|
282
|
+
annotations: {
|
|
283
|
+
readOnlyHint: false,
|
|
284
|
+
destructiveHint: false,
|
|
285
|
+
idempotentHint: true,
|
|
286
|
+
openWorldHint: true,
|
|
287
|
+
},
|
|
288
|
+
}, async (params) => {
|
|
289
|
+
try {
|
|
290
|
+
const client = await getClient();
|
|
291
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
292
|
+
if (!guild) {
|
|
293
|
+
return {
|
|
294
|
+
isError: true,
|
|
295
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const role = guild.roles.cache.get(params.role_id);
|
|
299
|
+
if (!role) {
|
|
300
|
+
return {
|
|
301
|
+
isError: true,
|
|
302
|
+
content: [{ type: "text", text: `Role not found: ${params.role_id}` }],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
// Build the edit options
|
|
306
|
+
const editOptions = {};
|
|
307
|
+
if (params.name !== undefined)
|
|
308
|
+
editOptions.name = params.name;
|
|
309
|
+
if (params.color !== undefined)
|
|
310
|
+
editOptions.color = params.color;
|
|
311
|
+
if (params.mentionable !== undefined)
|
|
312
|
+
editOptions.mentionable = params.mentionable;
|
|
313
|
+
if (params.hoist !== undefined)
|
|
314
|
+
editOptions.hoist = params.hoist;
|
|
315
|
+
// Handle permissions if provided
|
|
316
|
+
if (params.permissions !== undefined) {
|
|
317
|
+
const permissionBits = new PermissionsBitField();
|
|
318
|
+
for (const perm of params.permissions) {
|
|
319
|
+
try {
|
|
320
|
+
permissionBits.add(perm);
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return {
|
|
324
|
+
isError: true,
|
|
325
|
+
content: [{ type: "text", text: `Invalid permission: ${perm}. Valid permissions include: ${ROLE_PERMISSION_LIST.slice(0, 10).join(', ')}, ...` }],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
editOptions.permissions = permissionBits.bitfield;
|
|
330
|
+
}
|
|
331
|
+
const updatedRole = await role.edit(editOptions);
|
|
332
|
+
const changes = [];
|
|
333
|
+
if (params.name !== undefined)
|
|
334
|
+
changes.push(`name: "${updatedRole.name}"`);
|
|
335
|
+
if (params.color !== undefined)
|
|
336
|
+
changes.push(`color: ${updatedRole.hexColor}`);
|
|
337
|
+
if (params.mentionable !== undefined)
|
|
338
|
+
changes.push(`mentionable: ${updatedRole.mentionable}`);
|
|
339
|
+
if (params.hoist !== undefined)
|
|
340
|
+
changes.push(`hoist: ${updatedRole.hoist}`);
|
|
341
|
+
if (params.permissions !== undefined)
|
|
342
|
+
changes.push(`permissions updated`);
|
|
343
|
+
return {
|
|
344
|
+
content: [{ type: "text", text: `Updated role "${updatedRole.name}" (ID: ${updatedRole.id})\nChanges: ${changes.join(', ')}` }],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
return {
|
|
349
|
+
isError: true,
|
|
350
|
+
content: [{ type: "text", text: `Error editing role: ${error.message}` }],
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
server.registerTool("discord_set_role_positions", {
|
|
355
|
+
title: "Set Role Positions",
|
|
356
|
+
description: `Reorder role hierarchy by setting role positions.
|
|
357
|
+
|
|
358
|
+
Higher position = more authority in the hierarchy. The @everyone role is always at position 0.
|
|
359
|
+
Roles can only manage other roles below them in the hierarchy.
|
|
360
|
+
|
|
361
|
+
IMPORTANT: The bot can only move roles that are below its own highest role in the hierarchy.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
- guild_id (string): Discord server/guild ID
|
|
365
|
+
- positions (array): Array of objects with:
|
|
366
|
+
- role_id (string): Discord role ID
|
|
367
|
+
- position (number): New position for the role (higher = more authority)
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Confirmation of position changes`,
|
|
371
|
+
inputSchema: SetRolePositionsSchema,
|
|
372
|
+
annotations: {
|
|
373
|
+
readOnlyHint: false,
|
|
374
|
+
destructiveHint: false,
|
|
375
|
+
idempotentHint: true,
|
|
376
|
+
openWorldHint: true,
|
|
377
|
+
},
|
|
378
|
+
}, async (params) => {
|
|
379
|
+
try {
|
|
380
|
+
const client = await getClient();
|
|
381
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
382
|
+
if (!guild) {
|
|
383
|
+
return {
|
|
384
|
+
isError: true,
|
|
385
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
// Validate all roles exist before making changes
|
|
389
|
+
for (const pos of params.positions) {
|
|
390
|
+
const role = guild.roles.cache.get(pos.role_id);
|
|
391
|
+
if (!role) {
|
|
392
|
+
return {
|
|
393
|
+
isError: true,
|
|
394
|
+
content: [{ type: "text", text: `Role not found: ${pos.role_id}` }],
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Format positions for Discord API
|
|
399
|
+
const positionUpdates = params.positions.map(pos => ({
|
|
400
|
+
role: pos.role_id,
|
|
401
|
+
position: pos.position,
|
|
402
|
+
}));
|
|
403
|
+
await guild.roles.setPositions(positionUpdates);
|
|
404
|
+
// Build response with updated positions
|
|
405
|
+
const updatedRoles = params.positions.map(pos => {
|
|
406
|
+
const role = guild.roles.cache.get(pos.role_id);
|
|
407
|
+
return `"${role?.name}" → position ${pos.position}`;
|
|
408
|
+
});
|
|
409
|
+
return {
|
|
410
|
+
content: [{ type: "text", text: `Updated role positions:\n${updatedRoles.join('\n')}` }],
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
return {
|
|
415
|
+
isError: true,
|
|
416
|
+
content: [{ type: "text", text: `Error setting role positions: ${error.message}` }],
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { ListWebhooksSchema, CreateWebhookSchema, EditWebhookSchema, DeleteWebhookSchema, } from "../schemas/index.js";
|
|
2
|
+
import { getClient, formatResponse, truncateIfNeeded, } from "../services/discord.js";
|
|
3
|
+
export function registerWebhookTools(server) {
|
|
4
|
+
server.registerTool("discord_list_webhooks", {
|
|
5
|
+
title: "List Webhooks",
|
|
6
|
+
description: `List webhooks in a guild or channel.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
- guild_id (string, optional): List all webhooks in the server
|
|
10
|
+
- channel_id (string, optional): List webhooks for a specific channel
|
|
11
|
+
- response_format ('json' | 'markdown'): Output format
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
List of webhooks with name, channel, and URL`,
|
|
15
|
+
inputSchema: ListWebhooksSchema,
|
|
16
|
+
annotations: {
|
|
17
|
+
readOnlyHint: true,
|
|
18
|
+
destructiveHint: false,
|
|
19
|
+
idempotentHint: true,
|
|
20
|
+
openWorldHint: true,
|
|
21
|
+
},
|
|
22
|
+
}, async (params) => {
|
|
23
|
+
try {
|
|
24
|
+
const client = await getClient();
|
|
25
|
+
let webhooks;
|
|
26
|
+
if (params.channel_id) {
|
|
27
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
28
|
+
if (!channel || !('fetchWebhooks' in channel)) {
|
|
29
|
+
return {
|
|
30
|
+
isError: true,
|
|
31
|
+
content: [{ type: "text", text: `Channel not found or doesn't support webhooks: ${params.channel_id}` }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
webhooks = await channel.fetchWebhooks();
|
|
35
|
+
}
|
|
36
|
+
else if (params.guild_id) {
|
|
37
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
38
|
+
if (!guild) {
|
|
39
|
+
return {
|
|
40
|
+
isError: true,
|
|
41
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
webhooks = await guild.fetchWebhooks();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return {
|
|
48
|
+
isError: true,
|
|
49
|
+
content: [{ type: "text", text: `Must provide either guild_id or channel_id` }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const webhookList = webhooks.map(wh => ({
|
|
53
|
+
id: wh.id,
|
|
54
|
+
name: wh.name,
|
|
55
|
+
channel: wh.channel?.id,
|
|
56
|
+
url: wh.url,
|
|
57
|
+
owner: wh.owner?.username || 'Unknown',
|
|
58
|
+
avatar: wh.avatarURL(),
|
|
59
|
+
}));
|
|
60
|
+
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'));
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: "text", text: truncateIfNeeded(result) }],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
isError: true,
|
|
68
|
+
content: [{ type: "text", text: `Error listing webhooks: ${error.message}` }],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
server.registerTool("discord_create_webhook", {
|
|
73
|
+
title: "Create Webhook",
|
|
74
|
+
description: `Create a webhook in a channel.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
- channel_id (string): Discord channel ID
|
|
78
|
+
- name (string): Webhook name
|
|
79
|
+
- avatar_url (string, optional): Avatar image URL
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Created webhook details including URL`,
|
|
83
|
+
inputSchema: CreateWebhookSchema,
|
|
84
|
+
annotations: {
|
|
85
|
+
readOnlyHint: false,
|
|
86
|
+
destructiveHint: false,
|
|
87
|
+
idempotentHint: false,
|
|
88
|
+
openWorldHint: true,
|
|
89
|
+
},
|
|
90
|
+
}, async (params) => {
|
|
91
|
+
try {
|
|
92
|
+
const client = await getClient();
|
|
93
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
94
|
+
if (!channel || !('createWebhook' in channel)) {
|
|
95
|
+
return {
|
|
96
|
+
isError: true,
|
|
97
|
+
content: [{ type: "text", text: `Channel not found or doesn't support webhooks: ${params.channel_id}` }],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const webhook = await channel.createWebhook({
|
|
101
|
+
name: params.name,
|
|
102
|
+
avatar: params.avatar_url,
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: `Created webhook "${webhook.name}" (ID: ${webhook.id})\nURL: ${webhook.url}` }],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
isError: true,
|
|
111
|
+
content: [{ type: "text", text: `Error creating webhook: ${error.message}` }],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
server.registerTool("discord_edit_webhook", {
|
|
116
|
+
title: "Edit Webhook",
|
|
117
|
+
description: `Edit an existing webhook.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
- webhook_id (string): Discord webhook ID
|
|
121
|
+
- name (string, optional): New webhook name
|
|
122
|
+
- channel_id (string, optional): Move webhook to different channel
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Updated webhook details`,
|
|
126
|
+
inputSchema: EditWebhookSchema,
|
|
127
|
+
annotations: {
|
|
128
|
+
readOnlyHint: false,
|
|
129
|
+
destructiveHint: false,
|
|
130
|
+
idempotentHint: true,
|
|
131
|
+
openWorldHint: true,
|
|
132
|
+
},
|
|
133
|
+
}, async (params) => {
|
|
134
|
+
try {
|
|
135
|
+
const client = await getClient();
|
|
136
|
+
const webhook = await client.fetchWebhook(params.webhook_id).catch(() => null);
|
|
137
|
+
if (!webhook) {
|
|
138
|
+
return {
|
|
139
|
+
isError: true,
|
|
140
|
+
content: [{ type: "text", text: `Webhook not found: ${params.webhook_id}` }],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const editOptions = {};
|
|
144
|
+
if (params.name !== undefined)
|
|
145
|
+
editOptions.name = params.name;
|
|
146
|
+
if (params.channel_id !== undefined)
|
|
147
|
+
editOptions.channel = params.channel_id;
|
|
148
|
+
await webhook.edit(editOptions);
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text", text: `Updated webhook "${webhook.name}" (ID: ${webhook.id})` }],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
isError: true,
|
|
156
|
+
content: [{ type: "text", text: `Error editing webhook: ${error.message}` }],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
server.registerTool("discord_delete_webhook", {
|
|
161
|
+
title: "Delete Webhook",
|
|
162
|
+
description: `Delete a webhook.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
- webhook_id (string): Discord webhook ID
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Confirmation of deletion`,
|
|
169
|
+
inputSchema: DeleteWebhookSchema,
|
|
170
|
+
annotations: {
|
|
171
|
+
readOnlyHint: false,
|
|
172
|
+
destructiveHint: true,
|
|
173
|
+
idempotentHint: true,
|
|
174
|
+
openWorldHint: true,
|
|
175
|
+
},
|
|
176
|
+
}, async (params) => {
|
|
177
|
+
try {
|
|
178
|
+
const client = await getClient();
|
|
179
|
+
const webhook = await client.fetchWebhook(params.webhook_id).catch(() => null);
|
|
180
|
+
if (!webhook) {
|
|
181
|
+
return {
|
|
182
|
+
isError: true,
|
|
183
|
+
content: [{ type: "text", text: `Webhook not found: ${params.webhook_id}` }],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const name = webhook.name;
|
|
187
|
+
await webhook.delete();
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text", text: `Deleted webhook: ${name}` }],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return {
|
|
194
|
+
isError: true,
|
|
195
|
+
content: [{ type: "text", text: `Error deleting webhook: ${error.message}` }],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|