Skip to main content
Glama
yzr95924

agent-html-drop

by yzr95924

agent-html-drop

常驻 HTTP daemon,让本机 agent(Claude Code / OpenCode)经 MCP 把自包含 HTML (yzr-md-to-html 等工具的产物)推到远端 nginx server,同时提供一个 HTML 管理页供人浏览 / 预览 / 删除 / 复制公开 URL。

形态

┌──────────────────────┐                ┌──────────────────────────────────────┐
│  本机 agent           │   HTTPS        │  远端 nginx server                   │
│  (Claude Code /      │ ─────────────► │   ┌────────────────────────────┐     │
│   OpenCode)          │  /mcp + Bearer │   │ agent-html-drop daemon     │     │
│                      │                │   │  127.0.0.1:8765            │     │
│                      │                │   │  ├ POST /mcp    MCP server │     │
│                      │                │   │  ├ GET  /       HTML 管理页│     │
│                      │                │   │  └ /api/* + /files/        │     │
│                      │                │   └────────────┬───────────────┘     │
│  浏览器(人)         │ ──HTTPS──────► │       ┌────────▼────────────┐       │
│   https://notes...   │                │       │ nginx               │       │
│                      │                │       │  :443 → 反代 → :8765│       │
│                      │                │       │  + 直 serve /files/*│       │
│                      │                │       └─────────┬───────────┘       │
│                      │                │           docroot/                  │
└──────────────────────┘                └──────────────────────────────────────┘
  • daemon 监听 127.0.0.1 only——由 nginx 在前面 HTTPS 反代 + 终结 TLS

  • 单进程 stdlib http.server.ThreadingHTTPServer——无第三方运行时依赖

  • MCP Streamable HTTP 自实现 ~150 行——7 个 tool,无 MCP SDK

Related MCP server: agent-drop

Docker 部署

镜像发布在 ghcr.io/yzr95924/agent-html-drop(多架构 linux/amd64 + linux/arm64)。 docker compose up -d 会自动从 GHCR 拉取已发布版本,不需要本地 builddocker-compose.yaml 里的 image: 字段指向带版本 tag(默认 :v0.1.0),不会随 :latest 静默换版。想本地重建:在 compose 里去掉 image: 前面的 # 注释并取消 build: . 注释。

把 daemon 打成自包含镜像,用自己的 nginx 反代 HTTP 到容器端口。 容器自包含:daemon 自己服务 /files/*,nginx 是纯反代、不碰 docroot;TLS 仍在你的 nginx 终结(边缘 HTTPS + 内部 HTTP,Secure cookie / CSRF 照常工作)。详见 docs/design.md §15。

# 1. 编辑 docker-compose.yaml:把 PUBLIC_BASE_URL 改成你的 HTTPS origin
# 2. 起服务(首次自动生成 token + config,持久化在 ./data/)
docker compose up -d

# 3. 取 token,配给本机 agent 的 MCP config(url = https://<origin>/mcp)
docker compose exec agent-html-drop agent-html-drop token show

# 4. 拿 nginx 反代片段,贴进你现有 nginx 的 HTTPS server block,reload
docker compose exec agent-html-drop agent-html-drop nginx-config

数据与卷(备份 / 迁移就靠这两个目录):

容器路径

宿主路径

用途

docroot

/data/docroot

./data/docroot

HTML + 批注——备份目标

config+token

/data/config(→ …/agent-html-drop/config.toml

./data/config

凭据,0600,单独备份

迁移到另一台机:scp 整个 ./data/ + docker-compose.yaml(+ 重新 build 镜像)即可。

uid 对齐:容器以非 root(uid 1000)运行。若宿主 ./data/ 所有者不是 uid 1000, 写 docroot/config 会权限拒绝——chown -R 1000:1000 ./data,或在 compose 里覆盖 user:

docker exec 不走 ENTRYPOINT:跑子命令要用镜像里的 wrapper,即 docker compose exec agent-html-drop agent-html-drop <subcommand>(service 名与命令名各出现一次)。

容器冒烟测试:bash scripts/docker-smoke.sh(需要 docker)。

容器起来后,在本机 agent 侧配 MCP:

// Claude Code MCP config: ~/.claude.json (或类似)
{
  "mcpServers": {
    "agent-html-drop": {
      "url": "https://<origin>/mcp",
      "headers": {
        "Authorization": "Bearer <docker compose exec agent-html-drop agent-html-drop token show 输出>"
      }
    }
  }
}

agent 可以调 7 个 tool:upload_html / prepare_upload / list_html / delete_html / get_public_url / list_annotations / delete_annotation。 配合 yzr-md-to-html 使用流程:

# 1. 生成 HTML(agent shell)
yzr-md-tohtml file.md > /tmp/file.html

# 2. 流式上传 HTML 字节——不经过 LLM context
SHA=$(sha256sum /tmp/file.html | cut -d' ' -f1)
curl -X PUT -H "Authorization: Bearer $AGENT_HTML_DROP_TOKEN" \
     --data-binary @/tmp/file.html \
     "http://127.0.0.1:8765/files/file.html?force=true"
# 响应:{"name":"file.html","url":"https://.../files/file.html","size":...,"sha256":...}

# 3. MCP 上报(只传 ~80 字节元数据)—— LLM context 拿到 URL
upload_html(name="file.html", sha256=$SHA)

upload_html 现在是元数据校验 tool:HTML 字节走 PUT /files/<name>(Bearer 流式旁路), MCP 这一步只验证文件已落盘且 sha256 一致,再把 public URL 注入 LLM context。 这样大 HTML 不再吃掉 LLM 的 token 预算。

curl 不能带 token?走 prepare_upload 旁路

某些 agent 的 shell 策略会拦截携带 Authorization 头 / 已知密钥的 curl 命令,使上面的 curl -H "Authorization: Bearer ..." 跑不起来。这时改用 prepare_upload:它走已认证的 MCP 通道 签发一个短时(120s)、限定文件名的 capability,agent 拿到一个预签名 URL,PUT 时不带任何 Authorization 头——主 token 全程不进任何命令。

# 1. 经 MCP 通道签发 capability(MCP transport 自动带 token,agent 不碰)
prepare_upload(name="file.html")
#   → {"name":"file.html",
#      "upload_url":"http(s)://<HOST>/files/file.html?upload=<cap>&force=true",
#      "curl":"curl -X PUT --data-binary @/path/to/file.html \"<upload_url>\"",
#      "expires_in":120}

# 2. 用返回的 upload_url 上传(自带 capability + force=true,整段可直接喂给任意 HTTP client)
#    注意:没有 -H Authorization
curl -X PUT --data-binary @/tmp/file.html "<upload_url>"

# 3. MCP 上报(同前)
upload_html(name="file.html", sha256=$SHA)

capability 是 name-bound 的 HMAC 签名串(同 anno session cookie 的签名机制,独立 domain tag), stateless、不强制单次使用——TTL 内重放只是覆盖同名文件(单用户 + 公开 docroot 下无害)。 Bearer 路径完整保留:不限制 curl 的 agent 照旧用原流程。

命令一览

agent-html-drop init [--force]                  # 创建 config + 生成 token
agent-html-drop serve [--config PATH]           # 前台启动 daemon
agent-html-drop token show                      # stdout 明文 token(CLI 路径,UI 不再使用)
agent-html-drop token rotate                    # 重生成(daemon 需重启才生效)
agent-html-drop config show                     # 打印 config (token 掩码)
agent-html-drop config path                     # 打印 config 路径
agent-html-drop config edit                     # $EDITOR 打开 config
agent-html-drop nginx-config                    # stdout 打印反代片段(location 块)

## 查询 token

token 是 daemon 的凭据,不会出现在管理页 UI / localStorage(设计原则)。
所有查询都走 CLI。两条路径:

**Classic(直接装在宿主)**

```bash
# 明文,打印到 stdout —— 复制粘贴给 agent 的 MCP config
agent-html-drop token show

# Masked(首 4 + **** + 末 4),日常检查 token 是否设置推荐
agent-html-drop config show

# 生成新 token(旧 token 立刻作废,需重启 daemon 才生效)
agent-html-drop token rotate

Docker(容器化部署)

exec 不走 ENTRYPOINT,必须用镜像里的 wrapper —— agent-html-drop 在命令里出现两次是正常的:

# 明文
docker compose exec agent-html-drop agent-html-drop token show

# 或
docker exec <container_name> agent-html-drop token show

# 容器内的 config(含 masked token)
docker compose exec agent-html-drop agent-html-drop config show

安全提示:完整 token 会进 shell history / 终端快照 / 屏幕录制。 日常查看优先用 config show(自动 masked)。想批量管理凭据可: chmod 600 ~/.bash_history、设 HISTCONTROL=ignorespace(命令前加空格不进 history)、 或临时 unset HISTFILE 再跑。

Docker 常用命令

集中在这一节,避免翻各处凑。compose exec 一定要写两遍 agent-html-drop(service 名 + 命令名)—— 这是 exec 不走 ENTRYPOINT 的代价。

# —— 起停 ——
docker compose up -d --build       # 首次:构建镜像 + 后台启动
docker compose up -d               # 已有镜像:直接启动
docker compose restart             # 重启容器(卷 / config 保留)
docker compose down                # 停 + 删容器(卷 ./data 保留)
docker compose down --volumes      # ⚠️ 连 ./data 一起删——会丢 token + 所有 HTML

# —— 状态 / 日志 ——
docker compose ps                  # 容器状态
docker compose logs -f --tail 50   # 实时跟踪最近 50 行
docker compose logs agent-html-drop  # 只看 daemon 输出

# —— 取凭据 / 片段 ——
docker compose exec agent-html-drop agent-html-drop token show     # 明文 token
docker compose exec agent-html-drop agent-html-drop config show    # masked token
docker compose exec agent-html-drop agent-html-drop nginx-config   # nginx location 块
docker compose exec agent-html-drop agent-html-drop status         # config/token/docroot 状态

# —— 调试 / 验证 ——
docker compose exec agent-html-drop sh                             # 进容器 shell
docker compose exec agent-html-drop ls -la /data                   # 看持久化卷
bash scripts/docker-smoke.sh                                       # 一键冒烟:build → /api/health → /files → token

常见踩坑

  • 容器跑一会变 Restarting:当前版本(v0.1.0 之前)有此 bug,已修复;升级镜像即可。

  • exec 报 "executable file not found":写漏了第二个 agent-html-drop

  • 改了 docker-compose.yaml 但没生效:要 docker compose up -d(不是 restart)。

  • data/ 权限拒绝:容器以 uid 1000 跑,chown -R 1000:1000 ./data 对齐。 agent-html-drop nginx-config --write [PATH] # 写到 ~/.config/agent-html-drop/nginx.conf.example agent-html-drop status # 简报:config / token / docroot 状态


服务控制(用户级,无 sudo / systemd 依赖):

docker compose start|stop|restart|ps # 或 docker compose down / up -d


> 管理页只读:`GET /` 和 `GET /api/files` 都不鉴权;`DELETE /api/files/<name>` 与
> `GET /api/nginx-config` 仍要 Bearer(给运维 / 脚本用);`POST /mcp` 仍要 Bearer
> (agent 走)。**管理页里不再有 token 输入框,token 也不进 localStorage**——token
> 只在 server 端 `config.toml` 与本机 agent MCP config 之间手动同步。

## 文件命名 / 大小 / 覆盖规则

- **文件名 regex**:`^[A-Za-z0-9._-]+\.html$`(大小写不敏感匹配 `.html`),≤ 200 字符
- **大小**:默认上限 50 MB(`config.max_file_size` 可调)
- **同名上传**:默认 409 + `-32010`;带 `force=true` 才覆盖
- **公开 URL**:`<config.public_base_url>/files/<name>`(`public_base_url` 为纯 origin)

## 设计文档

- 设计:`docs/design.md`
- 任务书:`docs/tasks.md`

## 发布新版本(维护者)

发布流水线:push 一个 `v*` tag → GitHub Actions 自动 buildx 多架构构建(amd64 + arm64)
→ push 到 `ghcr.io/yzr95924/agent-html-drop` → post-publish smoke(pull + 起容器 + `/api/health`)。
详见 `docs/design.md` §16 / `.github/workflows/release-image.yml`。

```bash
# 1. 确保所有变更已 commit + push 到 master
# 2. 打语义化版本 tag
git tag v0.2.0
git push --tags

# 3. 看 CI:https://github.com/yzr95924/agent-html-drop/actions/workflows/release-image.yml
# 4. 镜像 tag 矩阵:v0.2.0, 0.2, 0.2.0, latest, sha-<short>
#    全部指向同一 manifest list(多架构自动挑)

首次发布:GHCR 包默认创建为 private。push 后去 https://github.com/yzr95924/agent-html-drop/settings/packages 改成 public, 否则别人 docker pull 不到。

失败回滚:如果 smoke 失败但镜像已推,workflow 标红 ✗ 但镜像留在 GHCR——手动到包页面删 坏 tag 或发一个 patch 版本覆盖;不要自动删,避免误删正在用的版本。

测试

pytest                    # 全量(含 install→serve→MCP→uninstall 冒烟)
pytest tests/test_cli.py -v

测试隔离:autouse fixture 把 ~/.config/agent-html-drop/ 重定向到 tmp,并在每个测试结束 校验真实配置目录 mtime 未变——详见 tests/conftest.py

局限性

  • 不管 nginx 配置 / 不 reload / 不写证书——daemon 只产生反代片段模板,用户自己装

  • 不做 mTLS / OAuth(单 Bearer 静态密钥)

  • 不做多 docroot / 多租户

  • 不存元数据库(title 从 HTML 解析,mtime/size 从 stat 取)

  • daemon 保活由 Docker 负责(docker-compose.yamlrestart: unless-stopped

  • 管理页只读:list / 预览 / 复制公开 URL 在浏览器完成;删除上传 只能通过 agent MCP(本机 Claude Code / OpenCode 调 delete_html / upload_html),管理页 故意不做删除按钮 / 上传表单,token 也不在 UI 出现。

批注(可选)

管理页支持浏览器侧批注:选中 iframe 中的文本,填评论,提交。批注不影响原始 HTML 文件(.html.meta 严格分离),所以可以放心反复修改设计稿,批注留档。

启用方式:

  1. 进入批注模式:管理页右上角 "批注(需 token)" 按钮 → 弹框 → 粘贴 token (在 server 端跑 agent-html-drop token show 获取)→ 进入。

  2. 选中文本 → 弹出评论输入框 → 提交。批注高亮(<mark>)自动注入到 iframe。

  3. 退出批注模式:点 "退出" 链接。cookie 30 分钟自动过期。

agent 视角:

  • list_annotations(name) —— 读取某文件的全部批注(结构化字段)

  • delete_annotation(name, id) —— 删除某条(用于清理 spam / 已解决)

  • 不开放 add_annotation 给 agent(写批注由浏览器发起)

安全模型:

  • 浏览器写批注走短期 session cookie(30 分钟,HttpOnly / Secure / SameSite=Lax)

  • agent 改 HTML 走原有 Bearer token

  • 两条路径互不重叠,agent 无批注写接口,浏览器无 HTML 写接口

  • nginx 模板默认带 limit_req/api/auth 暴力穷举

Maintenance

ActivityMaintained
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers