Skip to main content
Glama

Discord Agent MCP

by aj-geddes
moderation.ts9.51 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { DiscordClientManager } from "../discord/client.js"; import { Logger } from "../utils/logger.js"; import { z } from "zod"; import { PermissionDeniedError, ChannelNotFoundError, GuildNotFoundError, } from "../errors/discord.js"; import { PermissionFlagsBits } from "discord.js"; export function registerModerationTools( server: McpServer, discordManager: DiscordClientManager, logger: Logger, ) { // Bulk Delete Messages Tool server.registerTool( "bulk_delete_messages", { title: "Bulk Delete Messages", description: "Delete multiple messages at once (max 100, must be <14 days old)", inputSchema: { channelId: z.string().describe("Channel ID"), limit: z .number() .int() .min(2) .max(100) .describe("Number of messages to delete (2-100)"), filterUserId: z .string() .optional() .describe("Only delete messages from this user"), filterBots: z.boolean().optional().describe("Only delete bot messages"), reason: z .string() .optional() .describe("Reason for bulk delete (audit log)"), }, outputSchema: { success: z.boolean(), deletedCount: z.number().optional(), error: z.string().optional(), }, }, async ({ channelId, limit, filterUserId, filterBots, reason }) => { try { const client = discordManager.getClient(); const channel = await client.channels .fetch(channelId) .catch(() => null); if (!channel || !channel.isTextBased()) { throw new ChannelNotFoundError(channelId); } // Check permissions if ("guild" in channel && channel.guild) { const permissions = channel.permissionsFor(client.user!); if (!permissions?.has(PermissionFlagsBits.ManageMessages)) { throw new PermissionDeniedError("ManageMessages", channelId); } } // Fetch messages const messages = await channel.messages.fetch({ limit }); // Apply filters let messagesToDelete = Array.from(messages.values()); if (filterUserId) { messagesToDelete = messagesToDelete.filter( (msg) => msg.author.id === filterUserId, ); } if (filterBots) { messagesToDelete = messagesToDelete.filter((msg) => msg.author.bot); } // Filter out messages older than 14 days (Discord limitation) const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000; messagesToDelete = messagesToDelete.filter( (msg) => msg.createdTimestamp > twoWeeksAgo, ); if (messagesToDelete.length === 0) { throw new Error( "No messages found matching criteria or all messages are too old (>14 days)", ); } // Bulk delete if (!("bulkDelete" in channel)) { throw new ChannelNotFoundError(channelId); } const deleted = await (channel as any).bulkDelete( messagesToDelete, true, ); const deletedCount = deleted.size; const output = { success: true, deletedCount, }; logger.info("Bulk delete completed", { channelId, deletedCount, reason, }); return { content: [ { type: "text" as const, text: `Successfully deleted ${deletedCount} message(s)`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to bulk delete messages", { error: error.message, channelId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to bulk delete messages: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Get Bans Tool server.registerTool( "get_bans", { title: "Get Banned Users", description: "List all banned users in the server", inputSchema: { guildId: z.string().describe("Guild ID"), limit: z .number() .int() .min(1) .max(1000) .optional() .default(100) .describe("Max bans to retrieve (max 1000)"), }, outputSchema: { success: z.boolean(), bans: z .array( z.object({ userId: z.string(), username: z.string(), reason: z.string().nullable(), }), ) .optional(), totalCount: z.number().optional(), error: z.string().optional(), }, }, async ({ guildId, limit = 100 }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.BanMembers)) { throw new PermissionDeniedError("BanMembers", guildId); } const bans = await guild.bans.fetch({ limit }); const banList = bans.map((ban) => ({ userId: ban.user.id, username: ban.user.username, reason: ban.reason, })); const output = { success: true, bans: banList, totalCount: banList.length, }; logger.info("Bans retrieved", { guildId, count: banList.length }); return { content: [ { type: "text" as const, text: `Found ${banList.length} banned user(s)`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to get bans", { error: error.message, guildId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to get bans: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Nickname Member Tool server.registerTool( "set_nickname", { title: "Set Member Nickname", description: "Change a member's server nickname", inputSchema: { guildId: z.string().describe("Guild ID"), userId: z.string().describe("User ID"), nickname: z .string() .max(32) .optional() .describe("New nickname (null/empty to remove)"), reason: z .string() .optional() .describe("Reason for nickname change (audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), nickname: z.string().nullable().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, nickname, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.ManageNicknames)) { throw new PermissionDeniedError("ManageNicknames", guildId); } const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } // Check role hierarchy (can't change nickname of higher role) if (member.roles.highest.position >= botMember.roles.highest.position) { throw new Error( "Cannot change nickname - member's highest role is equal to or higher than bot's", ); } await member.setNickname(nickname || null, reason); const output = { success: true, userId, nickname: nickname || null, }; logger.info("Nickname set", { guildId, userId, nickname, reason }); return { content: [ { type: "text" as const, text: nickname ? `Nickname set to "${nickname}" for user ${userId}` : `Nickname removed for user ${userId}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to set nickname", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to set nickname: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); }

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/aj-geddes/discord-agent-mcp'

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