@chinchillaenterprises/mcp-discord
Version:
Modular Discord MCP server with 45+ tools for comprehensive server management, featuring clean architecture and discord_ prefixed tools
141 lines • 10.8 kB
JavaScript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
// Import all modules
import { createServices } from "./services/index.js";
import { allToolDefinitions, allToolHandlers } from "./tools/index.js";
/**
* Discord MCP Server - Modular Architecture
*
* This is the main entry point for the Discord MCP server using a clean modular architecture.
* All Discord bot functionality is organized into:
* - Services: Core functionality (accounts, credentials, Discord client)
* - Tools: MCP tool implementations organized by category
*/
class DiscordMCPServer {
server;
services;
constructor() {
this.server = new Server({
name: "mcp-discord",
version: "2.0.0", // Bumped for modular architecture
}, {
capabilities: {
tools: {},
},
});
this.setupRequestHandlers();
}
/**
* Initialize all services and prepare the server
*/
async initialize() {
try {
console.error('[Discord MCP Server] Initializing services...');
// Initialize all services
this.services = createServices();
await this.services.accountState.initialize();
console.error('[Discord MCP Server] Services initialized successfully');
}
catch (error) {
console.error('[Discord MCP Server] Failed to initialize services:', error);
throw error;
}
}
/**
* Setup MCP request handlers for listing and calling tools
*/
setupRequestHandlers() {
// ListTools handler - return all available tool definitions
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error(`[Discord MCP Server] Listing ${allToolDefinitions.length} available tools`);
return {
tools: allToolDefinitions,
};
});
// CallTool handler - execute tool by name
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
console.error(`[Discord MCP Server] Executing tool: ${name}`);
try {
// Look up handler by tool name
const handler = allToolHandlers[name];
if (!handler) {
console.error(`[Discord MCP Server] Unknown tool requested: ${name}`);
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
// Call handler with services
const result = await handler(args, this.services);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
// Handle different error types appropriately
if (error instanceof McpError) {
throw error;
}
if (error instanceof z.ZodError) {
console.error(`[Discord MCP Server] Invalid parameters for ${name}:`, error.message);
throw new McpError(ErrorCode.InvalidParams, `Invalid parameters: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
}
// Log unexpected errors for debugging
console.error(`[Discord MCP Server] Error in ${name}:`, error);
// Return user-friendly error message
const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
throw new McpError(ErrorCode.InternalError, errorMessage);
}
});
}
/**
* Start the MCP server with stdio transport
*/
async start() {
try {
await this.initialize();
console.error('[Discord MCP Server] Starting server with stdio transport...');
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('[Discord MCP Server] Server started successfully');
}
catch (error) {
console.error('[Discord MCP Server] Failed to start server:', error);
throw error;
}
}
}
/**
* Main entry point
*/
async function main() {
try {
const server = new DiscordMCPServer();
await server.start();
}
catch (error) {
console.error('[Discord MCP Server] Fatal error:', error);
process.exit(1);
}
}
// Handle uncaught errors gracefully
process.on('uncaughtException', (error) => {
console.error('[Discord MCP Server] Uncaught exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('[Discord MCP Server] Unhandled rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// Start the server
main().catch((error) => {
console.error('[Discord MCP Server] Unhandled error:', error);
process.exit(1);
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgtbW9kdWxhci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC1tb2R1bGFyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFFQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDbkUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDakYsT0FBTyxFQUFFLHNCQUFzQixFQUFFLHFCQUFxQixFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN4SCxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLHFCQUFxQjtBQUNyQixPQUFPLEVBQW9CLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUV2RTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxnQkFBZ0I7SUFDWixNQUFNLENBQVM7SUFDZixRQUFRLENBQW9CO0lBRXBDO1FBQ0UsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FDdEI7WUFDRSxJQUFJLEVBQUUsYUFBYTtZQUNuQixPQUFPLEVBQUUsT0FBTyxFQUFFLGtDQUFrQztTQUNyRCxFQUNEO1lBQ0UsWUFBWSxFQUFFO2dCQUNaLEtBQUssRUFBRSxFQUFFO2FBQ1Y7U0FDRixDQUNGLENBQUM7UUFFRixJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsVUFBVTtRQUNkLElBQUksQ0FBQztZQUNILE9BQU8sQ0FBQyxLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQztZQUUvRCwwQkFBMEI7WUFDMUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxjQUFjLEVBQUUsQ0FBQztZQUNqQyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFB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