<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>工具系统 on Renxin's Blog</title><link>https://zh.renxinblog.cn/tags/%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9F/</link><description>Recent content in 工具系统 on Renxin's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Tue, 09 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zh.renxinblog.cn/tags/%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9F/index.xml" rel="self" type="application/rss+xml"/><item><title>第八话｜MCP 协议：工具为什么需要一根 USB-C</title><link>https://zh.renxinblog.cn/post/c08-mcp/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><guid>https://zh.renxinblog.cn/post/c08-mcp/</guid><description>&lt;img src="https://zh.renxinblog.cn/images/c08-mcp-cover.png" alt="Featured image of post 第八话｜MCP 协议：工具为什么需要一根 USB-C" /&gt;&lt;!--
第八话演示代码:
Python: https://github.com/renxin2024/GYA/tree/main/c08-mcp
Java 21: https://github.com/renxin2024/GYA-Java/tree/main/c08-mcp
演进主线: 阶段3 MCP —— 从进程内 ToolRegistry 到跨进程、跨语言的工具协议
上一篇: 第七话｜记忆：上下文、短期、长期
后续篇章：重写后重新发布
--&gt;
&lt;p&gt;第四话里，我们把工具放进了一个 &lt;code&gt;ToolRegistry&lt;/code&gt;。工具多了以后，注册表比 &lt;code&gt;if-else&lt;/code&gt; 强很多：可以发现工具、统一调用、返回结构化错误。&lt;/p&gt;
&lt;p&gt;但它有一个隐含前提：&lt;strong&gt;工具和 Agent 在同一个进程，至少在同一套代码里。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果天气服务是另一个 Python 进程，数据库工具是 Java 服务，GitHub 工具由另一个团队维护，第四话的注册表还能直接解决吗？不能。你需要给每一种外部工具写一套适配器，工具数量和接入方数量一增加，就会重新掉进 N×M 集成地狱。&lt;/p&gt;
&lt;p&gt;这就是 MCP 要解决的问题。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;ToolRegistry 管理进程内工具，MCP 标准化工具提供方和 Agent 客户端之间的边界。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="一旧世界工具被绑在调用方代码里"&gt;一、旧世界：工具被绑在调用方代码里
&lt;/h2&gt;&lt;p&gt;第四话的最小闭环是：模型输出工具名和参数，注册表根据名称找到函数，函数执行后返回 &lt;code&gt;ToolResponse&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;LLM → ToolRegistry → Python 函数 → ToolResponse → LLM
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个结构对学习和单体应用非常好。你能清楚看到每一层的职责，也能自己控制错误码和参数校验。&lt;/p&gt;
&lt;p&gt;问题出在边界。假设 Agent 需要 20 个工具，而这 20 个工具分布在 5 个独立服务里。没有统一协议时，客户端要知道每个服务如何启动、如何描述工具、如何传参数、如何返回结果。每接一个服务，都要重新写一套通信和适配代码。&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 A[Agent 客户端] --&gt; P[多套私有适配器]
 P --&gt; S[多个外部服务]&lt;/pre&gt;&lt;p&gt;这不是工具本身太多，而是每个客户端都在重复理解每个服务的私有接口。&lt;/p&gt;
&lt;h2 id="二mcp-把什么放到了协议层"&gt;二、MCP 把什么放到了协议层
&lt;/h2&gt;&lt;p&gt;MCP 可以先用三个角色理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Host&lt;/strong&gt;：承载 Agent 的应用，例如桌面助手、IDE 或你的 Python 程序；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Client&lt;/strong&gt;：Host 内部的协议客户端，负责连接某一个 MCP Server；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Server&lt;/strong&gt;：工具、资源或提示模板的提供方。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它们的关系不是“模型直接连接 MCP Server”，而是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── MCP Client ── JSON-RPC/Transport ── MCP Server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── tools
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一次最小工具调用大致经历：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Client 与 Server 初始化并协商协议版本和能力；&lt;/li&gt;
&lt;li&gt;Client 请求工具列表；&lt;/li&gt;
&lt;li&gt;Server 返回工具名称、描述和输入 Schema；&lt;/li&gt;
&lt;li&gt;Client 发起工具调用；&lt;/li&gt;
&lt;li&gt;Server 执行函数并返回内容或错误。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有一个重要边界：MCP 标准化的是“怎么发现和调用”，不是“下一步该调用什么”。任务规划仍然由 Agent 循环和模型负责，工具的权限、业务正确性和结果验证仍然由应用负责。&lt;/p&gt;
&lt;h2 id="三stdio先把跨进程边界跑通"&gt;三、STDIO：先把跨进程边界跑通
&lt;/h2&gt;&lt;p&gt;MCP 支持多种传输方式。第八话先选择 STDIO，因为它最容易看清进程边界：Client 启动 Server 子进程，JSON-RPC 消息通过 stdin/stdout 传递。&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 C[MCP Client] --&gt; I[initialize]
 I --&gt; L[tools/list]
 L --&gt; X[tools/call add]
 X --&gt; R[CallToolResult]&lt;/pre&gt;&lt;p&gt;STDIO 有一个很容易踩的坑：&lt;strong&gt;stdout 是协议通道，不是日志通道。&lt;/strong&gt; Server 如果把调试日志打印到 stdout，Client 读到的就不再是合法的 JSON-RPC 消息。日志应该写 stderr，或者通过 MCP 的 logging 能力发送。&lt;/p&gt;
&lt;p&gt;生产环境更常用 Streamable HTTP：Server 可以独立部署，多个 Client 通过网络连接。但这同时带来认证、超时、并发、TLS、会话和部署问题。先跑通 STDIO，再讨论 HTTP，学习成本更低。&lt;/p&gt;
&lt;h2 id="四python用官方-sdk-跑通-clientserver"&gt;四、Python：用官方 SDK 跑通 Client/Server
&lt;/h2&gt;&lt;h3 id="前置环境"&gt;前置环境
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Python 3.10+；本次用 Python 3.11.15 实测；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mcp[cli]==2.0.0&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;不需要 LLM API Key。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代码位于 &lt;code&gt;GYA/c08-mcp/&lt;/code&gt;。Server 只有一个工具：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mcp.server&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MCPServer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MCPServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;c08-demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Add two integers and return the result.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;类型注解不仅服务于 Python 类型检查，也帮助 SDK 生成工具输入 Schema。Client 不需要自己手写 &lt;code&gt;a&lt;/code&gt;、&lt;code&gt;b&lt;/code&gt; 的 JSON Schema，就能通过 &lt;code&gt;tools/list&lt;/code&gt; 发现它。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;stdio_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;listed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_tools&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;add&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里使用的是 &lt;code&gt;mcp==2.0.0&lt;/code&gt; 的实际 API：&lt;code&gt;stdio_client&lt;/code&gt; 返回读写流，再交给 &lt;code&gt;ClientSession&lt;/code&gt;。在线文档中的更高层 &lt;code&gt;Client(StdioServerParameters(...))&lt;/code&gt; 写法属于更新的文档线，不能未经验证地复制到锁定版本。&lt;/p&gt;
&lt;p&gt;运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; GYA/c08-mcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m venv .venv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 main.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本次实测关键输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[1] 初始化 MCP Server: experimental={} ... tools=ToolsCapability(list_changed=False) ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2] 发现工具: [&amp;#39;add&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[3] 调用 add(2, 3): 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[4] 错误场景: is_error=True; Unknown tool: missing_tool
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Python SDK 把未知工具作为一个带 &lt;code&gt;is_error=True&lt;/code&gt; 的工具结果返回，Client 不会因为这个工具级错误直接崩溃。&lt;/p&gt;
&lt;h2 id="五java-21同一个协议不同的-sdk-表达"&gt;五、Java 21：同一个协议，不同的 SDK 表达
&lt;/h2&gt;&lt;h3 id="前置环境-1"&gt;前置环境
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Java 21；&lt;/li&gt;
&lt;li&gt;Gradle Wrapper 8.14.2；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;io.modelcontextprotocol.sdk:mcp:2.0.0&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;不需要 LLM API Key。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代码位于 &lt;code&gt;GYA-Java/c08-mcp/&lt;/code&gt;。Java 版使用 &lt;code&gt;StdioServerTransportProvider&lt;/code&gt; 暴露 Server，使用 &lt;code&gt;StdioClientTransport&lt;/code&gt; 启动 Server 子进程。工具定义需要显式提供输入 Schema：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;McpSyncServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;McpServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serverInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c08-java-demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServerCapabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;add&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Add two integers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CallToolResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;McpSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; GYA-Java
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;/usr/libexec/java_home -v 21&lt;span class="k"&gt;)&lt;/span&gt; ./gradlew :c08-mcp:run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本次实测关键输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[1] 初始化 MCP Server: c08-java-demo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2] 发现工具: [add]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[3] 调用 add(2, 3): 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[4] 错误场景: McpError; Unknown tool: invalid_tool_name
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Java SDK 2.0.0 对未知工具的处理和 Python 不一样：它返回 JSON-RPC 错误，客户端表现为 &lt;code&gt;McpError&lt;/code&gt;。这不是“谁对谁错”，而是两个 SDK 对协议级错误的 API 映射不同。跨语言教程不能只比较正常输出，也要把错误语义写清楚。&lt;/p&gt;
&lt;h2 id="六mcp-和第四话-toolregistry-的边界"&gt;六、MCP 和第四话 ToolRegistry 的边界
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;第四话 ToolRegistry&lt;/th&gt;
					&lt;th&gt;第八话 MCP&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;工具位置&lt;/td&gt;
					&lt;td&gt;同一进程或代码库&lt;/td&gt;
					&lt;td&gt;独立进程或远程服务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;发现方式&lt;/td&gt;
					&lt;td&gt;读取本地注册表&lt;/td&gt;
					&lt;td&gt;协议请求发现&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;调用边界&lt;/td&gt;
					&lt;td&gt;函数调用&lt;/td&gt;
					&lt;td&gt;JSON-RPC + 传输协议&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多语言&lt;/td&gt;
					&lt;td&gt;需要自己适配&lt;/td&gt;
					&lt;td&gt;Client/Server 可以异构&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;主要价值&lt;/td&gt;
					&lt;td&gt;进程内工具管理&lt;/td&gt;
					&lt;td&gt;工具接入与复用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;没有解决&lt;/td&gt;
					&lt;td&gt;任务规划、权限、结果验证&lt;/td&gt;
					&lt;td&gt;任务规划、权限、结果验证&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MCP 不是 Agent 的替代品，也不是把任何函数自动变成可靠工具的魔法。它只是把原来散落在每个应用里的“如何连接工具”这部分共性抽出来，变成一套可复用的协议。&lt;/p&gt;
&lt;h2 id="七从-demo-到生产还缺什么"&gt;七、从 Demo 到生产还缺什么
&lt;/h2&gt;&lt;p&gt;这个 demo 故意没有 API Key，也没有真实业务副作用。生产 MCP Server 至少还需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入 Schema 和业务层双重校验；&lt;/li&gt;
&lt;li&gt;超时、重试和幂等策略；&lt;/li&gt;
&lt;li&gt;认证、授权和最小权限；&lt;/li&gt;
&lt;li&gt;不把凭据通过环境继承或日志泄露；&lt;/li&gt;
&lt;li&gt;对工具结果做真实性和完整性验证；&lt;/li&gt;
&lt;li&gt;对 Server、Client、工具调用和错误建立可观测性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;尤其要记住：&lt;strong&gt;MCP 的“能调用”不等于业务上的“允许调用”。&lt;/strong&gt; 文件删除、支付、发邮件、修改生产数据，都需要在协议之外增加权限和人工审批边界。&lt;/p&gt;
&lt;h2 id="下一篇skill-把做法变成资产"&gt;下一篇：Skill 把“做法”变成资产
&lt;/h2&gt;&lt;p&gt;现在我们解决了“工具在哪里、怎么发现、怎么调用”。但还有一个问题：工具能调用，不代表 Agent 知道应该如何完成一个复杂任务。&lt;/p&gt;
&lt;p&gt;下一篇进入 Skill：把触发条件、步骤、约束、脚本和验证方式封装成可复用能力。工具解决“能做什么”，Skill 解决“应该怎么做”。&lt;/p&gt;
&lt;h2 id="参考资料与实测记录"&gt;参考资料与实测记录
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://py.sdk.modelcontextprotocol.io/" target="_blank" rel="noopener"
 &gt;Model Context Protocol 官方 Python SDK&lt;/a&gt;（Python 3.11.15、&lt;code&gt;mcp[cli]==2.0.0&lt;/code&gt;，本轮验证记录：2026-08-21）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://py.sdk.modelcontextprotocol.io/client/transports/" target="_blank" rel="noopener"
 &gt;MCP Python SDK Client Transports&lt;/a&gt;（STDIO 传输说明，本轮核对：2026-08-21）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://java.sdk.modelcontextprotocol.io/latest/" target="_blank" rel="noopener"
 &gt;Model Context Protocol 官方 Java SDK&lt;/a&gt;（Java 21、SDK 2.0.0，本轮验证记录：2026-08-21）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://java.sdk.modelcontextprotocol.io/latest/client/" target="_blank" rel="noopener"
 &gt;MCP Java SDK Client&lt;/a&gt;（STDIO Client 与工具调用 API，本轮核对：2026-08-21）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://java.sdk.modelcontextprotocol.io/latest/server/" target="_blank" rel="noopener"
 &gt;MCP Java SDK Server&lt;/a&gt;（STDIO Server 与 Tool Specification，本轮核对：2026-08-21）&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>