mcp-server
Description
# mcp-server { "name": "weather-mcp", "version": "1.0.0", "description": "Weather MCP server for Cline using OpenWeatherMap API", "main": "build/index.js", "scripts": { "build": "tsc", "start": "node build/index.js", "dev": "ts-node src/index.ts" }, "author": "", "license"…
About
# mcp-server { "name": "weather-mcp", "version": "1.0.0", "description": "Weather MCP server for Cline using OpenWeatherMap API", "main": "build/index.js", "scripts": { "build": "tsc", "start": "node build/index.js", "dev": "ts-node src/index.ts" }, "author": "", "license": "MIT", "dependencies": {…
Details
- Author
- milk19
- Downloads
- 366
- Categories
- Other
Jump to
- Provides current weather for any city or city,country
- Offers 5‑day weather forecasts (up to 5 days)
- Caches results to reduce API calls (30 min current, 1 hour forecast)
- Supports metric, imperial, and standard unit systems
- Returns formatted weather data including temperature, humidity, wind, and conditions
Setting up with Highlight
This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:
- Download and install Highlight from highlightai.com/download
- Navigate to the plugins tab and select "Add Custom Plugin"
-
Configure the plugin with the settings below
Plugin Name
mcp-serverCommand (node, npx, python, etc.)Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.
- Enable "Start Automatically" if you want the plugin to start when Highlight launches
From the repository
Set the OPENWEATHERMAP_API_KEY environment variable. Optionally set WEATHER_UNITS (metric/imperial/standard) and LOG_LEVEL. Build with npm run build and start with npm start. The server exposes two tools: get_current_weather and get_weather_forecast.
Claude Desktop / Cursor
Paste into your MCP client config file to install this server.
{
"mcpServers": {
"mcp-server": {
"mcp-server-milk19": {
"command": "npx",
"args": [
"@modelcontextprotocol/create-server",
"my-server"
]
}
}
}
}
McpServers
{
"mcp-server-milk19": {
"command": "npx",
"args": [
"@modelcontextprotocol/create-server",
"my-server"
]
}
}
mcp-server
{ "name": "weather-mcp", "version": "1.0.0", "description": "Weather MCP server for Cline using OpenWeatherMap API", "main": "build/index.js", "scripts": { "build": "tsc", "start": "node build/index.js", "dev": "ts-node src/index.ts" }, "author": "", "license": "MIT", "dependencies": { "@modelcontextprotocol/server": "^0.5.0", "axios": "^1.6.7", "node-cache": "^5.1.2" }, "devDependencies": { "@types/node": "^20.11.5", "ts-node": "^10.9.2", "typescript": "^5.3.3" } } { "compilerOptions": { "target": "es2020", "module": "commonjs", "lib": ["es2020"], "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true }, "include": ["src//"], "exclude": ["node_modules"] } import { Server, McpError, ErrorCode, Tool, ToolParam, ToolExecuteRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/server"; import axios from "axios"; import NodeCache from "node-cache";// 日志级别
const LOG_LEVEL = process.env.LOG_LEVEL || "info";
const DEBUG = LOG_LEVEL === "debug";
// 天气API配置
interface WeatherConfig {
apiKey: string;
baseUrl: string;
units: "metric" | "imperial" | "standard";
}
// 缓存配置(秒)
const CACHE_TTL = {
CURRENT_WEATHER: 30
FORECAST: 60 60, // 1小时
};
// 主类
class WeatherMcpServer {
private config: WeatherConfig;
private server: Server;
private cache: NodeCache;
constructor() {
console.error("[Setup] Initializing Weather MCP server...");
// 获取API密钥
const apiKey = process.env.OPENWEATHERMAP_API_KEY;
if (!apiKey) {
console.error("[Error] Missing OPENWEATHERMAP_API_KEY environment variable");
process.exit(1);
}
// 初始化配置
this.config = {
apiKey,
baseUrl: "https://api.openweathermap.org/data/2.5",
units: (process.env.WEATHER_UNITS as any) || "metric"
};
// 初始化缓存
this.cache = new NodeCache({
stdTTL: CACHE_TTL.CURRENT_WEATHER,
checkperiod: 120
});
// 初始化MCP服务器
this.server = new Server();
// 注册处理程序
this.registerHandlers();
console.error("[Setup] Weather MCP server initialized successfully");
}
private registerHandlers(): void {
// 工具执行处理
this.server.setRequestHandler(ToolExecuteRequestSchema, async (request) => {
console.error([Tool] Executing tool: ${request.params.name});
try {
switch (request.params.name) {
case "get_current_weather":
return await this.getCurrentWeather(request.params.params);
case "get_weather_forecast":
return await this.getWeatherForecast(request.params.params);
default:
throw new McpError(ErrorCode.MethodNotFound, Unknown tool: ${request.params.name});
}
} catch (error) {
console.error([Error] Tool execution failed: ${error instanceof Error ? error.message : String(error)});
if (error instanceof McpError) {
throw error;
}
return {
content: [{
type: "text",
text: Error: ${error instanceof Error ? error.message : String(error)}
}],
isError: true
};
}
});
// 工具列表
this.server.setRequestHandler(ToolExecuteRequestSchema.definitions.toolsRequest, async () => {
console.error("[Tools] Getting tools list");
return {
tools: [
{
name: "get_current_weather",
description: "Get the current weather for a location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "City name (e.g., 'London'), or city name with country code (e.g., 'London,uk')"
}
},
required: ["location"]
}
},
{
name: "get_weather_forecast",
description: "Get a 5-day weather forecast for a location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "City name (e.g., 'London'), or city name with country code (e.g., 'London,uk')"
},
days: {
type: "number",
description: "Number of days to forecast (1-5)",
default: 3
}
},
required: ["location"]
}
}
]
};
});
// 资源列表
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
console.error("[Resources] Getting resources list");
return {
resources: [
{
uri: "file:///weather-mcp/readme.md",
name: "Weather MCP Server README",
mimeType: "text/markdown"
}
]
};
});
// 资源读取
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
console.error([Resources] Reading resource: ${request.params.uri});
if (request.params.uri === "file:///weather-mcp/readme.md") {
return {
contents: [{
uri: request.params.uri,
mimeType: "text/markdown",
text: this.getReadmeContent()
}]
};
}
throw new McpError(ErrorCode.ResourceNotFound, Resource not found: ${request.params.uri});
});
}
private async getCurrentWeather(params: any): Promise<Tool.ExecuteResponse> {
this.validateLocation(params.location);
const location = params.location;
const cacheKey = current:${location};
// 检查缓存
const cachedData = this.cache.get<any>(cacheKey);
if (cachedData) {
console.error([Cache] Using cached current weather for: ${location});
return cachedData;
}
try {
console.error([API] Getting current weather for: ${location});
const response = await axios.get(${this.config.baseUrl}/weather, {
params: {
q: location,
appid: this.config.apiKey,
units: this.config.units
}
});
const data = response.data;
// 格式化数据
const result = {
content: [{
type: "text",
text: this.formatCurrentWeather(data)
}]
};
// 存入缓存
this.cache.set(cacheKey, result, CACHE_TTL.CURRENT_WEATHER);
return result;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
throw new McpError(ErrorCode.InvalidParams, Location not found: ${location});
}
throw error;
}
}
private async getWeatherForecast(params: any): Promise<Tool.ExecuteResponse> {
this.validateLocation(params.location);
const location = params.location;
const days = Math.min(Math.max(parseInt(params.days || "3", 10), 1), 5);
const cacheKey = forecast:${location}:${days};
// 检查缓存
const cachedData = this.cache.get<any>(cacheKey);
if (cachedData) {
console.error([Cache] Using cached forecast for: ${location} (${days} days));
return cachedData;
}
try {
console.error([API] Getting ${days}-day forecast for: ${location});
const response = await axios.get(${this.config.baseUrl}/forecast, {
params: {
q: location,
appid: this.config.apiKey,
units: this.config.units
}
});
const data = response.data;
// 格式化数据
const result = {
content: [{
type: "text",
text: this.formatForecast(data, days)
}]
};
// 存入缓存
this.cache.set(cacheKey, result, CACHE_TTL.FORECAST);
return result;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
throw new McpError(ErrorCode.InvalidParams, Location not found: ${location});
}
throw error;
}
}
private validateLocation(location: unknown): asserts location is string {
if (typeof location !== "string" || location.trim() === "") {
throw new McpError(ErrorCode.InvalidParams, "A valid location is required");
}
}
private formatCurrentWeather(data: any): string {
const temp = Math.round(data.main.temp);
const feelsLike = Math.round(data.main.feels_like);
const unit = this.config.units === "imperial" ? "°F" : "°C";
const windUnit = this.config.units === "imperial" ? "mph" : "m/s";
const weatherDescription = data.weather[0].description;
const weatherEmoji = this.getWeatherEmoji(data.weather[0].id);
return # Current Weather in ${data.name}, ${data.sys.country} ${weatherEmoji}\n\n +
Condition: ${this.capitalizeFirst(weatherDescription)}\n +Humidity: ${data.main.humidity}%\n +Wind: ${data.wind.speed} ${windUnit} from ${this.getWindDirection(data.wind.deg)}\n +Visibility: ${(data.visibility / 1000).toFixed(1)} km\n +Pressure: ${data.main.pressure} hPa\n\n +Last updated: ${new Date(data.dt 1000).toLocaleString()};}
private formatForecast(data: any, days: number): string {
const city = data.city.name;
const country = data.city.country;
const unit = this.config.units === "imperial" ? "°F" : "°C";
// 按天分组
const dailyForecasts: any = {};
// 处理5天/3小时预报数据
for (const item of data.list) {
const date = new Date(item.dt 1000);
const day = date.toISOString().split('T')[0];
if (!dailyForecasts[day]) {
dailyForecasts[day] = {
date: date,
minTemp: Infinity,
maxTemp: -Infinity,
conditions: [],
conditionIds: [],
rainfall: 0,
windSpeed: 0,
entries: 0
};
}
const forecast = dailyForecasts[day];
forecast.entries++;
// 更新温度
forecast.minTemp = Math.min(forecast.minTemp, item.main.temp_min);
forecast.maxTemp = Math.max(forecast.maxTemp, item.main.temp_max);
// 更新天气状况
forecast.conditions.push(item.weather[0].description);
forecast.conditionIds.push(item.weather[0].id);
// 统计降水量
if (item.rain && item.rain['3h']) {
forecast.rainfall += item.rain['3h'];
}
// 统计风速
forecast.windSpeed += item.wind.speed;
}
// 选择最靠前的几天
const forecastDays = Object.keys(dailyForecasts).sort().slice(0, days);
// 构建结果字符串
let result = # ${days}-Day Weather Forecast for ${city}, ${country}\n\n;
for (const day of forecastDays) {
const forecast = dailyForecasts[day];
// 计算每天的平均值
const avgWindSpeed = forecast.windSpeed / forecast.entries;
// 找出最常见的天气状况
const conditionCounts = forecast.conditionIds.reduce((acc: any, id: number) => {
acc[id] = (acc[id] || 0) + 1;
return acc;
}, {});
const mostCommonConditionId = Object.entries(conditionCounts)
.sort((a: any, b: any) => b[1] - a[1])[0][0];
const conditionIndex = forecast.conditionIds.indexOf(Number(mostCommonConditionId));
const mostCommonCondition = forecast.conditions[conditionIndex];
const weatherEmoji = this.getWeatherEmoji(Number(mostCommonConditionId));
// 格式化日期
const dateOptions: Intl.DateTimeFormatOptions = { weekday: 'long', month: 'short', day: 'numeric' };
const formattedDate = forecast.date.toLocaleDateString('en-US', dateOptions);
result += ## ${formattedDate} ${weatherEmoji}\n\n +
Conditions: ${this.capitalizeFirst(mostCommonCondition)}\n;if (forecast.rainfall > 0) {
result +=
Precipitation: ${forecast.rainfall.toFixed(1)} mm\n;}
result +=
Wind: ${avgWindSpeed.toFixed(1)} ${this.config.units === "imperial" ? "mph" : "m/s"}\n\n;}
return result;
}
private getWeatherEmoji(weatherId: number): string {
// 根据OpenWeatherMap的天气ID选择表情
if (weatherId >= 200 && weatherId < 300) return "⛈️"; // 雷暴
if (weatherId >= 300 && weatherId < 400) return "🌧️"; // 小雨
if (weatherId >= 500 && weatherId < 600) return "🌧️"; // 雨
if (weatherId >= 600 && weatherId < 700) return "❄️"; // 雪
if (weatherId >= 700 && weatherId < 800) return "🌫️"; // 雾
if (weatherId === 800) return "☀️"; // 晴天
if (weatherId > 800 && weatherId < 900) return "☁️"; // 多云
return "🌡️"; // 默认
}
private getWindDirection(degrees: number): string {
const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
const index = Math.round(degrees / 45) % 8;
return directions[index];
}
private capitalizeFirst(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
private getReadmeContent(): string {
return # Weather MCP Server
A Cline MCP server that provides real-time weather data using the OpenWeatherMap API.
Features
- Get current weather for any location worldwide
- Get 5-day weather forecasts with detailed information
- Support for metric and imperial units
- Efficient caching to minimize API calls
- Beautifully formatted markdown responses
Setup
1. Sign up for a free API key at OpenWeatherMap
2. Install the MCP server:
\\\json
{
"mcpServers": {
"weather-mcp": {
"command": "node",
"args": [
"/path/to/weather-mcp/build/index.js"
],
"env": {
"OPENWEATHERMAP_API_KEY": "YOUR_API_KEY",
"WEATHER_UNITS": "metric"
},
"disabled": false,
"autoApprove": []
}
}
}
\\\
3. Set \WEATHER_UNITS\ to "metric" (°C, m/s) or "imperial" (°F, mph)
Available Tools
get_current_weather
Gets the current weather conditions for a specified location.
Parameters:- \location\
: City name (e.g., 'London'), or city name with country code (e.g., 'London,uk')
get_weather_forecast
Gets a 5-day weather forecast for a specified location.
Parameters:*- \location\
: City name (e.g., 'London'), or city name with country code (e.g., 'London,uk')
- \days\: Number of days to forecast (1-5, default: 3)
Example Output
Current Weather
\
\\Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.



