@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
JavaScript
#!/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