最近把博客的发布链路改成了一个很直接的 MCP:本地启动 astro-blog-ssh,再通过 SSH 直接把 Markdown 写到远端 Astro 博客的 content/blog 目录。
有了这条链路之后,一个很自然的下一步,就是让 Codex 不只是“能调用工具”,而是“知道什么时候应该把当前对话整理成文章并直接发布”。这次做的,就是把这套行为沉淀成一个可安装的 skill。
目标不是安装说明,而是行为约束
一开始最需要收窄的问题,不是怎么写 SKILL.md,而是这个 skill 到底要教 Codex 做什么。
这次明确的目标是:
- 当用户明确说“发布到我的博客”时,Codex 直接整理并发布。
- 当当前对话已经形成成体系的技术内容时,Codex 也默认自动发布。
- 如果上下文不够,Codex 不能乱编内容,只能缩小主题、说明假设,或者停下来补信息。
也就是说,这个 skill 不是安装器,不是 MCP 教程,而是一个“从对话到公开文章”的行为规范。
先写 spec,再落 skill 目录
为了避免把 skill 写成随意提示词,先在仓库里补了一份设计文档:
docs/superpowers/specs/2026-05-01-astro-blog-auto-publish-design.md
这份 spec 主要固定了几件事:
- 触发条件分成显式发布和隐式发布两类。
- 默认行为是自动发布,而不是先出草稿等确认。
- 自动发布不等于可以编造内容,缺关键信息时必须收窄或中止。
- skill 只负责教 Codex 调用现有 MCP 工具,不改 MCP 服务本身。
接着又补了一份实现计划:
docs/superpowers/plans/2026-05-01-astro-blog-auto-publish.md
这样后续生成的 skill 文件就有了清晰边界,既不容易超范围,也更方便以后继续维护。
最终生成的 skill 结构
这次只保留最小必要文件:
skills/astro-blog-auto-publish/SKILL.md
skills/astro-blog-auto-publish/agents/openai.yaml
SKILL.md 负责定义核心行为:
- 什么时候使用这个 skill
- 什么时候不要使用
- 如何把对话重写成公共技术文章
- 如何处理信息不足
- 何时调用
generate_slug和create_post - 发布失败时如何保留草稿并说明失败原因
agents/openai.yaml 则负责给 Codex 的技能列表和显式调用入口提供元数据,比如:
display_nameshort_descriptiondefault_prompt
这个分层很重要。真正的工作规范应该放在 SKILL.md,而不是都挤进 description 里,不然模型很容易只读简短描述、不读完整 skill。
skill 里最重要的几条规则
相比“写出一篇文章”,更重要的是“什么时候不该发”。
所以这次 skill 里最关键的约束有几条:
- 默认自动发布,但前提是当前对话已经足够完整。
- 文章必须围绕一个清晰技术主题,而不是原样搬运聊天记录。
- 对于小缺口,可以用保守假设补齐,并在最终响应里说明。
- 对于关键缺口,不能编造事实,只能补问或缩小文章范围。
- 涉及私密、凭据、内部细节的内容,不能直接发。
这几条基本决定了 skill 的可用性。如果没有这些边界,所谓“自动发布”很容易变成“自动制造垃圾文章”。
一个实际踩到的坑:元数据生成脚本依赖 PyYAML
为了避免手写 agents/openai.yaml,我直接复用了现成 skill 自带的生成脚本。
第一次运行时,脚本报错了:缺少 yaml 模块。
根因不是脚本逻辑错误,而是它默认会读取 SKILL.md frontmatter,并使用 PyYAML 解析。本地当前 Python 环境没有这个依赖。
这时候最小改动不是去改环境,也不是去改脚本,而是直接使用它支持的 --name 参数,绕开 frontmatter 解析步骤。这样既不污染环境,也不偏离当前任务范围。
这个处理方式的好处很明显:
- 不增加仓库依赖
- 不修改外部 skill 提供的脚本
- 仍然能产出标准
openai.yaml
另一个小坑:$skill-name 被 shell 吃掉了
openai.yaml 里有个 default_prompt 字段,规范要求显式写出 $skill-name。
第一次生成后,结果从:
Use $astro-blog-auto-publish ...
变成了:
Use -blog-auto-publish ...
问题出在 shell 展开:$astro 被当成环境变量替换掉了,剩下后半截字符串。
根因明确之后,修复也很简单:不要怀疑 skill 内容本身,直接把生成后的 openai.yaml 用字面量修正为正确的 $astro-blog-auto-publish。
这个问题很小,但挺典型:
- 不是业务逻辑问题
- 不是 MCP 问题
- 是命令层的字符串展开问题
如果没有在最后做一次完整验证,很容易带着这个细小错误交付出去。
最后做了哪些验证
这次没有去验证“Codex 运行时一定会自动触发 skill”,因为那取决于实际的 skill 加载环境和 MCP 可用性。但本地已经做了这些可证实的检查:
- skill 目录结构完整。
SKILL.mdfrontmatter 合法,命名符合 hyphen-case。openai.yaml已生成,字段和 skill 语义一致。default_prompt中的$astro-blog-auto-publish已按字面量保留。- 仓库 diff 范围只包含 spec、plan 和 skill 目录,没有误改 MCP 服务代码。
这意味着“skill 包结构正确”这件事是可以确认的;至于“运行时是否自动发布成功”,则仍然依赖 Codex skill 加载能力和 astro-blog-ssh MCP 是否在线。
这类 skill 的价值
如果只是偶尔发一篇文章,直接手工整理也能做。
但当你已经有了 MCP 工具链,下一步最有价值的就不是重复调用工具,而是把“什么时候该调用、如何整理、什么情况下不能发”固化下来。
skill 的真正价值,不在于省掉几个命令,而在于让自动化行为有边界、有默认策略、也有失败回退路径。
这次的 astro-blog-auto-publish 就属于这种类型:它不是新能力,而是把已有能力变成了可重复复用的工作流。
发表回复