discord_send_message
Send messages to a specific Discord channel using a channel ID, optionally including attachments (up to 10 files) and text-to-speech functionality. Use this tool to communicate directly within Discord via the Discord MCP Server.
Instructions
Send a message to a Discord channel, optionally with up to 10 files
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| channelId | Yes | The Discord channel ID | |
| content | No | Message content (max 2000 characters) | |
| files | No | Files to attach to the message | |
| replyTo | No | Message ID to reply to | |
| tts | No | Send as text-to-speech message |
Implementation Reference
- src/index.ts:396-481 (handler)The handler function for the 'send-message' Discord tool, which validates input, finds the channel, checks permissions, sends the message with retry logic, and returns success or error response.private async handleSendMessage(request: McpRequest) { if (!isValidSendMessageArgs(request.params.arguments)) { throw new McpError( ErrorCode.InvalidParams, "Invalid arguments for send-message. Channel and message must be non-empty strings." ); } const { channel: channelInput, message } = request.params .arguments as SendMessageArgs; try { const channel = await this.findChannel(channelInput); if (!channel) { return { content: [ { type: "text", text: `Channel not found: ${channelInput}`, }, ], isError: true, }; } // Check if bot has permission to send messages if ( !channel.permissionsFor(this.discordClient.user?.id || "")?.has( PermissionsBitField.Flags.SendMessages ) ) { return { content: [ { type: "text", text: `Bot does not have permission to send messages in ${channel.name}`, }, ], isError: true, }; } // Use retry logic for API calls that might fail let attempt = 0; let sentMessage: Message | undefined = undefined; let lastError: Error | undefined = undefined; while (attempt < MAX_RETRY_ATTEMPTS && !sentMessage) { try { sentMessage = await channel.send(message); break; } catch (error) { lastError = error as Error; attempt++; if (attempt < MAX_RETRY_ATTEMPTS) { // Wait before retrying with exponential backoff await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))); } } } if (!sentMessage) { throw lastError || new Error("Failed to send message after multiple attempts"); } return { content: [ { type: "text", text: `Message sent to #${channel.name} in server ${channel.guild.name}`, }, ], }; } catch (error) { console.error("Error sending message:", error); return { content: [ { type: "text", text: `Error sending message: ${(error as Error).message}`, }, ], isError: true, }; } }
- src/index.ts:218-236 (schema)The tool schema definition for 'send-message', including name, description, and input schema with channel and message properties.{ name: "send-message", description: "Send a message to a Discord channel", inputSchema: { type: "object", properties: { channel: { type: "string", description: "Channel name or ID (use # for channel names, e.g. '#general')", }, message: { type: "string", description: "The message content to send", }, }, required: ["channel", "message"], }, },
- src/index.ts:340-341 (registration)Registration of the 'send-message' tool handler in the CallToolRequestSchema switch statement.case "send-message": return await this.handleSendMessage(request);
- src/index.ts:79-85 (schema)Input validation function for send-message arguments.const isValidSendMessageArgs = (args: unknown): args is SendMessageArgs => typeof args === "object" && args !== null && typeof (args as SendMessageArgs).channel === "string" && (args as SendMessageArgs).channel.trim().length > 0 && typeof (args as SendMessageArgs).message === "string" && (args as SendMessageArgs).message.trim().length > 0;
- src/index.ts:918-971 (helper)Helper function to find and cache Discord TextChannel by name or ID, used by send-message and other tools.private async findChannel( channelInput: string ): Promise<TextChannel | null> { // Check cache first const cacheKey = channelInput.toLowerCase(); const cachedEntry = this.channelCache.get(cacheKey); if (cachedEntry && Date.now() - cachedEntry.timestamp < CHANNEL_CACHE_TTL) { return cachedEntry.channel; } // Remove # prefix if present const cleanChannelInput = channelInput.startsWith("#") ? channelInput.substring(1) : channelInput; // Try to find channel by ID first if (/^\d+$/.test(cleanChannelInput)) { try { const channel = await this.discordClient.channels.fetch( cleanChannelInput ); if (channel && channel.type === ChannelType.GuildText) { // Cache the result this.channelCache.set(cacheKey, { channel: channel as TextChannel, timestamp: Date.now() }); return channel as TextChannel; } } catch (error) { // Not a valid ID or channel not found } } // Search by name across all servers for (const guild of this.discordClient.guilds.cache.values()) { const channel = guild.channels.cache.find( (c) => c.type === ChannelType.GuildText && c.name.toLowerCase() === cleanChannelInput.toLowerCase() ); if (channel) { // Cache the result this.channelCache.set(cacheKey, { channel: channel as TextChannel, timestamp: Date.now() }); return channel as TextChannel; } } return null; }