Skip to main content
Glama

Discord Agent MCP

by aj-geddes
members.ts26.4 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, GuildNotFoundError, } from "../errors/discord.js"; import { PermissionFlagsBits } from "discord.js"; export function registerMemberTools( server: McpServer, discordManager: DiscordClientManager, logger: Logger, ) { // Kick Member Tool server.registerTool( "kick_member", { title: "Kick Member from Server", description: "Remove a member from the server (they can rejoin)", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to kick"), reason: z .string() .optional() .describe("Reason for kick (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), username: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.KickMembers)) { throw new PermissionDeniedError("KickMembers", guildId); } // Fetch and kick member const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } // Check if bot can kick this member (role hierarchy) if (member.roles.highest.position >= botMember.roles.highest.position) { throw new Error( "Cannot kick this member - their highest role is equal to or higher than the bot's highest role", ); } const username = member.user.username; await member.kick(reason); const output = { success: true, userId, username, }; logger.info("Member kicked", { guildId, userId, reason }); return { content: [ { type: "text" as const, text: `Successfully kicked ${username} (${userId}) from server`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to kick member", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to kick member: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Ban Member Tool server.registerTool( "ban_member", { title: "Ban Member from Server", description: "Ban a member from the server (prevents rejoining)", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to ban"), reason: z .string() .optional() .describe("Reason for ban (shown in audit log)"), deleteMessageDays: z .number() .int() .min(0) .max(7) .optional() .describe("Delete messages from last N days (0-7)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), username: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, reason, deleteMessageDays }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.BanMembers)) { throw new PermissionDeniedError("BanMembers", guildId); } // Fetch member (may not be in server) const member = await guild.members.fetch(userId).catch(() => null); // Check role hierarchy if member is in server if (member) { if ( member.roles.highest.position >= botMember.roles.highest.position ) { throw new Error( "Cannot ban this member - their highest role is equal to or higher than the bot's highest role", ); } } // Fetch user to get username const user = await client.users.fetch(userId); const username = user.username; await guild.members.ban(userId, { reason, deleteMessageSeconds: deleteMessageDays ? deleteMessageDays * 24 * 60 * 60 : undefined, }); const output = { success: true, userId, username, }; logger.info("Member banned", { guildId, userId, reason }); return { content: [ { type: "text" as const, text: `Successfully banned ${username} (${userId}) from server`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to ban member", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to ban member: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Unban Member Tool server.registerTool( "unban_member", { title: "Unban Member from Server", description: "Remove a ban, allowing the user to rejoin", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to unban"), reason: z .string() .optional() .describe("Reason for unban (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.BanMembers)) { throw new PermissionDeniedError("BanMembers", guildId); } await guild.members.unban(userId, reason); const output = { success: true, userId, }; logger.info("Member unbanned", { guildId, userId, reason }); return { content: [ { type: "text" as const, text: `Successfully unbanned user ${userId}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to unban member", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to unban member: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Timeout Member Tool server.registerTool( "timeout_member", { title: "Timeout Member", description: "Temporarily mute a member (prevents sending messages, reactions, joining voice)", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to timeout"), durationMinutes: z .number() .int() .min(1) .max(40320) .describe("Timeout duration in minutes (max 28 days)"), reason: z .string() .optional() .describe("Reason for timeout (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), username: z.string().optional(), timeoutUntil: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, durationMinutes, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.ModerateMembers)) { throw new PermissionDeniedError("ModerateMembers", guildId); } // Fetch member const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } // Check role hierarchy if (member.roles.highest.position >= botMember.roles.highest.position) { throw new Error( "Cannot timeout this member - their highest role is equal to or higher than the bot's highest role", ); } const timeoutUntil = new Date(Date.now() + durationMinutes * 60 * 1000); await member.timeout(durationMinutes * 60 * 1000, reason); const output = { success: true, userId, username: member.user.username, timeoutUntil: timeoutUntil.toISOString(), }; logger.info("Member timed out", { guildId, userId, durationMinutes, reason, }); return { content: [ { type: "text" as const, text: `Successfully timed out ${member.user.username} for ${durationMinutes} minutes`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to timeout member", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to timeout member: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Remove Timeout Tool server.registerTool( "remove_timeout", { title: "Remove Member Timeout", description: "Remove timeout from a member, restoring their permissions", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to remove timeout from"), reason: z .string() .optional() .describe("Reason for removing timeout (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), username: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.ModerateMembers)) { throw new PermissionDeniedError("ModerateMembers", guildId); } // Fetch member const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } await member.timeout(null, reason); const output = { success: true, userId, username: member.user.username, }; logger.info("Timeout removed from member", { guildId, userId, reason }); return { content: [ { type: "text" as const, text: `Successfully removed timeout from ${member.user.username}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to remove timeout", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to remove timeout: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Get Member Info Tool server.registerTool( "get_member_info", { title: "Get Member Information", description: "Get detailed information about a server member", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to get info for"), }, outputSchema: { success: z.boolean(), member: z .object({ userId: z.string(), username: z.string(), discriminator: z.string(), nickname: z.string().nullable(), joinedAt: z.string(), roles: z.array( z.object({ id: z.string(), name: z.string(), color: z.number(), position: z.number(), }), ), isBot: z.boolean(), isOwner: z.boolean(), timeoutUntil: z.string().nullable(), avatar: z.string().nullable(), }) .optional(), error: z.string().optional(), }, }, async ({ guildId, userId }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Fetch member const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } const memberData = { userId: member.user.id, username: member.user.username, discriminator: member.user.discriminator, nickname: member.nickname, joinedAt: member.joinedAt?.toISOString() || null, roles: member.roles.cache .filter((role) => role.id !== guild.id) // Exclude @everyone .map((role) => ({ id: role.id, name: role.name, color: role.color, position: role.position, })), isBot: member.user.bot, isOwner: guild.ownerId === member.user.id, timeoutUntil: member.communicationDisabledUntil?.toISOString() || null, avatar: member.user.avatar, }; const output = { success: true, member: memberData, }; logger.info("Member info retrieved", { guildId, userId }); return { content: [ { type: "text" as const, text: `Member: ${member.user.username}${member.nickname ? ` (${member.nickname})` : ""}\nRoles: ${memberData.roles.length}\nJoined: ${memberData.joinedAt}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to get member info", { error: error.message, guildId, userId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to get member info: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // List Members Tool server.registerTool( "list_members", { title: "List Server Members", description: "List all members in the server with optional filters", inputSchema: { guildId: z.string().describe("Server/Guild ID"), limit: z .number() .int() .min(1) .max(1000) .optional() .default(100) .describe("Max members to return (default 100)"), roleId: z .string() .optional() .describe("Filter by role ID (only members with this role)"), botsOnly: z.boolean().optional().describe("Only return bot accounts"), }, outputSchema: { success: z.boolean(), members: z .array( z.object({ userId: z.string(), username: z.string(), nickname: z.string().nullable(), isBot: z.boolean(), joinedAt: z.string(), roleCount: z.number(), }), ) .optional(), totalCount: z.number().optional(), error: z.string().optional(), }, }, async ({ guildId, limit = 100, roleId, botsOnly }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Fetch all members const members = await guild.members.fetch({ limit }); // Apply filters let filteredMembers = Array.from(members.values()); if (roleId) { filteredMembers = filteredMembers.filter((m) => m.roles.cache.has(roleId), ); } if (botsOnly) { filteredMembers = filteredMembers.filter((m) => m.user.bot); } const memberList = filteredMembers.map((member) => ({ userId: member.user.id, username: member.user.username, nickname: member.nickname, isBot: member.user.bot, joinedAt: member.joinedAt?.toISOString() || "", roleCount: member.roles.cache.size - 1, // Exclude @everyone })); const output = { success: true, members: memberList, totalCount: memberList.length, }; logger.info("Members listed", { guildId, count: memberList.length, roleId, botsOnly, }); return { content: [ { type: "text" as const, text: `Found ${memberList.length} member(s) in server`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to list members", { error: error.message, guildId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to list members: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Assign Role Tool server.registerTool( "assign_role", { title: "Assign Role to Member", description: "Add a role to a server member", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to assign role to"), roleId: z.string().describe("Role ID to assign"), reason: z .string() .optional() .describe("Reason for role assignment (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), roleId: z.string().optional(), roleName: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, roleId, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.ManageRoles)) { throw new PermissionDeniedError("ManageRoles", guildId); } // Fetch member and role const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } const role = await guild.roles.fetch(roleId).catch(() => null); if (!role) { throw new Error(`Role ${roleId} not found in server`); } // Check role hierarchy if (role.position >= botMember.roles.highest.position) { throw new Error( "Cannot assign this role - it is equal to or higher than the bot's highest role", ); } await member.roles.add(role, reason); const output = { success: true, userId, roleId, roleName: role.name, }; logger.info("Role assigned to member", { guildId, userId, roleId, reason, }); return { content: [ { type: "text" as const, text: `Successfully assigned role "${role.name}" to ${member.user.username}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to assign role", { error: error.message, guildId, userId, roleId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to assign role: ${error.message}`, }, ], structuredContent: output, isError: true, }; } }, ); // Remove Role Tool server.registerTool( "remove_role", { title: "Remove Role from Member", description: "Remove a role from a server member", inputSchema: { guildId: z.string().describe("Server/Guild ID"), userId: z.string().describe("User ID to remove role from"), roleId: z.string().describe("Role ID to remove"), reason: z .string() .optional() .describe("Reason for role removal (shown in audit log)"), }, outputSchema: { success: z.boolean(), userId: z.string().optional(), roleId: z.string().optional(), roleName: z.string().optional(), error: z.string().optional(), }, }, async ({ guildId, userId, roleId, reason }) => { try { const client = discordManager.getClient(); const guild = await client.guilds.fetch(guildId).catch(() => null); if (!guild) { throw new GuildNotFoundError(guildId); } // Check bot permissions const botMember = await guild.members.fetchMe(); if (!botMember.permissions.has(PermissionFlagsBits.ManageRoles)) { throw new PermissionDeniedError("ManageRoles", guildId); } // Fetch member and role const member = await guild.members.fetch(userId).catch(() => null); if (!member) { throw new Error(`Member ${userId} not found in server`); } const role = await guild.roles.fetch(roleId).catch(() => null); if (!role) { throw new Error(`Role ${roleId} not found in server`); } // Check role hierarchy if (role.position >= botMember.roles.highest.position) { throw new Error( "Cannot remove this role - it is equal to or higher than the bot's highest role", ); } await member.roles.remove(role, reason); const output = { success: true, userId, roleId, roleName: role.name, }; logger.info("Role removed from member", { guildId, userId, roleId, reason, }); return { content: [ { type: "text" as const, text: `Successfully removed role "${role.name}" from ${member.user.username}`, }, ], structuredContent: output, }; } catch (error: any) { logger.error("Failed to remove role", { error: error.message, guildId, userId, roleId, }); const output = { success: false, error: error.message, }; return { content: [ { type: "text" as const, text: `Failed to remove role: ${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