Nodejs开发人员MCP快速入门
开始构建您自己的服务器,供 Claude 桌面应用和其他客户端使用。 在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到主机 Claude 桌面应用。我们将从基本设置开始,然后逐渐过渡到更复杂的用例。

开始构建您自己的服务器,供 Claude 桌面应用和其他客户端使用。
在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到主机 Claude 桌面应用。我们将从基本设置开始,然后逐渐过渡到更复杂的用例。
我们将要构建的内容
许多LLMs(包括 Claude)目前无法获取天气预报和严重天气警报。让我们使用 MCP 来解决这个问题!
我们将构建一个服务器,公开两个工具: get-alerts
和 get-forecast
。然后我们将把服务器连接到一个 MCP 主机(在这种情况下是 Claude 桌面应用):

服务器可以连接到任何客户端。我们在这里选择了 Claude 桌面应用,但我们也有关于构建自己客户端的指南,以及其他客户端的列表。
为什么选择桌面版的 Claude 而不是 Claude.ai?
因为服务器是在本地运行的,MCP 目前仅支持桌面主机。远程主机正在积极开发中。
Core MCP Concepts 核心 MCP 概念
MCP 服务器可以提供三种主要类型的功能:
- 资源:客户端可以读取的类文件数据(如 API 响应或文件内容)
- 工具:可以由LLM(经用户批准)调用的功能
- 提示:预先编写的模板,可帮助用户完成特定任务
本教程将主要关注工具。
让我们开始构建我们的天气服务器!您可以在这里找到我们将要构建的完整代码。
预备知识(Nodejs方向)
这个快速入门假设您熟悉:
- TypeScript
- LLMs 像 Claude
系统要求
对于 TypeScript,请确保已安装最新版本的 Node。
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
更新您的 package.json 文件,添加 type: "module" 和一个构建脚本:
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
},
"files": [
"build"
],
}
在您的项目根目录中创建一个 tsconfig.json
:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
现在让我们开始构建您的服务器。
构建您的服务器
导入包
将这些内容添加到您的 src/index.ts
的顶部:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
设置实例
然后初始化 NWS API 基本 URL、验证模式和服务器实例:
const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";
// Define Zod schemas for validation
const AlertsArgumentsSchema = z.object({
state: z.string().length(2),
});
const ForecastArgumentsSchema = z.object({
latitude: z.number().min(-90).max(90),
longitude: z.number().min(-180).max(180),
});
// Create server instance
const server = new Server(
{
name: "weather",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
实现工具列表
我们需要告诉客户有哪些工具可用。这个 server.setRequestHandler
调用将为我们注册这个列表:
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get-alerts",
description: "Get weather alerts for a state",
inputSchema: {
type: "object",
properties: {
state: {
type: "string",
description: "Two-letter state code (e.g. CA, NY)",
},
},
required: ["state"],
},
},
{
name: "get-forecast",
description: "Get weather forecast for a location",
inputSchema: {
type: "object",
properties: {
latitude: {
type: "number",
description: "Latitude of the location",
},
longitude: {
type: "number",
description: "Longitude of the location",
},
},
required: ["latitude", "longitude"],
},
},
],
};
});
这定义了我们的两个工具: get-alerts
和 get-forecast
。
辅助函数
接下来,让我们添加用于从国家气象局 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[];
};
}
实现工具执行
工具执行处理程序负责实际执行每个工具的逻辑。让我们添加它:
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (name === "get-alerts") {
const { state } = AlertsArgumentsSchema.parse(args);
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).slice(0, 20) // only take the first 20 alerts;
const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join(
"\n"
)}`;
return {
content: [
{
type: "text",
text: alertsText,
},
],
};
} else if (name === "get-forecast") {
const { latitude, longitude } = ForecastArgumentsSchema.parse(args);
// 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,
},
],
};
} else {
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(
`Invalid arguments: ${error.errors
.map((e) => `${e.path.join(".")}: ${e.message}`)
.join(", ")}`
);
}
throw error;
}
});
运行服务器
最后,实现主函数来运行服务器:
// Start the server
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 桌面应用测试您的服务器。
使用 Claude 桌面应用测试您的服务器
Claude 桌面应用尚未在 Linux 上提供。Linux 用户可以继续查看“构建客户端教程”,构建一个连接到我们刚刚构建的服务器的 MCP 客户端。
首先,请确保您已安装了桌面版的 Claude。您可以在这里安装最新版本。如果您已经安装了桌面版的 Claude,请确保它已更新到最新版本。
我们需要为您想要使用的任何 MCP 服务器配置 Claude 桌面应用。要做到这一点,请在文本编辑器中打开您的 Claude 桌面应用程序配置 ~/Library/Application Support/Claude/claude_desktop_config.json
。确保在文件不存在时创建该文件。
例如,如果您已安装了 VS Code:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
然后在 mcpServers
键中添加您的服务器。只有在至少一个服务器正确配置时,MCP UI 元素才会显示在 Claude 桌面应用中。
在这种情况下,我们将像这样添加我们的单个天气服务器:
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"
]
}
}
}
这告诉 Claude 桌面应用
- 有一个名为“weather”的 MCP 服务器
- 通过运行
node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js
来启动它
保存文件,并重新启动 Claude 桌面应用。
Test with commands 测试命令
让我们确保 Claude 桌面应用正在获取我们在 weather
服务器中暴露的两个工具。您可以通过查找锤子
图标来完成此操作。

点击锤子图标后,您应该看到列出了两个工具:

如果您的服务器未被 Claude 桌面应用检测到,请继续前往故障排除部分获取调试提示。
如果锤子图标已显示出来,您现在可以通过在 Claude 桌面应用中运行以下命令来测试您的服务器:
- 萨克拉门托的天气如何?
- 德克萨斯州有哪些活跃的天气警报?


由于这是美国国家气象局,查询仅适用于美国地点。
内部到底发生了什么
当你提出一个问题:
- 客户将您的问题发送给 Claude
- Claude 分析可用工具并决定使用哪一个
- 客户端通过 MCP 服务器执行所选的工具
- 结果将发送回 Claude
- Claude 制定了一个自然语言回复
- 显示响应!
故障排除
Claude 桌面集成问题:
从 Claude 获取桌面日志
Claude.app 与 MCP 相关的日志记录写入到日志文件中 ~/Library/Logs/Claude
:
mcp.log
将包含有关 MCP 连接和连接失败的一般日志记录。- 文件名为
mcp-server-SERVERNAME.log
的文件将包含来自命名服务器的错误(stderr)日志记录。
您可以运行以下命令来列出最近的日志并跟踪任何新日志:
# Check Claude's logs for errors
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
服务器未显示在 Claude 中
- 检查您的
claude_desktop_config.json
文件语法 - 确保项目路径是绝对路径,而不是相对路径
- 完全重新启动 Claude 桌面
工具调用失败时静默处理
如果 Claude 尝试使用工具但失败:
- 检查 Claude 的日志以查找错误
- 验证您的服务器构建并运行无错误
- 尝试重新启动 Claude 桌面应用
这一切都不起作用。我该怎么办?
请参考我们的调试指南,获取更好的调试工具和更详细的指导。
天气 API 问题
错误:无法检索网格点数据
这通常意味着:
- 定位位于美国之外
- NWS API 存在问题
- 您正在受到速率限制
修复:
- 验证您是否使用的是美国坐标
- 在请求之间添加一个小延迟
- 检查 NWS API 状态页面
错误:STATE 没有活动警报
这不是错误 - 这只是表示该州目前没有天气警报。尝试选择其他州,或在恶劣天气期间检查。
对于更高级的故障排除,请查看我们关于调试 MCP 的指南