把 MCP server 的工具接成 clj-agent 的内联工具。
(def servers [{:url "https://example.com/mcp" :server-id "demo"}
{:command ["npx" "-y" "@modelcontextprotocol/server-filesystem" "/tmp"]}])
(create-agent (update spec :tools into (connect-servers servers)))
产出是普通 map({:name :description :parameters :handler}),所以本 ns
——以及整个 clj-agent-mcp 模块——不需要 require 任何 clj-agent 的东西。
谁要用谁 conj 进 :tools。
connect-servers 会真的去连、去 tools/list。取舍与理由:server 挂了就在起
服务时报错(这里是记 warn 跳过那一个),不要每轮对话多一次 tools/list 的
网络往返。代价是「server 中途新增了工具」要重启才看得见——notifications/ tools/list_changed 那条路等有人真要再说。
工具的 _meta 里带 io.modelcontextprotocol/ui-resource-uri 说明它会画一块
界面。这里只做两件事:在描述里告诉模型「这个工具会出界面」,以及把 uri
记在工具的 metadata 上。真正把界面拉起来是前端的事(agui.mcp 那半边)。
把 MCP server 的工具接成 clj-agent 的**内联工具**。
```clojure
(def servers [{:url "https://example.com/mcp" :server-id "demo"}
{:command ["npx" "-y" "@modelcontextprotocol/server-filesystem" "/tmp"]}])
(create-agent (update spec :tools into (connect-servers servers)))
```
产出是**普通 map**(`{:name :description :parameters :handler}`),所以本 ns
——以及整个 `clj-agent-mcp` 模块——不需要 require 任何 clj-agent 的东西。
谁要用谁 conj 进 `:tools`。
## 连接发生在**装配期**
`connect-servers` 会真的去连、去 `tools/list`。取舍与理由:server 挂了就在起
服务时报错(这里是记 warn 跳过那一个),**不要每轮对话多一次 `tools/list` 的
网络往返**。代价是「server 中途新增了工具」要重启才看得见——`notifications/
tools/list_changed` 那条路等有人真要再说。
## MCP Apps
工具的 `_meta` 里带 `io.modelcontextprotocol/ui-resource-uri` 说明它会画一块
界面。这里只做两件事:**在描述里告诉模型**「这个工具会出界面」,以及把 uri
记在工具的 metadata 上。真正把界面拉起来是前端的事(`agui.mcp` 那半边)。(->inline-tool c mcp-tool)MCP 工具 → 内联工具 map。
:parameters 直接用 server 给的 inputSchema(JSON Schema 2020-12)——
不翻译、不校验:翻译一遍只会引入我们自己的 bug,而模型看的就是这份 schema。
描述里追加 [UI Resource: …] 与上游一致:这是给模型的提示——「这个工具会
画出一块界面」,它据此决定要不要再啰嗦一遍结果。
MCP 工具 → 内联工具 map。 `:parameters` 直接用 server 给的 `inputSchema`(JSON Schema 2020-12)—— 不翻译、不校验:翻译一遍只会引入我们自己的 bug,而模型看的就是这份 schema。 描述里追加 `[UI Resource: …]` 与上游一致:这是给**模型**的提示——「这个工具会 画出一块界面」,它据此决定要不要再啰嗦一遍结果。
(app-tools tools)工具集里带 UI 资源的那些(MCP Apps)。
工具集里带 UI 资源的那些(MCP Apps)。
(connect-servers servers)连上每个 server、列出工具、转成内联工具。
一个 server 挂了不拖垮别的:记一条 warn 然后跳过——不是静默吞掉,也不是 让整个 agent 起不来。返回值是所有成功接入的工具。
连上每个 server、列出工具、转成内联工具。 **一个 server 挂了不拖垮别的**:记一条 warn 然后跳过——不是静默吞掉,也不是 让整个 agent 起不来。返回值是所有成功接入的工具。
(result-text result)tools/call 的结果 → 喂回模型的字符串。
content 是块数组(text / image / resource / resource_link…)。取所有 text 块
拼起来;一块 text 都没有就整个 JSON 化——宁可给模型一坨 JSON,也别给它 nil。
structuredContent 优先:2025-06-18 起工具可以给结构化结果,有它就用它
(模型更好读,也免得 text 块只是它的一份字符串复读)。
`tools/call` 的结果 → 喂回模型的字符串。 `content` 是块数组(text / image / resource / resource_link…)。取所有 text 块 拼起来;**一块 text 都没有就整个 JSON 化**——宁可给模型一坨 JSON,也别给它 nil。 `structuredContent` 优先:2025-06-18 起工具可以给结构化结果,有它就用它 (模型更好读,也免得 text 块只是它的一份字符串复读)。
(server-hash {:keys [url command]})server 的稳定哈希——前端引用某个 server 用它,不必知道 url。
与上游 getServerHash 同款:md5 of {type,url}。
server 的稳定哈希——**前端引用某个 server 用它**,不必知道 url。
与上游 `getServerHash` 同款:md5 of `{type,url}`。(ui-resource-uri mcp-tool)这个 MCP 工具带 UI 资源吗?带就返回那个 uri(= 它是个 MCP App 工具)。
规范的位置是 _meta.ui.resourceUri(嵌套对象)。同时兼容扁平写法
_meta 里直接挂 ui/resourceUri ——那是本仓早期实现按猜测写的键,外面也可能
有 server 这么发。认多一种不会错认:两处都没有就不是 App 工具。
这个 MCP 工具带 UI 资源吗?带就返回那个 uri(= 它是个 MCP App 工具)。 规范的位置是 **`_meta.ui.resourceUri`**(嵌套对象)。同时兼容扁平写法 `_meta` 里直接挂 `ui/resourceUri` ——那是本仓早期实现按猜测写的键,外面也可能 有 server 这么发。**认多一种不会错认**:两处都没有就不是 App 工具。
(with-tools spec servers)把 MCP 工具并进一份 agent 配置的 :tools。一层包装,不是配置项。
把 MCP 工具并进一份 agent 配置的 `:tools`。**一层包装,不是配置项**。
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |