标签: MCP

  • MCP 发布测试文章

    MCP 发布测试

    这是一篇由 Codex 通过 MCP 创建的测试文章。

    • 发布时间:2026-04-30T18:03:30.512Z
    • 目的:验证 create_post 与远端写入流程

    如果你能在博客中看到这篇文章,说明发布链路工作正常。

  • 解决MCP Server SSH远程路径配置问题:macOS与Linux路径差异

    解决MCP Server SSH远程路径配置问题:macOS与Linux路径差异

    在开发基于Model Context Protocol (MCP)的博客管理工具时,遇到了一个典型的跨平台路径配置问题。本文详细记录了问题的发现、分析和解决过程,希望能帮助遇到类似问题的开发者。

    问题现象

    MCP Server astro-blog-ssh 在启动时报错:

    failed to initialize MCP client for astro-blog-ssh: MCP 服务启动失败: Error: Command failed: ssh -p 7122 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /Users/mhl/.ssh/id_ed25519 mahongliang@111.228.52.26 mkdir -p '/home/mahongliang/workspace/astro-site/content/blog' && test -d '/home/mahongl...

    问题分析

    1. 初步排查

    首先确认SSH密钥文件权限正确(600),SSH连接本身能够正常建立:

    ssh -p 7122 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /Users/mhl/.ssh/id_ed25519 mahongliang@111.228.52.26 "echo 'SSH connection successful'"

    2. 深入诊断

    执行完整的目录创建命令时发现问题:

    ssh -p 7122 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /Users/mhl/.ssh/id_ed25519 mahongliang@111.228.52.26 "mkdir -p '/home/mahongliang/workspace/astro-site/content/blog' && test -d '/home/mahongliang/workspace/astro-site/content/blog'"

    返回错误:mkdir: /home/mahongliang: Operation not supported

    3. 根本原因

    通过查询远程服务器的用户主目录:

    ssh -p 7122 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /Users/mhl/.ssh/id_ed25519 mahongliang@111.228.52.26 "echo \$HOME"

    发现返回结果是:/Users/mahongliang

    这说明远程服务器实际上是macOS系统,而不是假设的Linux系统!

    • Linux系统:用户主目录为 `/home/ `
    • macOS系统:用户主目录为 `/Users/ `

    解决方案

    将MCP配置中的远程路径从Linux路径改为macOS路径:

    错误配置:

    "BLOG_REMOTE_POSTS_DIR": "/home/mahongliang/workspace/astro-site/content/blog"

    正确配置:

    "BLOG_REMOTE_POSTS_DIR": "/Users/mahongliang/workspace/astro-site/content/blog"

    验证修复

    使用修正后的路径测试:

    ssh -p 7122 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /Users/mhl/.ssh/id_ed25519 mahongliang@111.228.52.26 "mkdir -p '/Users/mahongliang/workspace/astro-site/content/blog' && test -d '/Users/mahongliang/workspace/astro-site/content/blog' && echo 'Directory check successful'"

    返回:Directory check successful

    MCP Server成功启动:

    Astro Blog SSH MCP Server 已启动 -> mahongliang@111.228.52.26:/Users/mahongliang/workspace/astro-site/content/blog

    经验总结

    1. 不要假设远程系统类型:在配置远程路径时,务必确认远程操作系统的实际类型
    2. 动态验证路径:使用 echo $HOME 命令获取远程用户的实际主目录路径
    3. 跨平台兼容性:在开发工具时考虑不同操作系统的路径差异
    4. 详细的错误日志:保留完整的错误信息有助于快速定位问题

    这个问题虽然简单,但体现了开发中常见的"假设陷阱"。通过系统性的排查和验证,我们能够快速找到并解决问题。

    相关技术栈

    • MCP (Model Context Protocol): 标准化的AI上下文协议
    • Astro: 现代化静态站点生成器
    • SSH: 安全远程连接协议
    • macOS/Linux: 不同的操作系统路径约定
  • 用 Codex 连接 Blender MCP:从安装到做一辆儿童玩具小巴士

    这篇文章记录一次完整的实操:在 macOS 上安装 Blender 和 blender-mcp,把它接进 Codex,然后直接在 Blender 里做出一辆圆润卡通风格的儿童玩具小巴士。

    目标

    这次不是停留在“装好就算完”,而是希望把整条链路跑通:

    1. 安装 uvblender-mcp
    2. 配置 Codex 的 MCP server
    3. 安装 Blender 插件 addon.py
    4. 验证 Codex 能真实读取和操作 Blender 场景
    5. 基于自然语言,做出一个简单可爱的玩具小汽车模型

    环境

    • macOS
    • Blender 4.5.4 LTS
    • uv 0.11.8
    • Codex 本地 MCP 配置
    • 博客本身通过 astro-blog-ssh MCP 发布

    第一步:安装 Blender MCP

    blender-mcp 的结构其实很直接:

    • 一部分是 Blender 里的插件 addon.py
    • 一部分是本地 MCP server,由 uvx blender-mcp 启动

    在这台机器上,最终采用的是下面这套方式:

    1. 安装 uv

    uv 安装到用户目录下的 ~/bin,避免改系统环境。

    2. 在 Codex 里注册 Blender MCP

    注册成一个全局 MCP server,命令是:

    uvx blender-mcp

    同时额外加了:

    DISABLE_TELEMETRY=true

    3. 下载并安装 Blender 插件

    从仓库下载 addon.py,再安装到 Blender 用户插件目录。最终插件实际位于 Blender 的用户脚本目录里。

    第二步:安装 Blender 本体

    这台机器一开始并没有 Blender,所以后面直接走了官方 DMG:

    • 下载 Blender 官方 macOS Intel 版本
    • 安装到 ~/Applications/Blender.app
    • 用 Blender CLI 验证版本号,确认是 Blender 4.5.4 LTS

    这样做的好处是不用碰系统级 /Applications 权限,整个安装对现有环境影响更小。

    第三步:遇到的第一个坑

    表面上看,插件已经安装并启用了,但 Blender 侧栏里一开始没有看到 BlenderMCP 面板。

    排查后确认了几件事:

    • 插件模块已经被 Blender 识别
    • 插件已经启用
    • 面板类已经正确注册到 VIEW_3D > UI > BlenderMCP

    根因不是“没装上”,而是 Blender 界面和验证方式的问题。

    第四步:真正的连通问题

    更关键的问题出现在 MCP 调用阶段。

    最开始为了方便验证,尝试让 Blender 用 --background 模式启动,再自动拉起 blendermcp 服务。这个方式虽然能把 9876 端口打开,但真实请求会一直超时。

    后来定位到原因:

    blender-mcp 内部依赖 Blender 主线程上的 bpy.app.timers 来执行请求,而后台模式不会正常处理这类回调,所以会出现:

    • 端口能通
    • socket 能连上
    • 但真实命令没有响应

    这个点很关键。

    最后换成真正的 GUI Blender 实例启动,并自动执行插件的 start_server 操作之后,链路才真正打通。

    第五步:验证 Codex 可以直接控制 Blender

    连通后,先做了最基础的一次读取:

    • 获取当前场景信息
    • 返回了默认场景里的 CubeLightCamera

    接着做了第一个真正的操作:

    创建一个球体

    直接通过 Blender MCP 执行代码,在默认立方体上方创建一个球体:

    • 名称:Sphere
    • 位置:(0, 0, 2)

    这一步的意义不在于球体本身,而在于确认:

    • 命令确实执行到了 Blender
    • 场景读取结果能反映刚刚的变化

    做到这里,才算是真的“可以直接控制了”。

    第六步:做一辆儿童玩具小巴士

    接下来开始用自然语言一步步建一个玩具风模型。

    用户最终定下来的方向是:

    • 风格:圆润卡通
    • 类型:小巴士 / 面包车
    • 主色:蓝白色

    建模策略

    没有追求写实,而是走“基础几何体 + 圆角塑形”的路线:

    • 车身:方盒体,但做圆角
    • 车顶:白色独立盖板
    • 车窗:浅蓝半透明块面
    • 轮子:粗短圆柱
    • 细节:灯、条纹、后视镜、表情件

    这条路线的好处是非常适合儿童玩具感:

    • 比例夸张
    • 结构简单
    • 容易继续改可爱化风格
    • 不容易一不小心变成真实汽车

    第七步:建模过程中遇到的坑

    过程中也踩到了两个很典型的问题。

    1. 材质属性兼容问题

    第一次建模时,代码里用了 shadow_method,但当前 Blender 版本对应的材质对象没有这个属性,导致直接报错。

    处理方式很简单:

    • 去掉不兼容设置
    • 保留必要的 blend_method

    2. 父子关系叠加缩放问题

    第一次把零件都 parent 到车身时,车身本身还带有非 1 的缩放,于是子对象在世界坐标里被放大错位,轮子和装饰位置都跑掉了。

    根因定位后,采用了更稳妥的方式:

    • 先创建每个对象
    • 对需要的对象先应用缩放
    • 再做组合与摆放

    这样模型结构才恢复正常。

    第八步:从基础巴士到“更像玩具”

    模型先做出基础版本,然后又做了两轮增强。

    第一轮增强

    • 删除默认 CubeSphere
    • 把小巴士移到更容易查看的位置
    • 强化蓝白配色
    • 增加白色侧条纹
    • 增加车灯和腮红
    • 切换视口到材质预览

    第二轮增强

    继续往“可爱”而不是“写实”方向走:

    • 前后保险杠
    • 左右车门和把手
    • 后视镜
    • 顶部双色小警示灯
    • 尾灯
    • 前脸格栅

    第三轮增强:正脸表情

    为了让它更像儿童玩具,而不只是一个低模小车,最后又补了正脸表情:

    • 更大的眼睛底
    • 黑色瞳孔
    • 正脸腮红
    • 一个笑脸

    这样整个小巴士的气质就从“简化汽车模型”变成了更明显的“玩具角色车”。

    这次流程里最重要的经验

    如果只看结果,这篇文章像是在讲怎么做一个玩具车。但我觉得真正有价值的,是这几个经验:

    1. MCP 连上不等于真正能用

    最容易被误判的是:

    • 端口开了
    • 配置也写了
    • 工具看起来也注册成功了

    但真实请求未必能执行。

    一定要做至少一次真实读写验证,比如:

    • 读当前场景
    • 创建一个简单对象
    • 再把场景读回来

    2. Blender 的后台模式和 GUI 模式差异很重要

    某些插件依赖主线程回调或 UI 循环,后台模式并不等价于真正的 Blender 运行状态。

    这个问题如果不先定位,很容易误以为是:

    • 插件坏了
    • MCP server 坏了
    • 网络坏了

    实际上只是运行模式不对。

    3. 用 AI 控 Blender 时,先做最小可验证动作

    比起一上来就说“帮我做一个完整场景”,更稳妥的做法是:

    1. 先读场景
    2. 先创建球体
    3. 再做一个简单模型
    4. 最后再加风格和细节

    这样每一步都知道链路在哪一层是通的。

    最终效果

    最后得到的是一辆:

    • 蓝白主色
    • 圆润卡通比例
    • 小巴士轮廓
    • 带玩具化表情
    • 适合继续做展示图或继续精修

    它不追求工业设计精度,而是更偏“儿童玩具模型”的方向,这恰好也是自然语言建模特别适合的场景。

    总结

    这次最大的收获不是“装上了 Blender MCP”,而是把整条链路真正走通了:

    • 从本地安装
    • 到 Codex 接入 MCP
    • 到 Blender GUI 实际连通
    • 到真实建模
    • 再到风格化迭代

    如果只是把工具装上,最多算完成了 30%。

    真正值得记录的是:它已经可以被稳定地拿来做事情了。

    接下来如果继续往下走,很自然的方向会是:

    • 给这个玩具小巴士摆展示镜头和灯光
    • 继续做更多玩具角色车
    • 把 Blender MCP 接进更完整的内容生产流程

    比如:先让 AI 生成 3D 小道具,再把它们整理成博客里的可视化案例。这时候,MCP 就不只是“一个工具连接器”,而会变成整个创作工作流的一部分。

  • 给 Codex 增加自动整理并发布博客的 Skill

    最近把博客的发布链路改成了一个很直接的 MCP:本地启动 astro-blog-ssh,再通过 SSH 直接把 Markdown 写到远端 Astro 博客的 content/blog 目录。

    有了这条链路之后,一个很自然的下一步,就是让 Codex 不只是“能调用工具”,而是“知道什么时候应该把当前对话整理成文章并直接发布”。这次做的,就是把这套行为沉淀成一个可安装的 skill。

    目标不是安装说明,而是行为约束

    一开始最需要收窄的问题,不是怎么写 SKILL.md,而是这个 skill 到底要教 Codex 做什么。

    这次明确的目标是:

    1. 当用户明确说“发布到我的博客”时,Codex 直接整理并发布。
    2. 当当前对话已经形成成体系的技术内容时,Codex 也默认自动发布。
    3. 如果上下文不够,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_slugcreate_post
    • 发布失败时如何保留草稿并说明失败原因

    agents/openai.yaml 则负责给 Codex 的技能列表和显式调用入口提供元数据,比如:

    • display_name
    • short_description
    • default_prompt

    这个分层很重要。真正的工作规范应该放在 SKILL.md,而不是都挤进 description 里,不然模型很容易只读简短描述、不读完整 skill。

    skill 里最重要的几条规则

    相比“写出一篇文章”,更重要的是“什么时候不该发”。

    所以这次 skill 里最关键的约束有几条:

    1. 默认自动发布,但前提是当前对话已经足够完整。
    2. 文章必须围绕一个清晰技术主题,而不是原样搬运聊天记录。
    3. 对于小缺口,可以用保守假设补齐,并在最终响应里说明。
    4. 对于关键缺口,不能编造事实,只能补问或缩小文章范围。
    5. 涉及私密、凭据、内部细节的内容,不能直接发。

    这几条基本决定了 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 可用性。但本地已经做了这些可证实的检查:

    1. skill 目录结构完整。
    2. SKILL.md frontmatter 合法,命名符合 hyphen-case。
    3. openai.yaml 已生成,字段和 skill 语义一致。
    4. default_prompt 中的 $astro-blog-auto-publish 已按字面量保留。
    5. 仓库 diff 范围只包含 spec、plan 和 skill 目录,没有误改 MCP 服务代码。

    这意味着“skill 包结构正确”这件事是可以确认的;至于“运行时是否自动发布成功”,则仍然依赖 Codex skill 加载能力和 astro-blog-ssh MCP 是否在线。

    这类 skill 的价值

    如果只是偶尔发一篇文章,直接手工整理也能做。

    但当你已经有了 MCP 工具链,下一步最有价值的就不是重复调用工具,而是把“什么时候该调用、如何整理、什么情况下不能发”固化下来。

    skill 的真正价值,不在于省掉几个命令,而在于让自动化行为有边界、有默认策略、也有失败回退路径。

    这次的 astro-blog-auto-publish 就属于这种类型:它不是新能力,而是把已有能力变成了可重复复用的工作流。

  • 从零打通 WordPress + MCP:部署、反代、真实 CRUD 与可复用工具整理

    这篇文章想解决一个很具体的问题:怎么把一套 WordPress 博客,从本地部署、到公网访问、再到接入 MCP / AI 工具做文章增删改查,完整打通,并整理成可以上传 Git、让其他电脑直接复用的方案

    如果你刚接触 WordPress、Docker、反向代理,或者还不熟悉 MCP,这篇文章会尽量按“现象 → 原因 → 处理方式 → 注意事项”的顺序讲清楚。文中的敏感信息已经做过脱敏处理,例如:

    • 正式域名统一写成 your-blog.example
    • 服务器 IP 写成 your-server-ip
    • WordPress 用户名写成 your-wordpress-username
    • 应用密码写成 xxxx xxxx xxxx xxxx xxxx xxxx
    • 本地绝对路径写成 /path/to/project
    • 历史冲突域名写成 old-host.example

    一、这次最终达成了什么

    先看结果,再看过程。

    • 本地有一套稳定的 WordPress Docker 环境
    • 公网可以通过正式域名访问博客
    • Codex / MCP 可以对文章做真实的创建、读取、更新、删除
    • 工具目录已经整理成可直接上传 Git 的状态
    • 别人换一台电脑,也能按文档快速跑起来

    二、整体架构长什么样

    这套链路最终不是“直接把本地 8016 端口暴露出去”,而是做了一层更稳定的收口:

    your-blog.example
    -> 服务器 Nginx / 反向代理
    -> http://127.0.0.1:7412/
    -> nps / npc 隧道
    -> 本机 127.0.0.1:8016
    -> WordPress 容器

    这条链路的好处是:

    • 本地 WordPress 只监听在回环地址,不直接暴露在公网
    • 公网入口只有正式域名和服务器反代,访问路径更统一
    • 本地开发和线上访问逻辑可以分开处理,不容易混乱

    三、本地 WordPress 是怎么部署的

    本地 WordPress 使用 Docker 跑起来,最后固定在:

    http://127.0.0.1:8016

    为什么不用常见的 8080?原因很简单:开发环境里 8080 太常见,容易和已有服务、浏览器缓存、历史代理配置混在一起。换成一个明确的本地端口,可以减少排查成本。

    这一步的核心原则有两个:

    • 只监听本机:例如 127.0.0.1:8016:80
    • 本地端口只给本地和隧道使用,不要让它直接成为用户访问入口

    四、遇到的第一个坑:域名能打开,但会自动跳到 :8080 或 :8016

    这是整个过程中最容易让人误以为“浏览器坏了”或者“反向代理配置错了”的问题。

    1. 现象

    • 访问 https://your-blog.example,页面打不开
    • 浏览器地址栏会自动变成 https://your-blog.example:8080/:8016
    • 有时候域名本身能返回 200,但页面里资源、链接、跳转目标还是旧端口

    2. 原因

    根因通常不在浏览器,而在 WordPress 自己认错了“站点地址”。

    WordPress 有两个特别关键的概念:

    • WP_HOME
    • WP_SITEURL

    如果这两个值还保留着本地开发地址,比如 http://127.0.0.1:8016,那么用户虽然是通过正式域名进来的,WordPress 仍然会坚持把页面里的链接和重定向指回本地端口。

    3. 正确做法

    正式对外访问后,站点地址必须改成正式域名:

    define('WP_HOME', 'https://your-blog.example');
    define('WP_SITEURL', 'https://your-blog.example');

    一旦改对,再去验证:

    • 响应头里的 Link 是否已经变成正式域名
    • 页面 HTML 里的文章链接、静态资源、RSS 地址是否都换成正式域名

    4. 小白注意事项

    • 不要只看浏览器打开了没,要看响应头和页面源码
    • 如果你刚改完还在跳旧端口,先排除浏览器缓存和旧标签页
    • 如果你已经切换成正式域名,就不要再让文章内、配置内继续出现本地开发端口

    五、遇到的第二个坑:7412 端口反代后访问不了

    这个问题一开始看起来像“WordPress 没起来”,但实际并不是。

    1. 现象

    • 本地 127.0.0.1:8016 能访问
    • 服务器反向代理目标写的是 http://127.0.0.1:7412/
    • 但外部访问域名时仍然打不开

    2. 排查思路

    这个问题不能直接猜,要按链路一段一段排:

    1. 先确认 WordPress 容器是不是正常监听在本机端口
    2. 再确认 npc 当前转发目标是不是还是旧端口
    3. 再确认远端 nps 暴露的端口到底是不是你想要的那个端口
    4. 最后看服务器上的 Nginx 是否真的反代到了正确的本地入口

    3. 这次实际遇到的问题

    • 本地 WordPress 端口从 8080 切到了 8016
    • npc 配置文件里仍然保留着旧目标
    • 中途还有一条旧的 host 配置(这里脱敏写成 old-host.example)在服务端冲突,导致 npc 重连时报错

    换句话说,问题不是只有一层,而是“旧端口残留”和“旧反代项冲突”叠在了一起。

    4. 处理方式

    • 把本地服务统一切到 127.0.0.1:8016
    • npc 的 TCP 目标改成 127.0.0.1:8016
    • 把服务器端对外入口统一收口到 7412
    • 把正式域名只指向服务器反代,不再直接暴露本地开发端口

    5. 小白注意事项

    • “本地能访问”和“公网能访问”是两回事,别混在一起
    • 每改一层,就只验证这一层,不要一次改三四个地方
    • 端口切换后,一定把旧配置文件、旧代理项、旧浏览器缓存一起排查

    六、遇到的第三个坑:MCP / REST 明明配置好了,为什么还是 401

    这一段是最容易让人误判成“代码有问题”的地方。

    1. 现象

    • 读文章可以,写文章不行
    • 调用创建文章接口时返回 401
    • 错误像“不能创建文章”“未登录”“无权限”等

    2. 真正原因

    WordPress 写入接口最关键的不是普通账号密码,而是 Application Password

    这次排查里实际发现了两个问题:

    • 账号虽然是管理员,但用户下面根本没有实际生成的应用密码记录
    • 后来密码更新了,MCP 旧进程没重启,继续拿旧密码在请求

    3. 正确验证方式

    不要一上来就测创建文章,先测:

    /wp-json/wp/v2/users/me

    只有当它返回 200,并且用户身份正确时,才说明这套凭据真的可以用于后续写操作。

    4. 小白注意事项

    • 能登录 WordPress 后台,不等于能写 REST API
    • 应用密码要在对应用户资料页里单独生成
    • 密码更新后,如果工具进程是长驻的,记得重启进程

    七、MCP Server 和 Skill 到底各自做什么

    为了让整套方案更稳定,我把能力拆成了两层:

    • MCP Server:直接提供文章 CRUD 工具
    • Skill:告诉 Codex 在什么场景下优先走 MCP,在什么情况下可以回退 REST API

    这样做的好处是:

    • 在 Codex 里可以直接“像工具一样”管理文章
    • 就算某次会话里的内置 MCP transport 断了,也不影响独立验证服务是否正常
    • 工具目录可以独立迁移到其他电脑使用

    八、真实 CRUD 到底有没有跑通

    有,而且不是只做了“读一下列表”这种浅层验证,而是做了真正的闭环测试。

    1. REST API 闭环

    • 创建文章:成功
    • 读取文章:成功
    • 更新文章:成功
    • 删除文章:成功

    2. 独立 MCP 客户端闭环

    还额外写了一个独立测试脚本 test-mcp-client.mjs,通过 MCP 协议直接连接 wordpress-mcp.js,跑通了:

    • create_post
    • get_post
    • update_post
    • delete_post

    这说明问题真正的边界已经很清楚:

    • WordPress 站点没问题
    • 应用密码没问题
    • MCP 服务本身没问题
    • 如果某次会话仍然报 transport closed,那多半是“会话 transport 状态”坏了,不是你的博客坏了

    九、为什么还要专门整理成 Git 可上传状态

    因为“自己电脑上能跑”不等于“这套东西可以长期复用”。如果后续要放到 Git,给别的电脑继续使用,必须把敏感信息和环境耦合清掉。

    这次收尾主要做了这些事:

    • 去掉示例配置里的真实域名、用户名、密码
    • 把测试脚本改成只读取环境变量
    • 新增 .env.example
    • 让别人只需要复制一份 .env 就能跑
    • 新增 npm run start:envnpm run test:mcp:env

    十、如果你是第一次照着做,最短步骤是什么

    如果只是想先把工具跑起来,而不是一次性把所有细节都吃透,可以按下面这条最短路径走:

    cd wordpress-ai-tools/mcp-server
    cp .env.example .env
    # 把 .env 里的站点地址、用户名、应用密码改成你自己的
    npm install
    npm run start:env

    如果还想确认整条 MCP 链路真的能写文章,再执行:

    npm run test:mcp:env

    十一、这次最值得记住的几个注意事项

    • 正式域名上线后,WordPress 站点地址一定要改成正式域名,不要留本地端口
    • 反代问题排查要一层一层来,不要混着猜
    • REST API 写入靠的是 Application Password,不是后台登录密码
    • 进程是长驻的,密码变了不重启,工具可能还在用旧凭据
    • 上传 Git 前一定做脱敏,不要把密码、用户名、绝对路径、内网信息带进去

    结语

    这次最大的价值,不是“我把 WordPress 跑起来了”,而是把它整理成了一条真正可重复、可迁移、可验证的工作流:本地部署可控、公网访问稳定、AI 工具可写、目录结构可复用

    如果后续继续扩展,这套基础还可以往媒体上传、页面管理、分类标签自动化、自动发布工作流继续延伸。对个人博客来说,这已经不只是一个站点,而是一套可以长期积累的内容工具链。