import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";
import { z } from "zod";
// 使用和风天气 API
const QWEATHER_API_BASE = "https://ph5ctubwqh.re.qweatherapi.com";
const API_KEY = "dedccbe28e4d43a28e9db478df8aca71";
const server = new McpServer({
name: "china-weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
// 和风天气API请求辅助函数
async function makeQWeatherRequest<T>(
endpoint: string,
params: Record<string, string>
): Promise<T | null> {
const url = new URL(`${QWEATHER_API_BASE}${endpoint}`);
url.searchParams.append("key", API_KEY);
// 添加其他参数
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
try {
const response = await axios.get(url.toString());
if (!response.data) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = response.data;
// 检查和风天气API响应状态
if (data.code !== "200") {
throw new Error(`QWeather API error: ${data.code}`);
}
return data as T;
} catch (error) {
console.error("Error making QWeather request:", error);
return null;
}
}
// 和风天气API数据类型定义
interface Warning {
id: string;
sender: string;
pubTime: string;
title: string;
startTime: string;
endTime: string;
status: string;
level: string; // 已弃用,但保留兼容性
severity: string;
severityColor: string;
type: string;
typeName: string;
urgency: string;
certainty: string;
text: string;
related: string;
}
interface WarningResponse {
code: string;
updateTime: string;
fxLink: string;
warning: Warning[];
}
// 格式化预警信息
function formatWarning(warning: Warning): string {
const parts = [
`🚨 ${warning.title}`,
`类型: ${warning.typeName} (${warning.type})`,
`严重等级: ${warning.severity}${
warning.severityColor ? ` (${warning.severityColor})` : ""
}`,
`状态: ${warning.status}`,
`发布时间: ${warning.pubTime}`,
];
if (warning.startTime) {
parts.push(`生效时间: ${warning.startTime}`);
}
if (warning.endTime) {
parts.push(`结束时间: ${warning.endTime}`);
}
if (warning.sender) {
parts.push(`发布机构: ${warning.sender}`);
}
if (warning.urgency) {
parts.push(`紧迫程度: ${warning.urgency}`);
}
if (warning.certainty) {
parts.push(`确定性: ${warning.certainty}`);
}
parts.push(`详细信息: ${warning.text}`);
if (warning.related) {
parts.push(`相关预警ID: ${warning.related}`);
}
parts.push("---");
return parts.join("\n");
}
interface DailyForecast {
fxDate: string;
sunrise: string;
sunset: string;
moonrise: string;
moonset: string;
moonPhase: string;
tempMax: string;
tempMin: string;
iconDay: string;
textDay: string;
iconNight: string;
textNight: string;
wind360Day: string;
windDirDay: string;
windScaleDay: string;
windSpeedDay: string;
wind360Night: string;
windDirNight: string;
windScaleNight: string;
windSpeedNight: string;
humidity: string;
precip: string;
pressure: string;
vis: string;
cloud: string;
uvIndex: string;
}
interface DailyForecastResponse {
code: string;
daily: DailyForecast[];
}
interface LocationSearch {
name: string;
id: string;
lat: string;
lon: string;
adm2: string;
adm1: string;
country: string;
tz: string;
utcOffset: string;
isDst: string;
type: string;
rank: string;
fxLink: string;
}
interface LocationResponse {
code: string;
location: LocationSearch[];
}
// 实时天气数据类型定义
interface NowWeather {
obsTime: string;
temp: string;
feelsLike: string;
icon: string;
text: string;
wind360: string;
windDir: string;
windScale: string;
windSpeed: string;
humidity: string;
precip: string;
pressure: string;
vis: string;
cloud?: string;
dew?: string;
}
interface WeatherRefer {
sources: string[];
license: string[];
}
interface NowWeatherResponse {
code: string;
updateTime: string;
fxLink: string;
now: NowWeather;
refer: WeatherRefer;
}
const tool1 = async (city: string) => {
// 首先搜索城市获取locationId
const locationData = await makeQWeatherRequest<LocationResponse>(
"/geo/v2/city/lookup",
{
location: city,
}
);
if (
!locationData ||
!locationData.location ||
locationData.location.length === 0
) {
return {
content: [
{
type: "text",
text: `未找到天气信息,请检查城市名称是否正确`,
},
],
};
}
const locationId = locationData.location[0].id;
const locationName = locationData.location[0].name;
const province = locationData.location[0].adm1;
// 获取预警信息
const warningData = await makeQWeatherRequest<WarningResponse>(
"/v7/warning/now",
{
location: locationId,
}
);
if (!warningData) {
return {
content: [
{
type: "text",
text: "获取预警信息失败",
},
],
};
}
const warnings = warningData.warning || [];
if (warnings.length === 0) {
return {
content: [
{
type: "text",
text: `${province} ${locationName} 当前无气象预警信息`,
},
],
};
}
const formattedWarnings = warnings.map(formatWarning);
const warningsText = [
`${province} ${locationName} 气象预警信息:`,
``,
`数据更新时间: ${warningData.updateTime}`,
`详情页面: ${warningData.fxLink}`,
``,
`${formattedWarnings.join("\n")}`,
].join("\n");
return {
content: [
{
type: "text",
text: warningsText,
},
],
};
};
server.tool(
"get-weather-warnings",
"获取中国城市的气象预警信息",
{
city: z.string().describe("城市名称,例如:北京"),
},
async ({ city }) => {
return tool1(city) as Promise<CallToolResult>;
}
);
const tool2 = async (city: string) => {
// 首先搜索城市获取locationId
const locationData = await makeQWeatherRequest<LocationResponse>(
"/geo/v2/city/lookup",
{
location: city,
}
);
if (
!locationData ||
!locationData.location ||
locationData.location.length === 0
) {
return {
content: [
{
type: "text",
text: `未找到天气信息,请检查城市名称是否正确`,
},
],
};
}
const locationId = locationData.location[0].id;
const locationName = locationData.location[0].name;
const province = locationData.location[0].adm1;
// 获取实时天气
const nowWeatherData = await makeQWeatherRequest<NowWeatherResponse>(
`/v7/weather/now`,
{
location: locationId,
}
);
if (!nowWeatherData) {
return {
content: [
{
type: "text",
text: "获取实时天气失败",
},
],
};
}
const now = nowWeatherData.now;
if (!now) {
return {
content: [
{
type: "text",
text: "暂无实时天气数据",
},
],
};
}
// 格式化实时天气
const formattedWeather = [
`观测时间: ${now.obsTime}`,
`当前温度: ${now.temp}°C`,
`体感温度: ${now.feelsLike}°C`,
`天气状况: ${now.text}`,
`风向风力: ${now.windDir} ${now.windScale}级 (${now.windSpeed}km/h)`,
`湿度: ${now.humidity}%`,
`降水量: ${now.precip}mm`,
`气压: ${now.pressure}hPa`,
`能见度: ${now.vis}km`,
now.cloud ? `云量: ${now.cloud}%` : "",
now.dew ? `露点温度: ${now.dew}°C` : "",
]
.filter(Boolean)
.join("\n");
const weatherText = [
`${province} ${locationName} 实时天气:`,
``,
`数据更新时间: ${nowWeatherData.updateTime}`,
`详情页面: ${nowWeatherData.fxLink}`,
``,
formattedWeather,
].join("\n");
return {
content: [
{
type: "text",
text: weatherText,
},
],
};
};
server.tool(
"get-current-weather",
"获取中国城市的实时天气信息",
{
city: z.string().describe("城市名称,例如:北京"),
},
async ({ city }) => {
return tool2(city) as Promise<CallToolResult>;
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
// console.log("中国天气查询服务器正在运行...");
// console.log("请确保已设置 QWEATHER_API_KEY 环境变量");
// console.log("获取API密钥: https://dev.qweather.com");
}
main().catch((error) => {
console.error("Error starting server:", error);
process.exit(1);
});