From 91030ce5759cc1a3908eb6d9384c6e00fca9672e Mon Sep 17 00:00:00 2001 From: Barry Yip Date: Wed, 14 May 2025 00:01:59 +0800 Subject: [PATCH] feat: add unified Discord error handler with clear guidance for unauthorized server access --- src/errorHandler.ts | 80 +++++++++++++++++++++++++++++++++++++++ src/tools/channel.ts | 21 +++------- src/tools/forum.ts | 26 +++---------- src/tools/login.ts | 6 +-- src/tools/reactions.ts | 21 +++------- src/tools/send-message.ts | 6 +-- src/tools/webhooks.ts | 21 +++------- 7 files changed, 105 insertions(+), 76 deletions(-) create mode 100644 src/errorHandler.ts diff --git a/src/errorHandler.ts b/src/errorHandler.ts new file mode 100644 index 0000000..e3d33ba --- /dev/null +++ b/src/errorHandler.ts @@ -0,0 +1,80 @@ +// Discord API error handler for MCP tools +import { ToolResponse } from './tools/types.js'; + +/** + * A unified error handler for Discord API errors + * @param error - The error object from Discord API calls + * @param clientId - Optional Discord Client ID for custom invite links + * @returns A standard tool response with error message and potential solution + */ +export function handleDiscordError(error: any, clientId?: string): ToolResponse { + // Ensure error is in the expected format for checking + const errorMessage = typeof error === 'string' + ? error + : error?.message || String(error); + const errorCode = error?.code; + + // Generate invite link based on client ID if provided + const inviteLink = clientId + ? `https://discord.com/oauth2/authorize?client_id=${clientId}&scope=bot&permissions=8` + : "https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot&permissions=52076489808"; + + // Check for privileged intents errors + if (errorMessage.includes('Privileged intent provided is not enabled or whitelisted')) { + return { + content: [{ + type: "text", + text: `Error: Privileged intents are not enabled. + +Solution: Please enable the required intents (Message Content, Server Members, Presence) in the Discord Developer Portal for your bot application. + +For detailed instructions, check the Prerequisites section in our README.` + }], + isError: true + }; + } + + // Check for unauthorized/bot not in server errors + if ( + errorCode === 50001 || // Missing Access + errorCode === 10004 || // Unknown Guild + errorMessage.includes('Missing Access') || + errorMessage.includes('Unknown Guild') || + errorMessage.includes('Missing Permissions') + ) { + return { + content: [{ + type: "text", + text: `Error: The bot is not a member of the target Discord server or lacks required permissions. + +Solution: Please add the bot to the target server using this Discord invite link: +${inviteLink} + +According to Discord's security model, a bot can only access information from servers it has been explicitly added to.` + }], + isError: true + }; + } + + // Check for rate limiting + if (errorCode === 429 || errorMessage.includes('rate limit')) { + return { + content: [{ + type: "text", + text: `Error: Discord API rate limit reached. + +Solution: Please wait a moment before trying again. If this persists, consider spacing out your requests.` + }], + isError: true + }; + } + + // General error response for other cases + return { + content: [{ + type: "text", + text: `Discord API Error: ${errorMessage}` + }], + isError: true + }; +} \ No newline at end of file diff --git a/src/tools/channel.ts b/src/tools/channel.ts index 71c7c29..f14a6f8 100644 --- a/src/tools/channel.ts +++ b/src/tools/channel.ts @@ -7,6 +7,7 @@ import { ReadMessagesSchema, GetServerInfoSchema } from "../schemas.js"; +import { handleDiscordError } from "../errorHandler.js"; // Text channel creation handler export async function createTextChannelHandler( @@ -44,10 +45,7 @@ export async function createTextChannelHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to create text channel: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -91,10 +89,7 @@ export async function deleteChannelHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to delete channel: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -163,10 +158,7 @@ export async function readMessagesHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to read messages: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -233,9 +225,6 @@ export async function getServerInfoHandler( content: [{ type: "text", text: JSON.stringify(guildInfo, null, 2) }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to fetch server info: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } \ No newline at end of file diff --git a/src/tools/forum.ts b/src/tools/forum.ts index cb633f6..d1ae4e5 100644 --- a/src/tools/forum.ts +++ b/src/tools/forum.ts @@ -1,6 +1,7 @@ import { ChannelType, ForumChannel } from 'discord.js'; import { GetForumChannelsSchema, CreateForumPostSchema, GetForumPostSchema, ReplyToForumSchema, DeleteForumPostSchema } from '../schemas.js'; import { ToolHandler } from './types.js'; +import { handleDiscordError } from "../errorHandler.js"; export const getForumChannelsHandler: ToolHandler = async (args, { client }) => { const { guildId } = GetForumChannelsSchema.parse(args); @@ -44,10 +45,7 @@ export const getForumChannelsHandler: ToolHandler = async (args, { client }) => content: [{ type: "text", text: JSON.stringify(forumInfo, null, 2) }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to fetch forum channels: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; @@ -99,10 +97,7 @@ export const createForumPostHandler: ToolHandler = async (args, { client }) => { }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to create forum post: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; @@ -146,10 +141,7 @@ export const getForumPostHandler: ToolHandler = async (args, { client }) => { content: [{ type: "text", text: JSON.stringify(threadDetails, null, 2) }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to fetch forum post: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; @@ -189,10 +181,7 @@ export const replyToForumHandler: ToolHandler = async (args, { client }) => { }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to reply to forum post: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; @@ -225,9 +214,6 @@ export const deleteForumPostHandler: ToolHandler = async (args, { client }) => { }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to delete forum post: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; \ No newline at end of file diff --git a/src/tools/login.ts b/src/tools/login.ts index 73b3de3..02a9258 100644 --- a/src/tools/login.ts +++ b/src/tools/login.ts @@ -1,5 +1,6 @@ import { DiscordLoginSchema } from '../schemas.js'; import { ToolHandler } from './types.js'; +import { handleDiscordError } from "../errorHandler.js"; export const loginHandler: ToolHandler = async (args, { client }) => { DiscordLoginSchema.parse(args); @@ -25,9 +26,6 @@ export const loginHandler: ToolHandler = async (args, { client }) => { content: [{ type: "text", text: `Successfully logged in to Discord: ${client.user?.tag}` }] }; } catch (error) { - return { - content: [{ type: "text", text: `Login failed: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; \ No newline at end of file diff --git a/src/tools/reactions.ts b/src/tools/reactions.ts index 6e501ba..4b20a49 100644 --- a/src/tools/reactions.ts +++ b/src/tools/reactions.ts @@ -6,6 +6,7 @@ import { RemoveReactionSchema, DeleteMessageSchema } from "../schemas.js"; +import { handleDiscordError } from "../errorHandler.js"; // Add reaction handler export async function addReactionHandler( @@ -47,10 +48,7 @@ export async function addReactionHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to add reaction: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -98,10 +96,7 @@ export async function addMultipleReactionsHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to add reactions: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -168,10 +163,7 @@ export async function removeReactionHandler( }; } } catch (error) { - return { - content: [{ type: "text", text: `Failed to remove reaction: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -216,9 +208,6 @@ export async function deleteMessageHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to delete message: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } \ No newline at end of file diff --git a/src/tools/send-message.ts b/src/tools/send-message.ts index f1dfca9..ae12360 100644 --- a/src/tools/send-message.ts +++ b/src/tools/send-message.ts @@ -1,5 +1,6 @@ import { SendMessageSchema } from '../schemas.js'; import { ToolHandler } from './types.js'; +import { handleDiscordError } from "../errorHandler.js"; export const sendMessageHandler: ToolHandler = async (args, { client }) => { const { channelId, message } = SendMessageSchema.parse(args); @@ -33,9 +34,6 @@ export const sendMessageHandler: ToolHandler = async (args, { client }) => { }; } } catch (error) { - return { - content: [{ type: "text", text: `Send message failed: ${error}` }], - isError: true - }; + return handleDiscordError(error); } }; \ No newline at end of file diff --git a/src/tools/webhooks.ts b/src/tools/webhooks.ts index 2d1ff60..ea19222 100644 --- a/src/tools/webhooks.ts +++ b/src/tools/webhooks.ts @@ -6,6 +6,7 @@ import { EditWebhookSchema, DeleteWebhookSchema } from "../schemas.js"; +import { handleDiscordError } from "../errorHandler.js"; // Create webhook handler export async function createWebhookHandler( @@ -51,10 +52,7 @@ export async function createWebhookHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to create webhook: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -95,10 +93,7 @@ export async function sendWebhookMessageHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to send webhook message: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -139,10 +134,7 @@ export async function editWebhookHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to edit webhook: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } @@ -178,9 +170,6 @@ export async function deleteWebhookHandler( }] }; } catch (error) { - return { - content: [{ type: "text", text: `Failed to delete webhook: ${error}` }], - isError: true - }; + return handleDiscordError(error); } } \ No newline at end of file