@mahesvara/discord-mcpserver 1.0.9 → 1.1.2

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/README.md CHANGED
@@ -7,14 +7,22 @@ An MCP (Model Context Protocol) server that enables LLMs to control Discord serv
7
7
  ### Guild/Server Management
8
8
  - `discord_list_guilds` - List all servers the bot has access to
9
9
  - `discord_get_guild` - Get detailed info about a specific server
10
+ - `discord_edit_guild` - Edit server settings (name, verification level, system channels, etc.)
11
+ - `discord_leave_guild` - Leave a server
10
12
 
11
13
  ### Channel Management
12
14
  - `discord_list_channels` - List channels in a server (filter by type)
13
- - `discord_get_channel` - Get channel details
14
- - `discord_create_channel` - Create text/voice channels **and categories**
15
- - `discord_edit_channel` - Edit channel name, topic, position, category, and more
15
+ - `discord_get_channel` - Get channel details (includes forum tags for forum channels)
16
+ - `discord_create_channel` - Create text/voice/forum channels and categories
17
+ - `discord_edit_channel` - Edit channel name, topic, position, category, forum tags, and more
16
18
  - `discord_delete_channel` - Delete a channel or category
17
19
 
20
+ ### Forum Channel Features
21
+ - Create forum channels with `type: "forum"`
22
+ - Set default reaction emoji, sort order, and layout
23
+ - Manage forum tags (create, edit, delete)
24
+ - Tags support custom emojis and moderation settings
25
+
18
26
  ### Permission Management
19
27
  - `discord_set_channel_permissions` - Set permission overrides for roles/members on channels
20
28
  - `discord_remove_channel_permissions` - Remove permission overrides
@@ -39,6 +47,8 @@ An MCP (Model Context Protocol) server that enables LLMs to control Discord serv
39
47
  - `discord_ban_member` - Ban a member (with message deletion option)
40
48
  - `discord_unban_member` - Unban a user
41
49
  - `discord_set_nickname` - Set/clear member nickname
50
+ - `discord_move_member` - Move member to a different voice channel
51
+ - `discord_timeout_member` - Timeout/mute a member (up to 28 days)
42
52
 
43
53
  ### Role Management
44
54
  - `discord_list_roles` - List all server roles
@@ -50,12 +60,30 @@ An MCP (Model Context Protocol) server that enables LLMs to control Discord serv
50
60
  - `discord_set_role_positions` - Reorder role hierarchy
51
61
 
52
62
  ### Server Administration
53
- - `discord_edit_guild` - Edit server settings (name, verification level, system channels, etc.)
54
- - `discord_timeout_member` - Timeout/mute a member (up to 28 days)
55
63
  - `discord_list_bans` - List all banned users
56
64
  - `discord_prune_members` - Remove inactive members (with dry-run option)
57
65
  - `discord_get_audit_log` - View audit log entries
58
66
 
67
+ ### Community Features
68
+ - `discord_get_community_settings` - View community settings (rules channel, features, etc.)
69
+ - `discord_setup_community` - Configure community channels (rules, updates, safety alerts)
70
+
71
+ ### Welcome Screen
72
+ - `discord_get_welcome_screen` - Get welcome screen configuration
73
+ - `discord_edit_welcome_screen` - Edit welcome screen (description, channels, emojis)
74
+
75
+ ### Onboarding
76
+ - `discord_get_onboarding` - Get onboarding configuration
77
+ - `discord_edit_onboarding` - Edit existing onboarding settings
78
+ - `discord_setup_onboarding` - Setup onboarding from scratch (prompts, default channels, roles)
79
+
80
+ ### Auto Moderation
81
+ - `discord_list_automod_rules` - List auto moderation rules
82
+ - `discord_get_automod_rule` - Get details of an auto mod rule
83
+ - `discord_create_automod_rule` - Create auto moderation rule
84
+ - `discord_edit_automod_rule` - Edit auto moderation rule
85
+ - `discord_delete_automod_rule` - Delete auto moderation rule
86
+
59
87
  ### Emoji Management
60
88
  - `discord_list_emojis` - List custom emojis
61
89
  - `discord_create_emoji` - Create emoji from image URL
@@ -95,7 +123,7 @@ An MCP (Model Context Protocol) server that enables LLMs to control Discord serv
95
123
 
96
124
  ### 2. Invite the Bot to Your Server
97
125
 
98
- 1. Go to OAuth2 URL Generator
126
+ 1. Go to OAuth2 -> URL Generator
99
127
  2. Select scopes: `bot`, `applications.commands`
100
128
  3. Select bot permissions (choose based on features you need):
101
129
 
@@ -182,6 +210,7 @@ The server supports loading environment variables from a `.env` file in the curr
182
210
  DISCORD_BOT_TOKEN=your_bot_token_here
183
211
  TRANSPORT=http
184
212
  PORT=3000
213
+ HOST=localhost
185
214
  ```
186
215
 
187
216
  | Variable | Description | Default |
@@ -189,6 +218,7 @@ PORT=3000
189
218
  | `DISCORD_BOT_TOKEN` | Your Discord bot token (required) | - |
190
219
  | `TRANSPORT` | Transport mode: `stdio` or `http` | `stdio` |
191
220
  | `PORT` | HTTP server port (when using http transport) | `3000` |
221
+ | `HOST` | HTTP server host (use `0.0.0.0` for remote access) | `localhost` |
192
222
 
193
223
  ### HTTP Transport
194
224
 
@@ -196,17 +226,17 @@ For remote access, run the server with HTTP transport enabled:
196
226
 
197
227
  **Linux/macOS:**
198
228
  ```bash
199
- DISCORD_BOT_TOKEN=your_token TRANSPORT=http PORT=3000 discord-mcp-server
229
+ DISCORD_BOT_TOKEN=your_token TRANSPORT=http PORT=3000 HOST=0.0.0.0 discord-mcp-server
200
230
  ```
201
231
 
202
232
  **Windows (Command Prompt):**
203
233
  ```cmd
204
- set DISCORD_BOT_TOKEN=your_token && set TRANSPORT=http && set PORT=3000 && discord-mcp-server
234
+ set DISCORD_BOT_TOKEN=your_token && set TRANSPORT=http && set PORT=3000 && set HOST=0.0.0.0 && discord-mcp-server
205
235
  ```
206
236
 
207
237
  **Windows (PowerShell):**
208
238
  ```powershell
209
- $env:DISCORD_BOT_TOKEN="your_token"; $env:TRANSPORT="http"; $env:PORT="3000"; discord-mcp-server
239
+ $env:DISCORD_BOT_TOKEN="your_token"; $env:TRANSPORT="http"; $env:PORT="3000"; $env:HOST="0.0.0.0"; discord-mcp-server
210
240
  ```
211
241
 
212
242
  **Or use a `.env` file** (works on all platforms):
@@ -229,6 +259,85 @@ Then configure your MCP client to connect via HTTP:
229
259
 
230
260
  ## Tool Examples
231
261
 
262
+ ### Create a Forum Channel with Tags
263
+
264
+ ```json
265
+ {
266
+ "tool": "discord_create_channel",
267
+ "params": {
268
+ "guild_id": "1234567890123456789",
269
+ "name": "help-forum",
270
+ "type": "forum",
271
+ "topic": "Ask questions and get help",
272
+ "default_reaction_emoji": "✅",
273
+ "default_sort_order": "latest_activity",
274
+ "default_forum_layout": "list_view",
275
+ "available_tags": [
276
+ { "name": "Solved", "emoji_name": "✅", "moderated": false },
277
+ { "name": "Bug", "emoji_name": "🐛", "moderated": false },
278
+ { "name": "Question", "emoji_name": "❓", "moderated": false },
279
+ { "name": "Announcement", "emoji_name": "📢", "moderated": true }
280
+ ]
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### Setup Server Onboarding
286
+
287
+ ```json
288
+ {
289
+ "tool": "discord_setup_onboarding",
290
+ "params": {
291
+ "guild_id": "1234567890123456789",
292
+ "default_channel_ids": ["1111111111111111111"],
293
+ "prompts": [
294
+ {
295
+ "type": "multiple_choice",
296
+ "title": "What are you interested in?",
297
+ "single_select": false,
298
+ "required": true,
299
+ "options": [
300
+ { "title": "Gaming", "emoji_name": "🎮", "role_ids": ["2222222222222222222"] },
301
+ { "title": "Tech", "emoji_name": "💻", "role_ids": ["3333333333333333333"] }
302
+ ]
303
+ }
304
+ ]
305
+ }
306
+ }
307
+ ```
308
+
309
+ ### Configure Community Settings
310
+
311
+ ```json
312
+ {
313
+ "tool": "discord_setup_community",
314
+ "params": {
315
+ "guild_id": "1234567890123456789",
316
+ "rules_channel_id": "1111111111111111111",
317
+ "public_updates_channel_id": "2222222222222222222",
318
+ "description": "A friendly gaming community",
319
+ "preferred_locale": "en-US"
320
+ }
321
+ }
322
+ ```
323
+
324
+ ### Edit Welcome Screen
325
+
326
+ ```json
327
+ {
328
+ "tool": "discord_edit_welcome_screen",
329
+ "params": {
330
+ "guild_id": "1234567890123456789",
331
+ "enabled": true,
332
+ "description": "Welcome to our server!",
333
+ "welcome_channels": [
334
+ { "channel_id": "1111111111111111111", "description": "Read the rules", "emoji_name": "📜" },
335
+ { "channel_id": "2222222222222222222", "description": "Introduce yourself", "emoji_name": "👋" }
336
+ ]
337
+ }
338
+ }
339
+ ```
340
+
232
341
  ### Create a Category
233
342
 
234
343
  ```json
@@ -361,6 +470,7 @@ Depending on which tools you use, your bot needs these permissions:
361
470
  | Webhooks | Manage Webhooks |
362
471
  | Events | Manage Events |
363
472
  | Audit Log | View Audit Log |
473
+ | Community/Onboarding | Manage Guild |
364
474
 
365
475
  ## Tool Examples
366
476
 
@@ -25,30 +25,67 @@ export declare const GetChannelSchema: z.ZodObject<{
25
25
  export declare const CreateChannelSchema: z.ZodObject<{
26
26
  guild_id: z.ZodString;
27
27
  name: z.ZodString;
28
- type: z.ZodDefault<z.ZodEnum<["text", "voice", "category"]>>;
28
+ type: z.ZodDefault<z.ZodEnum<["text", "voice", "category", "forum"]>>;
29
29
  topic: z.ZodOptional<z.ZodString>;
30
30
  parent_id: z.ZodOptional<z.ZodString>;
31
31
  nsfw: z.ZodOptional<z.ZodBoolean>;
32
32
  bitrate: z.ZodOptional<z.ZodNumber>;
33
33
  user_limit: z.ZodOptional<z.ZodNumber>;
34
+ default_reaction_emoji: z.ZodOptional<z.ZodString>;
35
+ default_sort_order: z.ZodOptional<z.ZodEnum<["latest_activity", "creation_date"]>>;
36
+ default_forum_layout: z.ZodOptional<z.ZodEnum<["not_set", "list_view", "gallery_view"]>>;
37
+ available_tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
38
+ name: z.ZodString;
39
+ moderated: z.ZodOptional<z.ZodBoolean>;
40
+ emoji_id: z.ZodOptional<z.ZodString>;
41
+ emoji_name: z.ZodOptional<z.ZodString>;
42
+ }, "strip", z.ZodTypeAny, {
43
+ name: string;
44
+ moderated?: boolean | undefined;
45
+ emoji_id?: string | undefined;
46
+ emoji_name?: string | undefined;
47
+ }, {
48
+ name: string;
49
+ moderated?: boolean | undefined;
50
+ emoji_id?: string | undefined;
51
+ emoji_name?: string | undefined;
52
+ }>, "many">>;
34
53
  }, "strict", z.ZodTypeAny, {
35
54
  name: string;
36
- type: "text" | "voice" | "category";
55
+ type: "text" | "voice" | "category" | "forum";
37
56
  guild_id: string;
38
57
  topic?: string | undefined;
39
58
  parent_id?: string | undefined;
40
59
  nsfw?: boolean | undefined;
41
60
  bitrate?: number | undefined;
42
61
  user_limit?: number | undefined;
62
+ default_reaction_emoji?: string | undefined;
63
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
64
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
65
+ available_tags?: {
66
+ name: string;
67
+ moderated?: boolean | undefined;
68
+ emoji_id?: string | undefined;
69
+ emoji_name?: string | undefined;
70
+ }[] | undefined;
43
71
  }, {
44
72
  name: string;
45
73
  guild_id: string;
46
- type?: "text" | "voice" | "category" | undefined;
74
+ type?: "text" | "voice" | "category" | "forum" | undefined;
47
75
  topic?: string | undefined;
48
76
  parent_id?: string | undefined;
49
77
  nsfw?: boolean | undefined;
50
78
  bitrate?: number | undefined;
51
79
  user_limit?: number | undefined;
80
+ default_reaction_emoji?: string | undefined;
81
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
82
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
83
+ available_tags?: {
84
+ name: string;
85
+ moderated?: boolean | undefined;
86
+ emoji_id?: string | undefined;
87
+ emoji_name?: string | undefined;
88
+ }[] | undefined;
52
89
  }>;
53
90
  export declare const DeleteChannelSchema: z.ZodObject<{
54
91
  channel_id: z.ZodString;
@@ -66,6 +103,28 @@ export declare const EditChannelSchema: z.ZodObject<{
66
103
  nsfw: z.ZodOptional<z.ZodBoolean>;
67
104
  bitrate: z.ZodOptional<z.ZodNumber>;
68
105
  user_limit: z.ZodOptional<z.ZodNumber>;
106
+ default_reaction_emoji: z.ZodOptional<z.ZodString>;
107
+ default_sort_order: z.ZodOptional<z.ZodEnum<["latest_activity", "creation_date"]>>;
108
+ default_forum_layout: z.ZodOptional<z.ZodEnum<["not_set", "list_view", "gallery_view"]>>;
109
+ available_tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
110
+ id: z.ZodOptional<z.ZodString>;
111
+ name: z.ZodString;
112
+ moderated: z.ZodOptional<z.ZodBoolean>;
113
+ emoji_id: z.ZodOptional<z.ZodString>;
114
+ emoji_name: z.ZodOptional<z.ZodString>;
115
+ }, "strip", z.ZodTypeAny, {
116
+ name: string;
117
+ moderated?: boolean | undefined;
118
+ emoji_id?: string | undefined;
119
+ emoji_name?: string | undefined;
120
+ id?: string | undefined;
121
+ }, {
122
+ name: string;
123
+ moderated?: boolean | undefined;
124
+ emoji_id?: string | undefined;
125
+ emoji_name?: string | undefined;
126
+ id?: string | undefined;
127
+ }>, "many">>;
69
128
  }, "strict", z.ZodTypeAny, {
70
129
  channel_id: string;
71
130
  name?: string | undefined;
@@ -74,6 +133,16 @@ export declare const EditChannelSchema: z.ZodObject<{
74
133
  nsfw?: boolean | undefined;
75
134
  bitrate?: number | undefined;
76
135
  user_limit?: number | undefined;
136
+ default_reaction_emoji?: string | undefined;
137
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
138
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
139
+ available_tags?: {
140
+ name: string;
141
+ moderated?: boolean | undefined;
142
+ emoji_id?: string | undefined;
143
+ emoji_name?: string | undefined;
144
+ id?: string | undefined;
145
+ }[] | undefined;
77
146
  position?: number | undefined;
78
147
  }, {
79
148
  channel_id: string;
@@ -83,6 +152,16 @@ export declare const EditChannelSchema: z.ZodObject<{
83
152
  nsfw?: boolean | undefined;
84
153
  bitrate?: number | undefined;
85
154
  user_limit?: number | undefined;
155
+ default_reaction_emoji?: string | undefined;
156
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
157
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
158
+ available_tags?: {
159
+ name: string;
160
+ moderated?: boolean | undefined;
161
+ emoji_id?: string | undefined;
162
+ emoji_name?: string | undefined;
163
+ id?: string | undefined;
164
+ }[] | undefined;
86
165
  position?: number | undefined;
87
166
  }>;
88
167
  export declare const PermissionOverwriteSchema: z.ZodObject<{
@@ -17,18 +17,18 @@ export const CreateChannelSchema = z.object({
17
17
  .min(1)
18
18
  .max(100)
19
19
  .describe("Channel name"),
20
- type: z.enum(["text", "voice", "category"])
20
+ type: z.enum(["text", "voice", "category", "forum"])
21
21
  .default("text")
22
- .describe("Channel type: 'text', 'voice', or 'category'"),
22
+ .describe("Channel type: 'text', 'voice', 'category', or 'forum'"),
23
23
  topic: z.string()
24
24
  .max(1024)
25
25
  .optional()
26
- .describe("Channel topic (text channels only)"),
26
+ .describe("Channel topic (text/forum channels only)"),
27
27
  parent_id: ChannelIdSchema.optional()
28
28
  .describe("Category ID to create channel under (not for categories)"),
29
29
  nsfw: z.boolean()
30
30
  .optional()
31
- .describe("Whether the channel is NSFW (text channels only)"),
31
+ .describe("Whether the channel is NSFW (text/forum channels only)"),
32
32
  bitrate: z.number()
33
33
  .int()
34
34
  .min(8000)
@@ -40,7 +40,25 @@ export const CreateChannelSchema = z.object({
40
40
  .min(0)
41
41
  .max(99)
42
42
  .optional()
43
- .describe("User limit for voice channels (0 = unlimited)")
43
+ .describe("User limit for voice channels (0 = unlimited)"),
44
+ default_reaction_emoji: z.string()
45
+ .optional()
46
+ .describe("Default reaction emoji for forum posts - use emoji character (e.g., '👍') or custom emoji ID"),
47
+ default_sort_order: z.enum(["latest_activity", "creation_date"])
48
+ .optional()
49
+ .describe("Default sort order for forum posts"),
50
+ default_forum_layout: z.enum(["not_set", "list_view", "gallery_view"])
51
+ .optional()
52
+ .describe("Default layout for forum channel"),
53
+ available_tags: z.array(z.object({
54
+ name: z.string().max(20).describe("Tag name (max 20 chars)"),
55
+ moderated: z.boolean().optional().describe("Whether only moderators can apply this tag"),
56
+ emoji_id: z.string().optional().describe("Custom emoji ID for the tag"),
57
+ emoji_name: z.string().optional().describe("Unicode emoji for the tag (e.g., '🎮')")
58
+ }))
59
+ .max(20)
60
+ .optional()
61
+ .describe("Available tags for forum posts (max 20 tags)")
44
62
  }).strict();
45
63
  export const DeleteChannelSchema = z.object({
46
64
  channel_id: ChannelIdSchema
@@ -77,7 +95,26 @@ export const EditChannelSchema = z.object({
77
95
  .min(0)
78
96
  .max(99)
79
97
  .optional()
80
- .describe("User limit for voice channels")
98
+ .describe("User limit for voice channels"),
99
+ default_reaction_emoji: z.string()
100
+ .optional()
101
+ .describe("Default reaction emoji for forum posts - use emoji character (e.g., '👍') or custom emoji ID, use 'none' to remove"),
102
+ default_sort_order: z.enum(["latest_activity", "creation_date"])
103
+ .optional()
104
+ .describe("Default sort order for forum posts"),
105
+ default_forum_layout: z.enum(["not_set", "list_view", "gallery_view"])
106
+ .optional()
107
+ .describe("Default layout for forum channel"),
108
+ available_tags: z.array(z.object({
109
+ id: z.string().optional().describe("Tag ID (required when editing existing tags)"),
110
+ name: z.string().max(20).describe("Tag name (max 20 chars)"),
111
+ moderated: z.boolean().optional().describe("Whether only moderators can apply this tag"),
112
+ emoji_id: z.string().optional().describe("Custom emoji ID for the tag"),
113
+ emoji_name: z.string().optional().describe("Unicode emoji for the tag (e.g., '🎮')")
114
+ }))
115
+ .max(20)
116
+ .optional()
117
+ .describe("Available tags for forum posts (max 20 tags)")
81
118
  }).strict();
82
119
  // Permission schemas
83
120
  export const PermissionOverwriteSchema = z.object({
@@ -80,17 +80,17 @@ export declare const EditOnboardingSchema: z.ZodObject<{
80
80
  }, "strip", z.ZodTypeAny, {
81
81
  title: string;
82
82
  description?: string | undefined;
83
- id?: string | undefined;
84
83
  emoji_id?: string | undefined;
85
84
  emoji_name?: string | undefined;
85
+ id?: string | undefined;
86
86
  role_ids?: string[] | undefined;
87
87
  channel_ids?: string[] | undefined;
88
88
  }, {
89
89
  title: string;
90
90
  description?: string | undefined;
91
- id?: string | undefined;
92
91
  emoji_id?: string | undefined;
93
92
  emoji_name?: string | undefined;
93
+ id?: string | undefined;
94
94
  role_ids?: string[] | undefined;
95
95
  channel_ids?: string[] | undefined;
96
96
  }>, "many">;
@@ -99,9 +99,9 @@ export declare const EditOnboardingSchema: z.ZodObject<{
99
99
  options: {
100
100
  title: string;
101
101
  description?: string | undefined;
102
- id?: string | undefined;
103
102
  emoji_id?: string | undefined;
104
103
  emoji_name?: string | undefined;
104
+ id?: string | undefined;
105
105
  role_ids?: string[] | undefined;
106
106
  channel_ids?: string[] | undefined;
107
107
  }[];
@@ -115,9 +115,9 @@ export declare const EditOnboardingSchema: z.ZodObject<{
115
115
  options: {
116
116
  title: string;
117
117
  description?: string | undefined;
118
- id?: string | undefined;
119
118
  emoji_id?: string | undefined;
120
119
  emoji_name?: string | undefined;
120
+ id?: string | undefined;
121
121
  role_ids?: string[] | undefined;
122
122
  channel_ids?: string[] | undefined;
123
123
  }[];
@@ -138,9 +138,9 @@ export declare const EditOnboardingSchema: z.ZodObject<{
138
138
  options: {
139
139
  title: string;
140
140
  description?: string | undefined;
141
- id?: string | undefined;
142
141
  emoji_id?: string | undefined;
143
142
  emoji_name?: string | undefined;
143
+ id?: string | undefined;
144
144
  role_ids?: string[] | undefined;
145
145
  channel_ids?: string[] | undefined;
146
146
  }[];
@@ -160,9 +160,9 @@ export declare const EditOnboardingSchema: z.ZodObject<{
160
160
  options: {
161
161
  title: string;
162
162
  description?: string | undefined;
163
- id?: string | undefined;
164
163
  emoji_id?: string | undefined;
165
164
  emoji_name?: string | undefined;
165
+ id?: string | undefined;
166
166
  role_ids?: string[] | undefined;
167
167
  channel_ids?: string[] | undefined;
168
168
  }[];
@@ -175,7 +175,145 @@ export declare const EditOnboardingSchema: z.ZodObject<{
175
175
  default_channel_ids?: string[] | undefined;
176
176
  mode?: "onboarding_default" | "onboarding_advanced" | undefined;
177
177
  }>;
178
+ export declare const SetupOnboardingSchema: z.ZodObject<{
179
+ guild_id: z.ZodString;
180
+ default_channel_ids: z.ZodArray<z.ZodString, "many">;
181
+ prompts: z.ZodArray<z.ZodObject<{
182
+ type: z.ZodEnum<["multiple_choice", "dropdown"]>;
183
+ title: z.ZodString;
184
+ single_select: z.ZodDefault<z.ZodBoolean>;
185
+ required: z.ZodDefault<z.ZodBoolean>;
186
+ in_onboarding: z.ZodDefault<z.ZodBoolean>;
187
+ options: z.ZodArray<z.ZodObject<{
188
+ title: z.ZodString;
189
+ description: z.ZodOptional<z.ZodString>;
190
+ emoji_id: z.ZodOptional<z.ZodString>;
191
+ emoji_name: z.ZodOptional<z.ZodString>;
192
+ role_ids: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
193
+ channel_ids: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
194
+ }, "strip", z.ZodTypeAny, {
195
+ title: string;
196
+ description?: string | undefined;
197
+ emoji_id?: string | undefined;
198
+ emoji_name?: string | undefined;
199
+ role_ids?: string[] | undefined;
200
+ channel_ids?: string[] | undefined;
201
+ }, {
202
+ title: string;
203
+ description?: string | undefined;
204
+ emoji_id?: string | undefined;
205
+ emoji_name?: string | undefined;
206
+ role_ids?: string[] | undefined;
207
+ channel_ids?: string[] | undefined;
208
+ }>, "many">;
209
+ }, "strip", z.ZodTypeAny, {
210
+ title: string;
211
+ options: {
212
+ title: string;
213
+ description?: string | undefined;
214
+ emoji_id?: string | undefined;
215
+ emoji_name?: string | undefined;
216
+ role_ids?: string[] | undefined;
217
+ channel_ids?: string[] | undefined;
218
+ }[];
219
+ type: "multiple_choice" | "dropdown";
220
+ single_select: boolean;
221
+ required: boolean;
222
+ in_onboarding: boolean;
223
+ }, {
224
+ title: string;
225
+ options: {
226
+ title: string;
227
+ description?: string | undefined;
228
+ emoji_id?: string | undefined;
229
+ emoji_name?: string | undefined;
230
+ role_ids?: string[] | undefined;
231
+ channel_ids?: string[] | undefined;
232
+ }[];
233
+ type: "multiple_choice" | "dropdown";
234
+ single_select?: boolean | undefined;
235
+ required?: boolean | undefined;
236
+ in_onboarding?: boolean | undefined;
237
+ }>, "many">;
238
+ mode: z.ZodDefault<z.ZodEnum<["onboarding_default", "onboarding_advanced"]>>;
239
+ enabled: z.ZodDefault<z.ZodBoolean>;
240
+ }, "strict", z.ZodTypeAny, {
241
+ guild_id: string;
242
+ enabled: boolean;
243
+ prompts: {
244
+ title: string;
245
+ options: {
246
+ title: string;
247
+ description?: string | undefined;
248
+ emoji_id?: string | undefined;
249
+ emoji_name?: string | undefined;
250
+ role_ids?: string[] | undefined;
251
+ channel_ids?: string[] | undefined;
252
+ }[];
253
+ type: "multiple_choice" | "dropdown";
254
+ single_select: boolean;
255
+ required: boolean;
256
+ in_onboarding: boolean;
257
+ }[];
258
+ default_channel_ids: string[];
259
+ mode: "onboarding_default" | "onboarding_advanced";
260
+ }, {
261
+ guild_id: string;
262
+ prompts: {
263
+ title: string;
264
+ options: {
265
+ title: string;
266
+ description?: string | undefined;
267
+ emoji_id?: string | undefined;
268
+ emoji_name?: string | undefined;
269
+ role_ids?: string[] | undefined;
270
+ channel_ids?: string[] | undefined;
271
+ }[];
272
+ type: "multiple_choice" | "dropdown";
273
+ single_select?: boolean | undefined;
274
+ required?: boolean | undefined;
275
+ in_onboarding?: boolean | undefined;
276
+ }[];
277
+ default_channel_ids: string[];
278
+ enabled?: boolean | undefined;
279
+ mode?: "onboarding_default" | "onboarding_advanced" | undefined;
280
+ }>;
281
+ export declare const SetupCommunitySchema: z.ZodObject<{
282
+ guild_id: z.ZodString;
283
+ rules_channel_id: z.ZodString;
284
+ public_updates_channel_id: z.ZodString;
285
+ description: z.ZodOptional<z.ZodString>;
286
+ preferred_locale: z.ZodOptional<z.ZodString>;
287
+ safety_alerts_channel_id: z.ZodOptional<z.ZodString>;
288
+ }, "strict", z.ZodTypeAny, {
289
+ guild_id: string;
290
+ rules_channel_id: string;
291
+ public_updates_channel_id: string;
292
+ description?: string | undefined;
293
+ preferred_locale?: string | undefined;
294
+ safety_alerts_channel_id?: string | undefined;
295
+ }, {
296
+ guild_id: string;
297
+ rules_channel_id: string;
298
+ public_updates_channel_id: string;
299
+ description?: string | undefined;
300
+ preferred_locale?: string | undefined;
301
+ safety_alerts_channel_id?: string | undefined;
302
+ }>;
303
+ export declare const GetCommunitySettingsSchema: z.ZodObject<{
304
+ guild_id: z.ZodString;
305
+ response_format: z.ZodDefault<z.ZodNativeEnum<typeof import("../types.js").ResponseFormat>>;
306
+ }, "strict", z.ZodTypeAny, {
307
+ response_format: import("../types.js").ResponseFormat;
308
+ guild_id: string;
309
+ }, {
310
+ guild_id: string;
311
+ response_format?: import("../types.js").ResponseFormat | undefined;
312
+ }>;
178
313
  export type GetWelcomeScreenInput = z.infer<typeof GetWelcomeScreenSchema>;
179
314
  export type EditWelcomeScreenInput = z.infer<typeof EditWelcomeScreenSchema>;
180
315
  export type GetOnboardingInput = z.infer<typeof GetOnboardingSchema>;
181
316
  export type EditOnboardingInput = z.infer<typeof EditOnboardingSchema>;
317
+ export type SetupOnboardingInput = z.infer<typeof SetupOnboardingSchema>;
318
+ export type SetupCommunityInput = z.infer<typeof SetupCommunitySchema>;
319
+ export type GetCommunitySettingsInput = z.infer<typeof GetCommunitySettingsSchema>;
@@ -17,8 +17,8 @@ export const EditWelcomeScreenSchema = z.object({
17
17
  welcome_channels: z.array(z.object({
18
18
  channel_id: ChannelIdSchema,
19
19
  description: z.string().max(50).describe("Channel description (max 50 chars)"),
20
- emoji_id: z.string().optional().describe("Custom emoji ID (for custom server emojis)"),
21
- emoji_name: z.string().optional().describe("Emoji name (unicode emoji or custom emoji name)")
20
+ emoji_id: z.string().optional().describe("Custom emoji ID (only for custom server emojis)"),
21
+ emoji_name: z.string().optional().describe("Unicode emoji character (e.g., '👍') or custom emoji name if using emoji_id")
22
22
  }))
23
23
  .max(5)
24
24
  .optional()
@@ -44,8 +44,8 @@ export const EditOnboardingSchema = z.object({
44
44
  description: z.string().max(100).optional().describe("Option description"),
45
45
  emoji_id: z.string().optional().describe("Custom emoji ID"),
46
46
  emoji_name: z.string().optional().describe("Emoji name"),
47
- role_ids: z.array(RoleIdSchema).optional().describe("Roles to assign when selected"),
48
- channel_ids: z.array(ChannelIdSchema).optional().describe("Channels to show when selected")
47
+ role_ids: z.array(RoleIdSchema).optional().describe("Roles to assign when selected (at least one role OR channel required)"),
48
+ channel_ids: z.array(ChannelIdSchema).optional().describe("Channels to show when selected (at least one role OR channel required)")
49
49
  })).min(1).describe("Available options for this prompt")
50
50
  })).optional().describe("Onboarding prompts/questions"),
51
51
  default_channel_ids: z.array(ChannelIdSchema)
@@ -58,3 +58,54 @@ export const EditOnboardingSchema = z.object({
58
58
  .optional()
59
59
  .describe("Onboarding mode")
60
60
  }).strict();
61
+ // Setup Onboarding schema - for initial configuration
62
+ export const SetupOnboardingSchema = z.object({
63
+ guild_id: GuildIdSchema,
64
+ default_channel_ids: z.array(ChannelIdSchema)
65
+ .min(1)
66
+ .describe("Channels shown to new members by default (at least one required)"),
67
+ prompts: z.array(z.object({
68
+ type: z.enum(["multiple_choice", "dropdown"]).describe("Prompt type"),
69
+ title: z.string().max(100).describe("Prompt title"),
70
+ single_select: z.boolean().default(true).describe("Whether only one option can be selected"),
71
+ required: z.boolean().default(false).describe("Whether this prompt is required"),
72
+ in_onboarding: z.boolean().default(true).describe("Whether shown during onboarding"),
73
+ options: z.array(z.object({
74
+ title: z.string().max(50).describe("Option title"),
75
+ description: z.string().max(100).optional().describe("Option description"),
76
+ emoji_id: z.string().optional().describe("Custom emoji ID"),
77
+ emoji_name: z.string().optional().describe("Unicode emoji or custom emoji name"),
78
+ role_ids: z.array(RoleIdSchema).optional().describe("Roles to assign when selected (at least one role OR channel required)"),
79
+ channel_ids: z.array(ChannelIdSchema).optional().describe("Channels to show when selected (at least one role OR channel required)")
80
+ })).min(1).describe("Available options for this prompt")
81
+ })).min(1).describe("Onboarding prompts/questions (at least one required)"),
82
+ mode: z.enum(["onboarding_default", "onboarding_advanced"])
83
+ .default("onboarding_default")
84
+ .describe("Onboarding mode"),
85
+ enabled: z.boolean()
86
+ .default(true)
87
+ .describe("Whether to enable onboarding after setup")
88
+ }).strict();
89
+ // Setup Community schema
90
+ export const SetupCommunitySchema = z.object({
91
+ guild_id: GuildIdSchema,
92
+ rules_channel_id: ChannelIdSchema
93
+ .describe("Channel for server rules (required for Community)"),
94
+ public_updates_channel_id: ChannelIdSchema
95
+ .describe("Channel for Discord community updates (required for Community)"),
96
+ description: z.string()
97
+ .max(120)
98
+ .optional()
99
+ .describe("Server description (max 120 chars)"),
100
+ preferred_locale: z.string()
101
+ .optional()
102
+ .describe("Preferred language (e.g., 'en-US', 'de', 'fr')"),
103
+ safety_alerts_channel_id: ChannelIdSchema
104
+ .optional()
105
+ .describe("Channel for safety alerts from Discord")
106
+ }).strict();
107
+ // Get Community Settings schema
108
+ export const GetCommunitySettingsSchema = z.object({
109
+ guild_id: GuildIdSchema,
110
+ response_format: ResponseFormatSchema
111
+ }).strict();
@@ -74,6 +74,19 @@ export function formatChannel(channel) {
74
74
  if (channel.type === ChannelType.GuildText && 'topic' in channel) {
75
75
  baseChannel.topic = channel.topic ?? undefined;
76
76
  }
77
+ // Forum channel specific fields
78
+ if (channel.type === ChannelType.GuildForum && 'availableTags' in channel) {
79
+ const forumChannel = channel;
80
+ baseChannel.availableTags = forumChannel.availableTags?.map((tag) => ({
81
+ id: tag.id,
82
+ name: tag.name,
83
+ moderated: tag.moderated,
84
+ emoji: tag.emoji ? { id: tag.emoji.id, name: tag.emoji.name } : null,
85
+ }));
86
+ baseChannel.defaultReactionEmoji = forumChannel.defaultReactionEmoji;
87
+ baseChannel.defaultSortOrder = forumChannel.defaultSortOrder;
88
+ baseChannel.defaultForumLayout = forumChannel.defaultForumLayout;
89
+ }
77
90
  return baseChannel;
78
91
  }
79
92
  /**
@@ -1,6 +1,6 @@
1
1
  import { ListChannelsSchema, GetChannelSchema, CreateChannelSchema, DeleteChannelSchema, EditChannelSchema, SetChannelPermissionsSchema, RemoveChannelPermissionsSchema, GetChannelPermissionsSchema, SyncChannelPermissionsSchema, } from "../schemas/index.js";
2
2
  import { getClient, formatChannel, channelToMarkdown, formatResponse, truncateIfNeeded, getChannelType, } from "../services/discord.js";
3
- import { ChannelType, GuildChannel, PermissionsBitField, OverwriteType } from "discord.js";
3
+ import { ChannelType, GuildChannel, PermissionsBitField, OverwriteType, SortOrderType, ForumLayoutType } from "discord.js";
4
4
  // Helper function to convert permission strings to bitfield
5
5
  function parsePermissions(permissions) {
6
6
  let bits = BigInt(0);
@@ -122,12 +122,20 @@ Returns:
122
122
  Args:
123
123
  - guild_id (string): Discord server/guild ID
124
124
  - name (string): Channel name
125
- - type ('text' | 'voice' | 'category'): Channel type (default: 'text')
126
- - topic (string, optional): Channel topic (text channels only)
125
+ - type ('text' | 'voice' | 'category' | 'forum'): Channel type (default: 'text')
126
+ - topic (string, optional): Channel topic (text/forum channels only)
127
127
  - parent_id (string, optional): Category ID to create channel under (not for categories)
128
- - nsfw (boolean, optional): Whether the channel is NSFW (text channels only)
128
+ - nsfw (boolean, optional): Whether the channel is NSFW (text/forum channels only)
129
129
  - bitrate (number, optional): Bitrate for voice channels (8000-384000)
130
130
  - user_limit (number, optional): User limit for voice channels (0 = unlimited)
131
+ - default_reaction_emoji (string, optional): Default emoji for forum posts (emoji char or custom ID)
132
+ - default_sort_order ('latest_activity' | 'creation_date', optional): Sort order for forum posts
133
+ - default_forum_layout ('not_set' | 'list_view' | 'gallery_view', optional): Forum layout
134
+ - available_tags (array, optional): Tags for forum posts (max 20)
135
+ - name (string): Tag name (max 20 chars)
136
+ - moderated (boolean, optional): Only mods can apply this tag
137
+ - emoji_name (string, optional): Unicode emoji (e.g., '🎮')
138
+ - emoji_id (string, optional): Custom emoji ID
131
139
 
132
140
  Returns:
133
141
  The created channel's details`,
@@ -156,6 +164,9 @@ Returns:
156
164
  case "category":
157
165
  channelType = ChannelType.GuildCategory;
158
166
  break;
167
+ case "forum":
168
+ channelType = ChannelType.GuildForum;
169
+ break;
159
170
  default:
160
171
  channelType = ChannelType.GuildText;
161
172
  }
@@ -182,6 +193,44 @@ Returns:
182
193
  if (params.user_limit !== undefined)
183
194
  channelOptions.userLimit = params.user_limit;
184
195
  }
196
+ // Forum channel specific options
197
+ if (params.type === "forum") {
198
+ if (params.topic)
199
+ channelOptions.topic = params.topic;
200
+ if (params.nsfw !== undefined)
201
+ channelOptions.nsfw = params.nsfw;
202
+ if (params.default_reaction_emoji) {
203
+ // Check if it's a custom emoji ID (numeric) or unicode emoji
204
+ const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
205
+ channelOptions.defaultReactionEmoji = isCustomEmoji
206
+ ? { id: params.default_reaction_emoji }
207
+ : { name: params.default_reaction_emoji };
208
+ }
209
+ if (params.default_sort_order) {
210
+ channelOptions.defaultSortOrder = params.default_sort_order === "latest_activity"
211
+ ? SortOrderType.LatestActivity
212
+ : SortOrderType.CreationDate;
213
+ }
214
+ if (params.default_forum_layout) {
215
+ const layoutMap = {
216
+ not_set: ForumLayoutType.NotSet,
217
+ list_view: ForumLayoutType.ListView,
218
+ gallery_view: ForumLayoutType.GalleryView,
219
+ };
220
+ channelOptions.defaultForumLayout = layoutMap[params.default_forum_layout];
221
+ }
222
+ if (params.available_tags && params.available_tags.length > 0) {
223
+ channelOptions.availableTags = params.available_tags.map(tag => ({
224
+ name: tag.name,
225
+ moderated: tag.moderated ?? false,
226
+ emoji: tag.emoji_id
227
+ ? { id: tag.emoji_id, name: tag.emoji_name }
228
+ : tag.emoji_name
229
+ ? { id: null, name: tag.emoji_name }
230
+ : undefined,
231
+ }));
232
+ }
233
+ }
185
234
  const channel = await guild.channels.create(channelOptions);
186
235
  const typeLabel = params.type === "category" ? "category" : "channel";
187
236
  const prefix = params.type === "category" ? "📁" : "#";
@@ -248,6 +297,17 @@ Args:
248
297
  - nsfw (boolean, optional): Whether the channel is NSFW
249
298
  - bitrate (number, optional): Bitrate for voice channels (8000-384000)
250
299
  - user_limit (number, optional): User limit for voice channels
300
+ - default_reaction_emoji (string, optional): Default emoji for forum posts (emoji char or custom ID, 'none' to remove)
301
+ - default_sort_order ('latest_activity' | 'creation_date', optional): Sort order for forum posts
302
+ - default_forum_layout ('not_set' | 'list_view' | 'gallery_view', optional): Forum layout
303
+ - available_tags (array, optional): Tags for forum posts (max 20)
304
+ WARNING: This REPLACES all existing tags! To keep existing tags, first use discord_get_channel
305
+ to get current tags with their IDs, then include them in this array with their id field.
306
+ - id (string): Tag ID - REQUIRED to preserve existing tags, omit only for new tags
307
+ - name (string): Tag name (max 20 chars)
308
+ - moderated (boolean, optional): Only mods can apply this tag
309
+ - emoji_name (string, optional): Unicode emoji (e.g., '🎮')
310
+ - emoji_id (string, optional): Custom emoji ID
251
311
 
252
312
  Returns:
253
313
  Updated channel details`,
@@ -284,6 +344,44 @@ Returns:
284
344
  if (params.parent_id !== undefined) {
285
345
  updates.parent = params.parent_id === 'none' ? null : params.parent_id;
286
346
  }
347
+ // Forum channel specific options
348
+ if (params.default_reaction_emoji !== undefined) {
349
+ if (params.default_reaction_emoji === 'none') {
350
+ updates.defaultReactionEmoji = null;
351
+ }
352
+ else {
353
+ // Check if it's a custom emoji ID (numeric) or unicode emoji
354
+ const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
355
+ updates.defaultReactionEmoji = isCustomEmoji
356
+ ? { id: params.default_reaction_emoji }
357
+ : { name: params.default_reaction_emoji };
358
+ }
359
+ }
360
+ if (params.default_sort_order !== undefined) {
361
+ updates.defaultSortOrder = params.default_sort_order === "latest_activity"
362
+ ? SortOrderType.LatestActivity
363
+ : SortOrderType.CreationDate;
364
+ }
365
+ if (params.default_forum_layout !== undefined) {
366
+ const layoutMap = {
367
+ not_set: ForumLayoutType.NotSet,
368
+ list_view: ForumLayoutType.ListView,
369
+ gallery_view: ForumLayoutType.GalleryView,
370
+ };
371
+ updates.defaultForumLayout = layoutMap[params.default_forum_layout];
372
+ }
373
+ if (params.available_tags !== undefined) {
374
+ updates.availableTags = params.available_tags.map(tag => ({
375
+ id: tag.id,
376
+ name: tag.name,
377
+ moderated: tag.moderated ?? false,
378
+ emoji: tag.emoji_id
379
+ ? { id: tag.emoji_id, name: tag.emoji_name }
380
+ : tag.emoji_name
381
+ ? { id: null, name: tag.emoji_name }
382
+ : undefined,
383
+ }));
384
+ }
287
385
  await channel.edit(updates);
288
386
  return {
289
387
  content: [{ type: "text", text: `Updated channel ${params.channel_id}` }],
@@ -1,4 +1,4 @@
1
- import { GetWelcomeScreenSchema, EditWelcomeScreenSchema, GetOnboardingSchema, EditOnboardingSchema, } from "../schemas/index.js";
1
+ import { GetWelcomeScreenSchema, EditWelcomeScreenSchema, GetOnboardingSchema, EditOnboardingSchema, SetupOnboardingSchema, SetupCommunitySchema, GetCommunitySettingsSchema, } from "../schemas/index.js";
2
2
  import { getClient, formatResponse, } from "../services/discord.js";
3
3
  export function registerCommunityTools(server) {
4
4
  // ============================================================================
@@ -95,7 +95,8 @@ Returns:
95
95
  updateData.welcomeChannels = params.welcome_channels.map(ch => ({
96
96
  channel: ch.channel_id,
97
97
  description: ch.description,
98
- emoji: ch.emoji_id ? { id: ch.emoji_id, name: ch.emoji_name } : ch.emoji_name ? { name: ch.emoji_name } : undefined,
98
+ // For custom emojis, use emoji_id. For unicode emojis, just use the emoji string directly as emoji_name
99
+ emoji: ch.emoji_id ? { id: ch.emoji_id, name: ch.emoji_name } : ch.emoji_name || undefined,
99
100
  }));
100
101
  }
101
102
  const welcomeScreen = await guild.editWelcomeScreen(updateData);
@@ -234,14 +235,22 @@ Returns:
234
235
  singleSelect: p.single_select,
235
236
  required: p.required,
236
237
  inOnboarding: p.in_onboarding,
237
- options: p.options.map(o => ({
238
- id: o.id,
239
- title: o.title,
240
- description: o.description,
241
- emoji: o.emoji_id ? { id: o.emoji_id, name: o.emoji_name } : o.emoji_name ? { name: o.emoji_name } : undefined,
242
- roleIds: o.role_ids,
243
- channelIds: o.channel_ids,
244
- })),
238
+ options: p.options.map(o => {
239
+ // Discord requires at least one role OR channel per option
240
+ const hasRoles = o.role_ids && o.role_ids.length > 0;
241
+ const hasChannels = o.channel_ids && o.channel_ids.length > 0;
242
+ if (!hasRoles && !hasChannels) {
243
+ throw new Error(`Option "${o.title}" must have at least one role_ids or channel_ids. Received: role_ids=${JSON.stringify(o.role_ids)}, channel_ids=${JSON.stringify(o.channel_ids)}`);
244
+ }
245
+ return {
246
+ id: o.id,
247
+ title: o.title,
248
+ description: o.description,
249
+ emoji: o.emoji_id ? { id: o.emoji_id, name: o.emoji_name } : o.emoji_name || undefined,
250
+ roles: o.role_ids || [],
251
+ channels: o.channel_ids || [],
252
+ };
253
+ }),
245
254
  }));
246
255
  }
247
256
  const onboarding = await guild.editOnboarding(updateData);
@@ -256,4 +265,236 @@ Returns:
256
265
  };
257
266
  }
258
267
  });
268
+ server.registerTool("discord_setup_onboarding", {
269
+ title: "Setup Server Onboarding",
270
+ description: `Setup and enable onboarding for a server from scratch.
271
+
272
+ This tool configures the complete onboarding experience for new members.
273
+ The server must have Community features enabled.
274
+
275
+ Requirements:
276
+ - Server must have Community features enabled (done in Discord Server Settings)
277
+ - At least one default channel must be specified
278
+ - At least one prompt with options must be provided
279
+ - Each option must have at least one role_ids OR channel_ids
280
+
281
+ Args:
282
+ - guild_id (string): Discord server/guild ID
283
+ - default_channel_ids (string[]): Channels shown to all new members (required, at least one)
284
+ - prompts (array): Onboarding questions (required, at least one)
285
+ - type ('multiple_choice' | 'dropdown'): Prompt type
286
+ - title (string): Question title
287
+ - single_select (boolean): Allow only one selection (default: true)
288
+ - required (boolean): Must answer to continue (default: false)
289
+ - in_onboarding (boolean): Show during onboarding (default: true)
290
+ - options (array): Available choices (at least one)
291
+ - title (string): Option title
292
+ - description (string, optional): Option description
293
+ - emoji_name (string, optional): Unicode emoji (e.g., '🎮')
294
+ - role_ids (string[], optional): Roles to assign when selected
295
+ - channel_ids (string[], optional): Channels to show when selected
296
+ - mode ('onboarding_default' | 'onboarding_advanced', optional): Mode (default: 'onboarding_default')
297
+ - enabled (boolean, optional): Enable after setup (default: true)
298
+
299
+ Returns:
300
+ Confirmation with onboarding status`,
301
+ inputSchema: SetupOnboardingSchema,
302
+ annotations: {
303
+ readOnlyHint: false,
304
+ destructiveHint: false,
305
+ idempotentHint: false,
306
+ openWorldHint: true,
307
+ },
308
+ }, async (params) => {
309
+ try {
310
+ const client = await getClient();
311
+ const guild = client.guilds.cache.get(params.guild_id);
312
+ if (!guild) {
313
+ return {
314
+ isError: true,
315
+ content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
316
+ };
317
+ }
318
+ // Check if community features are enabled
319
+ if (!guild.features.includes('COMMUNITY')) {
320
+ return {
321
+ isError: true,
322
+ content: [{ type: "text", text: `Server "${guild.name}" does not have Community features enabled. Please enable Community in Discord Server Settings > Enable Community first.` }],
323
+ };
324
+ }
325
+ const promptTypeMap = { multiple_choice: 0, dropdown: 1 };
326
+ const modeMap = { onboarding_default: 0, onboarding_advanced: 1 };
327
+ // Build the onboarding data
328
+ const onboardingData = {
329
+ enabled: params.enabled ?? true,
330
+ defaultChannelIds: params.default_channel_ids,
331
+ mode: modeMap[params.mode || 'onboarding_default'],
332
+ prompts: params.prompts.map(p => ({
333
+ type: promptTypeMap[p.type],
334
+ title: p.title,
335
+ singleSelect: p.single_select ?? true,
336
+ required: p.required ?? false,
337
+ inOnboarding: p.in_onboarding ?? true,
338
+ options: p.options.map(o => {
339
+ // Validate that each option has at least one role or channel
340
+ const hasRoles = o.role_ids && o.role_ids.length > 0;
341
+ const hasChannels = o.channel_ids && o.channel_ids.length > 0;
342
+ if (!hasRoles && !hasChannels) {
343
+ throw new Error(`Option "${o.title}" must have at least one role_ids or channel_ids`);
344
+ }
345
+ return {
346
+ title: o.title,
347
+ description: o.description,
348
+ emoji: o.emoji_id ? { id: o.emoji_id, name: o.emoji_name } : o.emoji_name || undefined,
349
+ roles: o.role_ids || [],
350
+ channels: o.channel_ids || [],
351
+ };
352
+ }),
353
+ })),
354
+ };
355
+ const onboarding = await guild.editOnboarding(onboardingData);
356
+ return {
357
+ content: [{ type: "text", text: `Onboarding setup complete for "${guild.name}"!\nEnabled: ${onboarding.enabled}\nMode: ${params.mode || 'onboarding_default'}\nDefault Channels: ${params.default_channel_ids.length}\nPrompts: ${onboarding.prompts.size}` }],
358
+ };
359
+ }
360
+ catch (error) {
361
+ return {
362
+ isError: true,
363
+ content: [{ type: "text", text: `Error setting up onboarding: ${error.message}` }],
364
+ };
365
+ }
366
+ });
367
+ // ============================================================================
368
+ // COMMUNITY SETUP TOOLS
369
+ // ============================================================================
370
+ server.registerTool("discord_get_community_settings", {
371
+ title: "Get Community Settings",
372
+ description: `Get the community settings for a server.
373
+
374
+ Shows rules channel, updates channel, and other community-related settings.
375
+
376
+ Args:
377
+ - guild_id (string): Discord server/guild ID
378
+ - response_format ('markdown' | 'json'): Output format (default: 'json')
379
+
380
+ Returns:
381
+ Community settings including channels and features`,
382
+ inputSchema: GetCommunitySettingsSchema,
383
+ annotations: {
384
+ readOnlyHint: true,
385
+ destructiveHint: false,
386
+ idempotentHint: true,
387
+ openWorldHint: true,
388
+ },
389
+ }, async (params) => {
390
+ try {
391
+ const client = await getClient();
392
+ const guild = client.guilds.cache.get(params.guild_id);
393
+ if (!guild) {
394
+ return {
395
+ isError: true,
396
+ content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
397
+ };
398
+ }
399
+ const isCommunity = guild.features.includes('COMMUNITY');
400
+ const settings = {
401
+ isCommunity,
402
+ features: guild.features,
403
+ rulesChannelId: guild.rulesChannelId,
404
+ publicUpdatesChannelId: guild.publicUpdatesChannelId,
405
+ safetyAlertsChannelId: guild.safetyAlertsChannelId,
406
+ systemChannelId: guild.systemChannelId,
407
+ description: guild.description,
408
+ preferredLocale: guild.preferredLocale,
409
+ premiumTier: guild.premiumTier,
410
+ premiumSubscriptionCount: guild.premiumSubscriptionCount,
411
+ vanityURLCode: guild.vanityURLCode,
412
+ };
413
+ const text = formatResponse(settings, params.response_format, (data) => {
414
+ let output = `**Community Settings for ${guild.name}**\n`;
415
+ output += `Community Enabled: ${data.isCommunity ? 'Yes' : 'No'}\n\n`;
416
+ output += `**Channels:**\n`;
417
+ output += `- Rules: ${data.rulesChannelId ? `<#${data.rulesChannelId}>` : 'Not set'}\n`;
418
+ output += `- Public Updates: ${data.publicUpdatesChannelId ? `<#${data.publicUpdatesChannelId}>` : 'Not set'}\n`;
419
+ output += `- Safety Alerts: ${data.safetyAlertsChannelId ? `<#${data.safetyAlertsChannelId}>` : 'Not set'}\n`;
420
+ output += `- System: ${data.systemChannelId ? `<#${data.systemChannelId}>` : 'Not set'}\n\n`;
421
+ output += `**Server Info:**\n`;
422
+ output += `- Description: ${data.description || 'None'}\n`;
423
+ output += `- Locale: ${data.preferredLocale}\n`;
424
+ output += `- Boost Level: ${data.premiumTier} (${data.premiumSubscriptionCount} boosts)\n`;
425
+ output += `- Vanity URL: ${data.vanityURLCode || 'None'}\n\n`;
426
+ output += `**Features:** ${data.features.join(', ')}`;
427
+ return output;
428
+ });
429
+ return {
430
+ content: [{ type: "text", text }],
431
+ };
432
+ }
433
+ catch (error) {
434
+ return {
435
+ isError: true,
436
+ content: [{ type: "text", text: `Error getting community settings: ${error.message}` }],
437
+ };
438
+ }
439
+ });
440
+ server.registerTool("discord_setup_community", {
441
+ title: "Setup Community Server",
442
+ description: `Configure the required channels for a Community server.
443
+
444
+ Sets the rules channel and public updates channel which are required for Community features.
445
+ The server must already have Community enabled in Discord Server Settings.
446
+
447
+ Args:
448
+ - guild_id (string): Discord server/guild ID
449
+ - rules_channel_id (string): Channel for server rules (required)
450
+ - public_updates_channel_id (string): Channel for Discord updates (required)
451
+ - description (string, optional): Server description (max 120 chars)
452
+ - preferred_locale (string, optional): Preferred language (e.g., 'en-US', 'de')
453
+ - safety_alerts_channel_id (string, optional): Channel for safety alerts
454
+
455
+ Returns:
456
+ Updated community settings`,
457
+ inputSchema: SetupCommunitySchema,
458
+ annotations: {
459
+ readOnlyHint: false,
460
+ destructiveHint: false,
461
+ idempotentHint: true,
462
+ openWorldHint: true,
463
+ },
464
+ }, async (params) => {
465
+ try {
466
+ const client = await getClient();
467
+ const guild = client.guilds.cache.get(params.guild_id);
468
+ if (!guild) {
469
+ return {
470
+ isError: true,
471
+ content: [{ type: "text", text: `Guild not found: ${params.guild_id}` }],
472
+ };
473
+ }
474
+ const updateData = {
475
+ rulesChannel: params.rules_channel_id,
476
+ publicUpdatesChannel: params.public_updates_channel_id,
477
+ };
478
+ if (params.description !== undefined) {
479
+ updateData.description = params.description;
480
+ }
481
+ if (params.preferred_locale !== undefined) {
482
+ updateData.preferredLocale = params.preferred_locale;
483
+ }
484
+ if (params.safety_alerts_channel_id !== undefined) {
485
+ updateData.safetyAlertsChannel = params.safety_alerts_channel_id;
486
+ }
487
+ const updated = await guild.edit(updateData);
488
+ const isCommunity = updated.features.includes('COMMUNITY');
489
+ return {
490
+ content: [{ type: "text", text: `Community settings updated for "${updated.name}"!\nCommunity Enabled: ${isCommunity ? 'Yes' : 'No'}\nRules Channel: <#${params.rules_channel_id}>\nPublic Updates: <#${params.public_updates_channel_id}>${params.description ? `\nDescription: ${params.description}` : ''}` }],
491
+ };
492
+ }
493
+ catch (error) {
494
+ return {
495
+ isError: true,
496
+ content: [{ type: "text", text: `Error setting up community: ${error.message}` }],
497
+ };
498
+ }
499
+ });
259
500
  }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ export interface ForumTag {
2
+ id: string;
3
+ name: string;
4
+ moderated: boolean;
5
+ emoji: {
6
+ id: string | null;
7
+ name: string | null;
8
+ } | null;
9
+ }
1
10
  export interface DiscordChannel {
2
11
  id: string;
3
12
  name: string;
@@ -6,6 +15,13 @@ export interface DiscordChannel {
6
15
  position?: number;
7
16
  parentId?: string;
8
17
  parentName?: string;
18
+ availableTags?: ForumTag[];
19
+ defaultReactionEmoji?: {
20
+ id: string | null;
21
+ name: string | null;
22
+ } | null;
23
+ defaultSortOrder?: number | null;
24
+ defaultForumLayout?: number | null;
9
25
  }
10
26
  export interface DiscordGuild {
11
27
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mahesvara/discord-mcpserver",
3
- "version": "1.0.9",
3
+ "version": "1.1.2",
4
4
  "description": "MCP server for controlling Discord servers via bot token",
5
5
  "author": "Mahesvara",
6
6
  "repository": {
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "main": "dist/index.js",
29
29
  "bin": {
30
- "discord-mcp-server": "dist/index.js"
30
+ "discord-mcpserver": "dist/index.js"
31
31
  },
32
32
  "files": [
33
33
  "dist",