@mahesvara/discord-mcpserver 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,30 +25,39 @@ 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"]>>;
34
37
  }, "strict", z.ZodTypeAny, {
35
38
  name: string;
36
- type: "text" | "voice" | "category";
39
+ type: "text" | "voice" | "category" | "forum";
37
40
  guild_id: string;
38
41
  topic?: string | undefined;
39
42
  parent_id?: string | undefined;
40
43
  nsfw?: boolean | undefined;
41
44
  bitrate?: number | undefined;
42
45
  user_limit?: number | undefined;
46
+ default_reaction_emoji?: string | undefined;
47
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
48
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
43
49
  }, {
44
50
  name: string;
45
51
  guild_id: string;
46
- type?: "text" | "voice" | "category" | undefined;
52
+ type?: "text" | "voice" | "category" | "forum" | undefined;
47
53
  topic?: string | undefined;
48
54
  parent_id?: string | undefined;
49
55
  nsfw?: boolean | undefined;
50
56
  bitrate?: number | undefined;
51
57
  user_limit?: number | undefined;
58
+ default_reaction_emoji?: string | undefined;
59
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
60
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
52
61
  }>;
53
62
  export declare const DeleteChannelSchema: z.ZodObject<{
54
63
  channel_id: z.ZodString;
@@ -66,6 +75,9 @@ export declare const EditChannelSchema: z.ZodObject<{
66
75
  nsfw: z.ZodOptional<z.ZodBoolean>;
67
76
  bitrate: z.ZodOptional<z.ZodNumber>;
68
77
  user_limit: z.ZodOptional<z.ZodNumber>;
78
+ default_reaction_emoji: z.ZodOptional<z.ZodString>;
79
+ default_sort_order: z.ZodOptional<z.ZodEnum<["latest_activity", "creation_date"]>>;
80
+ default_forum_layout: z.ZodOptional<z.ZodEnum<["not_set", "list_view", "gallery_view"]>>;
69
81
  }, "strict", z.ZodTypeAny, {
70
82
  channel_id: string;
71
83
  name?: string | undefined;
@@ -74,6 +86,9 @@ export declare const EditChannelSchema: z.ZodObject<{
74
86
  nsfw?: boolean | undefined;
75
87
  bitrate?: number | undefined;
76
88
  user_limit?: number | undefined;
89
+ default_reaction_emoji?: string | undefined;
90
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
91
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
77
92
  position?: number | undefined;
78
93
  }, {
79
94
  channel_id: string;
@@ -83,6 +98,9 @@ export declare const EditChannelSchema: z.ZodObject<{
83
98
  nsfw?: boolean | undefined;
84
99
  bitrate?: number | undefined;
85
100
  user_limit?: number | undefined;
101
+ default_reaction_emoji?: string | undefined;
102
+ default_sort_order?: "latest_activity" | "creation_date" | undefined;
103
+ default_forum_layout?: "not_set" | "list_view" | "gallery_view" | undefined;
86
104
  position?: number | undefined;
87
105
  }>;
88
106
  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,16 @@ 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")
44
53
  }).strict();
45
54
  export const DeleteChannelSchema = z.object({
46
55
  channel_id: ChannelIdSchema
@@ -77,7 +86,16 @@ export const EditChannelSchema = z.object({
77
86
  .min(0)
78
87
  .max(99)
79
88
  .optional()
80
- .describe("User limit for voice channels")
89
+ .describe("User limit for voice channels"),
90
+ default_reaction_emoji: z.string()
91
+ .optional()
92
+ .describe("Default reaction emoji for forum posts - use emoji character (e.g., '👍') or custom emoji ID, use 'none' to remove"),
93
+ default_sort_order: z.enum(["latest_activity", "creation_date"])
94
+ .optional()
95
+ .describe("Default sort order for forum posts"),
96
+ default_forum_layout: z.enum(["not_set", "list_view", "gallery_view"])
97
+ .optional()
98
+ .describe("Default layout for forum channel")
81
99
  }).strict();
82
100
  // Permission schemas
83
101
  export const PermissionOverwriteSchema = z.object({
@@ -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)
@@ -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,15 @@ 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
131
134
 
132
135
  Returns:
133
136
  The created channel's details`,
@@ -156,6 +159,9 @@ Returns:
156
159
  case "category":
157
160
  channelType = ChannelType.GuildCategory;
158
161
  break;
162
+ case "forum":
163
+ channelType = ChannelType.GuildForum;
164
+ break;
159
165
  default:
160
166
  channelType = ChannelType.GuildText;
161
167
  }
@@ -182,6 +188,33 @@ Returns:
182
188
  if (params.user_limit !== undefined)
183
189
  channelOptions.userLimit = params.user_limit;
184
190
  }
191
+ // Forum channel specific options
192
+ if (params.type === "forum") {
193
+ if (params.topic)
194
+ channelOptions.topic = params.topic;
195
+ if (params.nsfw !== undefined)
196
+ channelOptions.nsfw = params.nsfw;
197
+ if (params.default_reaction_emoji) {
198
+ // Check if it's a custom emoji ID (numeric) or unicode emoji
199
+ const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
200
+ channelOptions.defaultReactionEmoji = isCustomEmoji
201
+ ? { id: params.default_reaction_emoji }
202
+ : { name: params.default_reaction_emoji };
203
+ }
204
+ if (params.default_sort_order) {
205
+ channelOptions.defaultSortOrder = params.default_sort_order === "latest_activity"
206
+ ? SortOrderType.LatestActivity
207
+ : SortOrderType.CreationDate;
208
+ }
209
+ if (params.default_forum_layout) {
210
+ const layoutMap = {
211
+ not_set: ForumLayoutType.NotSet,
212
+ list_view: ForumLayoutType.ListView,
213
+ gallery_view: ForumLayoutType.GalleryView,
214
+ };
215
+ channelOptions.defaultForumLayout = layoutMap[params.default_forum_layout];
216
+ }
217
+ }
185
218
  const channel = await guild.channels.create(channelOptions);
186
219
  const typeLabel = params.type === "category" ? "category" : "channel";
187
220
  const prefix = params.type === "category" ? "📁" : "#";
@@ -248,6 +281,9 @@ Args:
248
281
  - nsfw (boolean, optional): Whether the channel is NSFW
249
282
  - bitrate (number, optional): Bitrate for voice channels (8000-384000)
250
283
  - user_limit (number, optional): User limit for voice channels
284
+ - default_reaction_emoji (string, optional): Default emoji for forum posts (emoji char or custom ID, 'none' to remove)
285
+ - default_sort_order ('latest_activity' | 'creation_date', optional): Sort order for forum posts
286
+ - default_forum_layout ('not_set' | 'list_view' | 'gallery_view', optional): Forum layout
251
287
 
252
288
  Returns:
253
289
  Updated channel details`,
@@ -284,6 +320,32 @@ Returns:
284
320
  if (params.parent_id !== undefined) {
285
321
  updates.parent = params.parent_id === 'none' ? null : params.parent_id;
286
322
  }
323
+ // Forum channel specific options
324
+ if (params.default_reaction_emoji !== undefined) {
325
+ if (params.default_reaction_emoji === 'none') {
326
+ updates.defaultReactionEmoji = null;
327
+ }
328
+ else {
329
+ // Check if it's a custom emoji ID (numeric) or unicode emoji
330
+ const isCustomEmoji = /^\d+$/.test(params.default_reaction_emoji);
331
+ updates.defaultReactionEmoji = isCustomEmoji
332
+ ? { id: params.default_reaction_emoji }
333
+ : { name: params.default_reaction_emoji };
334
+ }
335
+ }
336
+ if (params.default_sort_order !== undefined) {
337
+ updates.defaultSortOrder = params.default_sort_order === "latest_activity"
338
+ ? SortOrderType.LatestActivity
339
+ : SortOrderType.CreationDate;
340
+ }
341
+ if (params.default_forum_layout !== undefined) {
342
+ const layoutMap = {
343
+ not_set: ForumLayoutType.NotSet,
344
+ list_view: ForumLayoutType.ListView,
345
+ gallery_view: ForumLayoutType.GalleryView,
346
+ };
347
+ updates.defaultForumLayout = layoutMap[params.default_forum_layout];
348
+ }
287
349
  await channel.edit(updates);
288
350
  return {
289
351
  content: [{ type: "text", text: `Updated channel ${params.channel_id}` }],
@@ -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`);
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
+ roleIds: o.role_ids || [],
251
+ channelIds: o.channel_ids || [],
252
+ };
253
+ }),
245
254
  }));
246
255
  }
247
256
  const onboarding = await guild.editOnboarding(updateData);
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.0",
4
4
  "description": "MCP server for controlling Discord servers via bot token",
5
5
  "author": "Mahesvara",
6
6
  "repository": {