先说结果:在一次对话里,我让 WorkBuddy 替我做完了这些事 ——
- 建了 33 个分类(9 个一级 + 23 个二级)
- 把 5 个文档组接进主题设置
- 建了 9 项主菜单,删掉多余的
- 重排首页四个模块,换掉全部演示文案
- 上传配图、发布了一篇 3000 字的文章
全程没打开几次后台。这不是演示,是真实发生的。
支撑这件事的是 MCP。这篇文章记录怎么配,以及——更重要的——它有哪些地方够不着,怎么补。
一、MCP 到底是什么(一句话版本)
Halo 的 MCP Server 插件把站点操作(搜文章、写文章、管分类、审评论、传附件)封装成一组标准工具,暴露在 https://你的域名/mcp 这个端点上。
任何支持 MCP 的 AI 客户端连上它,就「看懂」了你的网站能做什么、怎么做。
和官方另一条路线(CLI + Skills)相比,最大的区别是:MCP 的服务端跑在 Halo 进程里,你本地不用装任何环境。换台电脑照样能用。
二、三步接上
第 1 步:装插件
后台 → 插件 → 应用市场,搜索「MCP Server」,安装并启用。
要求 Halo 2.26.0 或更高版本。我这边是 2.26.1(halo-pro)。
第 2 步:创建访问密钥
后台 → 工具 → MCP 服务 → 创建访问密钥。
两个要点:
- 按工具粒度勾选权限。只想让 Agent 写文章,就只勾文章相关,评论、附件一概不给。别图省事全勾。
- 密钥只在创建或轮换后显示一次,当场复制保存。这个入口只有超级管理员能看到。
第 3 步:配置客户端
页面上会给出配置片段,复制到你的 AI 客户端。WorkBuddy 里是「连接器配置 → 添加自定义 MCP」。
⚠️ 官方明确提醒:出于安全考虑,通常不建议把 Token 直接写进 MCP 配置文件,优先查一下你的 Agent 是否支持通过环境变量注入。
配完让 Agent 列一下工具清单,能列出来就说明通了。
三、连上之后能干什么
按官方说明,MCP Server 内置工具覆盖:文章、独立页面、分类、标签、评论、附件、主题设置。
另外插件还能「贡献」工具 —— 装了 Docsme 就能让 Agent 管项目文档,装了 Image Stream 能让它去 Unsplash 找无版权图并转存附件库。这是 MCP 相比 CLI 的核心优势:能力随插件扩展,不用升级客户端。
我实际用得最多的四类操作:
| 操作 | 用在哪 |
|---|---|
| 批量建分类 | 一次性建 33 个,带父子关系和优先级 |
| 读写主题设置 | 文档组、页眉导航按钮、首页模块 |
| 上传附件 | hero 配图 |
| 创建并发布文章 | 写完直接发布 |
四、MCP 管不到的地方:菜单
这是本次最值得记的一个坑。
MCP 的工具集里没有菜单(Menu / MenuItem)。 官方工具清单列的是文章、页面、分类、标签、评论、附件、主题设置 —— 菜单不在其中。
而主题(我用的是「源蝶文档」)的页眉主菜单、首页菜单模块、页脚菜单,引用的都是 Halo 的菜单数据。也就是说:主题只决定「用哪个菜单、显示在哪」,菜单条目本身必须在 Halo 里建。
所以菜单这一块,得换条路走:用个人令牌(PAT)直接调 REST API。
先搞清一件事:Halo 的 API 有两套前缀
这是我卡了最久的地方。按直觉去试 /apis/api.console.halo.run/v1alpha1/menus,全部 404。
| 资源 | 正确前缀 |
|---|---|
| 菜单、菜单项、用户 | /api/v1alpha1/...(没有 s) |
| 分类、文章、页面、标签 | /apis/content.halo.run/v1alpha1/... |
| 主题实际读取菜单 | /apis/api.halo.run/v1alpha1/menus/primary |
记住一个判断方法:看资源的 group。菜单项指向分类时能看到 "group":"content.halo.run",那分类就走 content.halo.run 前缀;菜单属于核心组,所以是裸的 /api/v1alpha1/。
建菜单项(可直接改了用)
# 1. 先看现有菜单和菜单项
curl -s -H "Authorization: Bearer $PAT" https://你的域名/api/v1alpha1/menus
curl -s -H "Authorization: Bearer $PAT" https://你的域名/api/v1alpha1/menuitems
# 2. 建一个菜单项(指向某个分类)
curl -s -X POST https://你的域名/api/v1alpha1/menuitems \
-H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
-d '{"apiVersion":"v1alpha1","kind":"MenuItem","metadata":{"name":"nav-ops"},"spec":{"displayName":"运维手册","href":"/categories/ops","menuName":"primary","priority":1,"targetRef":{"group":"content.halo.run","kind":"Category","name":"ops","version":"v1alpha1"}}}'
# 3. 校验:主题实际读的是这个接口
curl -s -H "Authorization: Bearer $PAT" https://你的域名/apis/api.halo.run/v1alpha1/menus/primary
第 3 步是必须做的。菜单建完别凭感觉,去查主题真正读取的那个公开接口,确认新项在里面。
两个版本相关的细节
- Halo 2.26 起机制变了:菜单项改用
spec.menuName归属菜单、spec.parent记父级。老的Menu.spec.menuItems和MenuItem.spec.children已弃用。 - 带
targetRef的菜单项,显示名会被目标覆盖。我给菜单项起名「运维手册」,但因为targetRef指向分类,最终显示成了分类全名「运维实战手册」。好处是改分类名菜单会跟着变;想要短名就别带targetRef,只写href。
五、六个实战坑
1. 菜单 API 前缀是 /api 不是 /apis。 差一个字母,全部 404。
2. 附件上传的 policyName 必须放 multipart 表单字段。 放 URL query 会报 Invalid part of policyName。
3. 发文章传 Markdown 不会渲染。 前台渲染的是 snapshot.content(HTML)。只给 raw 的话,页面会原样显示 ## 和 |表格|,必须本地转成 HTML 再写回,然后重新发布一次。
4. 删除是异步的。 删分类或文章返回 200 时,响应里会带 deletionTimestamp 和 finalizers,此时资源还没真消失,通常 1-3 秒后才移除。别用「返回 200」当成功标准。
5. MCP 的部分工具本身有 bug。 我用 halo_delete_category 时报「返回结构缺少 hideFromList 字段」,换成 REST 就好了。工具报错时先怀疑工具,别怀疑自己的参数。
6. 页面里的 Markdown 源码不是渲染失败。 主题有个「复制 Markdown」工具栏,会把 raw 原文塞进 <div id="page-md-data" hidden>。用 grep 查页面时必然能搜到 Markdown 符号,那个容器是 hidden 的,不影响显示。
六、安全
MCP 把站点操作暴露在了网络上,这几条别省:
- 一个客户端一把专用密钥,最小权限,别共用。
- HTTPS 是硬要求,密钥走明文 HTTP 等于公开。
- IP 白名单是附加防护,不能替代 HTTPS 和最小权限。
- 别把密钥贴进聊天记录或提交到仓库。(这条我自己就犯过 —— 把 PAT 直接发在了对话里,事后第一件事就是去后台轮换。)
另外,Halo MCP 默认拒绝带 Origin 头的浏览器直连请求,所以调试要用 MCP 客户端或 Inspector,直接浏览器打开 /mcp 是连不上的。
七、MCP 还是 CLI
官方给的判断标准很清楚:
- 选 MCP:日常内容运营(批量改分类、补摘要、审评论)、跨插件联动、不想在本地折腾环境、需要按工具粒度授权并审计调用记录。
- 选 CLI:本地批量处理 Markdown 文件、改主题代码、系统级备份恢复、CI/CD 流水线。
两者不互斥。尤其当站点挂了、前台打不开时,服务端 MCP 会一起失效——那时候只有本地 CLI 能救场。所以别把 MCP 当唯一通道。
最后
接入的技术门槛其实很低:装个插件、建个密钥、粘一段配置,十分钟的事。
真正的门槛在后面 —— 知道 AI 能管到哪一层,管不到的地方用什么补。菜单这件事让我意识到,官方文档里的「能力清单」值得逐字读一遍:它写了什么很重要,它没写什么同样重要。
如果你的 Agent 卡在某个操作上反复失败,先别急着调 prompt,去查一下这个能力到底在不在工具集里。