@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
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { ListChannelsSchema, GetChannelSchema, CreateChannelSchema, DeleteChannelSchema, EditChannelSchema, SetChannelPermissionsSchema, RemoveChannelPermissionsSchema, GetChannelPermissionsSchema, SyncChannelPermissionsSchema, } from "../schemas/index.js";
|
|
2
|
+
import { getClient, formatChannel, channelToMarkdown, formatResponse, truncateIfNeeded, getChannelType, } from "../services/discord.js";
|
|
3
|
+
import { ChannelType, GuildChannel, PermissionsBitField, OverwriteType, SortOrderType, ForumLayoutType } from "discord.js";
|
|
4
|
+
// Helper function to convert permission strings to bitfield
|
|
5
|
+
function parsePermissions(permissions) {
|
|
6
|
+
let bits = BigInt(0);
|
|
7
|
+
for (const perm of permissions) {
|
|
8
|
+
const flag = PermissionsBitField.Flags[perm];
|
|
9
|
+
if (flag) {
|
|
10
|
+
bits |= flag;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return bits;
|
|
14
|
+
}
|
|
15
|
+
// List of common permission names for reference
|
|
16
|
+
const PERMISSION_LIST = [
|
|
17
|
+
'ViewChannel', 'ManageChannels', 'ManageRoles', 'CreateInstantInvite',
|
|
18
|
+
'SendMessages', 'SendMessagesInThreads', 'CreatePublicThreads', 'CreatePrivateThreads',
|
|
19
|
+
'EmbedLinks', 'AttachFiles', 'AddReactions', 'UseExternalEmojis', 'UseExternalStickers',
|
|
20
|
+
'MentionEveryone', 'ManageMessages', 'ManageThreads', 'ReadMessageHistory',
|
|
21
|
+
'SendTTSMessages', 'UseApplicationCommands',
|
|
22
|
+
'Connect', 'Speak', 'Stream', 'UseEmbeddedActivities', 'UseSoundboard',
|
|
23
|
+
'UseExternalSounds', 'UseVAD', 'PrioritySpeaker', 'MuteMembers', 'DeafenMembers', 'MoveMembers',
|
|
24
|
+
'ManageEvents', 'ManageWebhooks'
|
|
25
|
+
];
|
|
26
|
+
export function registerChannelTools(server) {
|
|
27
|
+
server.registerTool("discord_list_channels", {
|
|
28
|
+
title: "List Discord Channels",
|
|
29
|
+
description: `List channels in a Discord server.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
- guild_id (string): Discord server/guild ID
|
|
33
|
+
- type ('text' | 'voice' | 'category' | 'all'): Filter by channel type (default: 'all')
|
|
34
|
+
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of channels with id, name, type, topic, and category info`,
|
|
38
|
+
inputSchema: ListChannelsSchema,
|
|
39
|
+
annotations: {
|
|
40
|
+
readOnlyHint: true,
|
|
41
|
+
destructiveHint: false,
|
|
42
|
+
idempotentHint: true,
|
|
43
|
+
openWorldHint: true,
|
|
44
|
+
},
|
|
45
|
+
}, async (params) => {
|
|
46
|
+
try {
|
|
47
|
+
const client = await getClient();
|
|
48
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
49
|
+
if (!guild) {
|
|
50
|
+
return {
|
|
51
|
+
isError: true,
|
|
52
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Get all channels as array to avoid type predicate issues
|
|
56
|
+
const allChannels = Array.from(guild.channels.cache.values());
|
|
57
|
+
// Filter out threads and apply type filter
|
|
58
|
+
let filteredChannels = allChannels.filter(c => !c.isThread());
|
|
59
|
+
if (params.type !== "all") {
|
|
60
|
+
const targetType = getChannelType(params.type);
|
|
61
|
+
if (targetType !== null) {
|
|
62
|
+
filteredChannels = filteredChannels.filter(c => c.type === targetType);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const formatted = filteredChannels.map(c => formatChannel(c));
|
|
66
|
+
const text = formatResponse(formatted, params.response_format, (items) => items.map(channelToMarkdown).join("\n\n"));
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: truncateIfNeeded(text) }],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
isError: true,
|
|
74
|
+
content: [{ type: "text", text: `Error listing channels: ${error.message}` }],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
server.registerTool("discord_get_channel", {
|
|
79
|
+
title: "Get Discord Channel Info",
|
|
80
|
+
description: `Get detailed information about a specific channel.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
- channel_id (string): Discord channel ID
|
|
84
|
+
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Channel details including name, type, topic, and category`,
|
|
88
|
+
inputSchema: GetChannelSchema,
|
|
89
|
+
annotations: {
|
|
90
|
+
readOnlyHint: true,
|
|
91
|
+
destructiveHint: false,
|
|
92
|
+
idempotentHint: true,
|
|
93
|
+
openWorldHint: true,
|
|
94
|
+
},
|
|
95
|
+
}, async (params) => {
|
|
96
|
+
try {
|
|
97
|
+
const client = await getClient();
|
|
98
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
99
|
+
if (!channel || !(channel instanceof GuildChannel)) {
|
|
100
|
+
return {
|
|
101
|
+
isError: true,
|
|
102
|
+
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const formatted = formatChannel(channel);
|
|
106
|
+
const text = formatResponse(formatted, params.response_format, channelToMarkdown);
|
|
107
|
+
return {
|
|
108
|
+
content: [{ type: "text", text }],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
isError: true,
|
|
114
|
+
content: [{ type: "text", text: `Error getting channel: ${error.message}` }],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
server.registerTool("discord_create_channel", {
|
|
119
|
+
title: "Create Discord Channel",
|
|
120
|
+
description: `Create a new channel or category in a Discord server.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
- guild_id (string): Discord server/guild ID
|
|
124
|
+
- name (string): Channel name
|
|
125
|
+
- type ('text' | 'voice' | 'category' | 'forum'): Channel type (default: 'text')
|
|
126
|
+
- topic (string, optional): Channel topic (text/forum channels only)
|
|
127
|
+
- parent_id (string, optional): Category ID to create channel under (not for categories)
|
|
128
|
+
- nsfw (boolean, optional): Whether the channel is NSFW (text/forum channels only)
|
|
129
|
+
- bitrate (number, optional): Bitrate for voice channels (8000-384000)
|
|
130
|
+
- user_limit (number, optional): User limit for voice channels (0 = unlimited)
|
|
131
|
+
- default_reaction_emoji (string, optional): Default emoji for forum posts (emoji char or custom ID)
|
|
132
|
+
- default_sort_order ('latest_activity' | 'creation_date', optional): Sort order for forum posts
|
|
133
|
+
- default_forum_layout ('not_set' | 'list_view' | 'gallery_view', optional): Forum layout
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
The created channel's details`,
|
|
137
|
+
inputSchema: CreateChannelSchema,
|
|
138
|
+
annotations: {
|
|
139
|
+
readOnlyHint: false,
|
|
140
|
+
destructiveHint: false,
|
|
141
|
+
idempotentHint: false,
|
|
142
|
+
openWorldHint: true,
|
|
143
|
+
},
|
|
144
|
+
}, async (params) => {
|
|
145
|
+
try {
|
|
146
|
+
const client = await getClient();
|
|
147
|
+
const guild = client.guilds.cache.get(params.guild_id);
|
|
148
|
+
if (!guild) {
|
|
149
|
+
return {
|
|
150
|
+
isError: true,
|
|
151
|
+
content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
let channelType;
|
|
155
|
+
switch (params.type) {
|
|
156
|
+
case "voice":
|
|
157
|
+
channelType = ChannelType.GuildVoice;
|
|
158
|
+
break;
|
|
159
|
+
case "category":
|
|
160
|
+
channelType = ChannelType.GuildCategory;
|
|
161
|
+
break;
|
|
162
|
+
case "forum":
|
|
163
|
+
channelType = ChannelType.GuildForum;
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
channelType = ChannelType.GuildText;
|
|
167
|
+
}
|
|
168
|
+
const channelOptions = {
|
|
169
|
+
name: params.name,
|
|
170
|
+
type: channelType,
|
|
171
|
+
};
|
|
172
|
+
// Only add these for non-category channels
|
|
173
|
+
if (params.type !== "category") {
|
|
174
|
+
if (params.parent_id)
|
|
175
|
+
channelOptions.parent = params.parent_id;
|
|
176
|
+
}
|
|
177
|
+
// Text channel specific options
|
|
178
|
+
if (params.type === "text" || !params.type) {
|
|
179
|
+
if (params.topic)
|
|
180
|
+
channelOptions.topic = params.topic;
|
|
181
|
+
if (params.nsfw !== undefined)
|
|
182
|
+
channelOptions.nsfw = params.nsfw;
|
|
183
|
+
}
|
|
184
|
+
// Voice channel specific options
|
|
185
|
+
if (params.type === "voice") {
|
|
186
|
+
if (params.bitrate)
|
|
187
|
+
channelOptions.bitrate = params.bitrate;
|
|
188
|
+
if (params.user_limit !== undefined)
|
|
189
|
+
channelOptions.userLimit = params.user_limit;
|
|
190
|
+
}
|
|
191
|
+
// Forum channel specific options
|
|
192
|
+
if (params.type === "forum") {
|
|
193
|
+
if (params.topic)
|
|
194
|
+
channelOptions.topic = params.topic;
|
|
195
|
+
if (params.nsfw !== undefined)
|
|
196
|
+
channelOptions.nsfw = params.nsfw;
|
|
197
|
+
if (params.default_reaction_emoji) {
|
|
198
|
+
// Check if it's a custom emoji ID (numeric) or unicode emoji
|
|
199
|
+
const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
|
|
200
|
+
channelOptions.defaultReactionEmoji = isCustomEmoji
|
|
201
|
+
? { id: params.default_reaction_emoji }
|
|
202
|
+
: { name: params.default_reaction_emoji };
|
|
203
|
+
}
|
|
204
|
+
if (params.default_sort_order) {
|
|
205
|
+
channelOptions.defaultSortOrder = params.default_sort_order === "latest_activity"
|
|
206
|
+
? SortOrderType.LatestActivity
|
|
207
|
+
: SortOrderType.CreationDate;
|
|
208
|
+
}
|
|
209
|
+
if (params.default_forum_layout) {
|
|
210
|
+
const layoutMap = {
|
|
211
|
+
not_set: ForumLayoutType.NotSet,
|
|
212
|
+
list_view: ForumLayoutType.ListView,
|
|
213
|
+
gallery_view: ForumLayoutType.GalleryView,
|
|
214
|
+
};
|
|
215
|
+
channelOptions.defaultForumLayout = layoutMap[params.default_forum_layout];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const channel = await guild.channels.create(channelOptions);
|
|
219
|
+
const typeLabel = params.type === "category" ? "category" : "channel";
|
|
220
|
+
const prefix = params.type === "category" ? "📁" : "#";
|
|
221
|
+
return {
|
|
222
|
+
content: [{ type: "text", text: `Created ${typeLabel} ${prefix}${channel.name} (ID: ${channel.id})` }],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
isError: true,
|
|
228
|
+
content: [{ type: "text", text: `Error creating channel: ${error.message}` }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
server.registerTool("discord_delete_channel", {
|
|
233
|
+
title: "Delete Discord Channel",
|
|
234
|
+
description: `Delete a channel from a Discord server. This action is permanent!
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
- channel_id (string): Discord channel ID to delete
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Confirmation of deletion`,
|
|
241
|
+
inputSchema: DeleteChannelSchema,
|
|
242
|
+
annotations: {
|
|
243
|
+
readOnlyHint: false,
|
|
244
|
+
destructiveHint: true,
|
|
245
|
+
idempotentHint: false,
|
|
246
|
+
openWorldHint: true,
|
|
247
|
+
},
|
|
248
|
+
}, async (params) => {
|
|
249
|
+
try {
|
|
250
|
+
const client = await getClient();
|
|
251
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
252
|
+
if (!channel || !('delete' in channel)) {
|
|
253
|
+
return {
|
|
254
|
+
isError: true,
|
|
255
|
+
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const name = 'name' in channel ? channel.name : params.channel_id;
|
|
259
|
+
await channel.delete();
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: `Deleted channel: ${name}` }],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
return {
|
|
266
|
+
isError: true,
|
|
267
|
+
content: [{ type: "text", text: `Error deleting channel: ${error.message}` }],
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
server.registerTool("discord_edit_channel", {
|
|
272
|
+
title: "Edit Discord Channel",
|
|
273
|
+
description: `Edit a channel's properties including name, topic, category, position, and more.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
- channel_id (string): Discord channel ID
|
|
277
|
+
- name (string, optional): New channel name
|
|
278
|
+
- topic (string, optional): New channel topic
|
|
279
|
+
- parent_id (string, optional): Move to a different category (or 'none' to remove from category)
|
|
280
|
+
- position (number, optional): New position of the channel
|
|
281
|
+
- nsfw (boolean, optional): Whether the channel is NSFW
|
|
282
|
+
- bitrate (number, optional): Bitrate for voice channels (8000-384000)
|
|
283
|
+
- user_limit (number, optional): User limit for voice channels
|
|
284
|
+
- default_reaction_emoji (string, optional): Default emoji for forum posts (emoji char or custom ID, 'none' to remove)
|
|
285
|
+
- default_sort_order ('latest_activity' | 'creation_date', optional): Sort order for forum posts
|
|
286
|
+
- default_forum_layout ('not_set' | 'list_view' | 'gallery_view', optional): Forum layout
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Updated channel details`,
|
|
290
|
+
inputSchema: EditChannelSchema,
|
|
291
|
+
annotations: {
|
|
292
|
+
readOnlyHint: false,
|
|
293
|
+
destructiveHint: false,
|
|
294
|
+
idempotentHint: true,
|
|
295
|
+
openWorldHint: true,
|
|
296
|
+
},
|
|
297
|
+
}, async (params) => {
|
|
298
|
+
try {
|
|
299
|
+
const client = await getClient();
|
|
300
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
301
|
+
if (!channel || !('edit' in channel)) {
|
|
302
|
+
return {
|
|
303
|
+
isError: true,
|
|
304
|
+
content: [{ type: "text", text: `Channel not found: ${params.channel_id}` }],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const updates = {};
|
|
308
|
+
if (params.name)
|
|
309
|
+
updates.name = params.name;
|
|
310
|
+
if (params.topic !== undefined)
|
|
311
|
+
updates.topic = params.topic;
|
|
312
|
+
if (params.position !== undefined)
|
|
313
|
+
updates.position = params.position;
|
|
314
|
+
if (params.nsfw !== undefined)
|
|
315
|
+
updates.nsfw = params.nsfw;
|
|
316
|
+
if (params.bitrate !== undefined)
|
|
317
|
+
updates.bitrate = params.bitrate;
|
|
318
|
+
if (params.user_limit !== undefined)
|
|
319
|
+
updates.userLimit = params.user_limit;
|
|
320
|
+
if (params.parent_id !== undefined) {
|
|
321
|
+
updates.parent = params.parent_id === 'none' ? null : params.parent_id;
|
|
322
|
+
}
|
|
323
|
+
// Forum channel specific options
|
|
324
|
+
if (params.default_reaction_emoji !== undefined) {
|
|
325
|
+
if (params.default_reaction_emoji === 'none') {
|
|
326
|
+
updates.defaultReactionEmoji = null;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
// Check if it's a custom emoji ID (numeric) or unicode emoji
|
|
330
|
+
const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
|
|
331
|
+
updates.defaultReactionEmoji = isCustomEmoji
|
|
332
|
+
? { id: params.default_reaction_emoji }
|
|
333
|
+
: { name: params.default_reaction_emoji };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (params.default_sort_order !== undefined) {
|
|
337
|
+
updates.defaultSortOrder = params.default_sort_order === "latest_activity"
|
|
338
|
+
? SortOrderType.LatestActivity
|
|
339
|
+
: SortOrderType.CreationDate;
|
|
340
|
+
}
|
|
341
|
+
if (params.default_forum_layout !== undefined) {
|
|
342
|
+
const layoutMap = {
|
|
343
|
+
not_set: ForumLayoutType.NotSet,
|
|
344
|
+
list_view: ForumLayoutType.ListView,
|
|
345
|
+
gallery_view: ForumLayoutType.GalleryView,
|
|
346
|
+
};
|
|
347
|
+
updates.defaultForumLayout = layoutMap[params.default_forum_layout];
|
|
348
|
+
}
|
|
349
|
+
await channel.edit(updates);
|
|
350
|
+
return {
|
|
351
|
+
content: [{ type: "text", text: `Updated channel ${params.channel_id}` }],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
return {
|
|
356
|
+
isError: true,
|
|
357
|
+
content: [{ type: "text", text: `Error editing channel: ${error.message}` }],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// PERMISSION TOOLS
|
|
363
|
+
// ============================================================================
|
|
364
|
+
server.registerTool("discord_set_channel_permissions", {
|
|
365
|
+
title: "Set Channel Permissions",
|
|
366
|
+
description: `Set permission overrides for a role or member on a channel.
|
|
367
|
+
|
|
368
|
+
Common permission names:
|
|
369
|
+
- View: ViewChannel
|
|
370
|
+
- Messages: SendMessages, ReadMessageHistory, ManageMessages, EmbedLinks, AttachFiles, AddReactions
|
|
371
|
+
- Voice: Connect, Speak, Stream, MuteMembers, DeafenMembers, MoveMembers
|
|
372
|
+
- Threads: CreatePublicThreads, CreatePrivateThreads, SendMessagesInThreads
|
|
373
|
+
- Management: ManageChannels, ManageRoles, ManageWebhooks
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
- channel_id (string): Discord channel ID
|
|
377
|
+
- target_id (string): Role or User ID to set permissions for
|
|
378
|
+
- target_type ('role' | 'member'): Whether target is a role or member
|
|
379
|
+
- allow (string[], optional): Permissions to allow (e.g., ['ViewChannel', 'SendMessages'])
|
|
380
|
+
- deny (string[], optional): Permissions to deny (e.g., ['SendMessages'])
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
Confirmation of permission changes`,
|
|
384
|
+
inputSchema: SetChannelPermissionsSchema,
|
|
385
|
+
annotations: {
|
|
386
|
+
readOnlyHint: false,
|
|
387
|
+
destructiveHint: false,
|
|
388
|
+
idempotentHint: true,
|
|
389
|
+
openWorldHint: true,
|
|
390
|
+
},
|
|
391
|
+
}, async (params) => {
|
|
392
|
+
try {
|
|
393
|
+
const client = await getClient();
|
|
394
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
395
|
+
if (!channel || !('permissionOverwrites' in channel)) {
|
|
396
|
+
return {
|
|
397
|
+
isError: true,
|
|
398
|
+
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
const guildChannel = channel;
|
|
402
|
+
// Build permission overwrite object with individual permission keys
|
|
403
|
+
const permissionOverwrite = {};
|
|
404
|
+
// Set allowed permissions to true
|
|
405
|
+
if (params.allow) {
|
|
406
|
+
for (const perm of params.allow) {
|
|
407
|
+
permissionOverwrite[perm] = true;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Set denied permissions to false
|
|
411
|
+
if (params.deny) {
|
|
412
|
+
for (const perm of params.deny) {
|
|
413
|
+
permissionOverwrite[perm] = false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
await guildChannel.permissionOverwrites.edit(params.target_id, permissionOverwrite, {
|
|
417
|
+
type: params.target_type === 'role' ? OverwriteType.Role : OverwriteType.Member,
|
|
418
|
+
});
|
|
419
|
+
const allowList = params.allow?.join(', ') || 'none';
|
|
420
|
+
const denyList = params.deny?.join(', ') || 'none';
|
|
421
|
+
return {
|
|
422
|
+
content: [{ type: "text", text: `Updated permissions for ${params.target_type} ${params.target_id} on channel ${params.channel_id}\nAllowed: ${allowList}\nDenied: ${denyList}` }],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
return {
|
|
427
|
+
isError: true,
|
|
428
|
+
content: [{ type: "text", text: `Error setting permissions: ${error.message}` }],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
server.registerTool("discord_remove_channel_permissions", {
|
|
433
|
+
title: "Remove Channel Permission Overrides",
|
|
434
|
+
description: `Remove all permission overrides for a role or member on a channel.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
- channel_id (string): Discord channel ID
|
|
438
|
+
- target_id (string): Role or User ID to remove permission overrides for
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
Confirmation of removal`,
|
|
442
|
+
inputSchema: RemoveChannelPermissionsSchema,
|
|
443
|
+
annotations: {
|
|
444
|
+
readOnlyHint: false,
|
|
445
|
+
destructiveHint: true,
|
|
446
|
+
idempotentHint: true,
|
|
447
|
+
openWorldHint: true,
|
|
448
|
+
},
|
|
449
|
+
}, async (params) => {
|
|
450
|
+
try {
|
|
451
|
+
const client = await getClient();
|
|
452
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
453
|
+
if (!channel || !('permissionOverwrites' in channel)) {
|
|
454
|
+
return {
|
|
455
|
+
isError: true,
|
|
456
|
+
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
const guildChannel = channel;
|
|
460
|
+
await guildChannel.permissionOverwrites.delete(params.target_id);
|
|
461
|
+
return {
|
|
462
|
+
content: [{ type: "text", text: `Removed permission overrides for ${params.target_id} on channel ${params.channel_id}` }],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
return {
|
|
467
|
+
isError: true,
|
|
468
|
+
content: [{ type: "text", text: `Error removing permissions: ${error.message}` }],
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
server.registerTool("discord_get_channel_permissions", {
|
|
473
|
+
title: "Get Channel Permissions",
|
|
474
|
+
description: `Get all permission overrides for a channel.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
- channel_id (string): Discord channel ID
|
|
478
|
+
- response_format ('markdown' | 'json'): Output format (default: 'json')
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
List of permission overrides with allow/deny for each role/member`,
|
|
482
|
+
inputSchema: GetChannelPermissionsSchema,
|
|
483
|
+
annotations: {
|
|
484
|
+
readOnlyHint: true,
|
|
485
|
+
destructiveHint: false,
|
|
486
|
+
idempotentHint: true,
|
|
487
|
+
openWorldHint: true,
|
|
488
|
+
},
|
|
489
|
+
}, async (params) => {
|
|
490
|
+
try {
|
|
491
|
+
const client = await getClient();
|
|
492
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
493
|
+
if (!channel || !('permissionOverwrites' in channel)) {
|
|
494
|
+
return {
|
|
495
|
+
isError: true,
|
|
496
|
+
content: [{ type: "text", text: `Channel not found or doesn't support permissions: ${params.channel_id}` }],
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const guildChannel = channel;
|
|
500
|
+
const overwrites = guildChannel.permissionOverwrites.cache;
|
|
501
|
+
const formatted = overwrites.map(ow => {
|
|
502
|
+
const allowedPerms = new PermissionsBitField(ow.allow).toArray();
|
|
503
|
+
const deniedPerms = new PermissionsBitField(ow.deny).toArray();
|
|
504
|
+
return {
|
|
505
|
+
id: ow.id,
|
|
506
|
+
type: ow.type === OverwriteType.Role ? 'role' : 'member',
|
|
507
|
+
allow: allowedPerms,
|
|
508
|
+
deny: deniedPerms,
|
|
509
|
+
};
|
|
510
|
+
});
|
|
511
|
+
if (params.response_format === 'json') {
|
|
512
|
+
return {
|
|
513
|
+
content: [{ type: "text", text: JSON.stringify(Array.from(formatted.values()), null, 2) }],
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
// Markdown format
|
|
517
|
+
const lines = Array.from(formatted.values()).map(ow => {
|
|
518
|
+
const allowStr = ow.allow.length > 0 ? ow.allow.join(', ') : 'none';
|
|
519
|
+
const denyStr = ow.deny.length > 0 ? ow.deny.join(', ') : 'none';
|
|
520
|
+
return `### ${ow.type}: ${ow.id}\n- **Allow**: ${allowStr}\n- **Deny**: ${denyStr}`;
|
|
521
|
+
});
|
|
522
|
+
const text = lines.length > 0 ? lines.join('\n\n') : 'No permission overrides on this channel.';
|
|
523
|
+
return {
|
|
524
|
+
content: [{ type: "text", text }],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
return {
|
|
529
|
+
isError: true,
|
|
530
|
+
content: [{ type: "text", text: `Error getting permissions: ${error.message}` }],
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
server.registerTool("discord_sync_channel_permissions", {
|
|
535
|
+
title: "Sync Channel Permissions with Category",
|
|
536
|
+
description: `Sync a channel's permissions with its parent category.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
- channel_id (string): Discord channel ID
|
|
540
|
+
|
|
541
|
+
Returns:
|
|
542
|
+
Confirmation of sync`,
|
|
543
|
+
inputSchema: SyncChannelPermissionsSchema,
|
|
544
|
+
annotations: {
|
|
545
|
+
readOnlyHint: false,
|
|
546
|
+
destructiveHint: false,
|
|
547
|
+
idempotentHint: true,
|
|
548
|
+
openWorldHint: true,
|
|
549
|
+
},
|
|
550
|
+
}, async (params) => {
|
|
551
|
+
try {
|
|
552
|
+
const client = await getClient();
|
|
553
|
+
const channel = client.channels.cache.get(params.channel_id);
|
|
554
|
+
if (!channel || !('lockPermissions' in channel)) {
|
|
555
|
+
return {
|
|
556
|
+
isError: true,
|
|
557
|
+
content: [{ type: "text", text: `Channel not found or doesn't support permission sync: ${params.channel_id}` }],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
await channel.lockPermissions();
|
|
561
|
+
return {
|
|
562
|
+
content: [{ type: "text", text: `Synced permissions for channel ${params.channel_id} with its parent category` }],
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
return {
|
|
567
|
+
isError: true,
|
|
568
|
+
content: [{ type: "text", text: `Error syncing permissions: ${error.message}` }],
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
}
|