如何把外部服务经 MCP 接入 Agent 工具池:learn-claude-code s14 的工具发现、mcp__ 前缀与名称冲突检查
2026/9/9 22:29:58 网站建设 项目流程

如何把外部服务经 MCP 接入 Agent 工具池:learn-claude-code s14 的工具发现、mcp__ 前缀与名称冲突检查

【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code

如果你的 Agent harness 已有自己的基础工具(文件读写、shell 等),现在想接入一个外部服务(比如文档系统、部署平台),又不想为每个服务手写一套工具定义、参数 schema 和调用处理代码,learn-claude-code 的 s14 章节(s14_mcp_plugin/)演示了一条 MCP 风格的接入路径:server 提供工具列表和调用入口,harness 负责连接、命名、权限检查,再把发现的工具合并进同一个工具池交给模型。运行python s14_mcp_plugin/code.py后,你在交互提示符里输入一句话,就能观察模型调用connect_mcp完成发现、随后以mcp__docs__search这类带前缀的名字调用外部工具的全过程。

需要先明确一条边界:s14 中的docsdeploy两个 server 是进程内模拟实现,用来展示tools/listtools/call与动态工具池的边界,本章没有实现真实的 MCP transport(见 s14_mcp_plugin/README.md 的 “The Solution” 一节)。因此本文覆盖的是接入模式、命名规则、冲突检查与验证方式;把这套模式换成真实网络传输不在 s14 范围内。

准备条件:依赖、环境变量与运行入口

在仓库根目录下完成以下三步(requirements.txt 与 .env.example 即当前仓库自带文件):

  1. 安装依赖:
pip install -r requirements.txt

requirements.txt声明的依赖为anthropic>=0.25.0python-dotenv>=1.0.0pyyaml>=6.0

  1. 创建环境变量文件(会在仓库根目录生成.env,这是运行脚本的必要前置配置,不是对仓库源码的修改):
cp .env.example .env

然后编辑.env。.env.example 中标注了两项必填:

  • ANTHROPIC_API_KEY:模型 API 密钥;
  • MODEL_ID:模型 ID,文件给出的默认示例值为claude-sonnet-4-6

ANTHROPIC_BASE_URL可选项用于接入 Anthropic 兼容的提供商。注意 s14_mcp_plugin/code.py 通过MODEL = os.environ["MODEL_ID"]直接读取该变量,未设置时脚本会在启动阶段报错,不会自行降级。

  1. 启动脚本(在仓库根目录执行):
python s14_mcp_plugin/code.py

脚本进入交互循环,提示符为s14 >>,输入q退出。此时模型可见的工具是 s04 继承来的五个基础工具(bashread_filewrite_fileedit_fileglob)加上connect_mcp,尚无任何mcp__前缀工具。其中bash会真实执行 shell 命令(超时 120 秒),请在自己可控的目录中运行该脚本。

执行接入:触发工具发现并核对输出

启动后,输入 s14_mcp_plugin/README.md “Try It Out” 一节给出的示例指令:

Connect to the docs server, search for agent hooks, and tell me the current documentation API version.

判断接入是否生效,按顺序核对以下三类输出:

  1. 连接与发现:控制台会打印(对应 code.py 中connect_mcpprint):

    [mcp] connected: docs -> search, get_version

    模型侧收到的工具结果是Connected to MCP server 'docs'. Discovered 2 tools: search, get_version

  2. 工具轨迹:文档给出的典型轨迹如下(文档示例,实际调用顺序可能因模型而异):

    connect_mcp(name="docs") mcp__docs__search(query="agent hooks") mcp__docs__get_version()
  3. 调用结果:模拟 server 的 handler 返回固定字符串,s14_mcp_plugin/README.md 将其标注为典型输出(以下均为文档示例输出,不是你需要逐字比对的固定结果):

    [docs] Found 3 results for 'agent hooks' [docs] API v2.1.0

连接完成后,下一轮assemble_tool_pool()会把mcp__docs__searchmcp__docs__get_version加入模型输入,工具执行结果仍作为tool_result追加到消息中——基础 Agent Loop 本身没有改动。

再输入第二段示例指令,可以验证权限分支:

Connect to the deploy server and check the web service status. Do not trigger a deployment.

deployserver 暴露statustrigger两个工具:status按宿主策略直接执行(示例输出[deploy] web: running (v1.4.2),文档示例);trigger需要用户确认,下一节说明规则。

命名规则:mcp__ 前缀、归一化与 64 字符冲突检查

多个 server 都可能提供searchstatus这样的同名工具,s14 用固定格式避免混淆:

mcp__{server}__{tool}

名称进入工具池前经过三道检查,全部在 assemble_tool_pool() 中执行,且每轮模型调用前都会重跑:

  1. 归一化:normalize_mcp_name() 用正则[^a-zA-Z0-9_-]把字母、数字、下划线、连字符之外的字符替换为_;归一化结果为空串时直接抛ValueError

  2. 长度限制:带前缀后的名字超过 64 个字符时抛ValueError("MCP tool name is longer than 64 characters: ...")

  3. 冲突检查:若两个来源归一化后落到同一个带前缀名字(比如 README 中举的docs.one/get.versiondocs_one/get_version两个例子),抛出:

    ValueError: MCP tool name collision after normalization: '...' maps both ... and ...

    异常信息中会同时给出冲突双方的原始来源(origins字典),方便定位是哪个 server 的哪个工具撞名。文档强调这保证不同来源“不会悄悄映射到同一个名字”。

加入工具池时,模型看到的是带前缀的名字,而 handler 仍用 server 的原始工具名回调MCPClient.call_tool()。code.py 中 handler 写成带默认参数的 lambda(lambda *, client=server, tool=raw_name, **kwargs: ...),README 解释了原因:默认参数把当前 client 和 tool 捕获进闭包,避免循环里创建的 lambda 全部指向最后一个工具。

权限:宿主侧策略,而不是 server 自述

MCP server 的工具定义里可以带readOnlyHintdestructiveHint(两个模拟 server 都带),但文档明确:这些提示来自 server,不能直接作为授权依据;即使 description 写着readOnly,工具也不会自动放行。s14 采用宿主侧策略表 MCP_HOST_POLICY:

MCP_HOST_POLICY = { ("docs", "search"): "allow", ("docs", "get_version"): "allow", ("deploy", "status"): "allow", ("deploy", "trigger"): "confirm", }

permission_hook()PreToolUse阶段按规范化后的工具名查这张表(code.py):策略为allow的工具直接执行;策略为confirm未配置的外部工具默认弹出确认:

[permission] External tool mcp__deploy__trigger({'service': 'web'}) Allow? [y/N]

输入y/yes放行,其余回答返回Permission denied by user作为工具结果。也就是说,接入一个新 server 时,如果你想让某个外部工具免确认,需要在宿主侧策略里显式登记(server, tool): "allow",而不是依赖 server 的自述。

运行中的报错如何判断

以下现象均来自 s14 文档与代码,可作为对照排查:

现象来源与含义
Unknown server 'xxx'. Available: docs, deploy请求的 server 不在MOCK_SERVERS注册表里(code.py);s14 只内置docsdeploy两个模拟 server
MCP server 'docs' already connected重复连接同一 server,返回提示后不再重复发现(code.py)
MCP error: TypeError: <lambda>() missing 1 required argument: 'query'模型漏传了必填参数。execute_tool()MCPClient.call_tool()都会捕获异常并把它作为错误tool_result返回,Agent Loop 不会因此退出,模型可在下一轮自行修正参数
MCP error: unknown tool '{tool_name}'调用方请求了该 client 上没有注册的工具名
控制台[hook] Large output from ...: N charsPostToolUselarge_output_hook对超过 100000 字符的输出做提示

工具输入错误“留在工具边界内”是 s14 有意的设计:错误回到模型侧修正,而不是中断课程脚本。

限制与后续

  • s14 的docs/deploy是进程内 stand-in,不含真实 MCP transport;工具来源、发现、命名、冲突检查与宿主权限这套边界是本章实际演示的内容。
  • s14 是独立的课程分支,不带入 Task、Background、Cron、Team 或 Worktree。后续 s15_integrated_harness/README.md 的 Integrated Harness 会把基础工具、Hooks、Skills、Context、Memory、Task、Background、Cron、Teams 与 MCP 放进同一个运行时,且沿用 s14 的动态 MCP 路径与宿主侧策略;如需在完整 harness 中接入外部服务,以 s15 为入口继续读。
  • 完整章节说明见 s14_mcp_plugin/README.md(中文对照 s14_mcp_plugin/README.zh.md),可运行实现见 s14_mcp_plugin/code.py。

【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询