从零搭建一个 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。

相关推荐
Android洋芋2 小时前
利用Cloudflare构建无限邮箱:实现Cursor无限续杯
cursor
6confim4 小时前
超越代码的未来:对话Cursor CEO,AI时代工程师的终极进化
llm·cursor·trae
饼干哥哥9 小时前
用Cursor「自动开发」Playwright网页自动化脚本,并打包成api给工作流调用
ai编程·cursor
Q141 天前
Cursor vs Xcode: 深入理解iOS开发工具链
xcode·cursor
一块小方糖1 天前
Cursor助力Java项目开发
cursor
袋鼠云数栈UED团队1 天前
AI 赋能编程,Coding新范式
ai编程·cursor·mcp
Lucien要变强1 天前
Cursor! 让我们意念合一!
人工智能·ai编程·cursor
饼干哥哥2 天前
4大技巧,让Cursor突破500次快速请求限制,摆脱额度焦虑
ai编程·cursor
hresh2 天前
通辽宇宙知识库:从零开始的UI设计之旅——AI辅助设计的完整实践
aigc·ai编程·cursor