Skip to main content
Glama
permission-tools.ts9.04 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { OverwriteType, PermissionFlagsBits, GuildChannel } from 'discord.js'; import { getDiscordClient } from '../utils/discord-client.js'; import { withErrorHandling } from '../utils/error-handler.js'; export function registerPermissionTools(server: McpServer): void { // Get channel permission overwrites server.tool( 'get_channel_permissions', 'Get all permission overwrites for a channel', { guildId: z.string().describe('The ID of the server (guild)'), channelId: z.string().describe('The ID of the channel'), }, async ({ guildId, channelId }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const guild = await client.guilds.fetch(guildId); const channel = await guild.channels.fetch(channelId); if (!channel) throw new Error('Channel not found'); const overwrites = (channel as GuildChannel).permissionOverwrites.cache.map((overwrite) => ({ id: overwrite.id, type: overwrite.type === OverwriteType.Role ? 'role' : 'member', allow: overwrite.allow.toArray(), deny: overwrite.deny.toArray(), })); // Get role/member names for context const resolvedOverwrites = await Promise.all( overwrites.map(async (ow) => { let name = 'Unknown'; if (ow.type === 'role') { const role = await guild.roles.fetch(ow.id); name = role?.name ?? 'Unknown Role'; } else { try { const member = await guild.members.fetch(ow.id); name = member.displayName; } catch { name = 'Unknown Member'; } } return { ...ow, name }; }) ); return { channelId, channelName: channel.name, overwrites: resolvedOverwrites, }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // Set channel permission overwrite server.tool( 'set_channel_permission', 'Set permission overwrite for a role or member on a channel', { guildId: z.string().describe('The ID of the server (guild)'), channelId: z.string().describe('The ID of the channel'), targetId: z.string().describe('The ID of the role or member'), targetType: z.enum(['role', 'member']).describe('Whether the target is a role or member'), allow: z.array(z.string()).optional().describe('Array of permission names to allow'), deny: z.array(z.string()).optional().describe('Array of permission names to deny'), reason: z.string().optional().describe('Reason for the change'), }, async ({ guildId, channelId, targetId, targetType, allow, deny, reason }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const guild = await client.guilds.fetch(guildId); const channel = await guild.channels.fetch(channelId); if (!channel) throw new Error('Channel not found'); const guildChannel = channel as GuildChannel; // Build permission overwrite options const overwriteOptions: Record<string, boolean> = {}; if (allow) { for (const perm of allow) { if (perm in PermissionFlagsBits) { overwriteOptions[perm] = true; } } } if (deny) { for (const perm of deny) { if (perm in PermissionFlagsBits) { overwriteOptions[perm] = false; } } } await guildChannel.permissionOverwrites.edit(targetId, overwriteOptions, { reason, type: targetType === 'role' ? OverwriteType.Role : OverwriteType.Member, }); return { channelId, channelName: channel.name, targetId, targetType, message: 'Permission overwrite set successfully', }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // Delete channel permission overwrite server.tool( 'delete_channel_permission', 'Delete a permission overwrite for a role or member on a channel', { guildId: z.string().describe('The ID of the server (guild)'), channelId: z.string().describe('The ID of the channel'), targetId: z.string().describe('The ID of the role or member'), reason: z.string().optional().describe('Reason for the deletion'), }, async ({ guildId, channelId, targetId, reason }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const guild = await client.guilds.fetch(guildId); const channel = await guild.channels.fetch(channelId); if (!channel) throw new Error('Channel not found'); const guildChannel = channel as GuildChannel; await guildChannel.permissionOverwrites.delete(targetId, reason); return { channelId, channelName: channel.name, targetId, message: 'Permission overwrite deleted successfully', }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // List available permissions server.tool( 'list_permissions', 'List all available Discord permission names that can be used for roles and channel overwrites', {}, async () => { const permissions = Object.keys(PermissionFlagsBits).filter( (key) => isNaN(Number(key)) ); const categorized = { general: [ 'Administrator', 'ViewAuditLog', 'ViewGuildInsights', 'ManageGuild', 'ManageRoles', 'ManageChannels', 'KickMembers', 'BanMembers', 'CreateInstantInvite', 'ChangeNickname', 'ManageNicknames', 'ManageEmojisAndStickers', 'ManageWebhooks', 'ManageGuildExpressions', 'ViewChannel', ], text: [ 'SendMessages', 'SendMessagesInThreads', 'CreatePublicThreads', 'CreatePrivateThreads', 'EmbedLinks', 'AttachFiles', 'AddReactions', 'UseExternalEmojis', 'UseExternalStickers', 'MentionEveryone', 'ManageMessages', 'ManageThreads', 'ReadMessageHistory', 'SendTTSMessages', 'UseApplicationCommands', 'SendVoiceMessages', ], voice: [ 'Connect', 'Speak', 'Stream', 'UseEmbeddedActivities', 'UseSoundboard', 'UseExternalSounds', 'UseVAD', 'PrioritySpeaker', 'MuteMembers', 'DeafenMembers', 'MoveMembers', ], stage: [ 'RequestToSpeak', ], events: [ 'CreateEvents', 'ManageEvents', ], monetization: [ 'CreateGuildExpressions', 'ViewCreatorMonetizationAnalytics', ], }; return { content: [ { type: 'text', text: JSON.stringify({ message: 'Available Discord permissions', permissions, categorized, }, null, 2), }, ], }; } ); // Sync channel permissions with parent category server.tool( 'sync_channel_permissions', 'Sync a channel\'s permissions with its parent category', { guildId: z.string().describe('The ID of the server (guild)'), channelId: z.string().describe('The ID of the channel'), }, async ({ guildId, channelId }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const guild = await client.guilds.fetch(guildId); const channel = await guild.channels.fetch(channelId); if (!channel) throw new Error('Channel not found'); const guildChannel = channel as GuildChannel; if (!guildChannel.parent) { throw new Error('Channel does not have a parent category'); } await guildChannel.lockPermissions(); return { channelId, channelName: channel.name, parentId: guildChannel.parent.id, parentName: guildChannel.parent.name, message: 'Channel permissions synced with parent category', }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); }

Latest Blog Posts

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/scarecr0w12/discord-mcp'

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