UNPKG

discord-self-mcp

Version:

MCP server for Discord using selfbot to read channels

117 lines 4.74 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { Client } from 'discord.js-selfbot-v13'; import { toolDefinitions } from './tool-definitions.js'; import { readChannel, searchMessages, sendMessage } from './message-handlers.js'; import { listChannels, listGuilds } from './guild-handlers.js'; import { getUserInfo, listGuildMembers } from './user-handlers.js'; const DISCORD_TOKEN = process.env.DISCORD_TOKEN; if (!DISCORD_TOKEN) { console.error('Error: DISCORD_TOKEN not found in environment variables'); console.error('Please configure DISCORD_TOKEN in your MCP client settings'); console.error('Example configuration:'); console.error(JSON.stringify({ mcpServers: { discord: { command: 'npx', args: ['-y', 'discord-self-mcp'], env: { DISCORD_TOKEN: 'your_discord_token_here', }, }, }, }, null, 2)); process.exit(1); } function validateAndCastArgs(args, expectedKeys) { if (!args || typeof args !== 'object') { throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments provided'); } const hasRequiredKeys = expectedKeys.some((key) => key in args); if (!hasRequiredKeys && expectedKeys.length > 0) { throw new McpError(ErrorCode.InvalidParams, `Missing required parameters: ${expectedKeys.join(', ')}`); } return args; } class DiscordMCPServer { server; client; isReady = false; readyPromise; constructor() { this.server = new Server({ name: 'discord-self-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.client = new Client(); this.readyPromise = this.connectDiscord(); this.setupHandlers(); } async connectDiscord() { return new Promise((resolve, reject) => { this.client.once('ready', () => { console.error(`Discord client ready as ${this.client.user?.tag}`); this.isReady = true; resolve(); }); this.client.once('error', (error) => { console.error('Discord client error:', error); reject(error); }); this.client.login(DISCORD_TOKEN).catch(reject); }); } setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: toolDefinitions, })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (!this.isReady) { try { await this.readyPromise; } catch (error) { throw new McpError(ErrorCode.InternalError, `Discord connection failed: ${error}`); } } switch (request.params.name) { case 'read_channel': return await readChannel(this.client, validateAndCastArgs(request.params.arguments, [ 'channelId', ])); case 'search_messages': return await searchMessages(this.client, validateAndCastArgs(request.params.arguments, [ 'channelId', ])); case 'send_message': return await sendMessage(this.client, validateAndCastArgs(request.params.arguments, [ 'channelId', 'content', ])); case 'list_channels': return await listChannels(this.client, validateAndCastArgs(request.params.arguments, [])); case 'list_guilds': return await listGuilds(this.client); case 'get_user_info': return await getUserInfo(this.client); case 'list_guild_members': return await listGuildMembers(this.client, validateAndCastArgs(request.params.arguments, ['guildId'])); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); } } const server = new DiscordMCPServer(); server.run().catch(console.error); //# sourceMappingURL=index.js.map