让 WorkBuddy 接管 Halo 博客:MCP 接入实战,以及它管不到的地方

先说结果:在一次对话里,我让 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,去查一下这个能力到底在不在工具集里。