关于MCP Server 服务开发,官方以天气服务为例,介绍了如何开发一个MCP server,但仅能查询美国的天气服务。

这里我们也以天气服务为例,从零开始介绍如何开发一个MCP服务并集成到Cursor中使用。

通过这次分享,你可以收获到:

1、开发一个基于 Model Context Protocol (MCP) 工具集的代码框架,实现简单的代码分层,以便适应后续其他MCP服务的开发;

2、基于此框架开发一个MCP天气服务,使用OpenWeather提供的API能力,实现全球天气查询服务;

3、将该MCP工具集成配置到Cursor中使用;

开发环境要求

  • Node.js >= 16.0.0
  • npm >= 8.0.0
  • TypeScript >= 4.5.0
  • MacOS:15.3
  • Cursor版本: 0.47.8 

获取OpenWeather API密钥

OpenWeather 是一家位于英国伦敦的科技创新公司,专注开发智能环境数据产品,通过自研的 OW ML 数值天气预报模型,为全球超 600 万用户每日提供超 20 亿次高精度气象预测及 5PB 环境数据,其数据覆盖全球,可通过 API 等方式获取实时、历史及未来的天气数据,广泛服务于能源、物流、农业等多行业。

接下来主要是通过官网注册 OpenWeather 账户并获取 API 密钥。

搭建基础开发框架

良好的开发框架往往能让你获取到意想不到的开发效率。项目采用三层架构设计,从简出发,快速上手,每个服务都遵循相同的结构模式。

项目github地址:https://github.com/fist-maestro/mcp-servers

项目框架

以下是整个项目的分层结构:

mcp-servers/
├── src/                    # 源代码目录
│   ├── demo/              # 示例服务
│   │   ├── config/       # 配置层:常量、类型定义
│   │   │   ├── constants.ts    # 常量定义
│   │   │   └── types.ts        # 类型定义
│   │   ├── controllers/  # 控制器层:请求处理
│   │   │   └── GreetingController.ts  # 问候控制器
│   │   ├── service/      # 服务层:业务逻辑
│   │   │   └── GreetingService.ts     # 问候服务
│   │   ├── package.json  # 服务配置文件
│   │   ├── tsconfig.json # TypeScript 配置
│   │   └── index.ts      # 服务入口文件
│   │
│   └── weather/          # 天气服务
│       ├── config/       # 配置层:常量、类型定义
│       │   ├── constants.ts    # 常量定义
│       │   └── types.ts        # 类型定义
│       ├── controllers/  # 控制器层:请求处理
│       │   └── WeatherController.ts  # 天气控制器
│       ├── service/      # 服务层:业务逻辑
│       │   └── WeatherService.ts     # 天气服务
│       ├── package.json  # 服务配置文件
│       ├── tsconfig.json # TypeScript 配置
│       └── index.ts      # 服务入口文件
│
├── build/                  # 编译输出目录
├── node_modules/          # 依赖包
├── package.json           # 项目配置
├── tsconfig.json          # TypeScript 配置
└── README.md             # 项目文档

框架核心分层主要包含3+1(一个入口,三个分层):

1. 配置层(Config)

位置:服务目录/config/

  • 职责
    • 定义常量和配置项
    • 声明类型和接口
    • 管理环境变量
  • 主要文件
    • constants.ts: 常量定义
    • types.ts: 类型定义
  • 特点
    • 集中管理配置
    • 类型安全
    • 易于维护和修改
2. 控制器层(Controllers)

位置:服务目录/controllers/

  • 职责
    • 处理 MCP 请求和响应
    • 参数验证和错误处理
    • 调用服务层方法
  • 主要文件
    • XXXController.ts: 具体业务控制器
  • 特点
    • 请求参数验证
    • 错误处理和日志
    • 响应格式化
3. 服务层(Service)

位置:服务目录/service/

  • 职责
    • 实现核心业务逻辑
    • 处理数据转换
    • 调用外部 API
  • 主要文件
    • XXXService.ts: 具体业务服务
  • 特点
    • 业务逻辑封装
    • 数据处理和转换
    • 外部服务集成
4. 服务入口(index.ts)

位置:服务目录/index.ts

  • 职责
    • 初始化服务实例
    • 注册 MCP 工具
    • 处理标准输入输出
  • 特点
    • 统一的入口点
    • MCP 工具注册
    • 错误处理

本项目提供了一个demo示例,用于快速开始上手开发,git clone 到本地后可直接运行起来,详细流程可见文档说明。

自建Weather天气查询服务

接下来我们基于以上开发框架快速新建一个天气服务。

1、项目初始化

# 创建项目目录结构
mkdir -p src/weather/{config,controllers,service}
cd src/weather

# 初始化 package.json
npm init -y

# 安装基础依赖
npm install typescript @types/node

2、遵循三层架构开发

2.1、配置层(config)
  • constants.ts: 定义常量
   export const WEATHER_API_BASE_URL = 'https://api.openweathermap.org/data/2.5';
   export const WEATHER_API_KEY = process.env.OPENWEATHER_API_KEY;
  • types.ts: 定义接口和类型
   export interface WeatherData {
     temperature: number;
     humidity: number;
     description: string;
   }
2.2、控制器层(controllers)
  • 处理请求参数
  • 调用服务层方法
  • 格式化响应数据
   export class WeatherController {
     private weatherService: WeatherService;
     
     constructor() {
       this.weatherService = new WeatherService();
     }
     
     async getCurrentWeather(city: string): Promise<WeatherData> {
       // 参数验证
       if (!city) {
         throw new Error('City is required');
       }
       
       // 调用服务层
       return this.weatherService.getCurrentWeather(city);
     }
   }
2.3、服务层(service)
  • 实现核心业务逻辑
  • 调用外部 API
   export class WeatherService {
     async getCurrentWeather(city: string): Promise<WeatherData> {
       const response = await fetch(`${WEATHER_API_BASE_URL}/weather?q=${city}&appid=${WEATHER_API_KEY}`);
       const data = await response.json();
       return this.transformWeatherData(data);
     }
   }

3、创建服务入口文件

核心逻辑包含两点:

  • 注册天气服务工具
  • 主函数逻辑
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { config } from './config/index.js';
import { WeatherController } from './controllers/weather.js';

// Create server instance
const server = new McpServer({
  name: config.server.name,
  version: config.server.version,
  capabilities: {
    resources: {},
    tools: {},
  },
});

const weatherController = new WeatherController();

// Register weather tools
server.tool(
  "get-current-weather",
  "Get current weather for a location",
  {
    city: z.string().describe("City name (e.g. Beijing, London)"),
    country: z.string().optional().describe("Country code (e.g. CN, GB)")
  },
  async ({ city, country }) => {
    const weatherText = await weatherController.getCurrentWeather(city, country);
    return {
      content: [
        {
          type: "text",
          text: weatherText,
        },
      ],
    };
  }
);

server.tool(
  "get-forecast",
  "Get 5-day weather forecast for a location",
  {
    city: z.string().describe("City name (e.g. Beijing, London)"),
    country: z.string().optional().describe("Country code (e.g. CN, GB)")
  },
  async ({ city, country }) => {
    const forecastText = await weatherController.getForecast(city, country);
    return {
      content: [
        {
          type: "text",
          text: forecastText,
        },
      ],
    };
  }
);

async function main() {
  console.error("正在初始化 Weather MCP 服务...");
  console.error(`服务名称: ${config.server.name}`);
  console.error(`服务版本: ${config.server.version}`);
  console.error(`API Base URL: ${config.openWeather.apiBase}`);
  console.error(`单位系统: ${config.openWeather.units}`);
  
  try {
    console.error("正在创建服务传输层...");
    const transport = new StdioServerTransport();
    
    console.error("正在连接 MCP 服务器...");
    await server.connect(transport);
    
    console.error("服务初始化完成!");
    console.error("Weather MCP Server running on stdio");
  } catch (error) {
    console.error("服务启动失败:", error);
    throw error;
  }
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
}); 

4、配置编译选项

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "../../build/weather",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true
  }
}

5、启动服务

编译服务:

npm run build:weather

启动服务:

node build/weather/index.js

6、调试运行

7、运行结果

至此,整个服务就开发完成了,而且整个过程均是在Cursor中开发完成的,过程中完全不需要你自己手工写一行代码,只要在chat中输入你的开发要求,Cursor会自动帮你一键开发处理,整个过程不超过5分钟,简直是太高效了,而且代码质量很高,从开发到调试,到运行,到文档总结,全部都给你搞定。

AI时代,不会用工具,就要被淘汰了!!!

集成至Cursor IDE

天气服务开发好了,我们可以把它集成到Cursor IDE 中,通过Cursor来调用这个自建的天气MCP服务。

  • 让Cursor帮我生成配置文件

  • 在mcp.json配置文件中添加一下weather服务

  • 保存后,回到MCP配置界面,你会发现weather MCP服务已启动成功,前面小圆点是绿色的,且有两个Tools工具。

  • 我们在Cursor中直接输入我们的诉求:帮我查询下深圳未来3天的天气情况,可以看到,确实是调用了weather MCP 服务。

至此,我们的本地MCP天气服务就开发完成了!!!

MCP 服务开发实践总结

1、通过 Weather 服务的开发实践,我们深刻体验到 Model Context Protocol (MCP) 在智能编程辅助领域的创新价值。基于三层架构(配置层、控制器层、服务层)的设计模式,不仅使得服务结构清晰、易于维护,更重要的是为 AI 模型提供了标准化的交互接口。在实际应用中,Weather 服务能够实时响应天气查询请求,展示了 MCP 在处理复杂业务逻辑时的高效性和可靠性。

2、结合 Cursor IDE 的开发体验,其内置的 MCP 工具链极大简化了服务的开发和调试流程。从代码编写、编译构建到服务部署,实现了一站式的开发体验。特别是在处理 API 调用、错误处理等场景时,Cursor 的智能提示和代码补全功能显著提升了开发效率。这种"AI + 工具链"的组合,代表了未来智能化开发工具的发展方向,为开发者提供了更优质的编程体验。

3、这个实践案例不仅验证了 MCP 协议的技术价值,也展示了智能化开发工具在提升开发效率方面的巨大潜力。通过标准化的服务封装和智能化的开发支持,MCP 正在重新定义 AI 辅助编程的新范式。

相关资源

项目github地址:https://github.com/fist-maestro/mcp-servers

OpenWeather官网地址:Members

更多资源获取,猛戳这里领取👇👇👇:

从零开始:如何开发一个MCP Server

从零开始:如何在Cursor中集成GitHub MCP Server

MCP:AI时代的“USB-C”协议?深度解析下一代AI标准

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐