UNPKG

@iriseller/mcp-server

Version:

Model Context Protocol (MCP) server providing access to IRISeller's AI sales intelligence platform with 7 AI agents, multi-CRM integration, advanced sales workflows, email automation, Rosa demo functionality with action scoring, DNC compliance checking, G

180 lines (179 loc) 6.37 kB
#!/usr/bin/env node /** * Dedicated HTTP Server for IRISeller MCP Server * This file always starts the HTTP wrapper server */ import express from 'express'; import cors from 'cors'; import jwt from 'jsonwebtoken'; import { IRISellerAPIService } from './services/iriseller-api.js'; import { ToolHandlers } from './handlers/tool-handlers.js'; import dotenv from 'dotenv'; // Load environment variables dotenv.config(); const PORT = parseInt(process.env.PORT || '3002', 10); // Configuration const config = { name: process.env.MCP_SERVER_NAME || 'iriseller-mcp-server', version: process.env.MCP_SERVER_VERSION || '1.0.0', iriseller_api_url: process.env.IRISELLER_API_URL || 'http://localhost:3001', crewai_api_url: process.env.CREWAI_API_URL || 'http://localhost:8001', crm_connect_api_url: process.env.CRM_CONNECT_API_URL || 'http://localhost:3001/api/crm-connect', debug: process.env.DEBUG === 'true', jwt_secret: process.env.JWT_SECRET }; // Initialize services const apiService = new IRISellerAPIService(config); const toolHandlers = new ToolHandlers(apiService); // Global user token for authentication let globalUserToken; // Generate user token if credentials provided async function generateUserToken(email, password, jwtSecret) { if (!email || !password || !jwtSecret) { return undefined; } try { // Generate a proper user token that will be recognized by the backend // DO NOT use isSystemToken: true as it bypasses owner-based filtering const token = jwt.sign({ email, userId: email === 'jchen@iriseller.com' ? 'user_jchen_001' : `mcp-user-${email}`, // Use real user ID for jchen id: email === 'jchen@iriseller.com' ? 'user_jchen_001' : `mcp-user-${email}`, role: 'user', // isSystemToken: true, // REMOVED - This bypasses owner filtering! sub: email, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + (4 * 60 * 60) // 4 hours }, jwtSecret); return token; } catch (error) { console.error('[MCP-HTTP] Failed to generate user token:', error); return undefined; } } // Initialize authentication async function initializeAuthentication() { // Generate user token from environment variables const email = process.env.MCP_DEFAULT_USER_EMAIL; const password = process.env.MCP_DEFAULT_USER_PASSWORD; if (!email || !password) { // Warning: No default credentials configured - debug output removed return; } const userToken = await generateUserToken(email, password, config.jwt_secret); if (userToken) { globalUserToken = userToken; toolHandlers.setUserToken(userToken); // User token generated successfully } else { // No user token generated - using system authentication } } // Create Express app const app = express(); // Middleware app.use(cors()); app.use(express.json({ limit: '10mb' })); // Health check endpoint app.get('/health', async (req, res) => { try { const health = await apiService.checkHealth(); const overallHealth = health.backend && health.crewai && health.crmConnect; res.status(overallHealth ? 200 : 503).json({ status: overallHealth ? 'healthy' : 'degraded', services: health, timestamp: new Date().toISOString(), version: config.version }); } catch (error) { res.status(500).json({ status: 'error', error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString() }); } }); // Tools endpoint app.post('/tools/:toolName', async (req, res) => { const { toolName } = req.params; // Extract arguments from request body - handle both direct args and nested structure const toolArgs = req.body.arguments || req.body; try { // Extract user token from Authorization header const authHeader = req.headers.authorization; let userToken = globalUserToken; if (authHeader && authHeader.startsWith('Bearer ')) { // If client provides a token, use that instead userToken = authHeader.split(' ')[1]; console.log(`[MCP-HTTP] Tool ${toolName} - Using client-provided token`); } else { console.log(`[MCP-HTTP] Tool ${toolName} - Using global user token:`, userToken ? 'Present' : 'Missing'); } // Set the user token for this request if (userToken) { toolHandlers.setUserToken(userToken); } const mcpRequest = { params: { name: toolName, arguments: toolArgs }, method: 'tools/call' }; const result = await toolHandlers.handleToolCall(mcpRequest); res.json({ success: true, tool: toolName, result, timestamp: new Date().toISOString() }); } catch (error) { // Tool execution error - debug output removed res.status(500).json({ success: false, tool: toolName, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString() }); } }); // Start server async function startServer() { try { // Server startup - debug output removed to prevent JSON-RPC contamination // Initialize authentication await initializeAuthentication(); // Test connectivity - removed debug output try { const health = await apiService.checkHealth(); // Health check completed - debug output removed } catch (error) { // Health check warning - debug output removed } // Start HTTP server app.listen(PORT, '0.0.0.0', () => { // Server started successfully - debug output removed }); } catch (error) { // Server start failed - exit without debug output process.exit(1); } } // Graceful shutdown process.on('SIGINT', () => { process.exit(0); }); process.on('SIGTERM', () => { process.exit(0); }); // Start the server startServer().catch((error) => { process.exit(1); });