使用说明
mcp服务器
{ “name”:“天气mcp”, “版本”:“1.0.0”, “description”:“Weather MCP server for Cline using OpenWeatherMap API”, “main”:“build/index.js”, “脚本”:{ “构建”:“tsc”, “start”:“node-build/index.js”, “dev”:“ts节点src/index.ts” }, 作者 “许可证”:“MIT”, “依赖关系”:{ “@modelcontextprotocol/server”:“^0.5.0”, “轴”:“^1.6.7”, “节点缓存”:“^5.1.2” }, “devDependencies”: { “@types/node”:“^20.11.5”, “ts节点”:“^10.9.2”, “打字稿”:“^5.3.3” } } { “编译器选项”:{ “目标”:“es2020”, “模块”:“commonjs”, “lib”:[“es2020”], “outDir”:“./build”, “rootDir”:“./src”, “严格”:正确, “esModuleInterop”:正确, “forceConsistent-CasingInFileName”:正确, “skipLibCheck”:true }, “包括”:[“src/**/*”], “exclude”:[“node_modules”] } 进口{ 服务器, McpError, 错误代码, 工具, 工具参数, 工具执行请求模式, ListResourcesRequestSchema, ReadResourceRequestSchema }来自“@modelcontextprotocol/server”; 从“axios”导入axios; 从“节点缓存”导入NodeCache;
// 日志级别 const LOGLEVEL=进程.env。LOGLEVEL ||“信息”; const DEBUG=LOGLEVEL===“DEBUG”;
// 天气API配置 接口WeatherConfig{ apiKey:字符串; baseUrl:字符串; 单位:公制|英制|标准; }
// 缓存配置(秒) const CACHE_TTL={ CURRENT_WEATHER: 30 * 60, // 30分钟 FORECAST: 60 * 60, // 1小时 };
// 主类 类WeatherMcpServer{ 私有配置:WeatherConfig; 私有服务器:服务器; 私有缓存:NodeCache;
构造者(){ console.error(“[Setup]正在初始化天气MCP服务器…”);
// 获取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");
}
私有注册表处理程序():void{
// 工具执行处理
this.set.setRequestHandler(工具执行请求模式,异步(请求)=>{
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}`);
});
}
私有异步getCurrentWeather(params:any):Promise<Tool。执行响应>{ 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;
}
}
私有异步getWeatherForecast(参数:任意):Promise<Tool。执行响应>{ 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(位置:未知):断言位置是字符串{ if(位置类型!==“string”|| location.trim()==“”){ 抛出新的McpError(ErrorCode.InvalidParams,“需要有效位置”); } }
私有格式CurrentWeather(数据:任意):字符串{ const temp=Math.round(data.main.temp); const feelsLike=Math.round(data.main.feels_like); const unit=this配置单位===“英制”?“°F”:“°C”; const windUnit=this.config.units===“英制”?“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` +
`**Temperature:** ${temp}${unit} (Feels like: ${feelsLike}${unit})\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(数据:任意,天数:数字):字符串{ const city=数据城市名称; const country=数据城市国家; const unit=this配置单位===“英制”?“°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` +
`**Temperature:** ${Math.round(forecast.minTemp)}${unit} to ${Math.round(forecast.maxTemp)}${unit}\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):字符串{ // 根据OpenWeatherMap的天气ID选择表情 如果(weatherId>=200&&weatherId<300)返回“⛈️"; // 雷暴 如果(weatherId>=300&weatherId<400)返回“🌧️"; // 小雨 如果(weatherId>=500&&weatherId<600)返回“🌧️"; // 雨 如果(weatherId>=600&&weatherId<700)返回“❄️"; // 雪 如果(weatherId>=700&weatherId<800)返回“🌫️"; // 雾 如果(weatherId===800)返回“☀️"; // 晴天 如果(weatherId>800和weatherId<900)返回“☁️"; // 多云 return "🌡️"; // 默认 }
private getWindDirection(度数:数字):字符串{ const directions=[‘N’、‘NE’、‘E’、‘SE’、‘S’、‘SW’、‘W’、‘NW’]; const索引=数学整数(度/45)%8; 返回方向[索引]; }
私有大写First(str:string):string{ 返回str.charAt(0).toUpperCase()+str.slice(1); }
私有getReadmeContent():字符串{ return `#气象MCP服务器
Cline MCP服务器,使用OpenWeatherMap API提供实时天气数据。
特性
- 获取全球任何地点的当前天气
- 获取5天天气预报和详细信息
- 支持公制和英制单位
- 高效缓存,最大限度地减少API调用
- 格式精美的降价响应
设置
-
在上注册免费API密钥 OpenWeatherMap
-
安装MCP服务器:
```json { “mcpServers”:{ “天气mcp”:{ “command”:“node”, “args”:[ “/path/to/weather-mcp/build/index.js” ], “env”:{ “OPENWEATHERMAP_API_KEY”:“YOUR_API_KEY”, “WEATHER_UNITS”:“公制” }, “disabled”:false, “自动批准”:[] } } } ```
-
将“WEATHER_UNITS”设置为“公制”(°C,m/s)或“英制”(°F,mph)
可用工具
get_current天气
获取指定位置的当前天气状况。
参数:
- `地点:城市名称(例如“伦敦”),或带有国家代码的城市名称(如“英国伦敦”)
get_weather_forecast
获取指定位置的5天天气预报。
参数:
- `地点:城市名称(例如“伦敦”),或带有国家代码的城市名称(如“英国伦敦”)
- `days:要预测的天数(1-5,默认值:3)
输出示例
当前天气
```
英国伦敦当前天气☁️
温度: 12°C(感觉像:10°C) 条件: 阴云密布 湿度: 76% 风: 西南方向4.12 m/s 可见性: 10.0公里 压力: 1013百帕
最后更新时间:2024年1月15日下午2:30:00 ```
天气预报
```
英国伦敦三天天气预报
1月15日,星期一🌧️
温度: 10°C至13°C 条件: 小雨 降水量: 2.4毫米 风: 5.2米/秒
1月16日,星期二☁️
温度: 9°C至12°C 条件: 多云 风: 4.8米/秒
1月17日,星期三☀️
温度: 8°C至14°C 条件: 晴空 风: 3.5米/秒 ```
许可证
麻省理工学院 `; }
public start():无效{ this.server.start(); console.error(“[服务器]天气MCP服务器已成功启动”); } }
// 启动服务器 const服务器=新的WeatherMcpServer(); server.start();
MCP服务器开发协议
⚠️ 关键:测试前不要使用尝试完成⚠️
第一步:计划(计划模式)
- 这个工具解决了什么问题?
- 它将使用什么API/服务?
- 身份验证要求是什么? □ API标准密钥 □ OAuth(需要单独的安装脚本) □ 其他证件
步骤2:实施(ACT模式)
-
独自创立
- 对于web服务、JavaScript集成或Node.js环境:
npx @modelcontextprotocol/create-server my-server cd my-server npm install - 对于数据科学、机器学习工作流或Python环境:
pip install mcp # Or with uv (recommended) uv add "mcp[cli]"
- 对于web服务、JavaScript集成或Node.js环境:
-
核心实施
- 使用MCP SDK
- 实施全面日志记录
- TypeScript(用于web/JS项目):
console.error('[Setup] Initializing server...'); console.error('[API] Request to endpoint:', endpoint); console.error('[Error] Failed with:', error); - Python(用于数据科学/ML项目):
import logging logging.error('[Setup] Initializing server...') logging.error(f'[API] Request to endpoint: {endpoint}') logging.error(f'[Error] Failed with: {str(error)}')
- TypeScript(用于web/JS项目):
- 添加类型定义
- 根据上下文处理错误
- 必要时实施限速
-
配置
- 如果需要,从用户处获取凭据
- 添加到MCP设置:
- 对于TypeScript项目:
{ "mcpServers": { "my-server": { "command": "node", "args": ["path/to/build/index.js"], "env": { "API_KEY": "key" }, "disabled": false, "autoApprove": [] } } } - 对于Python项目:
# Directly with command line mcp install server.py -v API_KEY=key # Or in settings.json { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"], "env": { "API_KEY": "key" }, "disabled": false, "autoApprove": [] } } }
- 对于TypeScript项目:
步骤3:测试(阻塞器⛔️)
BEFORE using attempt_completion, I MUST verify: □ Have I tested EVERY tool? □ Have I confirmed success from the user for each test? □ Have I documented the test results?如果任何答案为“否”,则不得使用try_coompletion。
- 测试每个工具(必需) □ 使用有效输入测试每个工具 □ 验证输出格式是否正确 ⚠️ 在所有工具都经过测试之前,不要继续
第四步:完成
❗ 停止并验证: □ 每个工具都经过了有效输入的测试 □ 每个工具的输出格式都是正确的
只有在所有工具都经过测试后,才能使用try_coompletion。
关键要求
-
✓ 必须使用MCP SDK
-
✓ 必须有全面的日志记录
-
✓ 必须单独测试每个工具
-
✓ 必须优雅地处理错误
-
⛔️ 在完成之前,切勿跳过测试
气象MCP服务器
Cline MCP服务器,使用OpenWeatherMap API提供实时天气数据。
特性
- 获取全球任何地点的当前天气
- 获取5天天气预报和详细信息
- 支持公制和英制单位
- 高效缓存,最大限度地减少API调用
- 格式精美的降价响应
设置
-
在上注册免费API密钥 OpenWeatherMap
-
克隆此存储库:
git clone https://github.com/yourusername/weather-mcp.git cd weather-mcp npm install npm run build -
将MCP服务器添加到您的Cline设置中:
{ "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": [] } } } -
集
WEATHER_UNITS转换为“公制”(°C,m/s)或“英制”(°F,mph)
可用工具
get_current天气
获取指定位置的当前天气状况。
参数:
location:城市名称(例如“伦敦”),或带有国家代码的城市名称(如“英国伦敦”)
get_weather_forecast
获取指定位置的5天天气预报。
参数:
location:城市名称(例如“伦敦”),或带有国家代码的城市名称(如“英国伦敦”)days:要预测的天数(1-5,默认值:3)
发展
-
安装依赖项:
npm install -
在开发模式下运行:
npm run dev -
生产建设:
npm run build
许可证
麻省理工学院