从零搭建一个 Cursor MCP Server

🎯 目标

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 配置

核心要点

  1. 一个文件搞定 :所有代码在 src/index.ts
  2. 两种模式:STDIO 开发,SSE 生产
  3. 真实 API:美国国家气象局数据
  4. 即学即用:30分钟内在 Cursor 中可用

恭喜!您已经成功创建了一个完整的 MCP Server。

相关推荐
slowlybutsurely18 小时前
Cursor核心功能及开发实战
ai编程·cursor
ζั͡山 ั͡有扶苏 ั͡✾18 小时前
AI辅助编程工具对比分析:Cursor、Copilot及其他主流选择
人工智能·copilot·cursor
老A技术联盟2 天前
超实用的Cursor使用技巧之案列分析-教你基于Cursor零代码开发一个chrome插件
人工智能·cursor
slowlybutsurely2 天前
Cursor快速入门
java·ai编程·cursor
ZNineSun2 天前
MCP+Cursor入门
ai·cursor·mcp
鬼鬼鬼2 天前
从软件1.0到3.0:在这场AI浪潮中,我们如何面对?
aigc·ai编程·cursor
散步去海边2 天前
Cursor 进阶使用教程
前端·ai编程·cursor
极客密码11 天前
Cursor再见!简单两步,Augment真无限续杯,爽用Claude 4!
ai编程·cursor·trae
风生水气12 天前
关于AI Coding工具使用的一些经验分享
ai编程·cursor·vibecoding