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 fetch from 'node-fetch';
// Import tool handlers
import { getCurrentWeather } from './tools/current-weather.js';
import { getWeatherForecast } from './tools/weather-forecast.js';
import { getHistoricalWeather } from './tools/historical-weather.js';
import { searchLocations } from './tools/geocoding.js';
import { getAirQuality } from './tools/air-quality.js';
import { getMarineWeather } from './tools/marine-weather.js';
import { getClimateData } from './tools/climate-data.js';
const server = new Server(
{
name: 'mcp-open-meteo',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_current_weather',
description: 'Get current weather conditions for a specific location',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature units (default: celsius)',
default: 'celsius',
},
},
required: ['latitude', 'longitude'],
},
},
{
name: 'get_weather_forecast',
description: 'Get weather forecast (hourly and daily) for a location',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
days: {
type: 'number',
description: 'Number of forecast days (1-16, default: 7)',
minimum: 1,
maximum: 16,
default: 7,
},
hourly: {
type: 'boolean',
description: 'Include hourly forecast (default: true)',
default: true,
},
daily: {
type: 'boolean',
description: 'Include daily forecast (default: true)',
default: true,
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature units (default: celsius)',
default: 'celsius',
},
},
required: ['latitude', 'longitude'],
},
},
{
name: 'get_historical_weather',
description: 'Get historical weather data for a specific location and date range',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
start_date: {
type: 'string',
description: 'Start date in YYYY-MM-DD format',
pattern: '^\\d{4}-\\d{2}-\\d{2}$',
},
end_date: {
type: 'string',
description: 'End date in YYYY-MM-DD format',
pattern: '^\\d{4}-\\d{2}-\\d{2}$',
},
daily: {
type: 'boolean',
description: 'Include daily data (default: true)',
default: true,
},
hourly: {
type: 'boolean',
description: 'Include hourly data (default: false)',
default: false,
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature units (default: celsius)',
default: 'celsius',
},
},
required: ['latitude', 'longitude', 'start_date', 'end_date'],
},
},
{
name: 'search_locations',
description: 'Search for locations by name and get their coordinates',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Location name to search for',
},
count: {
type: 'number',
description: 'Maximum number of results (1-100, default: 10)',
minimum: 1,
maximum: 100,
default: 10,
},
language: {
type: 'string',
description: 'Language for the results (default: en)',
default: 'en',
},
},
required: ['name'],
},
},
{
name: 'get_air_quality',
description: 'Get air quality data and forecasts for a location',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
days: {
type: 'number',
description: 'Number of forecast days (1-5, default: 3)',
minimum: 1,
maximum: 5,
default: 3,
},
current: {
type: 'boolean',
description: 'Include current air quality (default: true)',
default: true,
},
},
required: ['latitude', 'longitude'],
},
},
{
name: 'get_marine_weather',
description: 'Get marine weather forecasts including waves and ocean conditions',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
days: {
type: 'number',
description: 'Number of forecast days (1-7, default: 7)',
minimum: 1,
maximum: 7,
default: 7,
},
},
required: ['latitude', 'longitude'],
},
},
{
name: 'get_climate_data',
description: 'Get climate change scenarios and long-term climate data',
inputSchema: {
type: 'object',
properties: {
latitude: {
type: 'number',
description: 'Latitude coordinate (-90 to 90)',
},
longitude: {
type: 'number',
description: 'Longitude coordinate (-180 to 180)',
},
start_date: {
type: 'string',
description: 'Start date in YYYY-MM-DD format',
pattern: '^\\d{4}-\\d{2}-\\d{2}$',
},
end_date: {
type: 'string',
description: 'End date in YYYY-MM-DD format',
pattern: '^\\d{4}-\\d{2}-\\d{2}$',
},
models: {
type: 'array',
items: {
type: 'string',
enum: ['EC_Earth3P_HR', 'FGOALS_f3_H', 'HiRAM_SIT_HR', 'MRI_AGCM3_2_S', 'EC_Earth3P', 'FGOALS_f3', 'MPI_ESM1_2_HR', 'MRI_AGCM3_2'],
},
description: 'Climate models to use (default: all available)',
},
},
required: ['latitude', 'longitude', 'start_date', 'end_date'],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'get_current_weather':
return await getCurrentWeather(args);
case 'get_weather_forecast':
return await getWeatherForecast(args);
case 'get_historical_weather':
return await getHistoricalWeather(args);
case 'search_locations':
return await searchLocations(args);
case 'get_air_quality':
return await getAirQuality(args);
case 'get_marine_weather':
return await getMarineWeather(args);
case 'get_climate_data':
return await getClimateData(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
content: [
{
type: 'text',
text: `Error: ${errorMessage}`,
},
],
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP Open Meteo server running on stdio');
}
main().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});