UNPKG

@glec/mcp-server

Version:

GLEC API MCP Server for Claude Desktop - Carbon emission calculation for logistics and transportation

663 lines (655 loc) 26.9 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import dotenv from 'dotenv'; import { GLECApiClient } from './services/glecApiClient.js'; import { logError, logAuditEvent, generateCorrelationId } from './utils/logger.js'; import { GLECApiError, ValidationError, AuthenticationError, RateLimitError } from './types/index.js'; // Load environment variables dotenv.config(); // Validate required environment variables const requiredEnvVars = ['GLEC_API_KEY']; for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { console.error(`❌ Missing required environment variable: ${envVar}`); console.error('Please set your GLEC API key in the environment variables.'); process.exit(1); } } // GLEC API Configuration const glecConfig = { apiKey: process.env.GLEC_API_KEY, bearerToken: process.env.GLEC_BEARER_TOKEN, baseUrl: process.env.GLEC_BASE_URL || 'https://open-api.glec.io', timeout: parseInt(process.env.GLEC_TIMEOUT || '30000'), retries: parseInt(process.env.GLEC_RETRIES || '3') }; // Initialize GLEC API client const glecClient = new GLECApiClient(glecConfig); // Create MCP server const server = new Server({ name: 'glec-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, }); // List tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'glec_calculate_road_emission', description: 'Calculate CO2 emissions for road transport using GLEC API', inputSchema: { type: 'object', properties: { transportMode: { type: 'string', description: 'Transport mode code (M001, M002, M003)', enum: ['M001', 'M002', 'M003'] }, startLocation: { type: 'string', description: 'Starting location (optional)' }, endLocation: { type: 'string', description: 'Ending location (optional)' }, distance: { type: 'number', description: 'Distance in kilometers', minimum: 0.1, maximum: 50000 }, weight: { type: 'number', description: 'Weight in tons', minimum: 0.1, maximum: 1000 }, transportDate: { type: 'string', description: 'Transport date (YYYY-MM-DD)', format: 'date' }, distanceUnit: { type: 'string', description: 'Distance unit', enum: ['km', 'miles'], default: 'km' }, weightUnit: { type: 'string', description: 'Weight unit', enum: ['t', 'kg', 'lbs'], default: 't' }, vehicleType: { type: 'string', description: 'Vehicle type (optional)' }, fuelType: { type: 'string', description: 'Fuel type (optional)' }, loadFactor: { type: 'number', description: 'Load factor (0-1)', minimum: 0, maximum: 1 } }, required: ['transportMode', 'distance', 'weight'] }, }, { name: 'glec_calculate_rail_emission', description: 'Calculate CO2 emissions for rail transport using GLEC API', inputSchema: { type: 'object', properties: { transportMode: { type: 'string', description: 'Rail transport mode code (R001, R002, R003)', enum: ['R001', 'R002', 'R003'] }, startLocation: { type: 'string', description: 'Starting location (optional)' }, endLocation: { type: 'string', description: 'Ending location (optional)' }, distance: { type: 'number', description: 'Distance in kilometers', minimum: 0.1, maximum: 50000 }, weight: { type: 'number', description: 'Weight in tons', minimum: 0.1, maximum: 1000 }, transportDate: { type: 'string', description: 'Transport date (YYYY-MM-DD)', format: 'date' }, distanceUnit: { type: 'string', description: 'Distance unit', enum: ['km', 'miles'], default: 'km' }, weightUnit: { type: 'string', description: 'Weight unit', enum: ['t', 'kg', 'lbs'], default: 't' }, trainType: { type: 'string', description: 'Train type (optional)' }, electrification: { type: 'string', description: 'Electrification type (optional)' } }, required: ['transportMode', 'distance', 'weight'] }, }, { name: 'glec_calculate_air_emission', description: 'Calculate CO2 emissions for air transport using GLEC API', inputSchema: { type: 'object', properties: { transportMode: { type: 'string', description: 'Air transport mode code (A001, A002, A003)', enum: ['A001', 'A002', 'A003'] }, startLocation: { type: 'string', description: 'Starting location (optional)' }, endLocation: { type: 'string', description: 'Ending location (optional)' }, distance: { type: 'number', description: 'Distance in kilometers', minimum: 0.1, maximum: 50000 }, weight: { type: 'number', description: 'Weight in tons', minimum: 0.1, maximum: 1000 }, transportDate: { type: 'string', description: 'Transport date (YYYY-MM-DD)', format: 'date' }, distanceUnit: { type: 'string', description: 'Distance unit', enum: ['km', 'miles'], default: 'km' }, weightUnit: { type: 'string', description: 'Weight unit', enum: ['t', 'kg', 'lbs'], default: 't' }, aircraftType: { type: 'string', description: 'Aircraft type (optional)' }, flightType: { type: 'string', description: 'Flight type', enum: ['domestic', 'international'] } }, required: ['transportMode', 'distance', 'weight'] }, }, { name: 'glec_calculate_sea_emission', description: 'Calculate CO2 emissions for sea transport using GLEC API', inputSchema: { type: 'object', properties: { transportMode: { type: 'string', description: 'Sea transport mode code (S001, S002, S003)', enum: ['S001', 'S002', 'S003'] }, startLocation: { type: 'string', description: 'Starting location (optional)' }, endLocation: { type: 'string', description: 'Ending location (optional)' }, distance: { type: 'number', description: 'Distance in kilometers', minimum: 0.1, maximum: 50000 }, weight: { type: 'number', description: 'Weight in tons', minimum: 0.1, maximum: 1000 }, transportDate: { type: 'string', description: 'Transport date (YYYY-MM-DD)', format: 'date' }, distanceUnit: { type: 'string', description: 'Distance unit', enum: ['km', 'miles'], default: 'km' }, weightUnit: { type: 'string', description: 'Weight unit', enum: ['t', 'kg', 'lbs'], default: 't' }, shipType: { type: 'string', description: 'Ship type (optional)' }, containerType: { type: 'string', description: 'Container type (optional)' } }, required: ['transportMode', 'distance', 'weight'] }, }, { name: 'glec_list_shippers', description: 'List all shippers using GLEC API', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number', minimum: 1, default: 1 }, size: { type: 'number', description: 'Page size', minimum: 1, maximum: 100, default: 10 }, search: { type: 'string', description: 'Search term (optional)' } } }, }, { name: 'glec_create_shipper', description: 'Create a new shipper using GLEC API', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Shipper name', minLength: 1, maxLength: 100 }, email: { type: 'string', description: 'Shipper email', format: 'email' }, phone: { type: 'string', description: 'Shipper phone number' }, address: { type: 'string', description: 'Shipper address' }, country: { type: 'string', description: 'Country code' }, industry: { type: 'string', description: 'Industry type' }, companySize: { type: 'string', description: 'Company size' } }, required: ['name'] }, }, { name: 'glec_get_emission_codes', description: 'Get emission factor codes for specific transport mode and category', inputSchema: { type: 'object', properties: { transportMode: { type: 'string', description: 'Transport mode', enum: ['road', 'rail', 'air', 'sea'] }, category: { type: 'string', description: 'Vehicle size category', enum: ['small', 'medium', 'large'] } }, required: ['transportMode', 'category'] }, }, { name: 'glec_generate_esg_report', description: 'Generate ESG report using GLEC API', inputSchema: { type: 'object', properties: { startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)', format: 'date' }, endDate: { type: 'string', description: 'End date (YYYY-MM-DD)', format: 'date' }, format: { type: 'string', description: 'Report format', enum: ['pdf', 'excel', 'json'], default: 'pdf' }, scope: { type: 'string', description: 'Emission scope', enum: ['1', '2', '3', '1,2', '1,3', '2,3', '1,2,3'], default: '1,2,3' }, language: { type: 'string', description: 'Report language', enum: ['ko', 'en', 'ja', 'zh'], default: 'ko' }, includeCharts: { type: 'boolean', description: 'Include charts', default: true }, includeDetails: { type: 'boolean', description: 'Include detailed data', default: true }, certificationStandard: { type: 'string', description: 'Certification standard', enum: ['ISO14083', 'GHG_Protocol', 'PAS2050'], default: 'ISO14083' } }, required: ['startDate', 'endDate'] }, }, { name: 'glec_health_check', description: 'Check GLEC API health status', inputSchema: { type: 'object', properties: {} }, } ], }; }); // List resources handler server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'glec://transport-modes', name: 'Transport Modes', description: 'Available transport modes for emission calculation', mimeType: 'application/json', }, { uri: 'glec://emission-factors', name: 'Emission Factors', description: 'Emission factors for different transport modes', mimeType: 'application/json', }, { uri: 'glec://api-documentation', name: 'API Documentation', description: 'GLEC API documentation and usage examples', mimeType: 'text/markdown', } ], }; }); // Read resource handler server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; switch (uri) { case 'glec://transport-modes': return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify({ road: { M001: 'Small truck (< 3.5t)', M002: 'Medium truck (3.5-7.5t)', M003: 'Large truck (> 7.5t)', }, rail: { R001: 'Freight train (short distance)', R002: 'Freight train (medium distance)', R003: 'Freight train (long distance)', }, air: { A001: 'Domestic cargo (small)', A002: 'Domestic cargo (medium)', A003: 'International cargo', }, sea: { S001: 'Container ship (small)', S002: 'Container ship (medium)', S003: 'Container ship (large)', }, }, null, 2), }, ], }; case 'glec://emission-factors': return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify({ description: 'Emission factors are provided by the GLEC API based on ISO 14083 standards', note: 'Use the glec_get_emission_codes tool to retrieve specific emission factors', standards: ['ISO 14083', 'GHG Protocol', 'PAS 2050'], lastUpdated: new Date().toISOString() }, null, 2), }, ], }; case 'glec://api-documentation': return { contents: [ { uri, mimeType: 'text/markdown', text: `# GLEC API MCP Server Documentation ## Overview This MCP server provides access to the GLEC API for calculating carbon emissions from logistics and transportation. ## Available Tools ### Emission Calculation - \`glec_calculate_road_emission\` - Calculate road transport emissions - \`glec_calculate_rail_emission\` - Calculate rail transport emissions - \`glec_calculate_air_emission\` - Calculate air transport emissions - \`glec_calculate_sea_emission\` - Calculate sea transport emissions ### Shipper Management - \`glec_list_shippers\` - List all shippers - \`glec_create_shipper\` - Create a new shipper ### Reports - \`glec_generate_esg_report\` - Generate ESG reports ### Utilities - \`glec_get_emission_codes\` - Get emission factor codes - \`glec_health_check\` - Check API health ## Getting Started 1. Get your API key from GLEC Console 2. Set the GLEC_API_KEY environment variable 3. Configure Claude Desktop to use this MCP server For more information, visit: https://glec.io `, }, ], }; default: throw new Error(`Unknown resource: ${uri}`); } }); // Call tool handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const correlationId = generateCorrelationId(); try { logAuditEvent('tool_call', name, 'mcp_user', { correlationId, args }); let result; switch (name) { case 'glec_calculate_road_emission': { result = await glecClient.calculateRoadEmission(args); break; } case 'glec_calculate_rail_emission': { result = await glecClient.calculateRailEmission(args); break; } case 'glec_calculate_air_emission': { result = await glecClient.calculateAirEmission(args); break; } case 'glec_calculate_sea_emission': { result = await glecClient.calculateSeaEmission(args); break; } case 'glec_list_shippers': { result = await glecClient.listShippers(args); break; } case 'glec_create_shipper': { result = await glecClient.createShipper(args); break; } case 'glec_get_emission_codes': { if (!args || typeof args !== 'object' || !('transportMode' in args) || !('category' in args)) { throw new Error('transportMode and category are required'); } result = await glecClient.getEmissionCodes(args.transportMode, args.category); break; } case 'glec_generate_esg_report': { result = await glecClient.generateESGReport(args); break; } case 'glec_health_check': { const isHealthy = await glecClient.healthCheck(); result = { success: true, data: { status: isHealthy ? 'healthy' : 'unhealthy', timestamp: new Date().toISOString(), version: '1.0.0' } }; break; } default: throw new Error(`Unknown tool: ${name}`); } return { content: [ { type: 'text', text: `✅ **${name}** completed successfully\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } catch (error) { logError(error, { context: 'tool_execution', tool: name, correlationId, args }); let errorMessage = 'An unknown error occurred'; let isError = true; if (error instanceof ValidationError) { errorMessage = `Validation Error: ${error.message}`; } else if (error instanceof AuthenticationError) { errorMessage = `Authentication Error: ${error.message}`; } else if (error instanceof RateLimitError) { errorMessage = `Rate Limit Error: ${error.message}`; } else if (error instanceof GLECApiError) { errorMessage = `GLEC API Error (${error.code}): ${error.message}`; } else if (error instanceof Error) { errorMessage = `Error: ${error.message}`; } return { content: [ { type: 'text', text: `❌ **${name}** failed\n\n${errorMessage}`, }, ], isError, }; } }); // Start server async function main() { try { const transport = new StdioServerTransport(); await server.connect(transport); // Log server startup console.error('🚀 GLEC MCP Server started successfully'); console.error(`📊 API Base URL: ${glecConfig.baseUrl}`); console.error(`🔑 API Key: ${glecConfig.apiKey.substring(0, 8)}...`); console.error('📝 Ready to process requests'); } catch (error) { logError(error, { context: 'server_startup' }); console.error('❌ Failed to start GLEC MCP Server'); process.exit(1); } } // Handle graceful shutdown process.on('SIGINT', async () => { console.error('🛑 Shutting down GLEC MCP Server...'); process.exit(0); }); process.on('SIGTERM', async () => { console.error('🛑 Shutting down GLEC MCP Server...'); process.exit(0); }); // Start the server main().catch((error) => { logError(error, { context: 'main_function' }); console.error('❌ Fatal error:', error.message); process.exit(1); }); //# sourceMappingURL=index.js.map