资讯动态

为 oh-my-openagent 添加内置 arXiv MCP:基于 createBuiltinMcps 三层 MCP 体系的完整改造指南

发布时间:2026/9/18 14:16:38 来源:尧图企业网站定制
为 oh-my-openagent 添加内置 arXiv MCP基于 createBuiltinMcps 三层 MCP 体系的完整改造指南【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent本篇技术指南围绕 oh-my-openagentOmO项目中新增一个内置远程 MCParXiv 论文检索的完整代码改造展开覆盖从新建arxiv.ts静态导出、扩展McpNameSchema枚举、在createBuiltinMcps工厂中注册、同步测试用例与模块文档的五个文件级改动。读者读完将掌握内置 MCP 的注册机制、disabled_mcps过滤语义、无鉴权 MCP 的标准实现范式以及如何在当前仓库中落地一次最小化、外科手术式的内置能力扩展。一、背景OmO 的三层 MCP 体系与内置远程 MCP在 oh-my-openagent 中MCP 服务器被组织为三层体系见 src/mcp 模块说明层级来源机制Tier 1内置src/mcp/即packages/omo-opencode/src/mcp/远程 HTTP MCP 本地 stdio MCP由createBuiltinMcps()统一创建Tier 2Claude Code.mcp.json${VAR}环境变量展开经claude-code-mcp-loader加载Tier 3技能内嵌SKILL.md YAML由SkillMcpManager管理stdio HTTP本文涉及的改造全部落在 Tier 1。当前createBuiltinMcps工厂见 index.ts注册了 4 个内置 MCP名称类型端点鉴权用途websearchremotemcp.exa.ai默认或mcp.tavily.comEXA_API_KEY可选网页搜索context7remotemcp.context7.com/mcpCONTEXT7_API_KEY可选库文档检索grep_appremotemcp.grep.app无GitHub 代码搜索lsplocalstdiopackages/lsp-tools-mcpCLI无代码诊断、定义跳转、符号等其中grep_app是所有无鉴权远程 MCP的标准范例——它不依赖环境变量、不需要配置工厂直接静态导出配置对象见 grep-app.ts。新增的 arXiv MCP 正是要复制这一模式将内置 MCP 数量从 4 提升到 5。二、新增src/mcp/arxiv.ts无鉴权静态导出范式第一步是新建src/mcp/arxiv.ts文件完整代码如下export const arxiv { type: remote as const, url: https://mcp.arxiv.org, enabled: true, oauth: false as const, }该对象的四个字段与RemoteMcpConfig类型定义于 index.ts逐一对应type: remote as const声明这是一个远程 HTTP MCP区别于lsp这类本地 stdio MCPLocalMcpConfigurlMCP 服务器端点地址enabled: true默认启用只有在disabled_mcps中显式列出时才被过滤oauth: false as const明确无 OAuth 流程。若未来需要鉴权可参考headers字段如context7的Authorization: Bearer头。⚠️ 合并前阻塞点文档明确指出https://mcp.arxiv.org是一个占位 URL真实端点需要在合并前验证。如果不存在官方托管的 arXiv MCP备选方案包括社区托管的 MCP 服务器或基于 arXiv REST APIexport.arxiv.org/api/query自建包装服务。这一不确定性是本次改造唯一的合并阻塞项——在端点被验证之前该改动不应合入主分支。选择静态导出而非配置工厂的原因arXiv API 是公开的不需要 API Key因此无需像websearch那样依据环境变量动态构造配置对比 websearch.ts 中 Tavily/Exa 的 provider 分支也无需像context7那样做CONTEXT7_API_KEY的可选鉴权头归一化对比 context7.ts。三、扩展McpNameSchema把arxiv纳入内置名称枚举第二步修改src/mcp/types.ts将arxiv加入 zod 枚举 Schemaimport { z } from zod -export const McpNameSchema z.enum([websearch, context7, grep_app]) export const McpNameSchema z.enum([websearch, context7, grep_app, arxiv]) export type McpName z.infertypeof McpNameSchema export const AnyMcpNameSchema z.string().min(1) export type AnyMcpName z.infertypeof AnyMcpNameSchema这段代码的作用对应现仓库 types.ts 的实现McpNameSchema是一个 zod 枚举约束了内置 MCP 的合法名称集合——任何配置引用不在枚举内的内置名称都会在校验阶段失败McpName是由z.infer推导出的联合类型websearch | context7 | grep_app | arxiv在工厂、加载器等代码中提供编译期类型安全AnyMcpNameSchema任意非空字符串与AnyMcpName与之互补用于放开对自定义/第三方 MCP 名称的限制——这正是测试用例中忽略未知名称行为的类型基础。新增arxiv意味着名称集合从 3 个扩到 4 个任何引用McpName类型或依赖McpNameSchema解析的代码路径都会同步感知到新成员。四、在createBuiltinMcps工厂中注册第三步修改src/mcp/index.ts改动包含一个 import 和一个注册块import { createWebsearchConfig } from ./websearch import { context7 } from ./context7 import { grep_app } from ./grep-app import { arxiv } from ./arxiv import type { OhMyOpenCodeConfig } from ../config/schema -export { McpNameSchema, type McpName } from ./types export { McpNameSchema, type McpName } from ./types type RemoteMcpConfig { type: remote url: string enabled: boolean headers?: Recordstring, string oauth?: false } export function createBuiltinMcps(disabledMcps: string[] [], config?: OhMyOpenCodeConfig) { const mcps: Recordstring, RemoteMcpConfig {} if (!disabledMcps.includes(websearch)) { mcps.websearch createWebsearchConfig(config?.websearch) } if (!disabledMcps.includes(context7)) { mcps.context7 context7 } if (!disabledMcps.includes(grep_app)) { mcps.grep_app grep_app } if (!disabledMcps.includes(arxiv)) { mcps.arxiv arxiv } return mcps }结合当前仓库 index.ts 的实现可以提炼出createBuiltinMcps的注册语义过滤机制工厂接收disabledMcps: string[]对应配置中的disabled_mcps通过includes逐一判断只过滤内置名称对playwright、custom等未知名称静默忽略注册顺序websearch→context7→grep_app→arxiv返回的mcps对象键序即此顺序测试中Object.keys(result).toHaveLength()断言依赖这一约定默认启用不传disabledMcps时 []默认值全部内置 MCP 注册配置注入websearch是唯一消费config的条目provider 选择arxiv与context7、grep_app一样直接静态导出、不依赖 config。五、同步测试数量断言修正与新用例第四步修改src/mcp/index.test.ts。由于内置 MCP 总数变化所有断言Object.keys(result)长度的既有用例必须同步 13 → 4、2 → 3同时新增一个针对arxiv的过滤用例。完整改动如下describe(createBuiltinMcps, () { test(should return all MCPs when disabled_mcps is empty, () { // given const disabledMcps: string[] [] // when const result createBuiltinMcps(disabledMcps) // then expect(result).toHaveProperty(websearch) expect(result).toHaveProperty(context7) expect(result).toHaveProperty(grep_app) - expect(Object.keys(result)).toHaveLength(3) expect(result).toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(4) }) test(should filter out disabled built-in MCPs, () { // given const disabledMcps [context7] // when const result createBuiltinMcps(disabledMcps) // then expect(result).toHaveProperty(websearch) expect(result).not.toHaveProperty(context7) expect(result).toHaveProperty(grep_app) - expect(Object.keys(result)).toHaveLength(2) expect(result).toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(3) }) test(should filter out all built-in MCPs when all disabled, () { // given - const disabledMcps [websearch, context7, grep_app] const disabledMcps [websearch, context7, grep_app, arxiv] // when const result createBuiltinMcps(disabledMcps) // then expect(result).not.toHaveProperty(websearch) expect(result).not.toHaveProperty(context7) expect(result).not.toHaveProperty(grep_app) expect(result).not.toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(0) }) test(should ignore custom MCP names in disabled_mcps, () { // given const disabledMcps [context7, playwright, custom] // when const result createBuiltinMcps(disabledMcps) // then expect(result).toHaveProperty(websearch) expect(result).not.toHaveProperty(context7) expect(result).toHaveProperty(grep_app) - expect(Object.keys(result)).toHaveLength(2) expect(result).toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(3) }) test(should handle empty disabled_mcps by default, () { // given // when const result createBuiltinMcps() // then expect(result).toHaveProperty(websearch) expect(result).toHaveProperty(context7) expect(result).toHaveProperty(grep_app) - expect(Object.keys(result)).toHaveLength(3) expect(result).toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(4) }) test(should only filter built-in MCPs, ignoring unknown names, () { // given const disabledMcps [playwright, sqlite, unknown-mcp] // when const result createBuiltinMcps(disabledMcps) // then expect(result).toHaveProperty(websearch) expect(result).toHaveProperty(context7) expect(result).toHaveProperty(grep_app) - expect(Object.keys(result)).toHaveLength(3) expect(result).toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(4) }) test(should filter out arxiv when disabled, () { // given const disabledMcps [arxiv] // when const result createBuiltinMcps(disabledMcps) // then expect(result).toHaveProperty(websearch) expect(result).toHaveProperty(context7) expect(result).toHaveProperty(grep_app) expect(result).not.toHaveProperty(arxiv) expect(Object.keys(result)).toHaveLength(3) }) // ... existing tavily test unchanged })这组用例覆盖了工厂的全部行为面空禁用列表返回全部、单名禁用、全量禁用、忽略自定义名称、默认参数、未知名称过滤以及新成员arxiv的专属禁用路径。数量断言是这套测试的注册完整性哨兵——任何未来新增内置 MCP 而忘记更新断言时测试会立即失败从而强制开发者同步维护注册表。六、模块文档同步src/mcp/AGENTS.md第五步更新src/mcp/AGENTS.md让模块文档与代码保持生成即同步的一致性-# src/mcp/ — 3 Built-in Remote MCPs # src/mcp/ — 4 Built-in Remote MCPs **Generated:** 2026-03-06 ## OVERVIEW -Tier 1 of the three-tier MCP system. 3 remote HTTP MCPs created via createBuiltinMcps(disabledMcps, config). Tier 1 of the three-tier MCP system. 4 remote HTTP MCPs created via createBuiltinMcps(disabledMcps, config). ## BUILT-IN MCPs | Name | URL | Env Vars | Tools | |------|-----|----------|-------| | **websearch** | mcp.exa.ai (default) or mcp.tavily.com | EXA_API_KEY (optional), TAVILY_API_KEY (if tavily) | Web search | | **context7** | mcp.context7.com/mcp | CONTEXT7_API_KEY (optional) | Library documentation | | **grep_app** | mcp.grep.app | None | GitHub code search | | **arxiv** | mcp.arxiv.org | None | arXiv paper search | ... ## FILES | File | Purpose | |------|---------| | index.ts | createBuiltinMcps() factory | -| types.ts | McpNameSchema: websearch \| context7 \| grep_app | | types.ts | McpNameSchema: websearch \| context7 \| grep_app \| arxiv | | websearch.ts | Exa/Tavily provider with config | | context7.ts | Context7 with optional auth header | | grep-app.ts | Grep.app (no auth) | | arxiv.ts | arXiv paper search (no auth) |注意文档中Env Vars一列为None再次确认arxiv属于无鉴权、零环境变量的 MCP。当前仓库的 src/mcp 模块说明 采用的正是这张表格的结构Name / Type / Endpoint / Env Vars / Tools新增条目只需照表补一行即可。七、改动总览与影响面评估本次改造共涉及 5 个文件总计约 37 行新增/修改属于刻意保持的最小化、外科手术式改动文件改动行数类型src/mcp/arxiv.ts6新建创建src/mcp/types.ts修改 1 行修改src/mcp/index.ts5import 注册块修改src/mcp/index.test.ts约 20 行数量修正 新用例修改src/mcp/AGENTS.md约 6 行修改影响面可以分三层评估编译期McpNameSchema与McpName类型变更会传播到所有依赖该枚举的模块types.ts从 index.ts 中被 re-exportexport { McpNameSchema, type McpName } from ./types因此任何import { McpName } from ./mcp的调用方都会感知新成员运行时createBuiltinMcps默认返回 5 个内置 MCP若沿用文档假定的 4 远程 lsp 的基线则为 5disabled_mcps新增arxiv关键字用户可在配置中通过disabled_mcps: [arxiv]关闭该功能测试与文档长度断言强制同步模块文档表格同步更新防止注册表与文档漂移。八、合并前的验收清单基于文档标注的阻塞点与仓库既有实践合并前应完成验证端点真实性确认https://mcp.arxiv.org是否存在可用的托管 MCP 服务若不存在评估社区托管服务器或基于export.arxiv.org/api/query的自建包装方案并将最终 URL 写入arxiv.ts跑通测试套件执行src/mcp/index.test.ts全部用例确认 6 个既有用例数量修正后与 1 个新用例全部通过类型检查确认McpNameSchema枚举扩展后无类型错误、无遗漏的 exhaustiveness 检查文档核验AGENTS.md的 Built-in 表格、FILES 表格与代码实际状态一致。同类实现还可参考 senpi 侧的 builtin-mcps 组件——它在 senpi 扩展 API 中以registerMcpServer注册context7与grep_app并引入lifecycle: lazy与exposure: search的延迟暴露策略避免低频工具常驻占用约 1.7K prompt tokens 的 schema 开销。若未来 arXiv 检索被判定为低频工具可借鉴该模式将其同样调整为按名延迟暴露。【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价