'use strict'; const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport, } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { CallToolRequestSchema, ListToolsRequestSchema, } = require('@modelcontextprotocol/sdk/types.js'); const { findValidTokenByValue } = require('../users/apiTokenService'); const { User } = require('../../models'); const { registerAllTools } = require('./toolRegistry'); /** * Start the MCP server * Validates authentication token and initializes stdio transport */ async function startMcpServer() { try { // Validate environment const apiToken = process.env.TUDUDI_API_TOKEN; if (!apiToken) { throw new Error( 'TUDUDI_API_TOKEN environment variable is required. ' + 'Generate a token in Profile → API Keys and add it to your Claude Desktop config.' ); } // Validate token and get user context const tokenRecord = await findValidTokenByValue(apiToken); if (!tokenRecord) { throw new Error( 'Invalid or expired TUDUDI_API_TOKEN. ' + 'Please generate a new token in Profile → API Keys.' ); } const user = await User.findByPk(tokenRecord.user_id); if (!user) { throw new Error('User not found for the provided token.'); } // Create context for all tools const context = { userId: user.id, user: user, apiToken: tokenRecord, }; // Initialize MCP server const server = new Server( { name: process.env.MCP_SERVER_NAME || 'tududi', version: process.env.MCP_SERVER_VERSION || '1.0.0', }, { capabilities: { tools: {}, }, } ); // Store tools registry for request handlers const tools = []; // Register list tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: tools, }; }); // Register call tool handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const toolName = request.params.name; const tool = tools.find((t) => t.name === toolName); if (!tool) { throw new Error(`Unknown tool: ${toolName}`); } try { // Call the tool handler with params and context const result = await tool.handler( request.params.arguments || {}, context ); return result; } catch (error) { console.error(`Error executing tool ${toolName}:`, error); return { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; } }); // Register all tools with context registerAllTools(server, context, tools); // Start stdio transport const transport = new StdioServerTransport(); await server.connect(transport); console.error('Tududi MCP server running on stdio'); console.error(`Authenticated as: ${user.email} (ID: ${user.id})`); console.error(`Available tools: ${tools.length}`); // Update token last_used_at (fire and forget) tokenRecord .update({ last_used_at: new Date() }) .catch((err) => console.error('Failed to update token last_used_at:', err) ); } catch (error) { console.error('Fatal MCP server error:', error.message); process.exit(1); } } // Start server if running directly if (require.main === module) { // Load environment variables require('dotenv').config(); // Initialize database connection const { sequelize } = require('../../models'); sequelize .authenticate() .then(() => { console.error('Database connection established'); return startMcpServer(); }) .catch((error) => { console.error('Failed to connect to database:', error); process.exit(1); }); } module.exports = startMcpServer;