#!/usr/bin/env node
/**
* Weather MCP Server
* Provides tools to fetch weather data from a weather API.
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios, { AxiosError } from "axios";
// Initialize the MCP server
const server = new McpServer({
name: "weather-mcp-server",
version: "1.0.0",
});
// Define Zod schema for input validation
const WeatherQuerySchema = z.object({
location: z
.string()
.min(1)
.max(100)
.describe("City name or location to get weather for (e.g., 'London', 'New York')"),
units: z
.enum(["metric", "imperial", "kelvin"])
.optional()
.default("metric")
.describe("Temperature units: 'metric' (Celsius), 'imperial' (Fahrenheit), or 'kelvin'"),
});
type WeatherQuery = z.infer<typeof WeatherQuerySchema>;
// Register the weather tool
server.tool(
"weather_get_current",
"Get current weather conditions for a location. Returns current temperature, conditions, humidity, and wind speed. Requires WEATHER_API_KEY environment variable.",
{
location: WeatherQuerySchema.shape.location,
units: WeatherQuerySchema.shape.units,
},
async (params: WeatherQuery): Promise<string> => {
// Validate environment
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) {
return "Error: Missing API key. Please set WEATHER_API_KEY environment variable.";
}
try {
// Make API request
const response = await axios.get("https://api.weatherapi.com/v1/current.json", {
params: {
key: apiKey,
q: params.location,
aqi: "no",
},
timeout: 10000,
});
const data = response.data;
const location = data.location;
const current = data.current;
// Convert temperature if needed
let temp: number = current.temp_c;
let unit: string = "°C";
if (params.units === "imperial") {
temp = current.temp_f;
unit = "°F";
} else if (params.units === "kelvin") {
temp = current.temp_c + 273.15;
unit = "K";
}
// Format output
const result = `Location: ${location.name}, ${location.country}
Temperature: ${temp}${unit}
Conditions: ${current.condition.text}
Humidity: ${current.humidity}%
Wind: ${current.wind_kph} km/h ${current.wind_dir}`;
return result;
} catch (error) {
// Handle different error types
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError;
if (axiosError.response?.status === 401) {
return "Error: Invalid API key. Please check your WEATHER_API_KEY.";
} else if (axiosError.response?.status === 400) {
return `Error: Location '${params.location}' not found. Please check the spelling.`;
} else if (axiosError.code === "ECONNABORTED") {
return "Error: Request timed out. The weather API is not responding.";
} else if (axiosError.response) {
return `Error: API request failed with status ${axiosError.response.status}`;
} else if (axiosError.request) {
return "Error: Network error occurred. Please check your internet connection.";
}
}
return `Error: An unexpected error occurred: ${error instanceof Error ? error.message : String(error)}`;
}
},
{
title: "Get Current Weather",
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
}
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP server running via stdio");
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});