@quadslab.io/discord-mcp 1.0.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/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/discord-client.d.ts +57 -0
- package/dist/discord-client.js +188 -0
- package/dist/discord-client.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +15 -0
- package/dist/mcp-server.js +65 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/tools/automod.d.ts +6 -0
- package/dist/tools/automod.js +376 -0
- package/dist/tools/automod.js.map +1 -0
- package/dist/tools/channels.d.ts +6 -0
- package/dist/tools/channels.js +840 -0
- package/dist/tools/channels.js.map +1 -0
- package/dist/tools/emojis.d.ts +6 -0
- package/dist/tools/emojis.js +295 -0
- package/dist/tools/emojis.js.map +1 -0
- package/dist/tools/events.d.ts +6 -0
- package/dist/tools/events.js +304 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/forums.d.ts +6 -0
- package/dist/tools/forums.js +276 -0
- package/dist/tools/forums.js.map +1 -0
- package/dist/tools/guild.d.ts +6 -0
- package/dist/tools/guild.js +88 -0
- package/dist/tools/guild.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.js +142 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/members.d.ts +6 -0
- package/dist/tools/members.js +628 -0
- package/dist/tools/members.js.map +1 -0
- package/dist/tools/messages.d.ts +6 -0
- package/dist/tools/messages.js +688 -0
- package/dist/tools/messages.js.map +1 -0
- package/dist/tools/reactions.d.ts +6 -0
- package/dist/tools/reactions.js +137 -0
- package/dist/tools/reactions.js.map +1 -0
- package/dist/tools/roles.d.ts +6 -0
- package/dist/tools/roles.js +469 -0
- package/dist/tools/roles.js.map +1 -0
- package/dist/tools/server.d.ts +6 -0
- package/dist/tools/server.js +607 -0
- package/dist/tools/server.js.map +1 -0
- package/dist/tools/stage.d.ts +6 -0
- package/dist/tools/stage.js +153 -0
- package/dist/tools/stage.js.map +1 -0
- package/dist/tools/threads.d.ts +6 -0
- package/dist/tools/threads.js +331 -0
- package/dist/tools/threads.js.map +1 -0
- package/dist/tools/utils.d.ts +21 -0
- package/dist/tools/utils.js +315 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/webhooks.d.ts +6 -0
- package/dist/tools/webhooks.js +195 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { getGuild, getServerCache } from '../discord-client.js';
|
|
2
|
+
import { ChannelType } from 'discord.js';
|
|
3
|
+
/**
|
|
4
|
+
* Utility functions for smart matching of Discord entities
|
|
5
|
+
* These functions use fuzzy matching to find channels, roles, and members
|
|
6
|
+
* even when the input isn't exact.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Calculate similarity between two strings (0-1)
|
|
10
|
+
*/
|
|
11
|
+
function similarity(a, b) {
|
|
12
|
+
const aLower = a.toLowerCase();
|
|
13
|
+
const bLower = b.toLowerCase();
|
|
14
|
+
if (aLower === bLower)
|
|
15
|
+
return 1;
|
|
16
|
+
if (aLower.includes(bLower) || bLower.includes(aLower))
|
|
17
|
+
return 0.8;
|
|
18
|
+
// Handle hyphen/space/underscore variations
|
|
19
|
+
const normalize = (s) => s.replace(/[-_\s]/g, '');
|
|
20
|
+
if (normalize(aLower) === normalize(bLower))
|
|
21
|
+
return 0.95;
|
|
22
|
+
if (normalize(aLower).includes(normalize(bLower)))
|
|
23
|
+
return 0.75;
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clean up channel identifier - remove # prefix, handle mentions
|
|
28
|
+
*/
|
|
29
|
+
function cleanChannelIdentifier(identifier) {
|
|
30
|
+
return identifier
|
|
31
|
+
.replace(/^#/, '') // Remove # prefix
|
|
32
|
+
.replace(/^<#(\d+)>$/, '$1') // Handle <#id> mention format
|
|
33
|
+
.trim();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Clean up role identifier - remove @ prefix, handle mentions
|
|
37
|
+
*/
|
|
38
|
+
function cleanRoleIdentifier(identifier) {
|
|
39
|
+
return identifier
|
|
40
|
+
.replace(/^@/, '') // Remove @ prefix
|
|
41
|
+
.replace(/^<@&(\d+)>$/, '$1') // Handle <@&id> mention format
|
|
42
|
+
.trim();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Clean up member identifier - remove @ prefix, handle mentions
|
|
46
|
+
*/
|
|
47
|
+
function cleanMemberIdentifier(identifier) {
|
|
48
|
+
return identifier
|
|
49
|
+
.replace(/^@/, '') // Remove @ prefix
|
|
50
|
+
.replace(/^<@!?(\d+)>$/, '$1') // Handle <@id> or <@!id> mention format
|
|
51
|
+
.trim();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Smart find a channel that can receive messages - includes text channels, announcement channels, and voice channel text chats
|
|
55
|
+
*/
|
|
56
|
+
export async function smartFindTextChannel(identifier) {
|
|
57
|
+
const guild = await getGuild();
|
|
58
|
+
const cache = await getServerCache();
|
|
59
|
+
const cleanId = cleanChannelIdentifier(identifier);
|
|
60
|
+
// Use cache for fuzzy matching first (faster)
|
|
61
|
+
// Include text, announcement, and voice channels (voice channels have text chat)
|
|
62
|
+
const messagableChannelTypes = ['text', 'announcement', 'voice', 'stage'];
|
|
63
|
+
const cachedMessageChannels = cache.channels.filter(c => messagableChannelTypes.includes(c.type));
|
|
64
|
+
// Try exact ID match
|
|
65
|
+
let matchedId = cachedMessageChannels.find(c => c.id === cleanId)?.id;
|
|
66
|
+
// Try exact name match (case-insensitive)
|
|
67
|
+
if (!matchedId) {
|
|
68
|
+
matchedId = cachedMessageChannels.find(c => c.name.toLowerCase() === cleanId.toLowerCase())?.id;
|
|
69
|
+
}
|
|
70
|
+
// Try fuzzy match using cache
|
|
71
|
+
if (!matchedId) {
|
|
72
|
+
let bestScore = 0;
|
|
73
|
+
for (const ch of cachedMessageChannels) {
|
|
74
|
+
const score = similarity(ch.name, cleanId);
|
|
75
|
+
if (score > bestScore && score >= 0.7) {
|
|
76
|
+
bestScore = score;
|
|
77
|
+
matchedId = ch.id;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// If found via cache, get the actual channel object
|
|
82
|
+
if (matchedId) {
|
|
83
|
+
const channel = guild.channels.cache.get(matchedId);
|
|
84
|
+
if (channel && (channel.type === ChannelType.GuildText ||
|
|
85
|
+
channel.type === ChannelType.GuildAnnouncement ||
|
|
86
|
+
channel.type === ChannelType.GuildVoice ||
|
|
87
|
+
channel.type === ChannelType.GuildStageVoice)) {
|
|
88
|
+
return channel;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Not found - provide helpful error with suggestions from cache
|
|
92
|
+
const suggestions = cachedMessageChannels
|
|
93
|
+
.map(c => ({ name: c.name, type: c.type, score: similarity(c.name, cleanId) }))
|
|
94
|
+
.filter(s => s.score > 0.3)
|
|
95
|
+
.sort((a, b) => b.score - a.score)
|
|
96
|
+
.slice(0, 3)
|
|
97
|
+
.map(s => s.type === 'voice' ? `🔊${s.name}` : `#${s.name}`);
|
|
98
|
+
let errorMsg = `Channel "${identifier}" not found.`;
|
|
99
|
+
if (suggestions.length > 0) {
|
|
100
|
+
errorMsg += ` Did you mean: ${suggestions.join(', ')}?`;
|
|
101
|
+
}
|
|
102
|
+
// Also list all available channels if few
|
|
103
|
+
if (cachedMessageChannels.length <= 15) {
|
|
104
|
+
const channelList = cachedMessageChannels.map(c => c.type === 'voice' ? `🔊${c.name}` : `#${c.name}`);
|
|
105
|
+
errorMsg += ` Available channels: ${channelList.join(', ')}`;
|
|
106
|
+
}
|
|
107
|
+
throw new Error(errorMsg);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Smart find any channel (text, voice, category) - uses cached data
|
|
111
|
+
*/
|
|
112
|
+
export async function smartFindChannel(identifier) {
|
|
113
|
+
const guild = await getGuild();
|
|
114
|
+
const cache = await getServerCache();
|
|
115
|
+
const cleanId = cleanChannelIdentifier(identifier);
|
|
116
|
+
// Use cache for fuzzy matching
|
|
117
|
+
const cachedChannels = cache.channels;
|
|
118
|
+
// Try exact ID match
|
|
119
|
+
let matchedId = cachedChannels.find(c => c.id === cleanId)?.id;
|
|
120
|
+
// Try exact name match (case-insensitive)
|
|
121
|
+
if (!matchedId) {
|
|
122
|
+
matchedId = cachedChannels.find(c => c.name.toLowerCase() === cleanId.toLowerCase())?.id;
|
|
123
|
+
}
|
|
124
|
+
// Try fuzzy match using cache
|
|
125
|
+
if (!matchedId) {
|
|
126
|
+
let bestScore = 0;
|
|
127
|
+
for (const ch of cachedChannels) {
|
|
128
|
+
const score = similarity(ch.name, cleanId);
|
|
129
|
+
if (score > bestScore && score >= 0.7) {
|
|
130
|
+
bestScore = score;
|
|
131
|
+
matchedId = ch.id;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// If found via cache, get the actual channel object
|
|
136
|
+
if (matchedId) {
|
|
137
|
+
const channel = guild.channels.cache.get(matchedId);
|
|
138
|
+
if (channel) {
|
|
139
|
+
return channel;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Not found - provide helpful error with suggestions from cache
|
|
143
|
+
const suggestions = cachedChannels
|
|
144
|
+
.map(c => ({ name: c.name, score: similarity(c.name, cleanId) }))
|
|
145
|
+
.filter(s => s.score > 0.3)
|
|
146
|
+
.sort((a, b) => b.score - a.score)
|
|
147
|
+
.slice(0, 3)
|
|
148
|
+
.map(s => s.name);
|
|
149
|
+
let errorMsg = `Channel "${identifier}" not found.`;
|
|
150
|
+
if (suggestions.length > 0) {
|
|
151
|
+
errorMsg += ` Did you mean: ${suggestions.join(', ')}?`;
|
|
152
|
+
}
|
|
153
|
+
throw new Error(errorMsg);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Smart find category channel - uses cached data
|
|
157
|
+
*/
|
|
158
|
+
export async function smartFindCategory(identifier) {
|
|
159
|
+
const guild = await getGuild();
|
|
160
|
+
const cache = await getServerCache();
|
|
161
|
+
const cleanId = cleanChannelIdentifier(identifier);
|
|
162
|
+
// Use cache for fuzzy matching
|
|
163
|
+
const cachedCategories = cache.channels.filter(c => c.type === 'category');
|
|
164
|
+
// Try exact ID match
|
|
165
|
+
let matchedId = cachedCategories.find(c => c.id === cleanId)?.id;
|
|
166
|
+
// Try exact name match (case-insensitive)
|
|
167
|
+
if (!matchedId) {
|
|
168
|
+
matchedId = cachedCategories.find(c => c.name.toLowerCase() === cleanId.toLowerCase())?.id;
|
|
169
|
+
}
|
|
170
|
+
// Try fuzzy match using cache
|
|
171
|
+
if (!matchedId) {
|
|
172
|
+
let bestScore = 0;
|
|
173
|
+
for (const cat of cachedCategories) {
|
|
174
|
+
const score = similarity(cat.name, cleanId);
|
|
175
|
+
if (score > bestScore && score >= 0.7) {
|
|
176
|
+
bestScore = score;
|
|
177
|
+
matchedId = cat.id;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// If found via cache, get the actual channel object
|
|
182
|
+
if (matchedId) {
|
|
183
|
+
const channel = guild.channels.cache.get(matchedId);
|
|
184
|
+
if (channel && channel.type === ChannelType.GuildCategory) {
|
|
185
|
+
return channel;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Not found - provide helpful error with suggestions from cache
|
|
189
|
+
const suggestions = cachedCategories
|
|
190
|
+
.map(c => ({ name: c.name, score: similarity(c.name, cleanId) }))
|
|
191
|
+
.filter(s => s.score > 0.3)
|
|
192
|
+
.sort((a, b) => b.score - a.score)
|
|
193
|
+
.slice(0, 3)
|
|
194
|
+
.map(s => s.name);
|
|
195
|
+
let errorMsg = `Category "${identifier}" not found.`;
|
|
196
|
+
if (suggestions.length > 0) {
|
|
197
|
+
errorMsg += ` Did you mean: ${suggestions.join(', ')}?`;
|
|
198
|
+
}
|
|
199
|
+
if (cachedCategories.length <= 10) {
|
|
200
|
+
errorMsg += ` Available categories: ${cachedCategories.map(c => c.name).join(', ')}`;
|
|
201
|
+
}
|
|
202
|
+
throw new Error(errorMsg);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Smart find role - uses cached data and fuzzy matching
|
|
206
|
+
*/
|
|
207
|
+
export async function smartFindRole(identifier) {
|
|
208
|
+
const guild = await getGuild();
|
|
209
|
+
const cache = await getServerCache();
|
|
210
|
+
const cleanId = cleanRoleIdentifier(identifier);
|
|
211
|
+
// Use cache for fuzzy matching
|
|
212
|
+
const cachedRoles = cache.roles;
|
|
213
|
+
// Try exact ID match
|
|
214
|
+
let matchedId = cachedRoles.find(r => r.id === cleanId)?.id;
|
|
215
|
+
// Try exact name match (case-insensitive)
|
|
216
|
+
if (!matchedId) {
|
|
217
|
+
matchedId = cachedRoles.find(r => r.name.toLowerCase() === cleanId.toLowerCase())?.id;
|
|
218
|
+
}
|
|
219
|
+
// Try fuzzy match using cache
|
|
220
|
+
if (!matchedId) {
|
|
221
|
+
let bestScore = 0;
|
|
222
|
+
for (const role of cachedRoles) {
|
|
223
|
+
const score = similarity(role.name, cleanId);
|
|
224
|
+
if (score > bestScore && score >= 0.7) {
|
|
225
|
+
bestScore = score;
|
|
226
|
+
matchedId = role.id;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// If found via cache, get the actual role object
|
|
231
|
+
if (matchedId) {
|
|
232
|
+
const role = guild.roles.cache.get(matchedId);
|
|
233
|
+
if (role) {
|
|
234
|
+
return role;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Not found - provide helpful error with suggestions from cache
|
|
238
|
+
const suggestions = cachedRoles
|
|
239
|
+
.map(r => ({ name: r.name, score: similarity(r.name, cleanId) }))
|
|
240
|
+
.filter(s => s.score > 0.3)
|
|
241
|
+
.sort((a, b) => b.score - a.score)
|
|
242
|
+
.slice(0, 3)
|
|
243
|
+
.map(s => s.name);
|
|
244
|
+
let errorMsg = `Role "${identifier}" not found.`;
|
|
245
|
+
if (suggestions.length > 0) {
|
|
246
|
+
errorMsg += ` Did you mean: ${suggestions.map(s => `@${s}`).join(', ')}?`;
|
|
247
|
+
}
|
|
248
|
+
if (cachedRoles.length <= 15) {
|
|
249
|
+
errorMsg += ` Available roles: ${cachedRoles.map(r => `@${r.name}`).join(', ')}`;
|
|
250
|
+
}
|
|
251
|
+
throw new Error(errorMsg);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Smart find member - uses cached data and fuzzy matching
|
|
255
|
+
*/
|
|
256
|
+
export async function smartFindMember(identifier) {
|
|
257
|
+
const guild = await getGuild();
|
|
258
|
+
const cache = await getServerCache();
|
|
259
|
+
const cleanId = cleanMemberIdentifier(identifier);
|
|
260
|
+
// Use cache for fuzzy matching first
|
|
261
|
+
const cachedMembers = cache.members;
|
|
262
|
+
// Try exact ID match
|
|
263
|
+
let matchedId = cachedMembers.find(m => m.id === cleanId)?.id;
|
|
264
|
+
// Try exact matches (case-insensitive) on various name fields
|
|
265
|
+
if (!matchedId) {
|
|
266
|
+
matchedId = cachedMembers.find(m => m.username.toLowerCase() === cleanId.toLowerCase() ||
|
|
267
|
+
m.displayName.toLowerCase() === cleanId.toLowerCase() ||
|
|
268
|
+
(m.nickname && m.nickname.toLowerCase() === cleanId.toLowerCase()))?.id;
|
|
269
|
+
}
|
|
270
|
+
// Try fuzzy match using cache
|
|
271
|
+
if (!matchedId) {
|
|
272
|
+
let bestScore = 0;
|
|
273
|
+
for (const member of cachedMembers) {
|
|
274
|
+
const scores = [
|
|
275
|
+
similarity(member.username, cleanId),
|
|
276
|
+
similarity(member.displayName, cleanId),
|
|
277
|
+
member.nickname ? similarity(member.nickname, cleanId) : 0,
|
|
278
|
+
];
|
|
279
|
+
const maxScore = Math.max(...scores);
|
|
280
|
+
if (maxScore > bestScore && maxScore >= 0.6) {
|
|
281
|
+
bestScore = maxScore;
|
|
282
|
+
matchedId = member.id;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// If found via cache, get the actual member object
|
|
287
|
+
if (matchedId) {
|
|
288
|
+
// Fetch the specific member to ensure we have latest data
|
|
289
|
+
try {
|
|
290
|
+
const member = await guild.members.fetch(matchedId);
|
|
291
|
+
if (member) {
|
|
292
|
+
return member;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Member might have left, fall through to error
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Not found - provide helpful error with suggestions from cache
|
|
300
|
+
const suggestions = cachedMembers
|
|
301
|
+
.map(m => ({
|
|
302
|
+
name: m.displayName,
|
|
303
|
+
score: Math.max(similarity(m.username, cleanId), similarity(m.displayName, cleanId), m.nickname ? similarity(m.nickname, cleanId) : 0)
|
|
304
|
+
}))
|
|
305
|
+
.filter(s => s.score > 0.3)
|
|
306
|
+
.sort((a, b) => b.score - a.score)
|
|
307
|
+
.slice(0, 3)
|
|
308
|
+
.map(s => s.name);
|
|
309
|
+
let errorMsg = `Member "${identifier}" not found.`;
|
|
310
|
+
if (suggestions.length > 0) {
|
|
311
|
+
errorMsg += ` Did you mean: ${suggestions.map(s => `@${s}`).join(', ')}?`;
|
|
312
|
+
}
|
|
313
|
+
throw new Error(errorMsg);
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { getGuild, getClient } from '../discord-client.js';
|
|
2
|
+
import { smartFindTextChannel } from './utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Webhook management tools
|
|
5
|
+
*/
|
|
6
|
+
export const webhookTools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'list_webhooks',
|
|
9
|
+
description: 'List all webhooks in the server, optionally filtered to a specific channel. Channel name is fuzzy-matched.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
channel: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Channel name or ID to filter webhooks by (fuzzy matched, optional)',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: [],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'create_webhook',
|
|
23
|
+
description: 'Create a new webhook for a channel. Channel name is fuzzy-matched.',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
channel: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'The channel name or ID to create the webhook in (fuzzy matched)',
|
|
30
|
+
},
|
|
31
|
+
name: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'The name for the webhook',
|
|
34
|
+
},
|
|
35
|
+
avatarUrl: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'URL of the avatar image for the webhook (optional)',
|
|
38
|
+
},
|
|
39
|
+
reason: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'The reason for creating this webhook (shown in audit log)',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ['channel', 'name'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'delete_webhook',
|
|
49
|
+
description: 'Delete a webhook by its ID.',
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
webhookId: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'The ID of the webhook to delete',
|
|
56
|
+
},
|
|
57
|
+
reason: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'The reason for deleting this webhook (shown in audit log)',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
required: ['webhookId'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'send_webhook_message',
|
|
67
|
+
description: 'Send a message via a webhook. Can optionally override the display name and avatar.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
webhookId: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'The ID of the webhook to send the message through',
|
|
74
|
+
},
|
|
75
|
+
content: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description: 'The message content to send',
|
|
78
|
+
},
|
|
79
|
+
username: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description: 'Override the webhook display name for this message (optional)',
|
|
82
|
+
},
|
|
83
|
+
avatarUrl: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: 'Override the webhook avatar URL for this message (optional)',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: ['webhookId', 'content'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
export async function executeWebhookTool(name, args) {
|
|
93
|
+
switch (name) {
|
|
94
|
+
case 'list_webhooks':
|
|
95
|
+
return await listWebhooks(args);
|
|
96
|
+
case 'create_webhook':
|
|
97
|
+
return await createWebhook(args);
|
|
98
|
+
case 'delete_webhook':
|
|
99
|
+
return await deleteWebhook(args);
|
|
100
|
+
case 'send_webhook_message':
|
|
101
|
+
return await sendWebhookMessage(args);
|
|
102
|
+
default:
|
|
103
|
+
throw new Error(`Unknown webhook tool: ${name}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function listWebhooks(args) {
|
|
107
|
+
const guild = await getGuild();
|
|
108
|
+
const channelIdentifier = args['channel'];
|
|
109
|
+
let webhooks;
|
|
110
|
+
if (channelIdentifier) {
|
|
111
|
+
const channel = await smartFindTextChannel(channelIdentifier);
|
|
112
|
+
if (!channel.isTextBased()) {
|
|
113
|
+
throw new Error(`Channel "${channelIdentifier}" is not a text-based channel and cannot have webhooks.`);
|
|
114
|
+
}
|
|
115
|
+
webhooks = await channel.fetchWebhooks();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
webhooks = await guild.fetchWebhooks();
|
|
119
|
+
}
|
|
120
|
+
const result = webhooks.map(wh => {
|
|
121
|
+
const channelObj = guild.channels.cache.get(wh.channelId);
|
|
122
|
+
return {
|
|
123
|
+
id: wh.id,
|
|
124
|
+
name: wh.name,
|
|
125
|
+
channelId: wh.channelId,
|
|
126
|
+
channelName: channelObj?.name ?? null,
|
|
127
|
+
url: wh.url,
|
|
128
|
+
creator: wh.owner ? { id: wh.owner.id, username: wh.owner.username } : null,
|
|
129
|
+
avatar: wh.avatar,
|
|
130
|
+
createdAt: wh.createdAt?.toISOString() ?? null,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return JSON.stringify(result, null, 2);
|
|
134
|
+
}
|
|
135
|
+
async function createWebhook(args) {
|
|
136
|
+
const channelIdentifier = args['channel'];
|
|
137
|
+
const name = args['name'];
|
|
138
|
+
const avatarUrl = args['avatarUrl'];
|
|
139
|
+
const reason = args['reason'];
|
|
140
|
+
const channel = await smartFindTextChannel(channelIdentifier);
|
|
141
|
+
if (!channel.isTextBased()) {
|
|
142
|
+
throw new Error(`Channel "${channelIdentifier}" is not a text-based channel and cannot have webhooks.`);
|
|
143
|
+
}
|
|
144
|
+
const webhook = await channel.createWebhook({
|
|
145
|
+
name,
|
|
146
|
+
avatar: avatarUrl,
|
|
147
|
+
reason: reason ?? 'Created via MCP',
|
|
148
|
+
});
|
|
149
|
+
return JSON.stringify({
|
|
150
|
+
success: true,
|
|
151
|
+
message: `Webhook "${webhook.name}" created successfully`,
|
|
152
|
+
webhook: {
|
|
153
|
+
id: webhook.id,
|
|
154
|
+
name: webhook.name,
|
|
155
|
+
url: webhook.url,
|
|
156
|
+
token: webhook.token,
|
|
157
|
+
channelId: webhook.channelId,
|
|
158
|
+
channelName: channel.name,
|
|
159
|
+
},
|
|
160
|
+
}, null, 2);
|
|
161
|
+
}
|
|
162
|
+
async function deleteWebhook(args) {
|
|
163
|
+
const webhookId = args['webhookId'];
|
|
164
|
+
const reason = args['reason'];
|
|
165
|
+
const client = getClient();
|
|
166
|
+
const webhook = await client.fetchWebhook(webhookId);
|
|
167
|
+
const webhookName = webhook.name;
|
|
168
|
+
await webhook.delete(reason ?? 'Deleted via MCP');
|
|
169
|
+
return JSON.stringify({
|
|
170
|
+
success: true,
|
|
171
|
+
message: `Webhook "${webhookName}" deleted successfully`,
|
|
172
|
+
}, null, 2);
|
|
173
|
+
}
|
|
174
|
+
async function sendWebhookMessage(args) {
|
|
175
|
+
const webhookId = args['webhookId'];
|
|
176
|
+
const content = args['content'];
|
|
177
|
+
const username = args['username'];
|
|
178
|
+
const avatarUrl = args['avatarUrl'];
|
|
179
|
+
const client = getClient();
|
|
180
|
+
const webhook = await client.fetchWebhook(webhookId);
|
|
181
|
+
const message = await webhook.send({
|
|
182
|
+
content,
|
|
183
|
+
username,
|
|
184
|
+
avatarURL: avatarUrl,
|
|
185
|
+
});
|
|
186
|
+
return JSON.stringify({
|
|
187
|
+
success: true,
|
|
188
|
+
message: {
|
|
189
|
+
id: message.id,
|
|
190
|
+
content: message.content,
|
|
191
|
+
channelId: message.channelId,
|
|
192
|
+
},
|
|
193
|
+
}, null, 2);
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quadslab.io/discord-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Full Discord server administration through Claude Code via MCP. 99 tools across 14 categories covering roles, channels, members, messages, moderation, forums, stages, webhooks, events, and more.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/mcp-server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"discord-mcp": "dist/mcp-server.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/mcp-server.js",
|
|
13
|
+
"dev": "npx tsx src/mcp-server.ts"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"discord",
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"claude",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"server-admin",
|
|
22
|
+
"discord-bot",
|
|
23
|
+
"discord-management",
|
|
24
|
+
"anthropic"
|
|
25
|
+
],
|
|
26
|
+
"author": "QuadsLab",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/HardHeadHackerHead/discord-mcp"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/HardHeadHackerHead/discord-mcp#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/HardHeadHackerHead/discord-mcp/issues"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
41
|
+
"discord.js": "^14.16.3",
|
|
42
|
+
"dotenv": "^16.4.5"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.10.2",
|
|
46
|
+
"typescript": "^5.7.2"
|
|
47
|
+
}
|
|
48
|
+
}
|