UNPKG

@asanstefanski/everhour-mcp-server

Version:

Complete Everhour API integration for Model Context Protocol (MCP) with 100% endpoint coverage

139 lines 4.42 kB
#!/usr/bin/env node /** * Everhour MCP Server * Complete Everhour API integration for Model Context Protocol * * Usage: npx @everhour/mcp-server * * Environment Variables: * - EVERHOUR_API_KEY (required): Your Everhour API key * - EVERHOUR_API_BASE_URL (optional): API base URL (default: https://api.everhour.com) */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { EverHourApiClient } from './api/everhour-client.js'; import { projectTools } from './tools/projects.js'; import { taskTools } from './tools/tasks.js'; import { taskExtensionTools } from './tools/task-extensions.js'; import { timeRecordTools } from './tools/time-records.js'; import { timerTools } from './tools/timers.js'; import { clientTools } from './tools/clients.js'; import { sectionTools } from './tools/sections.js'; import { userTools } from './tools/users.js'; import { readonlyMode } from './utils/readonly.js'; function getConfig() { const apiKey = process.env.EVERHOUR_API_KEY; if (!apiKey) { throw new Error('EVERHOUR_API_KEY environment variable is required'); } return { apiKey, baseUrl: process.env.EVERHOUR_API_BASE_URL || 'https://api.everhour.com', }; } // Create server instance const server = new Server({ name: 'everhour-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }); // Initialize Everhour API client let apiClient; try { const config = getConfig(); apiClient = new EverHourApiClient(config.apiKey, config.baseUrl); } catch (error) { console.error('Failed to initialize Everhour API client:', error); process.exit(1); } // Combine all tools const allTools = { ...projectTools, ...taskTools, ...taskExtensionTools, ...timeRecordTools, ...timerTools, ...clientTools, ...sectionTools, ...userTools, }; // Log readonly mode status readonlyMode.logStatus(allTools); // List tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: Object.entries(allTools).map(([name, tool]) => ({ name, description: tool.description, inputSchema: tool.inputSchema, })), }; }); // Call tool handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { const tool = allTools[name]; if (!tool) { throw new McpError(ErrorCode.MethodNotFound, `Tool "${name}" not found`); } // Check readonly mode restrictions if (!readonlyMode.isToolAllowed(tool)) { const errorMessage = readonlyMode.createBlockedToolError(name, tool); return { content: [ { type: 'text', text: errorMessage, }, ], isError: true, }; } const result = await tool.handler(apiClient, args || {}); return result; } catch (error) { if (error instanceof McpError) { throw error; } const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError(ErrorCode.InternalError, `Error executing tool "${name}": ${errorMessage}`); } }); // Error handling server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await server.close(); process.exit(0); }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); // Test connection on startup try { const isConnected = await apiClient.testConnection(); if (!isConnected) { console.error('Failed to connect to Everhour API. Please check your API key.'); } else { console.error('Successfully connected to Everhour API'); } } catch (error) { console.error('Error testing Everhour API connection:', error); } } main().catch((error) => { console.error('Fatal error:', error); process.exit(1); }); //# sourceMappingURL=index.js.map