#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// Import utilities
import { logger } from './utils/logger.js';
import { BaseError, ConfigurationError, serializeError } from './utils/errors.js';
import { ValidationError } from './utils/error-handler.js';
import { validateServerSetup, getSetupInstructions } from './utils/config-validator.js';
// Import tools
import { predictROI, PredictROISchema } from './tools/predict-roi.js';
import { compareProjects, CompareProjectsSchema } from './tools/compare-projects.js';
// Import response transformer for AI agent optimization
import { responseTransformer } from './services/response-transformer.js';
// Create MCP server instance with capabilities
const server = new McpServer(
{
name: '@spaik/mcp-server-roi',
version: '1.3.0',
},
{
capabilities: {
tools: {},
// Resources can be added later if needed
// resources: {}
}
}
);
// Register the predict_roi tool
server.tool(
'predict_roi',
'Generate comprehensive ROI predictions for AI implementation projects with mandatory Dutch market validation',
{},
async (args: any) => {
try {
logger.info('Executing predict_roi tool', { args });
// Parse and validate the input args with the schema
const parsedInput = PredictROISchema.parse(args);
const result = await predictROI(parsedInput);
// Transform response for AI agents
const transformedResult = await responseTransformer.transformPredictROI(result);
return {
content: [{
type: 'text',
text: JSON.stringify(transformedResult, null, 2)
}]
};
} catch (error) {
logger.error('Tool execution failed', error as Error);
// Handle different error types appropriately
if (error instanceof ValidationError) {
return {
content: [{
type: 'text',
text: `Validation Error: ${error.message}\n\nFields with issues:\n${error.fields.map(f => `- ${f.field}: ${f.message}`).join('\n')}`
}],
isError: true
};
} else if (error instanceof ConfigurationError) {
return {
content: [{
type: 'text',
text: `Configuration Error: ${error.message}\n\n${getSetupInstructions()}`
}],
isError: true
};
} else {
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
}],
isError: true
};
}
}
}
);
// Register the compare_projects tool
server.tool(
'compare_projects',
'Compare multiple AI projects side-by-side for ROI and other metrics with Dutch market validation',
{},
async (args: any) => {
try {
logger.info('Executing compare_projects tool', { args });
// Parse and validate the input args with the schema
const parsedInput = CompareProjectsSchema.parse(args);
const result = await compareProjects(parsedInput);
// Transform response for AI agents
const transformedResult = await responseTransformer.transformCompareProjects(result);
return {
content: [{
type: 'text',
text: JSON.stringify(transformedResult, null, 2)
}]
};
} catch (error) {
logger.error('Tool execution failed', error as Error);
// Handle different error types appropriately
if (error instanceof ValidationError) {
return {
content: [{
type: 'text',
text: `Validation Error: ${error.message}\n\nFields with issues:\n${error.fields.map(f => `- ${f.field}: ${f.message}`).join('\n')}`
}],
isError: true
};
} else if (error instanceof ConfigurationError) {
return {
content: [{
type: 'text',
text: `Configuration Error: ${error.message}\n\n${getSetupInstructions()}`
}],
isError: true
};
} else {
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
}],
isError: true
};
}
}
}
);
// Main server initialization
async function main() {
logger.info('Starting MCP Server ROI', {
version: '1.3.0',
environment: process.env.NODE_ENV || 'production'
});
// Validate server setup
try {
await validateServerSetup();
logger.info('Server setup validation completed successfully');
} catch (error) {
logger.error('Server setup validation failed', error as Error);
console.error('\n❌ Server Setup Error\n');
console.error((error as Error).message);
console.error('\n' + getSetupInstructions());
process.exit(1);
}
// Create transport
const transport = new StdioServerTransport();
// Handle transport errors
transport.onerror = (error) => {
logger.error('Transport error', error);
process.exit(1);
};
// Handle transport close
transport.onclose = () => {
logger.info('Transport closed, shutting down server');
process.exit(0);
};
try {
await server.connect(transport);
logger.info('MCP server started successfully', {
tools: ['predict_roi', 'compare_projects'],
perplexityEnabled: !!process.env.PERPLEXITY_API_KEY,
supabaseConfigured: !!process.env.SUPABASE_URL && !!process.env.SUPABASE_SERVICE_KEY
});
} catch (error) {
logger.error('Failed to start server', error as Error);
console.error('Failed to start server:', error);
process.exit(1);
}
}
// Handle process termination
process.on('SIGINT', () => {
logger.info('Received SIGINT, shutting down gracefully');
process.exit(0);
});
process.on('SIGTERM', () => {
logger.info('Received SIGTERM, shutting down gracefully');
process.exit(0);
});
// Handle uncaught errors
process.on('uncaughtException', (error) => {
logger.error('Uncaught exception', error);
console.error('Uncaught exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled rejection', reason as Error, { promise });
console.error('Unhandled rejection:', reason);
process.exit(1);
});
// Start the server
main().catch((error) => {
logger.error('Fatal error during startup', error);
console.error('Fatal error during startup:', error);
process.exit(1);
});