🎯 目标
30分钟完成:创建天气查询 MCP Server,在 Cursor 中可用
第一部分:准备工作 (5分钟)
1. 基本概念
MCP Server 是什么?
- 为 AI 助手提供自定义工具的服务器
- Cursor 通过 MCP 调用你的工具
- 本教程:创建天气查询工具
两种模式
- STDIO: 本地开发,Cursor 自动管理
- SSE: 远程访问,需手动启动服务器
2. 环境搭建
bash
# 创建项目
mkdir weather-mcp && cd weather-mcp
npm init -y
# 安装依赖
npm install @modelcontextprotocol/sdk zod express node-fetch
npm install -D typescript @types/node @types/express
# 创建 TypeScript 配置
echo '{"compilerOptions":{"target":"ES2022","module":"ESNext","moduleResolution":"node","outDir":"build","strict":true,"esModuleInterop":true}}' > tsconfig.json
# 创建源码目录
mkdir src
手动编辑 package.json
,添加 scripts 部分:
json
{
"scripts": {
"build": "tsc",
"start": "node build/index.js --stdio",
"debug": "tsc && npx @modelcontextprotocol/inspector node build/index.js --stdio"
}
}
第二部分:核心实现 (15分钟)
3. 完整代码实现
创建 src/index.ts
文件:
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { z } from "zod";
import express from "express";
import fetch from "node-fetch";
// 创建服务器
const server = new McpServer({
name: "weather-server",
version: "1.0.0",
}, {
capabilities: { tools: {} }
});
// 工具1:获取天气警报
server.tool("get_weather_alerts", "获取美国各州天气警报", {
state: z.string().length(2).describe("州代码,如 CA")
}, async ({ state }) => {
const url = `https://api.weather.gov/alerts/active/area/${state.toUpperCase()}`;
const response = await fetch(url, {
headers: { "User-Agent": "weather-mcp/1.0" }
});
if (!response.ok) {
return { content: [{ type: "text", text: "获取失败" }] };
}
const data = await response.json();
const alerts = data.features || [];
if (alerts.length === 0) {
return { content: [{ type: "text", text: `${state} 暂无警报` }] };
}
const alertText = alerts.map(alert =>
`事件: ${alert.properties.event}\n区域: ${alert.properties.areaDesc}`
).join("\n---\n");
return { content: [{ type: "text", text: alertText }] };
});
// 工具2:获取天气预报
server.tool("get_weather_forecast", "获取天气预报", {
lat: z.number().describe("纬度"),
lon: z.number().describe("经度")
}, async ({ lat, lon }) => {
// 第一步:获取预报网格
const pointUrl = `https://api.weather.gov/points/${lat},${lon}`;
const pointResponse = await fetch(pointUrl, {
headers: { "User-Agent": "weather-mcp/1.0" }
});
if (!pointResponse.ok) {
return { content: [{ type: "text", text: "位置不支持(仅限美国)" }] };
}
const pointData = await pointResponse.json();
const forecastUrl = pointData.properties.forecast;
// 第二步:获取预报数据
const forecastResponse = await fetch(forecastUrl, {
headers: { "User-Agent": "weather-mcp/1.0" }
});
const forecastData = await forecastResponse.json();
const periods = forecastData.properties.periods || [];
const forecast = periods.slice(0, 3).map(period =>
`${period.name}: ${period.temperature}°${period.temperatureUnit} - ${period.shortForecast}`
).join("\n");
return { content: [{ type: "text", text: forecast }] };
});
// STDIO 模式
if (process.argv.includes("--stdio")) {
const transport = new StdioServerTransport();
server.connect(transport);
}
// SSE 模式
else {
const app = express();
app.use(express.json());
const sessions = new Map();
app.get('/sse', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Access-Control-Allow-Origin', '*');
const transport = new SSEServerTransport('/sse', res);
server.connect(transport);
sessions.set(transport.sessionId, transport);
req.on('close', () => sessions.delete(transport.sessionId));
});
app.post('/sse', (req, res) => {
const sessionId = req.query.sessionId || req.headers['x-session-id'];
const transport = sessions.get(sessionId);
if (transport) {
transport.handleMessage(req.body)
.then(() => res.status(202).end())
.catch(() => res.status(500).end());
} else {
res.status(404).end();
}
});
app.listen(3000, () => console.log('SSE 服务器: http://localhost:3000'));
}
4. 编译和测试
bash
# 编译
npm run build
# 测试 STDIO
npm run debug
# 测试 SSE
node build/index.js
# 浏览器访问: http://localhost:3000/sse
第三部分:Cursor 配置 (5分钟)
5. STDIO 配置
通过 UI 配置:Cursor 右上角设置 → MCP Tools → 添加服务器
json
{
"name": "weather-server",
"command": "node",
"args": ["/Users/username/weather-mcp/build/index.js", "--stdio"]
}
或者直接编辑 mcp.json:
json
{
"mcpServers": {
"weather-server": {
"command": "node",
"args": ["/Users/username/weather-mcp/build/index.js", "--stdio"]
}
}
}
注意:必须使用绝对路径!
6. SSE 配置
先启动服务器:
bash
node build/index.js
通过 UI 配置:Cursor 设置 → MCP Tools → 添加服务器
json
{
"name": "weather-sse",
"type": "sse",
"url": "http://localhost:3000/sse"
}
或者直接编辑 mcp.json:
json
{
"mcpServers": {
"weather-sse": {
"type": "sse",
"url": "http://localhost:3000/sse"
}
}
}
第四部分:验证成果 (3分钟)
7. 功能验证
在 Cursor 中测试:
- "查询加州的天气警报"
- "查询纽约(40.7128, -74.0060)的天气预报"
成功标志:AI 助手能调用工具并返回天气信息
第五部分:调试方法 (2分钟)
8. 问题排查
连接失败
- 检查路径是否正确(必须是绝对路径)
- 确认编译成功:
npm run build
工具不可用
- 使用 Inspector 测试:
npm run debug
- 检查 Cursor MCP Tools 中的连接状态
SSE 无法访问
- 确认服务器启动:
curl http://localhost:3000/sse
- 检查端口是否被占用
🎯 总结
最终文件结构:
bash
weather-mcp/
├── src/index.ts # 唯一源文件
├── build/index.js # 编译输出
├── package.json # 依赖配置
└── tsconfig.json # TS 配置
核心要点:
- 一个文件搞定 :所有代码在
src/index.ts
- 两种模式:STDIO 开发,SSE 生产
- 真实 API:美国国家气象局数据
- 即学即用:30分钟内在 Cursor 中可用
恭喜!您已经成功创建了一个完整的 MCP Server。