diff --git a/README.md b/README.md index 5a91b9b..b2f8136 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A Discord MCP (Model Context Protocol) server that enables AI assistants to inte MCP-Discord provides the following Discord-related functionalities: - Login to Discord bot +- List servers the bot is a member of - Get server information - Read/delete channel messages - Send messages to specified channels (using either channel IDs or channel names) @@ -267,6 +268,8 @@ You can use Docker containers with both Claude and Cursor: ### Basic Functions +- `discord_login`: Login to Discord using the configured token +- `discord_list_servers`: List all Discord servers the bot is a member of - `discord_send`: Send a message to a specified channel (supports both channel ID and channel name) - `discord_get_server_info`: Get Discord server information diff --git a/package-lock.json b/package-lock.json index ff5a774..3dc3105 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "mcp-discord", - "version": "1.2.0", + "version": "1.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mcp-discord", - "version": "1.2.0", + "version": "1.3.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/sdk": "^1.11.0", "discord.js": "^14.19.3", "dotenv": "^16.5.0", "express": "^5.1.0", @@ -18,11 +19,13 @@ "mcp-discord": "build/index.js" }, "devDependencies": { - "@modelcontextprotocol/sdk": "^1.11.0", "@types/express": "^5.0.1", "@types/node": "^20.17.50", "ts-node": "^10.9.2", "typescript": "^5.8.3" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@cspotcode/source-map-support": { @@ -197,7 +200,6 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz", "integrity": "sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==", - "dev": true, "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -548,7 +550,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "license": "MIT", "dependencies": { "object-assign": "^4", @@ -569,7 +570,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -742,7 +742,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz", "integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==", - "dev": true, "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.0" @@ -755,7 +754,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", - "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" @@ -807,7 +805,6 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 16" @@ -995,7 +992,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/lodash": { @@ -1093,7 +1089,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1145,7 +1140,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1164,7 +1158,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16.20.0" @@ -1311,7 +1304,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -1324,7 +1316,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1548,7 +1539,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -1610,7 +1600,6 @@ "version": "3.24.5", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.24.1" diff --git a/src/schemas.ts b/src/schemas.ts index a492989..68a58ef 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -129,4 +129,6 @@ export const DeleteWebhookSchema = z.object({ webhookId: z.string(), webhookToken: z.string().optional(), reason: z.string().optional() -}); \ No newline at end of file +}); + +export const ListServersSchema = z.object({}); \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 9983e7a..4b328fe 100644 --- a/src/server.ts +++ b/src/server.ts @@ -29,7 +29,8 @@ import { deleteWebhookHandler, createCategoryHandler, editCategoryHandler, - deleteCategoryHandler + deleteCategoryHandler, + listServersHandler } from './tools/tools.js'; import { MCPTransport } from './transport.js'; import { info, error } from './logger.js'; @@ -178,6 +179,11 @@ export class DiscordMCPServer { toolResponse = await deleteWebhookHandler(args, this.toolContext); return toolResponse; + case "discord_list_servers": + this.logClientState("before discord_list_servers handler"); + toolResponse = await listServersHandler(args, this.toolContext); + return toolResponse; + default: throw new Error(`Unknown tool: ${name}`); } diff --git a/src/toolList.ts b/src/toolList.ts index bf4b939..5d70a28 100644 --- a/src/toolList.ts +++ b/src/toolList.ts @@ -292,5 +292,14 @@ export const toolList = [ }, required: ["webhookId"] } + }, + { + name: "discord_list_servers", + description: "Lists all Discord servers the bot is a member of", + inputSchema: { + type: "object", + properties: {}, + required: [] + } } ]; \ No newline at end of file diff --git a/src/tools/channel.ts b/src/tools/channel.ts index 1a406c5..f2f1e08 100644 --- a/src/tools/channel.ts +++ b/src/tools/channel.ts @@ -8,7 +8,8 @@ import { GetServerInfoSchema, CreateCategorySchema, EditCategorySchema, - DeleteCategorySchema + DeleteCategorySchema, + ListServersSchema } from "../schemas.js"; import { handleDiscordError } from "../errorHandler.js"; @@ -355,4 +356,40 @@ export async function getServerInfoHandler( } catch (error) { return handleDiscordError(error); } +} + +// List servers handler +export async function listServersHandler( + args: unknown, + context: ToolContext +): Promise { + ListServersSchema.parse(args); + try { + if (!context.client.isReady()) { + return { + content: [{ type: "text", text: "Discord client not logged in." }], + isError: true + }; + } + + const guilds = await context.client.guilds.fetch(); + + if (guilds.size === 0) { + return { + content: [{ type: "text", text: "No servers found. The bot is not a member of any servers." }] + }; + } + + const guildsInfo = guilds.map(guild => ({ + id: guild.id, + name: guild.name, + icon: guild.iconURL() + })); + + return { + content: [{ type: "text", text: JSON.stringify(guildsInfo, null, 2) }] + }; + } catch (error) { + return handleDiscordError(error); + } } \ No newline at end of file diff --git a/src/tools/tools.ts b/src/tools/tools.ts index 4540b62..1134a2d 100644 --- a/src/tools/tools.ts +++ b/src/tools/tools.ts @@ -17,7 +17,8 @@ import { getServerInfoHandler, createCategoryHandler, editCategoryHandler, - deleteCategoryHandler + deleteCategoryHandler, + listServersHandler } from './channel.js'; import { addReactionHandler, @@ -55,7 +56,8 @@ export { deleteWebhookHandler, createCategoryHandler, editCategoryHandler, - deleteCategoryHandler + deleteCategoryHandler, + listServersHandler }; // Export common types diff --git a/src/transport.ts b/src/transport.ts index aebf81d..0dde9f9 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -26,7 +26,8 @@ import { deleteWebhookHandler, editCategoryHandler, createCategoryHandler, - deleteCategoryHandler + deleteCategoryHandler, + listServersHandler } from './tools/tools.js'; import { Client, GatewayIntentBits } from "discord.js"; import { info, error } from './logger.js';