Astro Publisher MCP
by qixianshang
README.md
# Astro Publisher MCP
通过 AI 客户端管理 Markdown 草稿,将 Astro 静态博客发布到自己的服务器。
这是我的个人工程项目:把“写文章 → 本地检查 → 构建 → 上传 → 切换版本 → 验证”封装成标准 MCP 工具。它不依赖网站后台,不在公网新增管理 API,也不需要在低配置服务器上安装 Node.js 或执行 Astro 构建。
**当前版本:0.1.0 MVP。** 支持本地 stdio MCP、Markdown、SSH/SFTP 和 Linux Nginx 静态站。代码不包含真实私钥或个人服务器配置。真实博客使用的四分类 schema 是当前内容适配器的约定,适配其他站点时需对应调整。
## 工作方式
```mermaid
flowchart LR
A[MCP 客户端] --> B[本地 MCP 服务]
B --> C[草稿与摘要校验]
C --> D[隔离目录 Astro 构建]
D --> E[SSH 指纹校验与 SFTP 上传]
E --> F[远端 SHA-256 验证]
F --> G[原子切换 current 链接]
G --> H[HTTPS 验证文章]
H -->|失败| I[回退上一版本]
```
## 已实现能力
| 工具 | 用途 |
| ------------------ | ---------------------------------------- |
| `site_status` | 检查 SSH 连接及当前版本 |
| `list_posts` | 列出本地文章与摘要 |
| `read_post` | 读取文章或草稿(`draft: true`) |
| `save_draft` | 校验并保存 Markdown,不自动上线 |
| `check_draft` | 在临时副本中运行 Astro 检查与构建 |
| `publish_post` | 构建、持久保存本地文章、上传、切换并验证 |
| `list_releases` | 查看远端历史版本 |
| `rollback_release` | 按预期当前版本回退,防止覆盖别人的新发布 |
已有草稿更新需要 `expectedDigest`;已有正式文章更新需要 `expectedPublishedDigest`。摘要是读取结果中的 SHA-256,用来避免旧内容覆盖新修改。
发布使用本地跨进程锁;远端启用文件锁和版本比较,只有当前版本仍符合预期时才切换。历史版本不会自动删除。
## 快速开始
需要 Node.js 22+、已安装依赖的 Astro 站点、Linux 服务器上的 Python 3.10+、可用的 SSH 密钥和静态 Web 服务。
```bash
npm ci
npm run build
npm test
```
复制 `config.example.json` 为 `config.local.json`,填写自己的站点目录、域名、SSH 身份与发布目录。配置中的私钥是**本地文件路径**,不要粘贴私钥内容。服务器公钥指纹需要通过已可信的服务器控制台核对,例如在服务器上执行:
```bash
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256
```
`hostKeySha256` 填写输出中的 Base64 指纹,不含 `SHA256:` 前缀。实现会对 SSH 实际选择的主机密钥进行严格比对;若服务器提供多种主机密钥,应确认协商使用的那一把,不能通过关闭校验解决。
测试 MCP 进程:
```bash
node dist/server.js /absolute/path/to/config.local.json
```
正常启动后等待 MCP 客户端输入,不会打印普通欢迎信息到 stdout。通用客户端配置见 [examples/mcp.json](examples/mcp.json)。Codex 可用本地 stdio 注册命令(替换为自己的绝对路径):
```bash
codex mcp add astroPublisher -- node /absolute/path/to/astro-publisher-mcp/dist/server.js /absolute/path/to/config.local.json
```
参考 [官方 MCP 配置文档](https://developers.openai.com/codex/mcp)。长时间构建建议在客户端中将该 MCP 的工具调用超时设为至少 300 秒。
## 首次配置服务器
发布器不会自动改动 Nginx、创建账号或安装公钥。管理员需先创建初始版本并让 Nginx 使用 `current` 链接。下面用 `/var/www/astro-releases` 举例,实际目录必须与配置一致:
```bash
# 上传 remote/worker.py 和 remote/bootstrap.py 到服务器的同一目录
python3 bootstrap.py /var/www/astro-releases /path/to/current-static-site
```
将 Nginx 站点 `root` 设为 `/var/www/astro-releases/current`,保留原来的 TLS 证书、ACME 续期目录和其他路由;检查配置后重载。发布账号需要 release 根目录的读写权限与 SSH 命令、SFTP 能力。推荐使用专用发布账号;MCP 不需要 root 权限。
初始版本必须包含 `index.html`。bootstrap 会复制旧站,并生成清单与校验标记。回滚只接受已完成验证的版本,并重新验证每个文件摘要;未上传完成的目录不列为可回滚版本。站点如有 CDN,版本标记文件和 HTML 应按请求绕过缓存,否则验证可能触发回退。
## 使用示例
先调用 `save_draft`:
```json
{
"slug": "understanding-mcp",
"title": "理解 MCP 的工具调用边界",
"description": "从一个本地博客发布工具理解 MCP。",
"pubDate": "2026-11-11",
"category": "agent",
"tags": ["MCP", "Astro"],
"body": "## 为什么做这个工具\n\n把重复的发布步骤变成可检查的工作流。"
}
```
取得返回的 `digest`,调用 `check_draft`;用户要求上线时再调用 `publish_post`:
```json
{ "slug": "understanding-mcp", "digest": "从工具返回中取得的64位摘要" }
```
分类为 `agent`、`interview`、`bug`、`english`;站点可将 `interview` 显示为“基础知识”。草稿位于站点的 `.publisher/drafts/`,发布日志为 `.publisher/history.jsonl`。应在站点自身的 `.gitignore` 中加入 `.publisher/`。
## 失败状态
| 结果 | 含义与处理 |
| ------------------- | ---------------------------------------------------------------------------------------- |
| `published` | HTTPS 版本标记和目标文章字节摘要均通过 |
| `not_published` | 上传未完成、切换被拒绝,或验证失败后已回退;本地 Markdown 可能已保存,重试需读取最新摘要 |
| `unknown` | 切换阶段失去明确结果,先查 `site_status`,不要盲目重试 |
| `recovery_required` | 验证与回退均失败,需要人工核对当前链接与历史版本 |
构建失败不会上传或修改正式文章。上传失败保留本地文章便于重试。失败上传产生的未激活目录可能留在服务器上,当前版本不自动清理。进程崩溃留下的本地锁需要确认相关进程已退出后手动移除。
## 安全与边界
- 只处理限制长度的 Markdown,不支持工具输入执行任意命令、MDX 导入、脚本或任意路径写入。
- slug、日期、分类严格校验;危险 HTML 标签、事件属性及协议会被拒绝,支持 details 等常用教学排版标签。
- SSH 目的地来自可信本地配置;工具参数不能切换服务器。私钥仅用于内存中的 SSH 握手,不写入日志。
- 本地 Astro 源码及其依赖属于**可信代码**,构建不是沙箱。不要对陌生仓库运行该服务。
- 临时构建重用站点现有 node_modules,调用前应在站点执行 `npm ci`;不在每篇文章发布时重新安装。站点的 .env 不复制到临时构建;当前版本需将公开构建配置放在可信源码中。
- 输出日志与读回的文章都属于数据,不能视为新的指令。MCP 工具标注不取代客户端权限控制。
- 回滚工具校验远端当前版本并切换已有目录;自动发布额外执行 HTTPS 验证。不能声称提供容器级隔离、完整审计系统或数据库事务。
## 验证与后续计划
本地 15 项测试覆盖协议握手、草稿冲突、路径与符号链接、发布锁、构建/上传失败、SSH 指纹和 HTTPS 验证。Linux Python 6 项测试覆盖清单损坏拒绝切换、版本切换及回退、旧版本冲突和路径边界。实际 Astro 站点已完成临时副本构建,并对既有页面完成真实 SFTP 发布、HTTPS 验证和回退,未向公网发布测试文章。详见 [验证记录](docs/verification.md)。
GitHub Actions 定义了 Windows/Linux Node 检查及 Linux 远端 worker 测试;CI 的实际运行状态以仓库 Actions 为准。
后续计划:更多 Astro schema 适配器、图片资源管理、版本清理策略与更完整的发布状态恢复。当前不支持远程 HTTP MCP、定时发文和文章删除。
## 项目结构
```text
src/content.ts 草稿、校验、摘要与锁
src/build.ts 临时构建与资源清理
src/remote.ts SSH/SFTP、版本切换与 HTTPS 验证
src/publisher.ts 发布流程与结果日志
src/mcp.ts MCP 工具注册
src/server.ts stdio 启动入口
remote/worker.py Linux 远端发布操作
tests/ Node / MCP 测试
remote/test_worker.py Linux 文件系统测试
```
MIT License.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues