@guildforge/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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # GuildForge
2
+
3
+ A Model Context Protocol (MCP) server that allows AI assistants to manage Discord servers through a standard interface.
4
+
5
+ ## Features
6
+
7
+ - **Channel Management**: List, create, and delete text/voice channels
8
+ - **Category Management**: Create and delete channel categories
9
+ - **Role Management**: List, create, delete roles, and assign them to users
10
+ - **Permission Management**: Set permission overwrites for roles on channels and categories
11
+ - **Single-Guild Scoped**: Each instance manages one Discord server
12
+ - **100% TypeScript**: Full type safety throughout the codebase
13
+ - **Open Source**: Distributed under the MIT license
14
+
15
+ ## Prerequisites
16
+
17
+ - Node.js >= 24
18
+ - A Discord Bot Token with appropriate permissions
19
+ - The Guild ID you want to manage
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Copy `.env.example` to `.env` and fill in your values:
30
+
31
+ ```env
32
+ DISCORD_TOKEN=your_bot_token_here
33
+ GUILD_ID=your_guild_id_here
34
+ ```
35
+
36
+ ### Discord Bot Setup
37
+
38
+ 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
39
+ 2. Create a new application and add a Bot
40
+ 3. Enable the following privileged intents:
41
+ - **Guild Members Intent** (required for role assignment)
42
+ 4. Invite the bot to your server with these OAuth2 scopes:
43
+ - `bot`
44
+ - `applications.commands`
45
+ 5. Grant the bot these permissions in your server:
46
+ - **Manage Channels**
47
+ - **Manage Roles**
48
+
49
+ ## Usage
50
+
51
+ ### Build
52
+
53
+ ```bash
54
+ npm run build
55
+ ```
56
+
57
+ ### Start
58
+
59
+ ```bash
60
+ npm start
61
+ ```
62
+
63
+ ### Development
64
+
65
+ ```bash
66
+ npm run dev
67
+ ```
68
+
69
+ ### MCP Inspector (for debugging)
70
+
71
+ ```bash
72
+ # Windows PowerShell
73
+ $env:DISCORD_TOKEN="your_token"
74
+ $env:GUILD_ID="your_guild_id"
75
+ npm run inspect
76
+
77
+ # macOS/Linux
78
+ export DISCORD_TOKEN="your_token"
79
+ export GUILD_ID="your_guild_id"
80
+ npm run inspect
81
+ ```
82
+
83
+ ### Claude Desktop Configuration
84
+
85
+ Add this to your Claude Desktop config (`%APPDATA%\Claude\claude_desktop_config.json` on Windows, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
86
+
87
+ ```json
88
+ {
89
+ "mcpServers": {
90
+ "guildforge": {
91
+ "command": "node",
92
+ "args": ["path/to/guildforge/dist/index.mjs"],
93
+ "env": {
94
+ "DISCORD_TOKEN": "your_bot_token",
95
+ "GUILD_ID": "your_guild_id"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Available Tools
103
+
104
+ ### Channels
105
+
106
+ | Tool | Description | Input |
107
+ |------|-------------|-------|
108
+ | `list_channels` | Lists all channels and categories | — |
109
+ | `create_text_channel` | Creates a text channel | `{ name: string, categoryId?: string }` |
110
+ | `create_voice_channel` | Creates a voice channel | `{ name: string, categoryId?: string }` |
111
+ | `delete_channel` | Deletes a channel | `{ id: string, confirm: true }` |
112
+
113
+ ### Categories
114
+
115
+ | Tool | Description | Input |
116
+ |------|-------------|-------|
117
+ | `create_category` | Creates a category | `{ name: string }` |
118
+ | `delete_category` | Deletes a category | `{ id: string, confirm: true }` |
119
+
120
+ ### Roles
121
+
122
+ | Tool | Description | Input |
123
+ |------|-------------|-------|
124
+ | `list_roles` | Lists all roles | — |
125
+ | `create_role` | Creates a role | `{ name: string, color?: string }` |
126
+ | `delete_role` | Deletes a role | `{ id: string, confirm: true }` |
127
+ | `assign_role` | Assigns/removes a role from a user | `{ userId: string, roleId: string, action: "add" \| "remove" }` |
128
+
129
+ ### Permissions
130
+
131
+ | Tool | Description | Input |
132
+ |------|-------------|-------|
133
+ | `set_channel_permissions` | Sets permissions for a role on a channel | `{ channelId: string, roleId: string, allow?: string[], deny?: string[] }` |
134
+ | `set_category_permissions` | Sets permissions for a role on a category | `{ categoryId: string, roleId: string, allow?: string[], deny?: string[] }` |
135
+
136
+ ### Utility
137
+
138
+ | Tool | Description |
139
+ |------|-------------|
140
+ | `ping` | Verifies the server is running |
141
+
142
+ ### Permission Names
143
+
144
+ Use these strings when setting permissions:
145
+
146
+ - `ViewChannel`, `ManageChannels`, `ManageRoles`
147
+ - `SendMessages`, `SendMessagesInThreads`, `CreatePublicThreads`, `CreatePrivateThreads`
148
+ - `EmbedLinks`, `AttachFiles`, `AddReactions`, `UseExternalEmojis`, `UseExternalStickers`
149
+ - `MentionEveryone`, `ManageMessages`, `ManageThreads`, `ReadMessageHistory`
150
+ - `Connect`, `Speak`, `Stream`, `UseVAD`, `PrioritySpeaker`, `MuteMembers`, `DeafenMembers`, `MoveMembers`
151
+ - `Administrator`, `KickMembers`, `BanMembers`, `ViewAuditLog`
152
+ - And more (any key from `PermissionsBitField.Flags`)
153
+
154
+ ## Development
155
+
156
+ ### Linting
157
+
158
+ ```bash
159
+ npm run lint
160
+ ```
161
+
162
+ ### Formatting
163
+
164
+ ```bash
165
+ npm run format
166
+ ```
167
+
168
+ ### Testing
169
+
170
+ ```bash
171
+ npm test
172
+ npm run test:watch
173
+ ```
174
+
175
+ ### Type Checking
176
+
177
+ ```bash
178
+ npm run typecheck
179
+ ```
180
+
181
+ ## Architecture
182
+
183
+ ```
184
+ LLM (Claude, Cursor, etc.)
185
+ |
186
+ v
187
+ MCP Protocol (stdio)
188
+ |
189
+ v
190
+ guildforge (this server)
191
+ |
192
+ v
193
+ discord.js Client
194
+ |
195
+ v
196
+ Discord API
197
+ ```
198
+
199
+ Each tool follows the Lemoncode/quickmock pattern:
200
+ - `index.ts` — Barrel export
201
+ - `<tool>.tool.ts` — Metadata (name, description, schema, execute)
202
+ - `<tool>.handler.ts` — Business logic
203
+ - `<tool>.schema.ts` — Zod validation schema (when input required)
204
+ - `<tool>.handler.test.ts` — Colocated unit tests
205
+
206
+ ## Security
207
+
208
+ - **Single-guild scoped**: Each instance manages exactly one Discord server
209
+ - **Explicit confirmation**: Destructive actions (delete) require `confirm: true`
210
+ - **Permission validation**: Bot validates it has required permissions on startup
211
+ - **Type-safe inputs**: All tool inputs validated with Zod schemas
212
+
213
+ ## Contributing
214
+
215
+ Contributions are welcome! Please ensure:
216
+ - All tests pass (`npm test`)
217
+ - Code is formatted (`npm run format`)
218
+ - Linting passes (`npm run lint`)
219
+ - TypeScript compiles (`npm run typecheck`)
220
+
221
+ ## License
222
+
223
+ [MIT](LICENSE)
@@ -0,0 +1 @@
1
+ export { };
package/dist/index.mjs ADDED
@@ -0,0 +1,595 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { ChannelType, Client, GatewayIntentBits, PermissionsBitField } from "discord.js";
5
+ import { z } from "zod";
6
+ import "dotenv/config";
7
+ //#region src/commons/discord-client.service.ts
8
+ function createDiscordClientService(token, guildId) {
9
+ const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] });
10
+ let connected = false;
11
+ async function connect() {
12
+ if (connected) return;
13
+ await new Promise((resolve, reject) => {
14
+ const timeout = setTimeout(() => {
15
+ reject(/* @__PURE__ */ new Error("Discord client connection timed out after 30s"));
16
+ }, 3e4);
17
+ client.once("ready", () => {
18
+ clearTimeout(timeout);
19
+ connected = true;
20
+ resolve();
21
+ });
22
+ client.login(token).catch((err) => {
23
+ clearTimeout(timeout);
24
+ reject(err);
25
+ });
26
+ });
27
+ const guild = client.guilds.cache.get(guildId);
28
+ if (!guild) throw new Error(`Guild with ID "${guildId}" not found or bot is not a member. Ensure the bot has been invited to this server.`);
29
+ const botMember = await guild.members.fetchMe();
30
+ const missing = [PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.ManageRoles].filter((perm) => !botMember.permissions.has(perm));
31
+ if (missing.length > 0) {
32
+ const missingNames = missing.map((perm) => {
33
+ return Object.entries(PermissionsBitField.Flags).find(([, value]) => value === perm)?.[0] ?? String(perm);
34
+ });
35
+ throw new Error(`Bot is missing required permissions: ${missingNames.join(", ")}. Please grant Manage Channels and Manage Roles to the bot.`);
36
+ }
37
+ }
38
+ async function disconnect() {
39
+ if (!connected) return;
40
+ await client.destroy();
41
+ connected = false;
42
+ }
43
+ function getGuild() {
44
+ if (!connected) throw new Error("Discord client is not connected. Call connect() first.");
45
+ const guild = client.guilds.cache.get(guildId);
46
+ if (!guild) throw new Error(`Guild with ID "${guildId}" is no longer available.`);
47
+ return guild;
48
+ }
49
+ function isConnected() {
50
+ return connected;
51
+ }
52
+ return {
53
+ connect,
54
+ disconnect,
55
+ getGuild,
56
+ isConnected
57
+ };
58
+ }
59
+ //#endregion
60
+ //#region src/config.ts
61
+ const envSchema = z.object({
62
+ DISCORD_TOKEN: z.string().min(1, "DISCORD_TOKEN is required"),
63
+ GUILD_ID: z.string().min(1, "GUILD_ID is required")
64
+ });
65
+ function parseConfig(env) {
66
+ const parsed = envSchema.safeParse(env);
67
+ if (!parsed.success) {
68
+ const messages = parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`);
69
+ throw new Error(`Invalid environment variables: ${messages.join(", ")}`);
70
+ }
71
+ return parsed.data;
72
+ }
73
+ //#endregion
74
+ //#region src/commons/tool-response.helpers.ts
75
+ function toolText(text) {
76
+ return { content: [{
77
+ type: "text",
78
+ text
79
+ }] };
80
+ }
81
+ function toolError(text) {
82
+ return {
83
+ content: [{
84
+ type: "text",
85
+ text
86
+ }],
87
+ isError: true
88
+ };
89
+ }
90
+ //#endregion
91
+ //#region src/tools/assign-role/assign-role.handler.ts
92
+ async function assignRoleHandler(args, service) {
93
+ try {
94
+ const guild = service.getGuild();
95
+ const member = await guild.members.fetch(args.userId);
96
+ if (!member) return toolError(`User with ID "${args.userId}" not found in the server.`);
97
+ const role = guild.roles.cache.get(args.roleId);
98
+ if (!role) return toolError(`Role with ID "${args.roleId}" not found.`);
99
+ if (args.action === "add") await member.roles.add(role);
100
+ else await member.roles.remove(role);
101
+ return toolText(JSON.stringify({
102
+ userId: args.userId,
103
+ roleId: args.roleId
104
+ }));
105
+ } catch (err) {
106
+ return toolError(`Error assigning role: ${String(err)}`);
107
+ }
108
+ }
109
+ //#endregion
110
+ //#region src/tools/assign-role/assign-role.tool.ts
111
+ const assignRole = {
112
+ name: "assign_role",
113
+ description: "Assigns or removes a role from a user in the Discord server.",
114
+ schema: {
115
+ userId: z.string().describe("ID of the user to assign/remove the role from"),
116
+ roleId: z.string().describe("ID of the role to assign or remove"),
117
+ action: z.enum(["add", "remove"]).describe("Whether to add or remove the role from the user")
118
+ },
119
+ execute: assignRoleHandler
120
+ };
121
+ //#endregion
122
+ //#region src/tools/create-category/create-category.handler.ts
123
+ async function createCategoryHandler(args, service) {
124
+ try {
125
+ const category = await service.getGuild().channels.create({
126
+ name: args.name,
127
+ type: ChannelType.GuildCategory
128
+ });
129
+ return toolText(JSON.stringify({
130
+ id: category.id,
131
+ name: category.name
132
+ }));
133
+ } catch (err) {
134
+ return toolError(`Error creating category: ${String(err)}`);
135
+ }
136
+ }
137
+ //#endregion
138
+ //#region src/tools/create-category/create-category.tool.ts
139
+ const createCategory = {
140
+ name: "create_category",
141
+ description: "Creates a new category in the Discord server.",
142
+ schema: { name: z.string().min(1).max(100).describe("Name of the category to create") },
143
+ execute: createCategoryHandler
144
+ };
145
+ //#endregion
146
+ //#region src/tools/create-role/create-role.handler.ts
147
+ async function createRoleHandler(args, service) {
148
+ try {
149
+ const guild = service.getGuild();
150
+ const colorValue = args.color ? Number.parseInt(args.color.replace("#", ""), 16) : void 0;
151
+ const role = await guild.roles.create({
152
+ name: args.name,
153
+ color: colorValue
154
+ });
155
+ return toolText(JSON.stringify({
156
+ id: role.id,
157
+ name: role.name
158
+ }));
159
+ } catch (err) {
160
+ return toolError(`Error creating role: ${String(err)}`);
161
+ }
162
+ }
163
+ //#endregion
164
+ //#region src/tools/create-role/create-role.tool.ts
165
+ const createRole = {
166
+ name: "create_role",
167
+ description: "Creates a new role in the Discord server.",
168
+ schema: {
169
+ name: z.string().min(1).max(100).describe("Name of the role to create"),
170
+ color: z.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Optional hex color for the role (e.g., #FF0000)")
171
+ },
172
+ execute: createRoleHandler
173
+ };
174
+ //#endregion
175
+ //#region src/tools/create-text-channel/create-text-channel.handler.ts
176
+ async function createTextChannelHandler(args, service) {
177
+ try {
178
+ const channel = await service.getGuild().channels.create({
179
+ name: args.name,
180
+ type: ChannelType.GuildText,
181
+ parent: args.categoryId ?? void 0
182
+ });
183
+ return toolText(JSON.stringify({
184
+ id: channel.id,
185
+ name: channel.name
186
+ }));
187
+ } catch (err) {
188
+ return toolError(`Error creating text channel: ${String(err)}`);
189
+ }
190
+ }
191
+ //#endregion
192
+ //#region src/tools/create-text-channel/create-text-channel.tool.ts
193
+ const createTextChannel = {
194
+ name: "create_text_channel",
195
+ description: "Creates a new text channel in the Discord server.",
196
+ schema: {
197
+ name: z.string().min(1).max(100).describe("Name of the text channel to create"),
198
+ categoryId: z.string().optional().describe("Optional ID of the category to place the channel under")
199
+ },
200
+ execute: createTextChannelHandler
201
+ };
202
+ //#endregion
203
+ //#region src/tools/create-voice-channel/create-voice-channel.handler.ts
204
+ async function createVoiceChannelHandler(args, service) {
205
+ try {
206
+ const channel = await service.getGuild().channels.create({
207
+ name: args.name,
208
+ type: ChannelType.GuildVoice,
209
+ parent: args.categoryId ?? void 0
210
+ });
211
+ return toolText(JSON.stringify({
212
+ id: channel.id,
213
+ name: channel.name
214
+ }));
215
+ } catch (err) {
216
+ return toolError(`Error creating voice channel: ${String(err)}`);
217
+ }
218
+ }
219
+ //#endregion
220
+ //#region src/tools/create-voice-channel/create-voice-channel.tool.ts
221
+ const createVoiceChannel = {
222
+ name: "create_voice_channel",
223
+ description: "Creates a new voice channel in the Discord server.",
224
+ schema: {
225
+ name: z.string().min(1).max(100).describe("Name of the voice channel to create"),
226
+ categoryId: z.string().optional().describe("Optional ID of the category to place the channel under")
227
+ },
228
+ execute: createVoiceChannelHandler
229
+ };
230
+ //#endregion
231
+ //#region src/tools/delete-category/delete-category.handler.ts
232
+ async function deleteCategoryHandler(args, service) {
233
+ try {
234
+ const category = service.getGuild().channels.cache.get(args.id);
235
+ if (!category) return toolError(`Category with ID "${args.id}" not found.`);
236
+ if (category.type !== ChannelType.GuildCategory) return toolError(`Channel with ID "${args.id}" is not a category.`);
237
+ await category.delete();
238
+ return toolText(JSON.stringify({
239
+ id: category.id,
240
+ name: category.name
241
+ }));
242
+ } catch (err) {
243
+ return toolError(`Error deleting category: ${String(err)}`);
244
+ }
245
+ }
246
+ //#endregion
247
+ //#region src/tools/delete-category/delete-category.tool.ts
248
+ const deleteCategory = {
249
+ name: "delete_category",
250
+ description: "Deletes a category from the Discord server. Requires explicit confirmation.",
251
+ schema: {
252
+ id: z.string().describe("ID of the category to delete"),
253
+ confirm: z.literal(true).describe("Explicit confirmation required. Must be set to true.")
254
+ },
255
+ execute: deleteCategoryHandler
256
+ };
257
+ //#endregion
258
+ //#region src/tools/delete-channel/delete-channel.handler.ts
259
+ async function deleteChannelHandler(args, service) {
260
+ try {
261
+ const channel = service.getGuild().channels.cache.get(args.id);
262
+ if (!channel) return toolError(`Channel with ID "${args.id}" not found.`);
263
+ await channel.delete();
264
+ return toolText(JSON.stringify({
265
+ id: channel.id,
266
+ name: channel.name
267
+ }));
268
+ } catch (err) {
269
+ return toolError(`Error deleting channel: ${String(err)}`);
270
+ }
271
+ }
272
+ //#endregion
273
+ //#region src/tools/delete-channel/delete-channel.tool.ts
274
+ const deleteChannel = {
275
+ name: "delete_channel",
276
+ description: "Deletes a channel from the Discord server. Requires explicit confirmation.",
277
+ schema: {
278
+ id: z.string().describe("ID of the channel to delete"),
279
+ confirm: z.literal(true).describe("Explicit confirmation required. Must be set to true.")
280
+ },
281
+ execute: deleteChannelHandler
282
+ };
283
+ //#endregion
284
+ //#region src/tools/delete-role/delete-role.handler.ts
285
+ async function deleteRoleHandler(args, service) {
286
+ try {
287
+ const role = service.getGuild().roles.cache.get(args.id);
288
+ if (!role) return toolError(`Role with ID "${args.id}" not found.`);
289
+ await role.delete();
290
+ return toolText(JSON.stringify({
291
+ id: role.id,
292
+ name: role.name
293
+ }));
294
+ } catch (err) {
295
+ return toolError(`Error deleting role: ${String(err)}`);
296
+ }
297
+ }
298
+ //#endregion
299
+ //#region src/tools/delete-role/delete-role.tool.ts
300
+ const deleteRole = {
301
+ name: "delete_role",
302
+ description: "Deletes a role from the Discord server. Requires explicit confirmation.",
303
+ schema: {
304
+ id: z.string().describe("ID of the role to delete"),
305
+ confirm: z.literal(true).describe("Explicit confirmation required. Must be set to true.")
306
+ },
307
+ execute: deleteRoleHandler
308
+ };
309
+ //#endregion
310
+ //#region src/tools/list-channels/list-channels.handler.ts
311
+ function getChannelTypeName(type) {
312
+ return Object.entries(ChannelType).find(([, value]) => value === type)?.[0] ?? "Unknown";
313
+ }
314
+ async function listChannelsHandler(service) {
315
+ const channels = service.getGuild().channels.cache.map((channel) => ({
316
+ id: channel.id,
317
+ name: channel.name,
318
+ type: getChannelTypeName(channel.type)
319
+ }));
320
+ return toolText(JSON.stringify(channels));
321
+ }
322
+ //#endregion
323
+ //#region src/tools/list-channels/list-channels.tool.ts
324
+ const listChannels = {
325
+ name: "list_channels",
326
+ description: "Lists all channels and categories in the configured Discord server. Returns an array of objects with id, name, and type.",
327
+ execute: listChannelsHandler
328
+ };
329
+ //#endregion
330
+ //#region src/tools/list-channels-ordered/list-channels-ordered.handler.ts
331
+ async function listChannelsOrderedHandler(service) {
332
+ const allChannels = [...service.getGuild().channels.cache.values()];
333
+ const categories = allChannels.filter((ch) => ch.type === 4).sort((a, b) => a.position - b.position);
334
+ const uncategorized = allChannels.filter((ch) => ch.type !== 4 && !ch.parentId).sort((a, b) => a.position - b.position);
335
+ const result = [];
336
+ for (const cat of categories) {
337
+ const children = allChannels.filter((ch) => ch.parentId === cat.id).sort((a, b) => a.position - b.position).map((ch) => ({
338
+ id: ch.id,
339
+ name: ch.name,
340
+ type: String(ch.type),
341
+ position: ch.position
342
+ }));
343
+ result.push({
344
+ id: cat.id,
345
+ name: cat.name,
346
+ type: "GuildCategory",
347
+ position: cat.position,
348
+ children
349
+ });
350
+ }
351
+ for (const ch of uncategorized) result.push({
352
+ id: ch.id,
353
+ name: ch.name,
354
+ type: String(ch.type),
355
+ position: ch.position
356
+ });
357
+ return toolText(JSON.stringify(result, null, 2));
358
+ }
359
+ //#endregion
360
+ //#region src/tools/list-channels-ordered/list-channels-ordered.tool.ts
361
+ const listChannelsOrdered = {
362
+ name: "list_channels_ordered",
363
+ description: "Lists all channels and categories in the order they appear in the Discord client (top to bottom). Categories include their nested channels.",
364
+ execute: listChannelsOrderedHandler
365
+ };
366
+ //#endregion
367
+ //#region src/tools/list-roles/list-roles.handler.ts
368
+ async function listRolesHandler(service) {
369
+ const roles = service.getGuild().roles.cache.map((role) => ({
370
+ id: role.id,
371
+ name: role.name
372
+ }));
373
+ return toolText(JSON.stringify(roles));
374
+ }
375
+ //#endregion
376
+ //#region src/tools/list-roles/list-roles.tool.ts
377
+ const listRoles = {
378
+ name: "list_roles",
379
+ description: "Lists all roles in the configured Discord server. Returns an array of objects with id and name.",
380
+ execute: listRolesHandler
381
+ };
382
+ //#endregion
383
+ //#region src/tools/ping/ping.handler.ts
384
+ async function pingHandler() {
385
+ return toolText("pong");
386
+ }
387
+ //#endregion
388
+ //#region src/tools/ping/ping.tool.ts
389
+ const ping = {
390
+ name: "ping",
391
+ description: "Responds with pong. Useful for verifying the server is running.",
392
+ execute: pingHandler
393
+ };
394
+ //#endregion
395
+ //#region src/tools/reorder-channels/reorder-channels.handler.ts
396
+ async function reorderChannelsHandler(args, service) {
397
+ try {
398
+ const guild = service.getGuild();
399
+ const results = [];
400
+ for (const order of args.orders) {
401
+ const channel = guild.channels.cache.get(order.channelId);
402
+ if (!channel) {
403
+ results.push({
404
+ channelId: order.channelId,
405
+ name: "unknown",
406
+ success: false
407
+ });
408
+ continue;
409
+ }
410
+ if (order.categoryId !== void 0) await channel.setParent(order.categoryId, { lockPermissions: false });
411
+ await channel.setPosition(order.position);
412
+ results.push({
413
+ channelId: order.channelId,
414
+ name: channel.name,
415
+ success: true
416
+ });
417
+ }
418
+ return toolText(JSON.stringify(results));
419
+ } catch (err) {
420
+ return toolError(`Error reordering channels: ${String(err)}`);
421
+ }
422
+ }
423
+ //#endregion
424
+ //#region src/tools/reorder-channels/reorder-channels.tool.ts
425
+ const reorderChannels = {
426
+ name: "reorder_channels",
427
+ description: "Moves and/or reorders channels and categories. Accepts an array of operations, each specifying a channel ID, new position, and optionally a category ID to move under.",
428
+ schema: { orders: z.array(z.object({
429
+ channelId: z.string().describe("ID of the channel to move/reorder"),
430
+ position: z.number().int().min(0).describe("New position for the channel (0 = top)"),
431
+ categoryId: z.string().optional().describe("Optional category ID to move the channel under. Omit to keep current parent.")
432
+ })).describe("Array of channel reorder operations") },
433
+ execute: reorderChannelsHandler
434
+ };
435
+ //#endregion
436
+ //#region src/tools/set-category-permissions/set-category-permissions.handler.ts
437
+ function buildPermissionOptions$1(allow, deny) {
438
+ const options = {};
439
+ for (const perm of allow ?? []) {
440
+ if (PermissionsBitField.Flags[perm] === void 0) throw new Error(`Unknown permission: "${perm}"`);
441
+ options[perm] = true;
442
+ }
443
+ for (const perm of deny ?? []) {
444
+ if (PermissionsBitField.Flags[perm] === void 0) throw new Error(`Unknown permission: "${perm}"`);
445
+ options[perm] = false;
446
+ }
447
+ return options;
448
+ }
449
+ async function setCategoryPermissionsHandler(args, service) {
450
+ try {
451
+ const guild = service.getGuild();
452
+ const category = guild.channels.cache.get(args.categoryId);
453
+ if (!category) return toolError(`Category with ID "${args.categoryId}" not found.`);
454
+ if (category.type !== ChannelType.GuildCategory) return toolError(`Channel with ID "${args.categoryId}" is not a category.`);
455
+ const role = guild.roles.cache.get(args.roleId);
456
+ if (!role) return toolError(`Role with ID "${args.roleId}" not found.`);
457
+ const options = buildPermissionOptions$1(args.allow, args.deny);
458
+ await category.permissionOverwrites.edit(role.id, options);
459
+ return toolText(JSON.stringify({
460
+ categoryId: args.categoryId,
461
+ roleId: args.roleId
462
+ }));
463
+ } catch (err) {
464
+ return toolError(`Error setting category permissions: ${String(err)}`);
465
+ }
466
+ }
467
+ //#endregion
468
+ //#region src/tools/set-category-permissions/set-category-permissions.tool.ts
469
+ const setCategoryPermissions = {
470
+ name: "set_category_permissions",
471
+ description: "Sets permission overwrites for a role on a specific category. Accepts arrays of permission names to allow or deny.",
472
+ schema: {
473
+ categoryId: z.string().describe("ID of the category to modify permissions for"),
474
+ roleId: z.string().describe("ID of the role to set permissions for"),
475
+ allow: z.array(z.string()).optional().describe("Optional array of permission names to allow (e.g., SendMessages, ViewChannel)"),
476
+ deny: z.array(z.string()).optional().describe("Optional array of permission names to deny (e.g., SendMessages, ViewChannel)")
477
+ },
478
+ execute: setCategoryPermissionsHandler
479
+ };
480
+ //#endregion
481
+ //#region src/tools/set-channel-permissions/set-channel-permissions.handler.ts
482
+ function buildPermissionOptions(allow, deny) {
483
+ const options = {};
484
+ for (const perm of allow ?? []) {
485
+ if (PermissionsBitField.Flags[perm] === void 0) throw new Error(`Unknown permission: "${perm}"`);
486
+ options[perm] = true;
487
+ }
488
+ for (const perm of deny ?? []) {
489
+ if (PermissionsBitField.Flags[perm] === void 0) throw new Error(`Unknown permission: "${perm}"`);
490
+ options[perm] = false;
491
+ }
492
+ return options;
493
+ }
494
+ async function setChannelPermissionsHandler(args, service) {
495
+ try {
496
+ const guild = service.getGuild();
497
+ const channel = guild.channels.cache.get(args.channelId);
498
+ if (!channel) return toolError(`Channel with ID "${args.channelId}" not found.`);
499
+ if (!("permissionOverwrites" in channel)) return toolError(`Channel with ID "${args.channelId}" does not support permission overwrites.`);
500
+ const role = guild.roles.cache.get(args.roleId);
501
+ if (!role) return toolError(`Role with ID "${args.roleId}" not found.`);
502
+ const options = buildPermissionOptions(args.allow, args.deny);
503
+ await channel.permissionOverwrites.edit(role.id, options);
504
+ return toolText(JSON.stringify({
505
+ channelId: args.channelId,
506
+ roleId: args.roleId
507
+ }));
508
+ } catch (err) {
509
+ return toolError(`Error setting channel permissions: ${String(err)}`);
510
+ }
511
+ }
512
+ //#endregion
513
+ //#region src/tools/set-channel-permissions/set-channel-permissions.tool.ts
514
+ const setChannelPermissions = {
515
+ name: "set_channel_permissions",
516
+ description: "Sets permission overwrites for a role on a specific channel or category. Accepts arrays of permission names to allow or deny. Works on both regular channels and category channels.",
517
+ schema: {
518
+ channelId: z.string().describe("ID of the channel to modify permissions for"),
519
+ roleId: z.string().describe("ID of the role to set permissions for"),
520
+ allow: z.array(z.string()).optional().describe("Optional array of permission names to allow (e.g., SendMessages, ViewChannel)"),
521
+ deny: z.array(z.string()).optional().describe("Optional array of permission names to deny (e.g., SendMessages, ViewChannel)")
522
+ },
523
+ execute: setChannelPermissionsHandler
524
+ };
525
+ //#endregion
526
+ //#region src/index.ts
527
+ const config = parseConfig(process.env);
528
+ const discordService = createDiscordClientService(config.DISCORD_TOKEN, config.GUILD_ID);
529
+ const server = new McpServer({
530
+ name: "discord-manager-mcp",
531
+ version: "1.0.0"
532
+ });
533
+ server.registerTool(ping.name, { description: ping.description }, () => ping.execute());
534
+ server.registerTool(listChannels.name, { description: listChannels.description }, () => listChannels.execute(discordService));
535
+ server.registerTool(createTextChannel.name, {
536
+ description: createTextChannel.description,
537
+ inputSchema: createTextChannel.schema
538
+ }, (args) => createTextChannel.execute(args, discordService));
539
+ server.registerTool(createVoiceChannel.name, {
540
+ description: createVoiceChannel.description,
541
+ inputSchema: createVoiceChannel.schema
542
+ }, (args) => createVoiceChannel.execute(args, discordService));
543
+ server.registerTool(deleteChannel.name, {
544
+ description: deleteChannel.description,
545
+ inputSchema: deleteChannel.schema
546
+ }, (args) => deleteChannel.execute(args, discordService));
547
+ server.registerTool(createCategory.name, {
548
+ description: createCategory.description,
549
+ inputSchema: createCategory.schema
550
+ }, (args) => createCategory.execute(args, discordService));
551
+ server.registerTool(deleteCategory.name, {
552
+ description: deleteCategory.description,
553
+ inputSchema: deleteCategory.schema
554
+ }, (args) => deleteCategory.execute(args, discordService));
555
+ server.registerTool(listRoles.name, { description: listRoles.description }, () => listRoles.execute(discordService));
556
+ server.registerTool(createRole.name, {
557
+ description: createRole.description,
558
+ inputSchema: createRole.schema
559
+ }, (args) => createRole.execute(args, discordService));
560
+ server.registerTool(deleteRole.name, {
561
+ description: deleteRole.description,
562
+ inputSchema: deleteRole.schema
563
+ }, (args) => deleteRole.execute(args, discordService));
564
+ server.registerTool(assignRole.name, {
565
+ description: assignRole.description,
566
+ inputSchema: assignRole.schema
567
+ }, (args) => assignRole.execute(args, discordService));
568
+ server.registerTool(setChannelPermissions.name, {
569
+ description: setChannelPermissions.description,
570
+ inputSchema: setChannelPermissions.schema
571
+ }, (args) => setChannelPermissions.execute(args, discordService));
572
+ server.registerTool(setCategoryPermissions.name, {
573
+ description: setCategoryPermissions.description,
574
+ inputSchema: setCategoryPermissions.schema
575
+ }, (args) => setCategoryPermissions.execute(args, discordService));
576
+ server.registerTool(listChannelsOrdered.name, { description: listChannelsOrdered.description }, () => listChannelsOrdered.execute(discordService));
577
+ server.registerTool(reorderChannels.name, {
578
+ description: reorderChannels.description,
579
+ inputSchema: reorderChannels.schema
580
+ }, (args) => reorderChannels.execute(args, discordService));
581
+ async function main() {
582
+ console.error(`[startup] Connecting to Discord (GUILD_ID: ${config.GUILD_ID})...`);
583
+ await discordService.connect();
584
+ console.error("[startup] Discord connected and permissions verified.");
585
+ const transport = new StdioServerTransport();
586
+ await server.connect(transport);
587
+ }
588
+ main().catch((err) => {
589
+ console.error("fatal error:", err);
590
+ process.exit(1);
591
+ });
592
+ //#endregion
593
+ export {};
594
+
595
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["buildPermissionOptions"],"sources":["../src/commons/discord-client.service.ts","../src/config.ts","../src/commons/tool-response.helpers.ts","../src/tools/assign-role/assign-role.handler.ts","../src/tools/assign-role/assign-role.schema.ts","../src/tools/assign-role/assign-role.tool.ts","../src/tools/create-category/create-category.handler.ts","../src/tools/create-category/create-category.schema.ts","../src/tools/create-category/create-category.tool.ts","../src/tools/create-role/create-role.handler.ts","../src/tools/create-role/create-role.schema.ts","../src/tools/create-role/create-role.tool.ts","../src/tools/create-text-channel/create-text-channel.handler.ts","../src/tools/create-text-channel/create-text-channel.schema.ts","../src/tools/create-text-channel/create-text-channel.tool.ts","../src/tools/create-voice-channel/create-voice-channel.handler.ts","../src/tools/create-voice-channel/create-voice-channel.schema.ts","../src/tools/create-voice-channel/create-voice-channel.tool.ts","../src/tools/delete-category/delete-category.handler.ts","../src/tools/delete-category/delete-category.schema.ts","../src/tools/delete-category/delete-category.tool.ts","../src/tools/delete-channel/delete-channel.handler.ts","../src/tools/delete-channel/delete-channel.schema.ts","../src/tools/delete-channel/delete-channel.tool.ts","../src/tools/delete-role/delete-role.handler.ts","../src/tools/delete-role/delete-role.schema.ts","../src/tools/delete-role/delete-role.tool.ts","../src/tools/list-channels/list-channels.handler.ts","../src/tools/list-channels/list-channels.tool.ts","../src/tools/list-channels-ordered/list-channels-ordered.handler.ts","../src/tools/list-channels-ordered/list-channels-ordered.tool.ts","../src/tools/list-roles/list-roles.handler.ts","../src/tools/list-roles/list-roles.tool.ts","../src/tools/ping/ping.handler.ts","../src/tools/ping/ping.tool.ts","../src/tools/reorder-channels/reorder-channels.handler.ts","../src/tools/reorder-channels/reorder-channels.schema.ts","../src/tools/reorder-channels/reorder-channels.tool.ts","../src/tools/set-category-permissions/set-category-permissions.handler.ts","../src/tools/set-category-permissions/set-category-permissions.schema.ts","../src/tools/set-category-permissions/set-category-permissions.tool.ts","../src/tools/set-channel-permissions/set-channel-permissions.handler.ts","../src/tools/set-channel-permissions/set-channel-permissions.schema.ts","../src/tools/set-channel-permissions/set-channel-permissions.tool.ts","../src/index.ts"],"sourcesContent":["import type { Guild } from 'discord.js';\nimport { Client, GatewayIntentBits, PermissionsBitField } from 'discord.js';\n\nexport interface DiscordClientService {\n\tconnect(): Promise<void>;\n\tdisconnect(): Promise<void>;\n\tgetGuild(): Guild;\n\tisConnected(): boolean;\n}\n\nexport function createDiscordClientService(\n\ttoken: string,\n\tguildId: string,\n): DiscordClientService {\n\tconst client = new Client({\n\t\tintents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],\n\t});\n\n\tlet connected = false;\n\n\tasync function connect(): Promise<void> {\n\t\tif (connected) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\treject(new Error('Discord client connection timed out after 30s'));\n\t\t\t}, 30000);\n\n\t\t\tclient.once('ready', () => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tconnected = true;\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\tclient.login(token).catch((err) => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(err);\n\t\t\t});\n\t\t});\n\n\t\tconst guild = client.guilds.cache.get(guildId);\n\t\tif (!guild) {\n\t\t\tthrow new Error(\n\t\t\t\t`Guild with ID \"${guildId}\" not found or bot is not a member. Ensure the bot has been invited to this server.`,\n\t\t\t);\n\t\t}\n\n\t\tconst botMember = await guild.members.fetchMe();\n\t\tconst requiredPermissions = [\n\t\t\tPermissionsBitField.Flags.ManageChannels,\n\t\t\tPermissionsBitField.Flags.ManageRoles,\n\t\t];\n\n\t\tconst missing = requiredPermissions.filter(\n\t\t\t(perm) => !botMember.permissions.has(perm),\n\t\t);\n\n\t\tif (missing.length > 0) {\n\t\t\tconst missingNames = missing.map((perm) => {\n\t\t\t\tconst found = Object.entries(PermissionsBitField.Flags).find(\n\t\t\t\t\t([, value]) => value === perm,\n\t\t\t\t);\n\t\t\t\treturn found?.[0] ?? String(perm);\n\t\t\t});\n\t\t\tthrow new Error(\n\t\t\t\t`Bot is missing required permissions: ${missingNames.join(', ')}. Please grant Manage Channels and Manage Roles to the bot.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync function disconnect(): Promise<void> {\n\t\tif (!connected) {\n\t\t\treturn;\n\t\t}\n\t\tawait client.destroy();\n\t\tconnected = false;\n\t}\n\n\tfunction getGuild(): Guild {\n\t\tif (!connected) {\n\t\t\tthrow new Error('Discord client is not connected. Call connect() first.');\n\t\t}\n\t\tconst guild = client.guilds.cache.get(guildId);\n\t\tif (!guild) {\n\t\t\tthrow new Error(`Guild with ID \"${guildId}\" is no longer available.`);\n\t\t}\n\t\treturn guild;\n\t}\n\n\tfunction isConnected(): boolean {\n\t\treturn connected;\n\t}\n\n\treturn {\n\t\tconnect,\n\t\tdisconnect,\n\t\tgetGuild,\n\t\tisConnected,\n\t};\n}\n","import { z } from 'zod';\nimport 'dotenv/config';\n\nconst envSchema = z.object({\n\tDISCORD_TOKEN: z.string().min(1, 'DISCORD_TOKEN is required'),\n\tGUILD_ID: z.string().min(1, 'GUILD_ID is required'),\n});\n\nexport type Config = z.infer<typeof envSchema>;\n\nexport function parseConfig(env: Record<string, string | undefined>): Config {\n\tconst parsed = envSchema.safeParse(env);\n\n\tif (!parsed.success) {\n\t\tconst messages = parsed.error.issues.map(\n\t\t\t(issue) => `${issue.path.join('.')}: ${issue.message}`,\n\t\t);\n\t\tthrow new Error(`Invalid environment variables: ${messages.join(', ')}`);\n\t}\n\n\treturn parsed.data;\n}\n","type TextContent = { type: 'text'; text: string };\ntype ImageContent = { type: 'image'; data: string; mimeType: string };\ntype ToolContent = TextContent | ImageContent;\n\nexport function toolText(text: string) {\n\treturn { content: [{ type: 'text' as const, text }] };\n}\n\nexport function toolImage(data: string, mimeType: string) {\n\treturn { content: [{ type: 'image' as const, data, mimeType }] };\n}\n\nexport function toolMultiContent(items: ToolContent[]) {\n\treturn { content: items };\n}\n\nexport function toolError(text: string) {\n\treturn {\n\t\tcontent: [{ type: 'text' as const, text }],\n\t\tisError: true as const,\n\t};\n}\n","import type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function assignRoleHandler(\n\targs: { userId: string; roleId: string; action: 'add' | 'remove' },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst member = await guild.members.fetch(args.userId);\n\t\tif (!member) {\n\t\t\treturn toolError(\n\t\t\t\t`User with ID \"${args.userId}\" not found in the server.`,\n\t\t\t);\n\t\t}\n\n\t\tconst role = guild.roles.cache.get(args.roleId);\n\t\tif (!role) {\n\t\t\treturn toolError(`Role with ID \"${args.roleId}\" not found.`);\n\t\t}\n\n\t\tif (args.action === 'add') {\n\t\t\tawait member.roles.add(role);\n\t\t} else {\n\t\t\tawait member.roles.remove(role);\n\t\t}\n\n\t\treturn toolText(\n\t\t\tJSON.stringify({ userId: args.userId, roleId: args.roleId }),\n\t\t);\n\t} catch (err) {\n\t\treturn toolError(`Error assigning role: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const assignRoleSchema = {\n\tuserId: z.string().describe('ID of the user to assign/remove the role from'),\n\troleId: z.string().describe('ID of the role to assign or remove'),\n\taction: z\n\t\t.enum(['add', 'remove'])\n\t\t.describe('Whether to add or remove the role from the user'),\n};\n","import { assignRoleHandler } from './assign-role.handler';\nimport { assignRoleSchema } from './assign-role.schema';\n\nexport const assignRole = {\n\tname: 'assign_role' as const,\n\tdescription: 'Assigns or removes a role from a user in the Discord server.',\n\tschema: assignRoleSchema,\n\texecute: assignRoleHandler,\n};\n","import { ChannelType } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function createCategoryHandler(\n\targs: { name: string },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst category = await guild.channels.create({\n\t\t\tname: args.name,\n\t\t\ttype: ChannelType.GuildCategory,\n\t\t});\n\t\treturn toolText(JSON.stringify({ id: category.id, name: category.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error creating category: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const createCategorySchema = {\n\tname: z.string().min(1).max(100).describe('Name of the category to create'),\n};\n","import { createCategoryHandler } from './create-category.handler';\nimport { createCategorySchema } from './create-category.schema';\n\nexport const createCategory = {\n\tname: 'create_category' as const,\n\tdescription: 'Creates a new category in the Discord server.',\n\tschema: createCategorySchema,\n\texecute: createCategoryHandler,\n};\n","import type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function createRoleHandler(\n\targs: { name: string; color?: string },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst colorValue = args.color\n\t\t\t? Number.parseInt(args.color.replace('#', ''), 16)\n\t\t\t: undefined;\n\t\tconst role = await guild.roles.create({\n\t\t\tname: args.name,\n\t\t\tcolor: colorValue,\n\t\t});\n\t\treturn toolText(JSON.stringify({ id: role.id, name: role.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error creating role: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const createRoleSchema = {\n\tname: z.string().min(1).max(100).describe('Name of the role to create'),\n\tcolor: z\n\t\t.string()\n\t\t.regex(/^#?[0-9A-Fa-f]{6}$/)\n\t\t.optional()\n\t\t.describe('Optional hex color for the role (e.g., #FF0000)'),\n};\n","import { createRoleHandler } from './create-role.handler';\nimport { createRoleSchema } from './create-role.schema';\n\nexport const createRole = {\n\tname: 'create_role' as const,\n\tdescription: 'Creates a new role in the Discord server.',\n\tschema: createRoleSchema,\n\texecute: createRoleHandler,\n};\n","import { ChannelType } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function createTextChannelHandler(\n\targs: { name: string; categoryId?: string },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst channel = await guild.channels.create({\n\t\t\tname: args.name,\n\t\t\ttype: ChannelType.GuildText,\n\t\t\tparent: args.categoryId ?? undefined,\n\t\t});\n\t\treturn toolText(JSON.stringify({ id: channel.id, name: channel.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error creating text channel: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const createTextChannelSchema = {\n\tname: z\n\t\t.string()\n\t\t.min(1)\n\t\t.max(100)\n\t\t.describe('Name of the text channel to create'),\n\tcategoryId: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe('Optional ID of the category to place the channel under'),\n};\n","import { createTextChannelHandler } from './create-text-channel.handler';\nimport { createTextChannelSchema } from './create-text-channel.schema';\n\nexport const createTextChannel = {\n\tname: 'create_text_channel' as const,\n\tdescription: 'Creates a new text channel in the Discord server.',\n\tschema: createTextChannelSchema,\n\texecute: createTextChannelHandler,\n};\n","import { ChannelType } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function createVoiceChannelHandler(\n\targs: { name: string; categoryId?: string },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst channel = await guild.channels.create({\n\t\t\tname: args.name,\n\t\t\ttype: ChannelType.GuildVoice,\n\t\t\tparent: args.categoryId ?? undefined,\n\t\t});\n\t\treturn toolText(JSON.stringify({ id: channel.id, name: channel.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error creating voice channel: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const createVoiceChannelSchema = {\n\tname: z\n\t\t.string()\n\t\t.min(1)\n\t\t.max(100)\n\t\t.describe('Name of the voice channel to create'),\n\tcategoryId: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe('Optional ID of the category to place the channel under'),\n};\n","import { createVoiceChannelHandler } from './create-voice-channel.handler';\nimport { createVoiceChannelSchema } from './create-voice-channel.schema';\n\nexport const createVoiceChannel = {\n\tname: 'create_voice_channel' as const,\n\tdescription: 'Creates a new voice channel in the Discord server.',\n\tschema: createVoiceChannelSchema,\n\texecute: createVoiceChannelHandler,\n};\n","import { ChannelType } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function deleteCategoryHandler(\n\targs: { id: string; confirm: true },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst category = guild.channels.cache.get(args.id);\n\t\tif (!category) {\n\t\t\treturn toolError(`Category with ID \"${args.id}\" not found.`);\n\t\t}\n\t\tif (category.type !== ChannelType.GuildCategory) {\n\t\t\treturn toolError(`Channel with ID \"${args.id}\" is not a category.`);\n\t\t}\n\t\tawait category.delete();\n\t\treturn toolText(JSON.stringify({ id: category.id, name: category.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error deleting category: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const deleteCategorySchema = {\n\tid: z.string().describe('ID of the category to delete'),\n\tconfirm: z\n\t\t.literal(true)\n\t\t.describe('Explicit confirmation required. Must be set to true.'),\n};\n","import { deleteCategoryHandler } from './delete-category.handler';\nimport { deleteCategorySchema } from './delete-category.schema';\n\nexport const deleteCategory = {\n\tname: 'delete_category' as const,\n\tdescription:\n\t\t'Deletes a category from the Discord server. Requires explicit confirmation.',\n\tschema: deleteCategorySchema,\n\texecute: deleteCategoryHandler,\n};\n","import type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function deleteChannelHandler(\n\targs: { id: string; confirm: true },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst channel = guild.channels.cache.get(args.id);\n\t\tif (!channel) {\n\t\t\treturn toolError(`Channel with ID \"${args.id}\" not found.`);\n\t\t}\n\t\tawait channel.delete();\n\t\treturn toolText(JSON.stringify({ id: channel.id, name: channel.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error deleting channel: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const deleteChannelSchema = {\n\tid: z.string().describe('ID of the channel to delete'),\n\tconfirm: z\n\t\t.literal(true)\n\t\t.describe('Explicit confirmation required. Must be set to true.'),\n};\n","import { deleteChannelHandler } from './delete-channel.handler';\nimport { deleteChannelSchema } from './delete-channel.schema';\n\nexport const deleteChannel = {\n\tname: 'delete_channel' as const,\n\tdescription:\n\t\t'Deletes a channel from the Discord server. Requires explicit confirmation.',\n\tschema: deleteChannelSchema,\n\texecute: deleteChannelHandler,\n};\n","import type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function deleteRoleHandler(\n\targs: { id: string; confirm: true },\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst role = guild.roles.cache.get(args.id);\n\t\tif (!role) {\n\t\t\treturn toolError(`Role with ID \"${args.id}\" not found.`);\n\t\t}\n\t\tawait role.delete();\n\t\treturn toolText(JSON.stringify({ id: role.id, name: role.name }));\n\t} catch (err) {\n\t\treturn toolError(`Error deleting role: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const deleteRoleSchema = {\n\tid: z.string().describe('ID of the role to delete'),\n\tconfirm: z\n\t\t.literal(true)\n\t\t.describe('Explicit confirmation required. Must be set to true.'),\n};\n","import { deleteRoleHandler } from './delete-role.handler';\nimport { deleteRoleSchema } from './delete-role.schema';\n\nexport const deleteRole = {\n\tname: 'delete_role' as const,\n\tdescription:\n\t\t'Deletes a role from the Discord server. Requires explicit confirmation.',\n\tschema: deleteRoleSchema,\n\texecute: deleteRoleHandler,\n};\n","import { ChannelType } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolText } from '#/commons/tool-response.helpers';\n\nfunction getChannelTypeName(type: ChannelType): string {\n\tconst entry = Object.entries(ChannelType).find(([, value]) => value === type);\n\treturn entry?.[0] ?? 'Unknown';\n}\n\nexport async function listChannelsHandler(service: DiscordClientService) {\n\tconst guild = service.getGuild();\n\tconst channels = guild.channels.cache.map((channel) => ({\n\t\tid: channel.id,\n\t\tname: channel.name,\n\t\ttype: getChannelTypeName(channel.type),\n\t}));\n\treturn toolText(JSON.stringify(channels));\n}\n","import { listChannelsHandler } from './list-channels.handler';\n\nexport const listChannels = {\n\tname: 'list_channels' as const,\n\tdescription:\n\t\t'Lists all channels and categories in the configured Discord server. Returns an array of objects with id, name, and type.',\n\texecute: listChannelsHandler,\n};\n","import type { GuildChannel } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolText } from '#/commons/tool-response.helpers';\n\ninterface OrderedChannel {\n\tid: string;\n\tname: string;\n\ttype: string;\n\tposition: number;\n\tchildren?: OrderedChannel[];\n}\n\nexport async function listChannelsOrderedHandler(\n\tservice: DiscordClientService,\n) {\n\tconst guild = service.getGuild();\n\tconst allChannels = [...guild.channels.cache.values()] as GuildChannel[];\n\n\tconst categories = allChannels\n\t\t.filter((ch) => ch.type === 4)\n\t\t.sort((a, b) => a.position - b.position);\n\n\tconst uncategorized = allChannels\n\t\t.filter((ch) => ch.type !== 4 && !ch.parentId)\n\t\t.sort((a, b) => a.position - b.position);\n\n\tconst result: OrderedChannel[] = [];\n\n\tfor (const cat of categories) {\n\t\tconst children = allChannels\n\t\t\t.filter((ch) => ch.parentId === cat.id)\n\t\t\t.sort((a, b) => a.position - b.position)\n\t\t\t.map((ch) => ({\n\t\t\t\tid: ch.id,\n\t\t\t\tname: ch.name,\n\t\t\t\ttype: String(ch.type),\n\t\t\t\tposition: ch.position,\n\t\t\t}));\n\n\t\tresult.push({\n\t\t\tid: cat.id,\n\t\t\tname: cat.name,\n\t\t\ttype: 'GuildCategory',\n\t\t\tposition: cat.position,\n\t\t\tchildren,\n\t\t});\n\t}\n\n\tfor (const ch of uncategorized) {\n\t\tresult.push({\n\t\t\tid: ch.id,\n\t\t\tname: ch.name,\n\t\t\ttype: String(ch.type),\n\t\t\tposition: ch.position,\n\t\t});\n\t}\n\n\treturn toolText(JSON.stringify(result, null, 2));\n}\n","import { listChannelsOrderedHandler } from './list-channels-ordered.handler';\n\nexport const listChannelsOrdered = {\n\tname: 'list_channels_ordered' as const,\n\tdescription:\n\t\t'Lists all channels and categories in the order they appear in the Discord client (top to bottom). Categories include their nested channels.',\n\texecute: listChannelsOrderedHandler,\n};\n","import type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolText } from '#/commons/tool-response.helpers';\n\nexport async function listRolesHandler(service: DiscordClientService) {\n\tconst guild = service.getGuild();\n\tconst roles = guild.roles.cache.map((role) => ({\n\t\tid: role.id,\n\t\tname: role.name,\n\t}));\n\treturn toolText(JSON.stringify(roles));\n}\n","import { listRolesHandler } from './list-roles.handler';\n\nexport const listRoles = {\n\tname: 'list_roles' as const,\n\tdescription:\n\t\t'Lists all roles in the configured Discord server. Returns an array of objects with id and name.',\n\texecute: listRolesHandler,\n};\n","import { toolText } from '#/commons/tool-response.helpers';\n\nexport async function pingHandler() {\n\treturn toolText('pong');\n}\n","import { pingHandler } from './ping.handler';\n\nexport const ping = {\n\tname: 'ping' as const,\n\tdescription:\n\t\t'Responds with pong. Useful for verifying the server is running.',\n\texecute: pingHandler,\n};\n","import type { GuildChannel } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\nexport async function reorderChannelsHandler(\n\targs: {\n\t\torders: Array<{\n\t\t\tchannelId: string;\n\t\t\tposition: number;\n\t\t\tcategoryId?: string;\n\t\t}>;\n\t},\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst results: Array<{\n\t\t\tchannelId: string;\n\t\t\tname: string;\n\t\t\tsuccess: boolean;\n\t\t}> = [];\n\n\t\tfor (const order of args.orders) {\n\t\t\tconst channel = guild.channels.cache.get(order.channelId) as\n\t\t\t\t| GuildChannel\n\t\t\t\t| undefined;\n\t\t\tif (!channel) {\n\t\t\t\tresults.push({\n\t\t\t\t\tchannelId: order.channelId,\n\t\t\t\t\tname: 'unknown',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (order.categoryId !== undefined) {\n\t\t\t\tawait channel.setParent(order.categoryId, {\n\t\t\t\t\tlockPermissions: false,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait channel.setPosition(order.position);\n\n\t\t\tresults.push({\n\t\t\t\tchannelId: order.channelId,\n\t\t\t\tname: channel.name,\n\t\t\t\tsuccess: true,\n\t\t\t});\n\t\t}\n\n\t\treturn toolText(JSON.stringify(results));\n\t} catch (err) {\n\t\treturn toolError(`Error reordering channels: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const reorderChannelsSchema = {\n\torders: z\n\t\t.array(\n\t\t\tz.object({\n\t\t\t\tchannelId: z.string().describe('ID of the channel to move/reorder'),\n\t\t\t\tposition: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.int()\n\t\t\t\t\t.min(0)\n\t\t\t\t\t.describe('New position for the channel (0 = top)'),\n\t\t\t\tcategoryId: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe(\n\t\t\t\t\t\t'Optional category ID to move the channel under. Omit to keep current parent.',\n\t\t\t\t\t),\n\t\t\t}),\n\t\t)\n\t\t.describe('Array of channel reorder operations'),\n};\n","import { reorderChannelsHandler } from './reorder-channels.handler';\nimport { reorderChannelsSchema } from './reorder-channels.schema';\n\nexport const reorderChannels = {\n\tname: 'reorder_channels' as const,\n\tdescription:\n\t\t'Moves and/or reorders channels and categories. Accepts an array of operations, each specifying a channel ID, new position, and optionally a category ID to move under.',\n\tschema: reorderChannelsSchema,\n\texecute: reorderChannelsHandler,\n};\n","import { ChannelType, PermissionsBitField } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\ntype PermissionOverwriteOptions = Partial<\n\tRecord<keyof typeof PermissionsBitField.Flags, boolean | null>\n>;\n\nfunction buildPermissionOptions(\n\tallow: string[] | undefined,\n\tdeny: string[] | undefined,\n): PermissionOverwriteOptions {\n\tconst options: PermissionOverwriteOptions = {};\n\n\tfor (const perm of allow ?? []) {\n\t\tif (\n\t\t\tPermissionsBitField.Flags[\n\t\t\t\tperm as keyof typeof PermissionsBitField.Flags\n\t\t\t] === undefined\n\t\t) {\n\t\t\tthrow new Error(`Unknown permission: \"${perm}\"`);\n\t\t}\n\t\toptions[perm as keyof typeof PermissionsBitField.Flags] = true;\n\t}\n\n\tfor (const perm of deny ?? []) {\n\t\tif (\n\t\t\tPermissionsBitField.Flags[\n\t\t\t\tperm as keyof typeof PermissionsBitField.Flags\n\t\t\t] === undefined\n\t\t) {\n\t\t\tthrow new Error(`Unknown permission: \"${perm}\"`);\n\t\t}\n\t\toptions[perm as keyof typeof PermissionsBitField.Flags] = false;\n\t}\n\n\treturn options;\n}\n\nexport async function setCategoryPermissionsHandler(\n\targs: {\n\t\tcategoryId: string;\n\t\troleId: string;\n\t\tallow?: string[];\n\t\tdeny?: string[];\n\t},\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst category = guild.channels.cache.get(args.categoryId);\n\t\tif (!category) {\n\t\t\treturn toolError(`Category with ID \"${args.categoryId}\" not found.`);\n\t\t}\n\n\t\tif (category.type !== ChannelType.GuildCategory) {\n\t\t\treturn toolError(\n\t\t\t\t`Channel with ID \"${args.categoryId}\" is not a category.`,\n\t\t\t);\n\t\t}\n\n\t\tconst role = guild.roles.cache.get(args.roleId);\n\t\tif (!role) {\n\t\t\treturn toolError(`Role with ID \"${args.roleId}\" not found.`);\n\t\t}\n\n\t\tconst options = buildPermissionOptions(args.allow, args.deny);\n\n\t\tawait category.permissionOverwrites.edit(role.id, options);\n\n\t\treturn toolText(\n\t\t\tJSON.stringify({ categoryId: args.categoryId, roleId: args.roleId }),\n\t\t);\n\t} catch (err) {\n\t\treturn toolError(`Error setting category permissions: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const setCategoryPermissionsSchema = {\n\tcategoryId: z\n\t\t.string()\n\t\t.describe('ID of the category to modify permissions for'),\n\troleId: z.string().describe('ID of the role to set permissions for'),\n\tallow: z\n\t\t.array(z.string())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Optional array of permission names to allow (e.g., SendMessages, ViewChannel)',\n\t\t),\n\tdeny: z\n\t\t.array(z.string())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Optional array of permission names to deny (e.g., SendMessages, ViewChannel)',\n\t\t),\n};\n","import { setCategoryPermissionsHandler } from './set-category-permissions.handler';\nimport { setCategoryPermissionsSchema } from './set-category-permissions.schema';\n\nexport const setCategoryPermissions = {\n\tname: 'set_category_permissions' as const,\n\tdescription:\n\t\t'Sets permission overwrites for a role on a specific category. Accepts arrays of permission names to allow or deny.',\n\tschema: setCategoryPermissionsSchema,\n\texecute: setCategoryPermissionsHandler,\n};\n","import { PermissionsBitField } from 'discord.js';\nimport type { DiscordClientService } from '#/commons/discord-client.service';\nimport { toolError, toolText } from '#/commons/tool-response.helpers';\n\ntype PermissionOverwriteOptions = Partial<\n\tRecord<keyof typeof PermissionsBitField.Flags, boolean | null>\n>;\n\nfunction buildPermissionOptions(\n\tallow: string[] | undefined,\n\tdeny: string[] | undefined,\n): PermissionOverwriteOptions {\n\tconst options: PermissionOverwriteOptions = {};\n\n\tfor (const perm of allow ?? []) {\n\t\tif (\n\t\t\tPermissionsBitField.Flags[\n\t\t\t\tperm as keyof typeof PermissionsBitField.Flags\n\t\t\t] === undefined\n\t\t) {\n\t\t\tthrow new Error(`Unknown permission: \"${perm}\"`);\n\t\t}\n\t\toptions[perm as keyof typeof PermissionsBitField.Flags] = true;\n\t}\n\n\tfor (const perm of deny ?? []) {\n\t\tif (\n\t\t\tPermissionsBitField.Flags[\n\t\t\t\tperm as keyof typeof PermissionsBitField.Flags\n\t\t\t] === undefined\n\t\t) {\n\t\t\tthrow new Error(`Unknown permission: \"${perm}\"`);\n\t\t}\n\t\toptions[perm as keyof typeof PermissionsBitField.Flags] = false;\n\t}\n\n\treturn options;\n}\n\nexport async function setChannelPermissionsHandler(\n\targs: {\n\t\tchannelId: string;\n\t\troleId: string;\n\t\tallow?: string[];\n\t\tdeny?: string[];\n\t},\n\tservice: DiscordClientService,\n) {\n\ttry {\n\t\tconst guild = service.getGuild();\n\t\tconst channel = guild.channels.cache.get(args.channelId);\n\t\tif (!channel) {\n\t\t\treturn toolError(`Channel with ID \"${args.channelId}\" not found.`);\n\t\t}\n\n\t\tif (!('permissionOverwrites' in channel)) {\n\t\t\treturn toolError(\n\t\t\t\t`Channel with ID \"${args.channelId}\" does not support permission overwrites.`,\n\t\t\t);\n\t\t}\n\n\t\tconst role = guild.roles.cache.get(args.roleId);\n\t\tif (!role) {\n\t\t\treturn toolError(`Role with ID \"${args.roleId}\" not found.`);\n\t\t}\n\n\t\tconst options = buildPermissionOptions(args.allow, args.deny);\n\n\t\tawait (\n\t\t\tchannel as Extract<typeof channel, { permissionOverwrites: unknown }>\n\t\t).permissionOverwrites.edit(role.id, options);\n\n\t\treturn toolText(\n\t\t\tJSON.stringify({ channelId: args.channelId, roleId: args.roleId }),\n\t\t);\n\t} catch (err) {\n\t\treturn toolError(`Error setting channel permissions: ${String(err)}`);\n\t}\n}\n","import { z } from 'zod';\n\nexport const setChannelPermissionsSchema = {\n\tchannelId: z.string().describe('ID of the channel to modify permissions for'),\n\troleId: z.string().describe('ID of the role to set permissions for'),\n\tallow: z\n\t\t.array(z.string())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Optional array of permission names to allow (e.g., SendMessages, ViewChannel)',\n\t\t),\n\tdeny: z\n\t\t.array(z.string())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Optional array of permission names to deny (e.g., SendMessages, ViewChannel)',\n\t\t),\n};\n","import { setChannelPermissionsHandler } from './set-channel-permissions.handler';\nimport { setChannelPermissionsSchema } from './set-channel-permissions.schema';\n\nexport const setChannelPermissions = {\n\tname: 'set_channel_permissions' as const,\n\tdescription:\n\t\t'Sets permission overwrites for a role on a specific channel or category. Accepts arrays of permission names to allow or deny. Works on both regular channels and category channels.',\n\tschema: setChannelPermissionsSchema,\n\texecute: setChannelPermissionsHandler,\n};\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createDiscordClientService } from './commons/discord-client.service';\nimport { parseConfig } from './config';\nimport { assignRole } from './tools/assign-role';\nimport { createCategory } from './tools/create-category';\nimport { createRole } from './tools/create-role';\nimport { createTextChannel } from './tools/create-text-channel';\nimport { createVoiceChannel } from './tools/create-voice-channel';\nimport { deleteCategory } from './tools/delete-category';\nimport { deleteChannel } from './tools/delete-channel';\nimport { deleteRole } from './tools/delete-role';\nimport { listChannels } from './tools/list-channels';\nimport { listChannelsOrdered } from './tools/list-channels-ordered';\nimport { listRoles } from './tools/list-roles';\nimport { ping } from './tools/ping';\nimport { reorderChannels } from './tools/reorder-channels';\nimport { setCategoryPermissions } from './tools/set-category-permissions';\nimport { setChannelPermissions } from './tools/set-channel-permissions';\n\nconst config = parseConfig(process.env);\nconst discordService = createDiscordClientService(\n\tconfig.DISCORD_TOKEN,\n\tconfig.GUILD_ID,\n);\n\nconst server = new McpServer({\n\tname: 'discord-manager-mcp',\n\tversion: '1.0.0',\n});\n\nserver.registerTool(ping.name, { description: ping.description }, () =>\n\tping.execute(),\n);\n\nserver.registerTool(\n\tlistChannels.name,\n\t{ description: listChannels.description },\n\t() => listChannels.execute(discordService),\n);\n\nserver.registerTool(\n\tcreateTextChannel.name,\n\t{\n\t\tdescription: createTextChannel.description,\n\t\tinputSchema: createTextChannel.schema,\n\t},\n\t(args) => createTextChannel.execute(args, discordService),\n);\n\nserver.registerTool(\n\tcreateVoiceChannel.name,\n\t{\n\t\tdescription: createVoiceChannel.description,\n\t\tinputSchema: createVoiceChannel.schema,\n\t},\n\t(args) => createVoiceChannel.execute(args, discordService),\n);\n\nserver.registerTool(\n\tdeleteChannel.name,\n\t{\n\t\tdescription: deleteChannel.description,\n\t\tinputSchema: deleteChannel.schema,\n\t},\n\t(args) => deleteChannel.execute(args, discordService),\n);\n\nserver.registerTool(\n\tcreateCategory.name,\n\t{\n\t\tdescription: createCategory.description,\n\t\tinputSchema: createCategory.schema,\n\t},\n\t(args) => createCategory.execute(args, discordService),\n);\n\nserver.registerTool(\n\tdeleteCategory.name,\n\t{\n\t\tdescription: deleteCategory.description,\n\t\tinputSchema: deleteCategory.schema,\n\t},\n\t(args) => deleteCategory.execute(args, discordService),\n);\n\nserver.registerTool(\n\tlistRoles.name,\n\t{ description: listRoles.description },\n\t() => listRoles.execute(discordService),\n);\n\nserver.registerTool(\n\tcreateRole.name,\n\t{\n\t\tdescription: createRole.description,\n\t\tinputSchema: createRole.schema,\n\t},\n\t(args) => createRole.execute(args, discordService),\n);\n\nserver.registerTool(\n\tdeleteRole.name,\n\t{\n\t\tdescription: deleteRole.description,\n\t\tinputSchema: deleteRole.schema,\n\t},\n\t(args) => deleteRole.execute(args, discordService),\n);\n\nserver.registerTool(\n\tassignRole.name,\n\t{\n\t\tdescription: assignRole.description,\n\t\tinputSchema: assignRole.schema,\n\t},\n\t(args) => assignRole.execute(args, discordService),\n);\n\nserver.registerTool(\n\tsetChannelPermissions.name,\n\t{\n\t\tdescription: setChannelPermissions.description,\n\t\tinputSchema: setChannelPermissions.schema,\n\t},\n\t(args) => setChannelPermissions.execute(args, discordService),\n);\n\nserver.registerTool(\n\tsetCategoryPermissions.name,\n\t{\n\t\tdescription: setCategoryPermissions.description,\n\t\tinputSchema: setCategoryPermissions.schema,\n\t},\n\t(args) => setCategoryPermissions.execute(args, discordService),\n);\n\nserver.registerTool(\n\tlistChannelsOrdered.name,\n\t{ description: listChannelsOrdered.description },\n\t() => listChannelsOrdered.execute(discordService),\n);\n\nserver.registerTool(\n\treorderChannels.name,\n\t{\n\t\tdescription: reorderChannels.description,\n\t\tinputSchema: reorderChannels.schema,\n\t},\n\t(args) => reorderChannels.execute(args, discordService),\n);\n\nasync function main() {\n\tconsole.error(\n\t\t`[startup] Connecting to Discord (GUILD_ID: ${config.GUILD_ID})...`,\n\t);\n\tawait discordService.connect();\n\tconsole.error('[startup] Discord connected and permissions verified.');\n\n\tconst transport = new StdioServerTransport();\n\tawait server.connect(transport);\n}\n\nmain().catch((err) => {\n\tconsole.error('fatal error:', err);\n\tprocess.exit(1);\n});\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@guildforge/mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server to manage Discord channels, categories, roles and permissions",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.mts",
9
+ "default": "./dist/index.mjs"
10
+ },
11
+ "./package.json": "./package.json"
12
+ },
13
+ "imports": {
14
+ "#*": "./src/*"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "bin": {
20
+ "guildforge": "./dist/index.mjs"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "discord",
25
+ "bot",
26
+ "manager"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/guildforge/guildforge.git",
33
+ "directory": "packages/mcp"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.29.0",
37
+ "discord.js": "^14.26.4",
38
+ "dotenv": "^17.4.2",
39
+ "zod": "^4.4.3"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.9.1",
43
+ "tsdown": "^0.22.1",
44
+ "typescript": "^6.0.3",
45
+ "vitest": "^4.1.7",
46
+ "@guildforge/tsdown-config": "0.0.0",
47
+ "@guildforge/typescript-config": "0.0.0",
48
+ "@guildforge/vitest-config": "0.0.0"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "engines": {
54
+ "node": ">=24.0.0"
55
+ },
56
+ "scripts": {
57
+ "build": "tsdown",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "test:coverage": "vitest run --coverage",
62
+ "inspect": "npm run build && npx @modelcontextprotocol/inspector node dist/index.mjs"
63
+ }
64
+ }