#!/usr/bin/env node
/**
* FitSlot MCP Server
*
* An MCP server for integrating with the FitSlot API, providing:
* - Ticket management for support
* - Chatbot assistance with FAQs
* - Bioimpedance PDF analysis
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { FitSlotAPIService } from './services/fitslot-api.service.js';
import { ChatbotService } from './services/chatbot.service.js';
import { PDFAnalysisService } from './services/pdf-analysis.service.js';
import { OpenAIService } from './services/openai.service.js';
import { createTicketTools } from './tools/ticket.tools.js';
import { createChatbotTools } from './tools/chatbot.tools.js';
import { createPDFTools } from './tools/pdf.tools.js';
import { logger } from './utils/logger.js';
import { FitSlotConfig, OpenAIConfig } from './types/index.js';
/**
* Configuration for FitSlot API
*/
const config: FitSlotConfig = {
apiUrl: process.env.FITSLOT_API_URL || 'https://api.fitslot.com',
apiKey: process.env.FITSLOT_API_KEY,
timeout: parseInt(process.env.FITSLOT_API_TIMEOUT || '30000')
};
const openAIConfig: OpenAIConfig = {
apiKey: process.env.OPENAI_API_KEY,
model: process.env.OPENAI_MODEL,
organization: process.env.OPENAI_ORG,
project: process.env.OPENAI_PROJECT,
temperature: process.env.OPENAI_TEMPERATURE ? Number(process.env.OPENAI_TEMPERATURE) : undefined,
maxTokens: process.env.OPENAI_MAX_TOKENS ? Number(process.env.OPENAI_MAX_TOKENS) : undefined
};
/**
* Initialize services
*/
const apiService = new FitSlotAPIService(config);
const openAIService = new OpenAIService(openAIConfig);
const chatbotService = new ChatbotService(openAIService);
const pdfService = new PDFAnalysisService(openAIService);
/**
* Create all tools
*/
const ticketTools = createTicketTools(apiService);
const chatbotTools = createChatbotTools(chatbotService);
const pdfTools = createPDFTools(pdfService);
const allTools = {
...ticketTools,
...chatbotTools,
...pdfTools
};
/**
* Create and configure MCP server
*/
const server = new Server(
{
name: 'fitslot-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
/**
* Handler for listing available tools
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
logger.info('Listing available tools');
return {
tools: Object.entries(allTools).map(([name, tool]) => {
const shape = tool.parameters.shape as Record<string, any>;
return {
name,
description: tool.description,
inputSchema: {
type: 'object' as const,
properties: shape,
required: Object.keys(shape).filter(
key => !(shape[key].isOptional && shape[key].isOptional())
)
}
};
})
};
});
/**
* Handler for tool execution
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
logger.info('Executing tool', { toolName, params: request.params.arguments });
const tool = allTools[toolName as keyof typeof allTools];
if (!tool) {
logger.error('Tool not found', { toolName });
throw new Error(`Unknown tool: ${toolName}`);
}
try {
// Validate and parse arguments
const parsedArgs = tool.parameters.parse(request.params.arguments || {}) as any;
// Execute the tool
const result = await tool.execute(parsedArgs);
logger.info('Tool executed successfully', { toolName });
return result;
} catch (error) {
logger.error('Tool execution failed', { toolName, error });
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
details: error instanceof Error ? error.stack : undefined
},
null,
2
)
}
],
isError: true
};
}
});
/**
* Start the server
*/
async function main() {
logger.info('Starting FitSlot MCP Server', {
apiUrl: config.apiUrl,
hasApiKey: !!config.apiKey
});
const transport = new StdioServerTransport();
await server.connect(transport);
logger.info('FitSlot MCP Server started successfully');
logger.info('Available tools:', {
tools: Object.keys(allTools),
count: Object.keys(allTools).length
});
}
// Handle graceful shutdown
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);
});
// Start the server
main().catch((error) => {
logger.error('Failed to start server', error);
process.exit(1);
});