#OpenClaw# #MCP#

MCP协议

移步:https://www.runoob.com/np/mcp-protocol.html 更详细的协议标准


MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 推出的开源协议,旨在实现大型语言模型(LLM)与外部数据源和工具的无缝集成,用来在大模型和数据源之间建立安全双向的链接

MCP工具调用流程

用户发送问题 -> LLM 分析可用工具 -> 客户端通过 MCP 服务器来执行所选工具 -> 将结果发送回 LLM -> LLM根据工具返回结果和用户问题进行回答。


MCP 定义了三种标准化交互方式,覆盖不同控制主体:

原语 控制权 能力 典型场景
Resources 应用 / 服务器 只读数据访问 读取本地文件、查询数据库、获取知识库
Tools 模型 主动执行操作 发送邮件、运行代码、调用 API、控制设备
Prompts 用户 复用工作流 代码生成模板、报告撰写模板、客服话术

实战

自己编写(大部分是Trae SOLO 完成)天气预报的MCP服务(Tools交互类型)
以weather MCP服务为例,源码:https://gitcode.com/SUI517/gitcode_knowledge

项目截图(weather,mysql[实现三种模式:Tools,Resources,Prompts交互])



1、创建McpServer

const server=new McpServer({
    name:"weather-server",
    version:"1.0.0"
});

2、注册服务(MCP工具类型交互)

server.registerTool('get_weather',{
    description:'获取城市的天气预报MCP服务',
    inputSchema:{
        city:z.string().describe('城市名称,如:北京、上海'),
        days:z.number().min(1).max(7).default(3).describe('预报天数(1-7天)')
    }
}),asyns({city,days}) => {
    ...
    return {
        content:[
            {
                type:'text',
                text:JSON.stringify(weatherData, null, 2)
            }
        }
    };
    
}
catch(error) {
    return {
        content:[{type:'text',text:' ${error.message}'}]
    };
}

3、入口函数

async function main(){
    //创建标准I/O通信,用于本地进程通信
    const transport=new StudioServerTransport(); 
    //开始连接
    await server.connet(transport);
}
//捕获主函数异常
main().catch((error)=>{})

index.js

#!/usr/bin/env node

/**
 * Weather MCP Server
 * 提供天气预报查询功能的MCP服务器
 * 支持查询指定城市的1-7天天气预报
 */

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import * as z from 'zod/v4';
import { getWeatherForecast } from './weather.js';

// 创建MCP服务器实例
const server = new McpServer({
  name: 'weather-server',
  version: '1.0.0'
}, {
  capabilities: {
    tools: {}
  }
});

// 注册天气预报工具
server.registerTool('get_weather', {
  description: '获取指定城市的天气预报信息',
  inputSchema: {
    city: z.string().describe('城市名称(例如:北京、上海、广州)'),
    days: z.number().min(1).max(7).default(3).describe('预报天数(1-7天)')
  }
}, async ({ city, days }) => {
  try {
    if (!city) {
      throw new Error('城市名称不能为空');
    }

    const weatherData = await getWeatherForecast(city, days);

    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(weatherData, null, 2),
        },
      ],
    };
  } catch (error) {
    return {
      content: [
        {
          type: 'text',
          text: `获取天气预报失败: ${error.message}`,
        },
      ],
      isError: true,
    };
  }
});

// 主函数:启动服务器
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);
});

2、weather.js

/**
 * 天气预报模块
 * 提供从Open-Meteo API获取天气数据的功能
 */

import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';

/**
 * 持久化缓存实现
 */
class WeatherCache {
  constructor() {
    this.cacheTimeout = 30 * 60 * 1000; // 30分钟缓存过期时间
    this.cacheFile = this.getCacheFilePath();
    this.cache = this.loadCacheFromFile();
  }

  /**
   * 获取缓存文件路径
   * @returns {string} 缓存文件路径
   */
  getCacheFilePath() {
    try {
      const __filename = fileURLToPath(import.meta.url);
      const cacheDir = join(dirname(__filename), '.cache');
      
      // 确保缓存目录存在
      if (!existsSync(cacheDir)) {
        mkdirSync(cacheDir, { recursive: true });
      }
      
      return join(cacheDir, 'weather-cache.json');
    } catch (error) {
      // 如果无法获取文件路径,使用临时目录
      return join(process.cwd(), '.cache', 'weather-cache.json');
    }
  }

  /**
   * 从文件加载缓存
   * @returns {Map} 缓存Map
   */
  loadCacheFromFile() {
    try {
      if (!existsSync(this.cacheFile)) {
        return new Map();
      }

      const data = readFileSync(this.cacheFile, 'utf-8');
      const parsed = JSON.parse(data);
      const cache = new Map();

      // 清除过期缓存
      const now = Date.now();
      for (const [key, value] of Object.entries(parsed)) {
        if (now - value.timestamp <= this.cacheTimeout) {
          cache.set(key, value);
        }
      }

      // 保存清理后的缓存
      this.saveCacheToFile(cache);
      return cache;
    } catch (error) {
      console.error('加载缓存失败:', error.message);
      return new Map();
    }
  }

  /**
   * 保存缓存到文件
   * @param {Map} cache - 缓存Map
   */
  saveCacheToFile(cache) {
    try {
      const data = Object.fromEntries(cache.entries());
      writeFileSync(this.cacheFile, JSON.stringify(data, null, 2), 'utf-8');
    } catch (error) {
      console.error('保存缓存失败:', error.message);
    }
  }

  /**
   * 生成缓存键
   * @param {string} city - 城市名称
   * @param {number} days - 预报天数
   * @returns {string} 缓存键
   */
  getCacheKey(city, days) {
    return `${city}_${days}`;
  }

  /**
   * 获取缓存数据
   * @param {string} city - 城市名称
   * @param {number} days - 预报天数
   * @returns {Object|null} 缓存数据或null
   */
  get(city, days) {
    const key = this.getCacheKey(city, days);
    const cached = this.cache.get(key);
    
    if (!cached) {
      return null;
    }

    // 检查是否过期
    if (Date.now() - cached.timestamp > this.cacheTimeout) {
      this.cache.delete(key);
      this.saveCacheToFile(this.cache);
      return null;
    }

    return cached.data;
  }

  /**
   * 设置缓存数据
   * @param {string} city - 城市名称
   * @param {number} days - 预报天数
   * @param {Object} data - 天气数据
   */
  set(city, days, data) {
    const key = this.getCacheKey(city, days);
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
    this.saveCacheToFile(this.cache);
  }

  /**
   * 清除所有缓存
   */
  clear() {
    this.cache.clear();
    this.saveCacheToFile(this.cache);
  }

  /**
   * 清除过期缓存
   */
  clearExpired() {
    const now = Date.now();
    for (const [key, cached] of this.cache.entries()) {
      if (now - cached.timestamp > this.cacheTimeout) {
        this.cache.delete(key);
      }
    }
    this.saveCacheToFile(this.cache);
  }
}

// 创建全局缓存实例
const weatherCache = new WeatherCache();

/**
 * 获取指定城市的天气预报
 * @param {string} city - 城市名称
 * @param {number} days - 预报天数(默认3天)
 * @returns {Promise<Object>} 天气预报数据
 */
async function getWeatherForecast(city, days = 3) {
  try {
    // 先检查缓存
    const cachedData = weatherCache.get(city, days);
    if (cachedData) {
      console.error(`使用缓存数据: ${city},未来${days}天`);
      return cachedData;
    }

    console.error(`正在查询城市: ${city},未来${days}天的天气预报...`);
    
    // 第一步:调用地理编码API获取城市坐标
    const geoResponse = await fetch(
      `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1&language=zh&format=json`
    );
    
    if (!geoResponse.ok) {
      throw new Error('地理编码API请求失败');
    }

    const geoData = await geoResponse.json();

    if (!geoData.results || geoData.results.length === 0) {
      throw new Error(`未找到城市: ${city}`);
    }

    // 提取城市位置信息
    const location = geoData.results[0];
    const { latitude, longitude, name, country, admin1 } = location;

    // 第二步:使用坐标调用天气API获取预报数据
    const weatherResponse = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max&timezone=auto&forecast_days=${days}`
    );

    if (!weatherResponse.ok) {
      throw new Error('天气API请求失败');
    }

    const weatherData = await weatherResponse.json();

    // 将天气数据转换为更友好的格式
    const forecast = weatherData.daily.time.map((date, index) => {
      return {
        date: date,
        weather: getWeatherDescription(weatherData.daily.weathercode[index]),
        temperature: {
          max: weatherData.daily.temperature_2m_max[index],
          min: weatherData.daily.temperature_2m_min[index],
        },
        precipitation: weatherData.daily.precipitation_sum[index],
        windSpeed: weatherData.daily.windspeed_10m_max[index],
      };
    });

    // 返回格式化的天气数据
    const result = {
      location: {
        name: name,
        country: country,
        province: admin1 || '',
        coordinates: {
          latitude: latitude,
          longitude: longitude,
        },
      },
      forecast: forecast,
    };

    // 存入缓存
    weatherCache.set(city, days, result);

    return result;
  } catch (error) {
    throw new Error(`获取天气预报失败: ${error.message}`);
  }
}

/**
 * 根据WMO天气代码返回中文天气描述
 * @param {number} code - WMO天气代码
 * @returns {string} 中文天气描述
 */
function getWeatherDescription(code) {
  const weatherCodes = {
    0: '晴朗',
    1: '主要晴朗',
    2: '部分多云',
    3: '阴天',
    45: '雾',
    48: '雾凇',
    51: '毛毛雨',
    53: '中度毛毛雨',
    55: '大毛毛雨',
    61: '小雨',
    63: '中雨',
    65: '大雨',
    66: '冻雨',
    67: '大冻雨',
    71: '小雪',
    73: '中雪',
    75: '大雪',
    77: '雪粒',
    80: '小阵雨',
    81: '中阵雨',
    82: '大阵雨',
    85: '小阵雪',
    86: '大阵雪',
    95: '雷暴',
    96: '雷暴伴冰雹',
    99: '强雷暴伴冰雹',
  };

  return weatherCodes[code] || '未知天气';
}

export { getWeatherForecast };

package.json

{
  "name": "mcp-weather-server",
  "version": "1.0.0",
  "description": "MCP server for weather forecast",
  "type": "module",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js"
  },
  "keywords": ["mcp", "weather", "forecast"],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.4"
  }
}

readme.md

# MCP 天气预报服务器

这是一个基于MCP(Model Context Protocol)协议的天气预报服务器,可以获取指定城市的天气预报信息。

## 功能特性

- 支持全球城市天气预报查询
- 支持1-7天的天气预报
- 提供详细的天气信息,包括温度、降水、风速等
- 使用免费的Open-Meteo API,无需API密钥

## 安装

1. 克隆或下载此项目
2. 安装依赖:

```bash
npm install
```

## 使用方法

### 启动服务器

```bash
npm start
```

### 在MCP客户端中配置

在你的MCP客户端配置文件中添加以下配置:

```json
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["c:\\work\\ai\\mcp\\src\\index.js"]
    }
  }
}
```

### 可用工具

#### get_weather

获取指定城市的天气预报信息。

**参数:**
- `city` (必需): 城市名称,例如:北京、上海、广州、New York、Tokyo等
- `days` (可选): 预报天数,默认为3天,范围1-7天

**示例请求:**
```json
{
  "name": "get_weather",
  "arguments": {
    "city": "北京",
    "days": 5
  }
}
```

**示例响应:**
```json
{
  "location": {
    "name": "Beijing",
    "country": "China",
    "province": "Beijing",
    "coordinates": {
      "latitude": 39.9042,
      "longitude": 116.4074
    }
  },
  "forecast": [
    {
      "date": "2026-03-10",
      "weather": "晴朗",
      "temperature": {
        "max": 15,
        "min": 3
      },
      "precipitation": 0,
      "windSpeed": 12
    }
  ]
}
```

## 项目结构

```
mcp-weather-server/
├── package.json          # 项目配置文件
├── src/
│   ├── index.js          # MCP服务器主文件
│   └── weather.js        # 天气预报功能实现
└── README.md            # 项目说明文档
```

## 技术栈

- Node.js
- @modelcontextprotocol/sdk - MCP SDK
- Open-Meteo API - 免费的天气API
- Open-Meteo Geocoding API - 地理编码API

## API说明

本项目使用以下免费API:

1. **Open-Meteo Geocoding API**
   - 用于将城市名称转换为经纬度坐标
   - 文档: https://open-meteo.com/en/docs/geocoding-api

2. **Open-Meteo Weather API**
   - 用于获取天气预报数据
   - 文档: https://open-meteo.com/en/docs

## 注意事项

- 使用的是免费API,有请求频率限制
- 需要网络连接才能获取天气数据
- 支持中文和英文城市名称

## 开发

### 开发模式

```bash
npm run dev
```

### 添加新工具

1. 在 `src/index.js` 中的 `ListToolsRequestSchema` 处理器中添加工具定义
2. 在 `CallToolRequestSchema` 处理器中添加工具实现逻辑
3. 如需要,在 `src/` 目录下创建新的功能模块

## 许可证

MIT License

TRAE_SOLO_INTEGRATION.md(MCP服务添加到Trae中)

# TRAE SOLO 集成指南

本文档详细说明如何将天气预报MCP服务器集成到TRAE SOLO中。

## 什么是TRAE SOLO?

TRAE SOLO是一个支持MCP协议的AI开发平台,可以轻松集成各种MCP服务器来扩展AI助手的能力。通过MCP协议,TRAE SOLO可以调用外部工具和服务。

## 集成方式

TRAE SOLO提供了两种配置MCP服务器的方式:

1. **从MCP市场直接添加** - 适合使用预置的MCP服务器
2. **手动配置MCP Server** - 适合自定义MCP服务器(推荐用于本项目)

## 方法一:手动配置MCP Server(推荐)

### 步骤1:准备MCP服务器

确保你的天气预报MCP服务器已经准备就绪:

```bash
cd c:\work\ai\mcp
npm install
npm start
```

### 步骤2:在TRAE SOLO中添加MCP服务器

1. 打开TRAE SOLO IDE
2. 点击左侧边栏的"MCP"图标或进入设置
3. 点击"添加MCP Server"按钮
4. 选择"手动配置"选项

### 步骤3:配置MCP服务器

在配置界面中,输入以下配置信息:

**服务器名称:** `weather-server`

**命令:** `node`

**参数:** `["c:\\work\\ai\\mcp\\src\\index.js"]`

**完整JSON配置示例:**

```json
{
  "name": "weather-server",
  "command": "node",
  "args": ["c:\\work\\ai\\mcp\\src\\index.js"],
  "env": {}
}
```

### 步骤4:保存并测试

1. 点击"保存"或"确认"按钮
2. TRAE SOLO会自动启动MCP服务器
3. 查看服务器状态,确保显示"已连接"或"运行中"

## 方法二:通过配置文件集成

### 创建TRAE配置文件

在TRAE SOLO项目根目录创建 `.trae/mcp.json` 文件:

```json
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["c:\\work\\ai\\mcp\\src\\index.js"],
      "env": {}
    }
  }
}
```

### 重启TRAE SOLO

保存配置文件后,重启TRAE SOLO以加载新的MCP服务器配置。

## 使用天气预报功能

集成完成后,你可以在TRAE SOLO中使用天气预报功能:

### 示例对话

**用户:** 查询北京未来3天的天气预报

**TRAE SOLO:** 会自动调用weather MCP服务器的get_weather工具,返回详细的天气预报信息。

### 可用工具

- **get_weather**: 获取指定城市的天气预报
  - 参数:
    - `city` (必需): 城市名称,如"北京"、"上海"、"New York"
    - `days` (可选): 预报天数,默认3天,范围1-7天

## 验证集成

### 方法1:通过TRAE SOLO界面验证

1. 在TRAE SOLO的聊天界面输入:"查询北京的天气预报"
2. 观察是否正确调用weather服务器并返回结果

### 方法2:通过日志验证

1. 查看TRAE SOLO的控制台输出
2. 确认看到类似以下的日志:
   ```
   Weather MCP Server running on stdio
   ```

### 方法3:通过测试脚本验证

运行项目中的测试脚本:

```bash
cd c:\work\ai\mcp
node test.js
```

## 故障排除

### 问题1:MCP服务器无法启动

**解决方案:**
- 检查Node.js是否已安装:`node --version`
- 确认依赖已安装:`npm install`
- 检查文件路径是否正确

### 问题2:连接超时

**解决方案:**
- 确保MCP服务器正在运行
- 检查防火墙设置
- 验证配置文件中的路径是否正确

### 问题3:工具调用失败

**解决方案:**
- 检查网络连接(天气API需要网络)
- 验证城市名称是否正确
- 查看MCP服务器的错误日志

### 问题4:返回的天气数据不准确

**解决方案:**
- 确认城市名称拼写正确
- 尝试使用英文城市名称
- 检查Open-Meteo API服务状态

## 高级配置

### 环境变量配置

如果需要配置环境变量,可以在配置文件中添加:

```json
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["c:\\work\\ai\\mcp\\src\\index.js"],
      "env": {
        "NODE_ENV": "production",
        "LOG_LEVEL": "debug"
      }
    }
  }
}
```

### 工作目录配置

指定MCP服务器的工作目录:

```json
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["src\\index.js"],
      "cwd": "c:\\work\\ai\\mcp",
      "env": {}
    }
  }
}
```

## 性能优化

### 1. 启用缓存

在 `src/weather.js` 中添加缓存逻辑,减少API调用:

```javascript
const cache = new Map();
const CACHE_TTL = 3600000; // 1小时

async function getWeatherForecast(city, days = 3) {
  const cacheKey = `${city}-${days}`;
  const cached = cache.get(cacheKey);
  
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }
  
  // ... 原有的API调用逻辑
  
  cache.set(cacheKey, {
    data: result,
    timestamp: Date.now()
  });
  
  return result;
}
```

### 2. 批量查询优化

如果需要查询多个城市的天气,可以实现批量查询功能。

## 安全建议

1. **API密钥保护**:虽然本项目使用免费API,但如果将来使用需要密钥的API,请妥善保管密钥
2. **输入验证**:验证用户输入的城市名称,防止注入攻击
3. **错误处理**:妥善处理API错误,避免泄露敏感信息
4. **访问控制**:如果部署到生产环境,考虑添加访问控制

## 扩展功能

### 添加更多天气工具

你可以扩展MCP服务器,添加更多天气相关工具:

```javascript
// 在 src/index.js 中添加新工具
{
  name: 'get_air_quality',
  description: '获取指定城市的空气质量指数',
  inputSchema: {
    type: 'object',
    properties: {
      city: {
        type: 'string',
        description: '城市名称'
      }
    },
    required: ['city']
  }
}
```

### 集成其他数据源

除了Open-Meteo,你还可以集成其他天气API:
- WeatherAPI
- OpenWeatherMap
- 和风天气

## 相关资源

- [MCP协议文档](https://modelcontextprotocol.io/)
- [TRAE SOLO官方文档](https://docs.trae.ai/)
- [Open-Meteo API文档](https://open-meteo.com/en/docs)

## 总结

通过以上步骤,你已经成功将天气预报MCP服务器集成到TRAE SOLO中。现在你可以在TRAE SOLO中使用强大的天气预报功能,提升AI助手的能力。

如果遇到任何问题,请参考故障排除部分或查看项目的README.md文档。

weather-client.js(客户端测试)
这个js是用户测试mcp服务用的,当IDE加载了 mpc-weather 服务后,你在聊天窗口查询天气时,IDE会 直接调用MCP工具, 为了理解整个MCP的调用过程是必须要了解的内容;

1、启动服务进程 构造函数

//- pipe :创建管道,用于JSON-RPC通信
//- inherit :直接输出日志到终端
this.server.spawn(command,args,stdio:['pipe','pipe','inherit']);

2、监听服务器输出

// 每行都是独立的JSON-RPC消息
// 通过id匹配请求和响应
this.rl.on('line', (line) => {...});

3、发送请求

sendRequest(request) {
  return new Promise((resolve, reject) => {
    this.pendingRequests.set(request.id, { resolve, reject });
    this.server.stdin.write(JSON.stringify(request) + '\n');
  });
}

4、MCP握手

async initialize() {
  // 步骤1:发送initialize请求
  const result = await this.sendRequest({
    jsonrpc: '2.0',
    id: this.requestId++,
    method: 'initialize',
    params: { /* ... */ }
  });

  // 步骤2:发送initialized通知
  this.sendNotification({
    jsonrpc: '2.0',
    method: 'initialized'
  });

  return result;
}

总结

客户端                    服务器
  │                        │
  │  1. spawn()            │
  ├───────────────────────>│ 启动进程
  │                        │
  │  2. initialize请求     │
  ├───────────────────────>│ 处理初始化
  │                        │
  │  3. initialize响应     │
  │<───────────────────────┤
  │                        │
  │  4. initialized通知    │
  ├───────────────────────>│ 握手完成
  │                        │
  │  5. tools/call请求     │
  ├───────────────────────>│ 执行工具
  │                        │
  │  6. tools/call响应     │
  │<───────────────────────│ 返回结果
  │                        │
  │  7. 关闭连接            │
  ├───────────────────────>│ 进程退出

#!/usr/bin/env node

/**
 * Weather MCP Client
 * 用于测试Weather MCP服务器的客户端程序
 * 可以直接从命令行查询天气预报
 */

import { spawn } from 'child_process';
import { createInterface } from 'readline';

/**
 * MCP客户端类
 * 负责与MCP服务器进行通信
 */
class MCPClient {
  constructor(command, args) {
    // 启动MCP服务器进程
    // stdio配置:
    //   - 'pipe': stdin, stdout - 用于JSON-RPC通信
    //   - 'inherit': stderr - 继承父进程的stderr,用于日志输出
    this.server = spawn(command, args, {
      stdio: ['pipe', 'pipe', 'inherit']
    });

    // 创建readline接口读取服务器输出
    this.rl = createInterface({
      input: this.server.stdout,
      crlfDelay: Infinity
    });

    // 使用时间戳作为requestId的基础,避免ID冲突
    this.requestId = Date.now();
    this.pendingRequests = new Map();

    // 监听服务器输出的每一行
    this.rl.on('line', (line) => {
      try {
        const response = JSON.parse(line);
        const { id, result, error } = response;
        
        // 查找对应的pending请求并处理响应
        if (this.pendingRequests.has(id)) {
          const { resolve, reject } = this.pendingRequests.get(id);
          this.pendingRequests.delete(id);
          
          if (error) {
            reject(new Error(error.message));
          } else {
            resolve(result);
          }
        }
      } catch (e) {
        console.error('Failed to parse response:', e);
      }
    });

    // 监听服务器错误
    this.server.on('error', (error) => {
      console.error('Server error:', error);
    });

    // 监听服务器退出
    this.server.on('exit', (code) => {
      console.log(`Server exited with code ${code}`);
    });
  }

  /**
   * 初始化MCP连接
   * @returns {Promise<Object>} 初始化结果
   */
  async initialize() {
    // 发送initialize请求并等待响应
    const result = await this.sendRequest({
      jsonrpc: '2.0',
      id: this.requestId++,
      method: 'initialize',
      params: {
        protocolVersion: '2024-11-05',
        capabilities: {},
        clientInfo: {
          name: 'weather-client',
          version: '1.0.0'
        }
      }
    });

    // 发送initialized通知,完成握手流程
    this.sendNotification({
      jsonrpc: '2.0',
      method: 'initialized'
    });

    return result;
  }

  /**
   * 调用MCP工具
   * @param {string} name - 工具名称
   * @param {Object} args - 工具参数
   * @returns {Promise<Object>} 工具执行结果
   */
  async callTool(name, args) {
    return this.sendRequest({
      jsonrpc: '2.0',
      id: this.requestId++,
      method: 'tools/call',
      params: {
        name,
        arguments: args
      }
    });
  }

  /**
   * 发送MCP请求
   * @param {Object} request - 请求对象
   * @returns {Promise<Object>} 响应结果
   */
  sendRequest(request) {
    return new Promise((resolve, reject) => {
      this.pendingRequests.set(request.id, { resolve, reject });
      this.server.stdin.write(JSON.stringify(request) + '\n');
    });
  }

  /**
   * 发送MCP通知(无需等待响应)
   * @param {Object} notification - 通知对象
   */
  sendNotification(notification) {
    this.server.stdin.write(JSON.stringify(notification) + '\n');
  }

  /**
   * 关闭MCP客户端
   */
  close() {
    this.server.kill();
  }
}

/**
 * 主函数
 * 解析命令行参数并执行天气查询
 */
async function main() {
  // 解析命令行参数
  const args = process.argv.slice(2);
  
  // 检查参数
  if (args.length === 0) {
    console.log('用法: node weather-client.js <城市名> [天数]');
    console.log('示例:');
    console.log('  node weather-client.js 北京');
    console.log('  node weather-client.js 上海 5');
    console.log('  node weather-client.js Hefei 3');
    process.exit(1);
  }

  const city = args[0];
  const days = parseInt(args[1]) || 3;

  console.log('连接到Weather MCP服务器...\n');

  // 创建MCP客户端实例
  const client = new MCPClient('node', ['src/index.js']);

  try {
    // 初始化连接
    await client.initialize();
    console.log('✓ 已连接到MCP服务器\n');

    console.log(`正在查询 ${city} 未来${days}天的天气预报...\n`);

    // 调用天气查询工具
    const result = await client.callTool('get_weather', {
      city: city,
      days: days
    });

    // 解析并显示天气数据
    if (result.content && result.content[0]) {
      const weatherData = JSON.parse(result.content[0].text);

      console.log('='.repeat(60));
      console.log('📍 位置信息');
      console.log('='.repeat(60));
      console.log(`城市: ${weatherData.location.name}`);
      console.log(`国家: ${weatherData.location.country}`);
      console.log(`省份: ${weatherData.location.province || ''}`);
      console.log(`坐标: ${weatherData.location.coordinates.latitude}, ${weatherData.location.coordinates.longitude}`);

      console.log('\n' + '='.repeat(60));
      console.log(`🌤️  未来${days}天天气预报`);
      console.log('='.repeat(60));

      weatherData.forecast.forEach((day, index) => {
        console.log(`\n第${index + 1}天 (${day.date}):`);
        console.log(`  天气: ${day.weather}`);
        console.log(`  温度: ${day.temperature.min}°C - ${day.temperature.max}°C`);
        console.log(`  降水: ${day.precipitation}mm`);
        console.log(`  风速: ${day.windSpeed}km/h`);
      });

      console.log('\n' + '='.repeat(60));
      console.log('✓ 查询完成!');
      console.log('='.repeat(60));
    }
  } catch (error) {
    console.error('错误:', error.message);
  } finally {
    // 确保关闭客户端连接
    client.close();
  }
}

main().catch(console.error);

手动添加到SOLO mcp服务中

查询结果


 

附注:
源码地址:https://gitcode.com/SUI517/gitcode_knowledge

其中包含 MCP resource 类型交互的实例:mcp-mysql服务

下面是怎么在IDE ->LLM 中使用MySQL MCP服务

MySQL MCP的两种交互方式
方式1:自然语言对话(推荐)
您可以直接用自然语言描述需求,我会自动理解并调用合适的功能:

示例:

- "查看数据库中有哪些表" → 我自动访问 db://tables
- "显示users表的结构" → 我自动访问 db://table/users
- "查询所有活跃用户" → 我调用 execute_query 工具
- "统计订单数量" → 我调用 execute_query 工具
### 方式2:直接使用URI
您也可以直接使用Resource URI格式:

示例:

- db://tables → 获取表列表
- db://table/users → 获取users表结构
- db://data/users → 获取users表数据

🎯 天气MCP的Resource实现建议

有兴趣的可以让LLM帮你实现
基于您的天气预报MCP,可以添加以下Resource:

当前天气 实时天气数据

weather://current/{city}

天气预报 N天预报数据

weather://forecast/{city}/{days}days

历史天气 历史天气记录

weather://historical/{city}/{date}

天气预警 天气预警信息

weather://alert/{city}

缓存状态 缓存统计信息

weather://cache/status

我与Trae的聊天

你是怎么判断需要调用 tools prompt还是resource的?

好问题!让我详细解释我是如何判断调用 tools、prompts 还是 resources 的:

## 🧠 我的判断逻辑

### 第一步:分析用户意图

| 用户意图 | 推荐类型 | 示例 |
|---------|-----------|------|
| **浏览/查看数据** | Resources | "查看users表的数据" |
| **执行自定义查询** | Tools | "执行:SELECT * FROM users WHERE age > 25" |
| **常用快捷操作** | Prompts | "查询活跃用户" |

### 第二步:判断查询复杂度

| 复杂度 | 推荐类型 | 示例 |
|---------|-----------|------|
| **简单常用** | Prompts | "统计订单数量" |
| **复杂自定义** | Tools | "SELECT u.*, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id" |
| **浏览查看** | Resources | "db://data/users" |

### 第三步:考虑参数需求

| 参数类型 | 推荐类型 | 示例 |
|---------|-----------|------|
| **无参数/固定参数** | Prompts | "查询活跃用户" |
| **动态参数** | Tools | "查询年龄大于25的用户" |
| **URI格式** | Resources | "db://table/users" |

## 📊 详细判断流程图

```
用户输入
    ↓
分析意图
    ↓
┌─────────────┬─────────────┬─────────────┐
│             │             │             │
浏览数据    执行操作     常用查询
│             │             │             │
↓             ↓             ↓             ↓
Resources     Tools        Prompts
```

## 🎯 具体判断规则

### ✅ 优先使用 Prompts 的情况

**条件1:常用操作**
```
"查询活跃用户" → query_active_users
"统计订单数量" → count_orders
"查看产品库存" → product_inventory
```

**条件2:无参数或简单参数**
```
"查询活跃用户" → 无参数,直接调用
"查看alice的订单汇总" → 单个参数,调用 prompt
```

**条件3:明确的功能名称**
```
用户明确说出功能名称,优先匹配Prompt
```

### ✅ 使用 Tools 的情况

**条件1:自定义SQL**
```
"执行:SELECT * FROM users WHERE age > ?"
"执行SQL:SELECT u.username, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id"
```

**条件2:复杂查询**
```
多表JOIN、子查询、聚合函数等复杂操作
```

**条件3:参数化查询**
```
"查询年龄大于25的用户" → 需要参数化,使用Tools
"查询状态为active且年龄大于20的用户" → 多个条件
```

### ✅ 使用 Resources 的情况

**条件1:浏览数据**
```
"查看users表的数据"
"显示orders表的结构"
"数据库中有哪些表"
```

**条件2:URI格式**
```
"db://data/users"
"db://table/orders"
```

**条件3:探索性查询**
```
"看看数据库里有什么"
"浏览一下users表"
```

## 💡 实际判断示例

### 示例1:用户说"查询活跃用户"

**我的分析**:
1. ✅ 意图:查询数据
2. ✅ 复杂度:简单
3. ✅ 参数:无参数
4. ✅ 常用操作:有对应的Prompt

**判断结果**:使用 `query_active_users` Prompt

---

### 示例2:用户说"执行SQL:SELECT * FROM users WHERE age > 25"

**我的分析**:
1. ✅ 意图:执行操作
2. ✅ 复杂度:自定义SQL
3. ✅ 参数:动态参数(25)
4. ✅ 明确指定SQL

**判断结果**:使用 `execute_query` Tool

---

### 示例3:用户说"查看users表的数据"

**我的分析**:
1. ✅ 意图:浏览数据
2. ✅ 复杂度:简单查看
3. ✅ 参数:URI格式(db://data/users)

**判断结果**:使用 `db://data/users` Resource

---

### 示例4:用户说"统计订单数量"

**我的分析**:
1. ✅ 意图:统计数据
2. ✅ 复杂度:简单
3. ✅ 参数:无参数
4. ✅ 常用操作:有对应的Prompt

**判断结果**:使用 `count_orders` Prompt

---

### 示例5:用户说"查询年龄大于25的用户"

**我的分析**:
1. ✅ 意图:查询数据
2. ✅ 复杂度:中等(需要参数)
3. ✅ 参数:动态参数(25)
4. ✅ 无对应Prompt

**判断结果**:使用 `execute_query` Tool

## 🎯 判断优先级

```
1. Prompts(最高优先级)
   ↓ 有匹配的Prompt?
   ↓ 是 → 使用Prompt
   ↓ 否 → 继续

2. Resources
   ↓ 是浏览/查看数据?
   ↓ 是 → 使用Resource
   ↓ 否 → 继续

3. Tools(默认)
   ↓ 使用execute_query
```

## 📝 总结

| 类型 | 使用场景 | 判断依据 |
|------|---------|---------|
| **Prompts** | 常用快捷操作 | 有对应Prompt + 简单参数 |
| **Tools** | 自定义查询 | 明确SQL或复杂查询 |
| **Resources** | 浏览数据 | 查看表数据/结构 |

## 💡 最佳实践

**对用户来说**:
- ✅ 用自然语言描述需求
- ✅ 不需要记住具体功能名称
- ✅ 不需要知道URI格式
- ✅ 不需要写SQL语句

**对我来说**:
- ✅ 分析用户意图
- ✅ 判断查询复杂度
- ✅ 选择最合适的方式
- ✅ 自动调用对应功能

所以您只需要用自然语言表达需求,我会自动判断并选择最合适的调用方式!

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐