@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
JavaScript
/**
* 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);
});