当前位置: 首页 > news >正文

openEuler-portal-mcp开发者指南:如何扩展自定义查询工具

openEuler-portal-mcp开发者指南:如何扩展自定义查询工具

【免费下载链接】openEuler-portal-mcpThe repository of openEuler portal MCP Server项目地址: https://gitcode.com/openeuler/openEuler-portal-mcp

前往项目官网免费下载:https://ar.openeuler.org/ar/

openEuler-portal-mcp是一个基于Model Context Protocol(MCP)的开源项目,为Claude等AI工具提供openEuler官网相关信息的查询能力。本文将详细介绍如何扩展自定义查询工具,帮助开发者快速上手并为项目添加新的功能模块。

📋 项目架构概述

openEuler-portal-mcp采用分层架构设计,包含以下几个核心模块:

  • MCP协议层:使用@modelcontextprotocol/sdk实现标准MCP协议
  • 传输层:支持Stdio本地连接和SSE远程连接两种模式
  • 核心层:工具注册中心和请求路由分发
  • 工具层:包含21个查询工具和操作工具
  • 服务层:提供共享服务如文档版本缓存
  • 辅助层:格式化工具和智能推荐系统

🛠️ 现有工具概览

项目目前提供21个工具,分为两大类:

查询类工具(19个)

  • get_sig_info:SIG信息查询
  • get_cve_info:CVE安全公告查询
  • get_docs_info:文档内容检索
  • get_package_info:软件包信息查询
  • get_issue_info:社区Issue查询
  • get_pull_request_info:社区PR查询

操作类工具(2个)

  • execute_user_operation:用户操作执行(需OPENEULER_TOKEN)
  • execute_forum_operation:论坛用户操作执行(需FORUM_TOKEN)

🚀 创建新工具的完整步骤

步骤1:创建工具文件

src/tools/目录下创建新的工具文件,例如src/tools/getMyCustomTool.js

import { addField, addFields, addIndentedField, addIndentedFields, truncate, stripHtml } from "../utils/formatHelpers.js"; import { appendRecommendation } from "../utils/toolRecommendations.js"; // 数据源URL定义 const MY_API_URL = "https://api.example.com/data"; // 缓存机制(可选) let cachedData = null; let cacheExpiry = 0; const CACHE_DURATION = 15 * 60 * 1000; // 15分钟 // 主工具函数 export async function getMyCustomTool(params) { const { query_type = "list", keyword } = params; try { let result = ""; // 根据查询类型处理不同逻辑 switch (query_type) { case "list": result = await fetchListData(keyword); break; case "detail": result = await fetchDetailData(keyword); break; default: return "请指定有效的查询类型:list(列表)或 detail(详情)"; } // 添加智能推荐 result = appendRecommendation(result, "get_my_custom_tool"); return result; } catch (error) { console.error("自定义工具执行错误:", error); return `查询失败: ${error.message}`; } } // 辅助函数:获取列表数据 async function fetchListData(keyword) { const now = Date.now(); // 检查缓存 if (cachedData && now < cacheExpiry) { return formatListData(cachedData); } // 发起API请求 const response = await fetch(MY_API_URL, { signal: AbortSignal.timeout(15000) // 15秒超时 }); if (!response.ok) { throw new Error(`API请求失败: ${response.status}`); } const data = await response.json(); // 更新缓存 cachedData = data; cacheExpiry = now + CACHE_DURATION; return formatListData(data); } // 辅助函数:格式化列表数据 function formatListData(data) { let output = "## 自定义数据列表\n\n"; if (data.length === 0) { return "未找到相关数据。"; } data.forEach((item, index) => { output += `### ${index + 1}. ${item.name}\n`; output += addField("描述", item.description || "暂无描述"); output += addField("状态", item.status || "未知"); output += addField("更新时间", item.updated_at || "未知"); output += "\n"; }); return output; } // 工具定义(必须导出) export const toolDefinition = { name: "get_my_custom_tool", description: "查询自定义数据信息,支持列表和详情查询", inputSchema: { type: "object", properties: { query_type: { type: "string", description: "查询类型:list(列表查询)或 detail(详情查询)", enum: ["list", "detail"], default: "list" }, keyword: { type: "string", description: "搜索关键词(可选)" } } } };

步骤2:注册工具到主入口

src/index.js中添加新工具的导入和注册:

// 在导入部分添加 import { getMyCustomTool, toolDefinition as getMyCustomToolDef } from "./tools/getMyCustomTool.js"; // 在工具注册部分添加 const tools = [ // ... 现有工具 getMyCustomToolDef, ]; const toolHandlers = { // ... 现有处理器 get_my_custom_tool: getMyCustomTool, };

步骤3:配置智能推荐

src/utils/toolRecommendations.js中添加新工具的推荐配置:

// 在toolRecommendations对象中添加 const toolRecommendations = { // ... 现有配置 get_my_custom_tool: { related: ["get_sig_info", "get_organization_info", "get_search_info"], scenarios: [ "用户想查询自定义数据时", "需要了解特定信息时", "搜索相关数据时" ] }, // 在其他工具的推荐中添加新工具 get_sig_info: { related: ["get_my_custom_tool", "get_organization_info", "get_meeting_info", "get_development_info"], // ... } };

步骤4:编写单元测试

tests/目录下创建测试文件getMyCustomTool.test.js

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { getMyCustomTool } from "../src/tools/getMyCustomTool.js"; describe("getMyCustomTool", () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it("应该返回列表数据", async () => { const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("自定义数据列表"); }); it("应该处理API错误", async () => { global.fetch = vi.fn(() => Promise.reject(new Error("网络错误"))); const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("查询失败"); }); });

🔧 工具开发最佳实践

1. 缓存策略设计

项目采用三级缓存机制,建议根据数据特性选择合适的缓存策略:

// 共享缓存(跨工具使用) // 位置:src/services/docsVersionService.js // 适用场景:多个工具共享的数据,如文档版本信息 // 本地缓存(工具内部使用) // 适用场景:单个工具专用的数据 const CACHE_DURATION = 15 * 60 * 1000; // 15分钟 let cachedData = null; let cacheExpiry = 0; // 长期缓存(24小时) // 适用场景:用户信息等变化频率低的数据 const LONG_CACHE_DURATION = 24 * 60 * 60 * 1000;

2. 错误处理规范

遵循统一的错误处理模式:

try { // 业务逻辑 const response = await fetch(url, { signal: AbortSignal.timeout(15000) // 15秒超时 }); if (!response.ok) { throw new Error(`API请求失败: ${response.status}`); } // 数据处理 const data = await response.json(); return formatData(data); } catch (error) { console.error("工具执行错误:", error); // 返回用户友好的错误信息 if (error.name === "TimeoutError") { return "请求超时,请稍后重试。"; } return `查询失败: ${error.message}`; }

3. 输出格式化标准

使用项目提供的格式化工具确保输出一致性:

import { addField, addFields, addIndentedField, addIndentedFields, truncate, stripHtml } from "../utils/formatHelpers.js"; function formatData(data) { let output = "## 数据标题\n\n"; // 添加字段 output += addField("名称", data.name); output += addField("描述", data.description); // 添加多行字段 output += addIndentedField("详细信息", data.details); // 处理HTML内容 const cleanContent = stripHtml(data.content); return output; }

4. 智能推荐集成

每个工具都应集成智能推荐系统:

import { appendRecommendation } from "../utils/toolRecommendations.js"; async function myTool(params) { // ... 业务逻辑 let result = formatData(data); // 添加推荐 result = appendRecommendation(result, "my_tool_name"); return result; }

📁 项目文件结构详解

了解项目结构有助于更好地扩展功能:

openEuler-portal-mcp/ ├── src/ │ ├── index.js # 主入口文件,工具注册中心 │ ├── services/ │ │ └── docsVersionService.js # 共享服务(文档版本缓存) │ ├── tools/ # 工具函数目录(21个工具) │ │ ├── getSigInfo.js # SIG信息查询 │ │ ├── getCveInfo.js # CVE安全公告查询 │ │ ├── getDocsInfo.js # 文档内容检索 │ │ ├── getDocsSearchContent.js # 文档内容搜索 │ │ ├── getPackageInfo.js # 软件包信息查询 │ │ ├── getIssueInfo.js # 社区Issue查询 │ │ ├── getPullRequestInfo.js # 社区PR查询 │ │ ├── executeUserOperation.js # 用户操作执行 │ │ └── executeForumOperation.js # 论坛用户操作执行 │ └── utils/ │ ├── formatHelpers.js # 输出格式化辅助函数 │ └── toolRecommendations.js # 智能推荐系统配置 ├── tests/ # 测试文件目录 │ ├── getSigInfo.test.js # SIG查询测试 │ ├── getCveInfo.test.js # CVE查询测试 │ └── ... # 其他工具测试 ├── docs/ │ ├── ARCHITECTURE.md # 项目架构文档 │ └── TOOL_SELECTION.md # 工具选择说明 └── package.json # 项目配置

🔄 工具间的协作模式

1. 数据共享机制

通过服务层实现工具间的数据共享:

// 在services目录下创建共享服务 // src/services/mySharedService.js export class MySharedService { static cache = new Map(); static async getSharedData() { const cacheKey = "shared_data"; const cached = this.cache.get(cacheKey); if (cached && Date.now() < cached.expiry) { return cached.data; } // 获取数据 const data = await fetchData(); // 更新缓存(15分钟) this.cache.set(cacheKey, { data, expiry: Date.now() + 15 * 60 * 1000 }); return data; } } // 在工具中使用共享服务 import { MySharedService } from "../services/mySharedService.js"; async function myTool() { const sharedData = await MySharedService.getSharedData(); // 使用共享数据 }

2. 推荐链设计

工具之间形成推荐链,引导用户深入查询:

// 在toolRecommendations.js中配置推荐关系 const toolRecommendations = { get_sig_info: { related: ["get_meeting_info", "get_organization_info", "get_development_info"], scenarios: [ "查询SIG信息后,可能需要查看该SIG的会议安排", "了解SIG后,可能需要查看社区组织架构", "对SIG感兴趣,可能需要查看相关开发活动" ] }, get_meeting_info: { related: ["get_sig_info", "execute_user_operation", "get_forum_info"], scenarios: [ "查看会议后,可能需要了解相关SIG信息", "如需参加会议,可能需要执行用户操作", "会议讨论可能在论坛有相关帖子" ] } };

🧪 测试与验证

1. 单元测试编写

为每个工具编写全面的单元测试:

// tests/getMyCustomTool.test.js import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { getMyCustomTool } from "../src/tools/getMyCustomTool.js"; describe("getMyCustomTool工具测试", () => { beforeEach(() => { vi.useFakeTimers(); // 模拟fetch global.fetch = vi.fn(); }); afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); }); describe("列表查询", () => { it("应该成功返回数据列表", async () => { const mockData = [{ name: "测试数据", status: "active" }]; global.fetch.mockResolvedValue({ ok: true, json: () => Promise.resolve(mockData) }); const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("自定义数据列表"); expect(result).toContain("测试数据"); }); it("应该处理空数据情况", async () => { global.fetch.mockResolvedValue({ ok: true, json: () => Promise.resolve([]) }); const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("未找到相关数据"); }); }); describe("错误处理", () => { it("应该处理网络错误", async () => { global.fetch.mockRejectedValue(new Error("网络错误")); const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("查询失败"); }); it("应该处理API错误响应", async () => { global.fetch.mockResolvedValue({ ok: false, status: 500 }); const result = await getMyCustomTool({ query_type: "list" }); expect(result).toContain("API请求失败"); }); }); });

2. 集成测试

验证工具在MCP环境下的集成:

# 启动开发服务器 npm start # 测试工具调用 curl -X POST http://localhost:3000/message \ -H "Content-Type: application/json" \ -d '{ "tool": "get_my_custom_tool", "params": { "query_type": "list" } }'

📈 性能优化建议

1. 缓存优化策略

// 根据数据特性选择缓存时间 const CACHE_CONFIG = { STATIC_DATA: 60 * 60 * 1000, // 1小时(静态数据) DYNAMIC_DATA: 15 * 60 * 1000, // 15分钟(动态数据) USER_DATA: 24 * 60 * 60 * 1000, // 24小时(用户数据) }; // 实现智能缓存失效 class SmartCache { constructor(duration) { this.cache = new Map(); this.duration = duration; } get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.data; } set(key, data) { this.cache.set(key, { data, expiry: Date.now() + this.duration }); } // 批量清理过期缓存 cleanup() { const now = Date.now(); for (const [key, item] of this.cache.entries()) { if (now > item.expiry) { this.cache.delete(key); } } } }

2. 请求优化

// 使用AbortSignal控制超时 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 15000); try { const response = await fetch(url, { signal: controller.signal, headers: { "User-Agent": "openEuler-portal-mcp/1.0", "Accept": "application/json" } }); clearTimeout(timeoutId); // 处理响应 } catch (error) { clearTimeout(timeoutId); if (error.name === "AbortError") { throw new Error("请求超时"); } throw error; }

🔗 工具间的依赖关系

了解现有工具的依赖关系有助于设计新工具:

工具名称依赖的服务依赖的工具输出格式
get_docs_infodocsVersionServiceget_docs_search_contentMarkdown文档
get_docs_search_contentdocsVersionServiceget_docs_info结构化搜索结果
execute_user_operationget_meeting_info操作结果+推荐
get_development_infoget_issue_info,get_pull_request_info开发活动报告

🎯 实际扩展案例

案例1:添加新的API查询工具

假设需要添加一个查询openEuler社区活动的新工具:

// src/tools/getCommunityEvents.js export async function getCommunityEvents(params) { const { event_type, date_range } = params; // 实现逻辑... } export const toolDefinition = { name: "get_community_events", description: "查询openEuler社区活动信息", inputSchema: { type: "object", properties: { event_type: { type: "string", description: "活动类型:meetup、webinar、hackathon", enum: ["meetup", "webinar", "hackathon"] }, date_range: { type: "string", description: "时间范围:upcoming、past、all" } } } };

案例2:扩展现有工具功能

在现有工具基础上添加新功能:

// 在getSigInfo.js中添加新功能 async function getSigStatistics(sigName, period) { // 添加SIG统计信息查询 const stats = await fetchSigStats(sigName, period); let output = `## ${sigName} SIG 统计信息(${period})\n\n`; output += addField("活跃成员数", stats.active_members); output += addField("PR数量", stats.pr_count); output += addField("Issue数量", stats.issue_count); output += addField("代码提交数", stats.commits); return output; }

📚 学习资源与参考

1. 核心参考文件

  • 项目架构文档:docs/ARCHITECTURE.md - 详细的项目架构说明
  • 工具选择机制:docs/TOOL_SELECTION.md - AI如何选择工具的机制
  • 主入口文件:src/index.js - 工具注册和MCP服务器配置

2. 最佳实践示例

  • 复杂工具示例:src/tools/getSigInfo.js - 包含缓存、模糊匹配的完整实现
  • API集成示例:src/tools/getCveInfo.js - 外部API调用和错误处理
  • 用户操作示例:src/tools/executeUserOperation.js - Token验证和用户操作

3. 实用工具模块

  • 格式化工具:src/utils/formatHelpers.js - 统一的输出格式化函数
  • 推荐系统:src/utils/toolRecommendations.js - 智能推荐配置

🚨 常见问题与解决方案

问题1:工具无法被AI识别

解决方案

  1. 确保在src/index.js中正确导入和注册工具
  2. 检查toolDefinitionname属性是否符合MCP命名规范
  3. 验证inputSchema定义是否正确

问题2:API调用失败

解决方案

  1. 添加适当的超时控制(15秒)
  2. 实现重试机制
  3. 提供友好的错误信息

问题3:缓存不生效

解决方案

  1. 检查缓存时间戳逻辑
  2. 确保缓存键的唯一性
  3. 验证缓存清理机制

问题4:推荐系统不工作

解决方案

  1. toolRecommendations.js中正确配置推荐关系
  2. 确保在工具函数中调用appendRecommendation
  3. 检查推荐场景描述是否清晰

🔮 未来扩展方向

1. 数据源扩展

  • 集成更多openEuler官方API
  • 支持第三方数据源
  • 添加离线数据支持

2. 功能增强

  • 实现更复杂的查询条件
  • 添加数据可视化输出
  • 支持批量操作

3. 性能优化

  • 实现增量更新
  • 添加压缩传输
  • 优化内存使用

4. 用户体验改进

  • 添加更多上下文感知
  • 实现个性化推荐
  • 支持多语言输出

💡 总结

扩展openEuler-portal-mcp的自定义查询工具是一个系统化的过程,需要遵循项目的架构规范和最佳实践。通过本文的指南,您可以:

  1. 快速创建新工具:按照标准模板创建工具文件
  2. 正确集成到系统:在主入口注册并配置推荐
  3. 确保代码质量:编写全面的单元测试
  4. 优化性能:合理使用缓存和错误处理
  5. 提供良好用户体验:集成智能推荐系统

记住,每个新工具都应该:

  • 遵循单一职责原则
  • 实现适当的错误处理
  • 集成缓存机制
  • 提供清晰的文档
  • 包含完整的测试用例

通过遵循这些指导原则,您可以为openEuler-portal-mcp项目贡献高质量的工具扩展,帮助更多开发者通过AI工具更好地访问openEuler社区资源。🎯

【免费下载链接】openEuler-portal-mcpThe repository of openEuler portal MCP Server项目地址: https://gitcode.com/openeuler/openEuler-portal-mcp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.jsqmd.com/news/1111260/

相关文章:

  • 利用AI快速构建pytest接口自动化测试框架:从零到一的最佳实践
  • Eggo在生产环境的实战应用:大规模Kubernetes集群部署经验分享
  • 深色主题适配指南:如何配置Kiran图标主题支持深色模式 [特殊字符]
  • conda-ecopkgs开发者手册:package.yml和supported-versions.yml配置详解
  • 新手必看:Kiran-wallpapers安装与切换的完整教程
  • 百度网盘直链解析终极指南:3分钟获取高速下载链接的完整教程
  • 终极指南:如何5分钟上手Anno 1800模组加载器,告别繁琐的游戏文件修改
  • AtomCode IDE插件深度体验:VS Code与JetBrains双平台对比
  • BMI270与STM32F334R8在运动追踪中的优化应用
  • UB系统硬件调试实战:使用ubctl进行设备状态监控的10个技巧
  • 为什么你用 GPT 总是跑题?可能是提示词没写对
  • IIM-42652与PIC18F45K22实现6DoF运动追踪系统
  • openEuler/cve-void高级技巧:如何处理复杂CVE补丁冲突与依赖分析的完整指南
  • 开源AI Agent生态盘点:2024年最值得关注的10个Agent项目
  • openEuler RISC-V SIG:多语言文档与国际化支持体系完整指南
  • utipmitool社区贡献指南:如何参与开源IPMI工具的开发与维护
  • 专业视频对比解决方案:5大核心技术架构提升画质分析效率
  • OpenHarmony dsoftbus安全机制:保障分布式通信的全方位防护
  • 终极指南:如何将Switch游戏画面无线投屏到电脑?SysDVR完整教程
  • cu-cockpit架构设计原理:深入了解轻量级运维平台实现
  • Kiran-cc-daemon安全权限控制:Polkit代理与DBus权限管理的完整实现
  • 10分钟掌握dde_autotest_euler:面向新手的测试用例编写实战
  • 终极揭秘:OpenHarmony dsoftbus核心组件与架构设计详解
  • AI4C未来展望:编译器优化的AI革命路线图
  • 股市学习心得-三星+SK海力士概念 核心公司
  • STM32与AD74413R构建高精度混合信号处理系统
  • conda-ecopkgs成功案例:科研机构和企业如何利用该项目加速开发
  • async-libfuse开发者指南:贡献代码前必须了解的CLA流程
  • hpcpilot网卡驱动配置:Mellanox网卡安装与优化的终极方案
  • 1bit量化技术RaBitQ:突破AI显存困境的实践指南