Node.js 天气服务器开发
➡️ 预备知识
本快速入门假设你熟悉:
- TypeScript
- LLM(如 Claude)等大语言模型
➡️系统要求
对于 TypeScript,请确保你已安装最新版本的 Node。
➡️ 设置你的环境
首先,如果你还没有安装 Node.js 和 npm,让我们先安装它们。你可以从 nodejs.org 下载。安装完成后验证你的 Node.js 安装:
node --version
npm --version
对于本教程,你需要 Node.js 16 或更高版本。 现在,让我们创建并设置我们的项目:
# Create a new directory for our project
mkdir weather
cd weather
# Initialize a new npm project
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# Create our files
mkdir src
touch src/index.ts
# Create a new directory for our project
md weather
cd weather
# Initialize a new npm project
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# Create our files
md src
new-item src\index.ts
更新你的 package.json 文件以添加 type: "module" 和一个构建脚本:
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
],
}
在你的roots下创建tsconfig.json:
{
"compilerOptions": {
"outDir": "build",
"rootDir": "src",
"sourceMap": true
}
}
构建你的服务器
导入包并设置实例 将这些添加到你的 src/index.ts 文件顶部:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";
// Create server instance
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
辅助函数
接下来,让我们添加用于查询和格式化国家气象局API数据的辅助函数
// Helper function for making NWS API requests
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": 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()) as T;
} catch (error) {
console.error("Error making NWS request:", error);
return null;
}
}
interface AlertFeature {
properties: {
event?: string;
areaDesc?: string;
severity?: string;
status?: string;
headline?: string;
};
}
// Format alert data
function formatAlert(feature: AlertFeature): string {
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");
}
interface ForecastPeriod {
name?: string;
temperature?: number;
temperatureUnit?: string;
windSpeed?: string;
windDirection?: string;
shortForecast?: string;
}
interface AlertsResponse {
features: AlertFeature[];
}
interface PointsResponse {
properties: {
forecast?: string;
};
}
interface ForecastResponse {
properties: {
periods: ForecastPeriod[];
};
}
工具实现
工具执行处理程序负责实际执行每个工具的逻辑。让我们添加它:
// Register weather tools
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 stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);
if (!alertsData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve alerts data",
},
],
};
}
const features = alertsData.features || [];
if (features.length === 0) {
return {
content: [
{
type: "text",
text: `No active alerts for ${stateCode}`,
},
],
};
}
const formattedAlerts = features.map(formatAlert);
const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`;
return {
content: [
{
type: "text",
text: alertsText,
},
],
};
},
);
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 }) => {
// Get grid point data
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);
if (!pointsData) {
return {
content: [
{
type: "text",
text: `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 {
content: [
{
type: "text",
text: "Failed to get forecast URL from grid point data",
},
],
};
}
// Get forecast data
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve forecast data",
},
],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [
{
type: "text",
text: "No forecast periods available",
},
],
};
}
// Format forecast periods
const formattedForecast = periods.map((period: ForecastPeriod) =>
[
`${period.name || "Unknown"}:`,
`Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
`Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
`${period.shortForecast || "No forecast available"}`,
"---",
].join("\n"),
);
const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`;
return {
content: [
{
type: "text",
text: forecastText,
},
],
};
},
);
运行服务器
最后,实现运行服务器的主函数:
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
确保运行 npm run build 来构建你的服务器!这是让你的服务器连接的重要步骤。
现在让我们从现有的 MCP 主机 Claude for Desktop 测试你的服务器。
测试你的服务器
温馨提示
Claude桌面端目前尚未在 Linux 上提供。Linux 用户可以继续参阅构建客户端教程,以构建一个连接到我们刚刚构建的服务器的 MCP 客户端。
首先,确保你已经安装了 Claude for Desktop。你可以在这里安装最新版本。如果你已经有 Claude for Desktop,确保它更新到最新版本。我们需要为你想要使用的任何 MCP 服务器配置 Claude for Desktop。为此,打开你的 Claude for Desktop 应用程序配置文件,路径为 ~/Library/Application Support/Claude/claude_desktop_config.json,使用文本编辑器打开。如果文件不存在,请确保创建该文件。举个例子,如果你安装了 VS Code:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\Claude\claude_desktop_config.json
然后你将在 mcpServers 键中添加你的服务器。只有在至少一个服务器正确配置的情况下,MCP UI 元素才会在 Claude 桌面版中显示。 在这种情况下,我们将以如下方式添加我们的单个天气服务器:
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"
]
}
}
}
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\index.js"
]
}
}
}
这告诉桌面版 Claude:
- 有一个名为“天气”的 MCP 服务器。
- 通过运行 node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js 启动它。
保存文件,并重启桌面版 Claude。
使用命令进行测试
让我们确保 Claude for Desktop 正在获取我们在天气服务器中暴露的两个工具。您可以通过寻找锤子图标来做到这一点:
点击锤子图标后,您应该可以看到两个工具列出。
如果您的服务器无法被Claude for Desktop识别,请前往故障排除部分获取调试提示。
如果锤子图标已经出现,您现在可以通过在Claude for Desktop中运行以下命令来测试您的服务器:
- Sacramento的天气如何?
- Texas的天气警报有哪些?
提示
由于这是美国国家气象局,因此查询只适用于美国地区。
发生了什么
当你提问时:
- 客户端将你的问题发送给Claude
- Claude分析可用的工具并决定使用哪个工具
- 客户端通过MCP服务器执行所选工具
- 结果被发送回Claude
- Claude形成自然语言回应
- 回应显示给你!
故障排除
Claude 桌面端故障排除
- 从 Claude for Desktop 获取日志
Claude.app 相关的 MCP 日志文件保存在 ~/Library/Logs/Claude
目录下:
mcp.log
- 包含关于 MCP 连接和连接失败的一般日志信息mcp-server-服务器名称.log
- 包含指定服务器的错误(stderr)日志
你可以运行以下命令来查看最近的日志并实时跟踪新日志:
# 检查 Claude 的错误日志
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
- 服务器未在 Claude 中显示
- 检查你的
claude_desktop_config.json
文件语法 - 确保项目路径是绝对路径而不是相对路径
- 完全重启 Claude for Desktop
- 工具调用静默失败
如果 Claude 尝试使用工具但失败了:
- 检查 Claude 的错误日志
- 验证你的服务器构建和运行时没有错误
- 尝试重启 Claude for Desktop
- 以上方法都不起作用,我该怎么办?
请参考我们的调试指南以获取更好的调试工具和更详细的指导。
Weather API 故障排除
- 错误:无法获取网格点数据
这通常意味着以下情况之一:
- 坐标位置在美国境外
- NWS API 服务出现问题
- 你遇到了请求频率限制
解决方案:
验证你使用的是美国境内的坐标
在请求之间添加短暂延迟
检查 NWS API 状态页面
错误:[州名] 没有活动警报
这不是真正的错误 - 它只是表示该州目前没有天气警报。可以尝试查看其他州,或在恶劣天气期间再次检查。
提示
有关更高级的故障排除,请查看我们关于调试MCP的指南。