Skip to main content
Glama
webhook-tools.ts8.63 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { TextChannel, NewsChannel, VoiceChannel, ForumChannel, ChannelType } from 'discord.js'; import { getDiscordClient } from '../utils/discord-client.js'; import { withErrorHandling } from '../utils/error-handler.js'; type WebhookableChannel = TextChannel | NewsChannel | VoiceChannel | ForumChannel; function isWebhookableChannel(channel: unknown): channel is WebhookableChannel { if (!channel || typeof channel !== 'object') return false; const ch = channel as { type?: number }; return ch.type === ChannelType.GuildText || ch.type === ChannelType.GuildAnnouncement || ch.type === ChannelType.GuildVoice || ch.type === ChannelType.GuildForum; } export function registerWebhookTools(server: McpServer): void { // List webhooks in a channel server.tool( 'list_channel_webhooks', 'List all webhooks in 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 (!isWebhookableChannel(channel)) { throw new Error('Channel does not support webhooks'); } const webhooks = await channel.fetchWebhooks(); return webhooks.map((wh) => ({ id: wh.id, name: wh.name, type: wh.type, channelId: wh.channelId, guildId: wh.guildId, avatar: wh.avatar, token: wh.token ? '[REDACTED]' : null, owner: wh.owner ? { id: wh.owner.id, username: wh.owner.username } : null, applicationId: wh.applicationId, sourceGuild: wh.sourceGuild ? { id: wh.sourceGuild.id, name: wh.sourceGuild.name } : null, sourceChannel: wh.sourceChannel ? { id: wh.sourceChannel.id, name: wh.sourceChannel.name } : null, url: wh.url, createdAt: wh.createdAt?.toISOString(), })); }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // List all webhooks in a guild server.tool( 'list_guild_webhooks', 'List all webhooks in a server', { guildId: z.string().describe('The ID of the server (guild)'), }, async ({ guildId }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const guild = await client.guilds.fetch(guildId); const webhooks = await guild.fetchWebhooks(); return webhooks.map((wh) => ({ id: wh.id, name: wh.name, type: wh.type, channelId: wh.channelId, avatar: wh.avatar, owner: wh.owner ? { id: wh.owner.id, username: wh.owner.username } : null, createdAt: wh.createdAt?.toISOString(), })); }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // Create webhook server.tool( 'create_webhook', 'Create a webhook in a channel', { guildId: z.string().describe('The ID of the server (guild)'), channelId: z.string().describe('The ID of the channel'), name: z.string().describe('Name for the webhook'), avatar: z.string().optional().describe('URL of the avatar image'), reason: z.string().optional().describe('Reason for creating'), }, async ({ guildId, channelId, name, avatar, 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 (!isWebhookableChannel(channel)) { throw new Error('Channel does not support webhooks'); } const webhook = await channel.createWebhook({ name, avatar, reason }); return { id: webhook.id, name: webhook.name, channelId: webhook.channelId, token: webhook.token, url: webhook.url, message: 'Webhook created 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 webhook server.tool( 'delete_webhook', 'Delete a webhook', { webhookId: z.string().describe('The ID of the webhook'), reason: z.string().optional().describe('Reason for deleting'), }, async ({ webhookId, reason }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const webhook = await client.fetchWebhook(webhookId); const webhookName = webhook.name; await webhook.delete(reason); return { webhookId, webhookName, message: 'Webhook 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) }] }; } ); // Modify webhook server.tool( 'modify_webhook', 'Modify a webhook', { webhookId: z.string().describe('The ID of the webhook'), name: z.string().optional().describe('New name for the webhook'), avatar: z.string().optional().describe('URL of the new avatar image'), channelId: z.string().optional().describe('Move webhook to different channel'), reason: z.string().optional().describe('Reason for modifying'), }, async ({ webhookId, name, avatar, channelId, reason }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const webhook = await client.fetchWebhook(webhookId); const editData: { name?: string; avatar?: string; channel?: string; reason?: string } = {}; if (name) editData.name = name; if (avatar) editData.avatar = avatar; if (channelId) editData.channel = channelId; if (reason) editData.reason = reason; const updated = await webhook.edit(editData); return { id: updated.id, name: updated.name, channelId: updated.channelId, message: 'Webhook updated successfully', }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); // Send message via webhook server.tool( 'send_webhook_message', 'Send a message using a webhook', { webhookId: z.string().describe('The ID of the webhook'), webhookToken: z.string().describe('The token of the webhook'), content: z.string().optional().describe('The message content'), username: z.string().optional().describe('Override the webhook username'), avatarUrl: z.string().optional().describe('Override the webhook avatar'), threadId: z.string().optional().describe('Send to a specific thread'), }, async ({ webhookId, webhookToken, content, username, avatarUrl, threadId }) => { const result = await withErrorHandling(async () => { const client = await getDiscordClient(); const webhook = await client.fetchWebhook(webhookId, webhookToken); const message = await webhook.send({ content, username, avatarURL: avatarUrl, threadId, }); return { messageId: message.id, channelId: message.channelId, content: message.content, createdAt: message.createdAt.toISOString(), message: 'Webhook message sent successfully', }; }); if (!result.success) { return { content: [{ type: 'text', text: result.error }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] }; } ); }

Implementation Reference

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