@trashwbin/weather-service
Version:
A weather service that provides weather alerts and forecasts using the National Weather Service API
167 lines • 5.99 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
/**
* @Author Abin
* @Description 天气服务类,提供天气预报和警报功能
*/
export class WeatherService {
static NWS_API_BASE = "https://api.weather.gov";
static USER_AGENT = "weather-app/1.0";
server;
constructor() {
this.server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
this.registerTools();
}
/**
* 发起 NWS API 请求
* @param url API 请求地址
* @returns 请求结果
*/
async makeNWSRequest(url) {
const headers = {
"User-Agent": WeatherService.USER_AGENT,
Accept: "application/geo+json",
};
try {
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json());
}
catch (error) {
console.error("Error making NWS request:", error);
return null;
}
}
/**
* 格式化警报数据
* @param feature 警报特征数据
* @returns 格式化后的警报文本
*/
formatAlert(feature) {
const props = feature.properties;
return [
`Event: ${props.event || "Unknown"}`,
`Area: ${props.areaDesc || "Unknown"}`,
`Severity: ${props.severity || "Unknown"}`,
`Status: ${props.status || "Unknown"}`,
`Headline: ${props.headline || "No headline"}`,
"---",
].join("\n");
}
/**
* 注册天气工具
*/
registerTools() {
this.server.tool("get_alerts", "Get weather alerts for a state", {
state: z.string().length(2).describe("Two-letter state code (e.g. CA, NY)"),
}, async ({ state }) => {
const alerts = await this.getAlerts(state);
return {
content: [
{
type: "text",
text: alerts,
},
],
};
});
this.server.tool("get_forecast", "Get weather forecast for a location", {
latitude: z.number().min(-90).max(90).describe("Latitude of the location"),
longitude: z
.number()
.min(-180)
.max(180)
.describe("Longitude of the location"),
}, async ({ latitude, longitude }) => {
const forecast = await this.getForecast(latitude, longitude);
return {
content: [
{
type: "text",
text: forecast,
},
],
};
});
}
/**
* 获取指定州的天气警报
* @param state 两字母的州代码
* @returns 警报信息文本
*/
async getAlerts(state) {
const stateCode = state.toUpperCase();
const alertsUrl = `${WeatherService.NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await this.makeNWSRequest(alertsUrl);
if (!alertsData) {
return "Failed to retrieve alerts data";
}
const features = alertsData.features || [];
if (features.length === 0) {
return `No active alerts for ${stateCode}`;
}
const formattedAlerts = features.map((feature) => this.formatAlert(feature));
return `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`;
}
/**
* 获取指定位置的天气预报
* @param latitude 纬度
* @param longitude 经度
* @returns 天气预报文本
*/
async getForecast(latitude, longitude) {
const pointsUrl = `${WeatherService.NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await this.makeNWSRequest(pointsUrl);
if (!pointsData) {
return `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`;
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return "Failed to get forecast URL from grid point data";
}
const forecastData = await this.makeNWSRequest(forecastUrl);
if (!forecastData) {
return "Failed to retrieve forecast data";
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return "No forecast periods available";
}
const formattedForecast = periods.map((period) => [
`${period.name || "Unknown"}:`,
`Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
`Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
`${period.shortForecast || "No forecast available"}`,
"---",
].join("\n"));
return `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`;
}
/**
* 启动天气服务
*/
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
}
// 如果作为命令行工具运行
if (import.meta.url === new URL(import.meta.url).href) {
const weatherService = new WeatherService();
weatherService.start().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
}
//# sourceMappingURL=index.js.map