Skip to main content
Glama

MCP-Discord

import { z } from "zod"; import { ChannelType } from "discord.js"; import { ToolContext, ToolResponse } from "./types.js"; import { CreateTextChannelSchema, DeleteChannelSchema, ReadMessagesSchema, GetServerInfoSchema, CreateCategorySchema, EditCategorySchema, DeleteCategorySchema } from "../schemas.js"; import { handleDiscordError } from "../errorHandler.js"; // Category creation handler export async function createCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { guildId, name, position, reason } = CreateCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const guild = await context.client.guilds.fetch(guildId); if (!guild) { return { content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }], isError: true }; } const options: any = { name, type: ChannelType.GuildCategory }; if (typeof position === "number") options.position = position; if (reason) options.reason = reason; const category = await guild.channels.create(options); return { content: [{ type: "text", text: `Successfully created category "${name}" with ID: ${category.id}` }] }; } catch (error) { return handleDiscordError(error); } } // Category edit handler export async function editCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { categoryId, name, position, reason } = EditCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const category = await context.client.channels.fetch(categoryId); if (!category || category.type !== ChannelType.GuildCategory) { return { content: [{ type: "text", text: `Cannot find category with ID: ${categoryId}` }], isError: true }; } const update: any = {}; if (name) update.name = name; if (typeof position === "number") update.position = position; if (reason) update.reason = reason; await category.edit(update); return { content: [{ type: "text", text: `Successfully edited category with ID: ${categoryId}` }] }; } catch (error) { return handleDiscordError(error); } } // Category deletion handler export async function deleteCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { categoryId, reason } = DeleteCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const category = await context.client.channels.fetch(categoryId); if (!category || category.type !== ChannelType.GuildCategory) { return { content: [{ type: "text", text: `Cannot find category with ID: ${categoryId}` }], isError: true }; } await category.delete(reason || "Category deleted via API"); return { content: [{ type: "text", text: `Successfully deleted category with ID: ${categoryId}` }] }; } catch (error) { return handleDiscordError(error); } } // Text channel creation handler export async function createTextChannelHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { guildId, channelName, topic, reason } = CreateTextChannelSchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const guild = await context.client.guilds.fetch(guildId); if (!guild) { return { content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }], isError: true }; } // Create the text channel const channelOptions: any = { name: channelName, type: ChannelType.GuildText }; if (topic) channelOptions.topic = topic; if (reason) channelOptions.reason = reason; const channel = await guild.channels.create(channelOptions); return { content: [{ type: "text", text: `Successfully created text channel "${channelName}" with ID: ${channel.id}` }] }; } catch (error) { return handleDiscordError(error); } } // Channel deletion handler export async function deleteChannelHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { channelId, reason } = DeleteChannelSchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const channel = await context.client.channels.fetch(channelId); if (!channel) { return { content: [{ type: "text", text: `Cannot find channel with ID: ${channelId}` }], isError: true }; } // Check if channel can be deleted (has delete method) if (!('delete' in channel)) { return { content: [{ type: "text", text: `This channel type does not support deletion or the bot lacks permissions` }], isError: true }; } // Delete the channel await channel.delete(reason || "Channel deleted via API"); return { content: [{ type: "text", text: `Successfully deleted channel with ID: ${channelId}` }] }; } catch (error) { return handleDiscordError(error); } } // Message reading handler export async function readMessagesHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { channelId, limit } = ReadMessagesSchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const channel = await context.client.channels.fetch(channelId); if (!channel) { return { content: [{ type: "text", text: `Cannot find channel with ID: ${channelId}` }], isError: true }; } // Check if channel has messages (text channel, thread, etc.) if (!channel.isTextBased() || !('messages' in channel)) { return { content: [{ type: "text", text: `Channel type does not support reading messages` }], isError: true }; } // Fetch messages const messages = await channel.messages.fetch({ limit }); if (messages.size === 0) { return { content: [{ type: "text", text: `No messages found in channel` }] }; } // Format messages const formattedMessages = messages.map(msg => ({ id: msg.id, content: msg.content, author: { id: msg.author.id, username: msg.author.username, bot: msg.author.bot }, timestamp: msg.createdAt, attachments: msg.attachments.size, embeds: msg.embeds.length, replyTo: msg.reference ? msg.reference.messageId : null })).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()); return { content: [{ type: "text", text: JSON.stringify({ channelId, messageCount: formattedMessages.length, messages: formattedMessages }, null, 2) }] }; } catch (error) { return handleDiscordError(error); } } // Server information handler export async function getServerInfoHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { guildId } = GetServerInfoSchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: "text", text: "Discord client not logged in." }], isError: true }; } const guild = await context.client.guilds.fetch(guildId); if (!guild) { return { content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }], isError: true }; } // Fetch additional server data await guild.fetch(); // Fetch channel information const channels = await guild.channels.fetch(); // Categorize channels by type const channelsByType = { text: channels.filter(c => c?.type === ChannelType.GuildText).size, voice: channels.filter(c => c?.type === ChannelType.GuildVoice).size, category: channels.filter(c => c?.type === ChannelType.GuildCategory).size, forum: channels.filter(c => c?.type === ChannelType.GuildForum).size, announcement: channels.filter(c => c?.type === ChannelType.GuildAnnouncement).size, stage: channels.filter(c => c?.type === ChannelType.GuildStageVoice).size, total: channels.size }; // Get detailed information for all channels const channelDetails = channels.map(channel => { if (!channel) return null; return { id: channel.id, name: channel.name, type: ChannelType[channel.type] || channel.type, categoryId: channel.parentId, position: channel.position, // Only add topic for text channels topic: 'topic' in channel ? channel.topic : null, }; }).filter(c => c !== null); // Filter out null values // Group channels by type const groupedChannels = { text: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildText] || c.type === ChannelType.GuildText), voice: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildVoice] || c.type === ChannelType.GuildVoice), category: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildCategory] || c.type === ChannelType.GuildCategory), forum: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildForum] || c.type === ChannelType.GuildForum), announcement: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildAnnouncement] || c.type === ChannelType.GuildAnnouncement), stage: channelDetails.filter(c => c.type === ChannelType[ChannelType.GuildStageVoice] || c.type === ChannelType.GuildStageVoice), all: channelDetails }; // Get member count const approximateMemberCount = guild.approximateMemberCount || "unknown"; // Format guild information const guildInfo = { id: guild.id, name: guild.name, description: guild.description, icon: guild.iconURL(), owner: guild.ownerId, createdAt: guild.createdAt, memberCount: approximateMemberCount, channels: { count: channelsByType, details: groupedChannels }, features: guild.features, premium: { tier: guild.premiumTier, subscriptions: guild.premiumSubscriptionCount } }; return { content: [{ type: "text", text: JSON.stringify(guildInfo, null, 2) }] }; } catch (error) { return handleDiscordError(error); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/barryyip0625/mcp-discord'

If you have feedback or need assistance with the MCP directory API, please join our Discord server