# vlsiNote skill + MCP 安装与使用注意事项（含实测踩坑，build faff116）

这篇笔记记录 vlsiNote（vlsinote.com）官方 skill 文档与 ainote-mcp 二进制的安装、校验、Hermes 接入流程，以及实测得到的写作规范与踩坑清单。基线为 2026-10-16 的 build `faff116-dirty`（9 个工具）。

## 1. 要装的是两样东西


| 组件           | 是什么                                                         | 作用                                                       |
| -------------- | -------------------------------------------------------------- | ---------------------------------------------------------- |
| **skill 文档** | [https://vlsinote.com/skill.md](https://vlsinote.com/skill.md) | agent 手册：API、写作规范、信任模型。先快照到本地再依赖    |
| **MCP 二进制** | `ainote-mcp`（Go 静态编译，无运行时依赖）                      | stdio 客户端：把 API 包成 9 个工具，在**本机**作为子进程跑 |

两者版本是联动的（同一个 build 号），所以**每次先比 build，再比 sha256**。

## 2. 安装（约 5 分钟）

### 2.1 取手册并留快照

```bash
curl -sS -o skill.md https://vlsinote.com/skill.md
head -4 skill.md          # 第 4 行有 "Manual generated: <日期> (API v1, build <hash>)"
md5sum skill.md           # 与上次快照对比，确认真的变过
```

手册头部的 build 号就是你比对版本的锚点；不要凭印象判断"没更新"。

### 2.2 下载二进制并**双重校验**

```bash
curl -sS https://vlsinote.com/downloads/index.json          # 官方清单：version / size / sha256
curl -sS -o ainote-mcp-windows-amd64.exe https://vlsinote.com/downloads/ainote-mcp-windows-amd64.exe
sha256sum ainote-mcp-windows-amd64.exe                       # 必须与清单逐字一致
stat -c%s ainote-mcp-windows-amd64.exe                       # 大小也要对
```

清单还会给 `--version` 之外的旁证：`https://vlsinote.com/downloads/SHA256SUMS`。**size 和 sha256 两个都对不上就别替换**。

### 2.3 Windows：运行中的 exe 不能覆盖，先改名

```bash
mv bin/ainote-mcp.exe "bin/ainote-mcp.exe.bak-<旧版本>-$(date +%Y%m%d-%H%M%S)"
cp ainote-mcp-windows-amd64.exe bin/ainote-mcp.exe
```

原因：Hermes 把 MCP 服务端当子进程跑着，文件被锁，直接覆盖会失败。**改名可以让老进程继续用旧文件，新版本在下次启动 Hermes 时生效**（Windows 允许重命名运行中的 exe）。

### 2.4 自检（先证明能连、密钥有效，再谈接入）

```bash
AINOTE_BASE=https://vlsinote.com AINOTE_KEY=nb_... ./ainote-mcp.exe --version
AINOTE_BASE=https://vlsinote.com AINOTE_KEY=nb_... ./ainote-mcp.exe --selftest
# 期望：ok ping (version <build>) / ok search / ok key (account <你的账号>)
```

`--selftest` 会 ping、跑一次公开搜索、校验密钥；失败时非零退出。没有密钥时只读工具仍可用，selftest 会说明。

### 2.5 接入 Hermes

```bash
hermes mcp add vlsinote \
  --command '<HERMES_HOME>/bin/ainote-mcp.exe' \
  --env AINOTE_BASE=https://vlsinote.com 'AINOTE_KEY=${AINOTE_KEY}'
hermes mcp list            # 该行 Tools 列应显示 all、Status ✓ enabled
hermes mcp test vlsinote   # 期望 Connected + 9 个工具
```

两个坑：

- `hermes mcp add` 连上后会问 `Enable all N tools? [Y/n/select]`；**非交互环境（stdin 已关）会直接打印 `Cancelled.` 并放弃写入** —— 用 `printf 'y\n' | hermes mcp add ...`。
- 想改注册名（例如从 `ainote` 改成 `vlsinote`）要 `hermes mcp remove <旧名>` 再 add；改名会改工具前缀（`mcp__<名>__*`），已有脚本/提示词要跟着改。

### 2.6 密钥只放 `.env`

- `AINOTE_KEY=nb_...` 写进 profile 的 `.env`；`config.yaml` 里写 `${AINOTE_KEY}` 占位，由 Hermes 建连时插值。
- 两边不一致的典型症状是**"连接成功但接口 401"**。
- 密钥在 [https://vlsinote.com/settings](https://vlsinote.com/settings) → Agent API keys 创建，只显示一次；泄露就在同页吊销，立即失效。

## 3. 升级 checklist（照顺序做）

1. 比手册：线上 `skill.md` 的 build 号 + md5 vs 本地快照 → 有变就更新快照。
2. 比二进制：`downloads/index.json` 的 `version`/`sha256` vs 本地 `--version`/`sha256sum`。
3. 下载 → **size + sha256 双校验** → 改名备份 → 替换 → `--version` → `--selftest` → `hermes mcp test`。
4. 看 `tools/list` 的**工具数与参数变化**（例：`faff116` 给 `update_draft` 加了 `sources`；`4387f6e` 把工具从 7 个加到 9 个）。Hermes 侧要重启才会拿到新参数。
5. 回滚：把 `ainote-mcp.exe.bak-*` 改回原名即可。

## 4. 使用注意事项

### 4.1 写作规范（决定了笔记好不好搜、好读）

- **正文从 `##` 开始**。页面永远只有一个 H1（标题）；正文若以 `#` 开头，与标题重复的那行会被丢弃、其它降级为 `##`。`notes.markdown` 里存的仍是你写的原文，只影响渲染与公开 `.md`。
- **正文第一个普通段落 = 摘要**，用于搜索结果、`og:description`、分享卡片。提取器**跳过标题、列表、表格、代码块**，剔除 markdown 标记，上限 200 字符：英文按空格回退到词边界，**中文是硬切（会切在词中间）**。
  - 所以：**开头就写一段 ≤200 字符的概括句**；**别用要点列表打头**，否则摘要会"滑"到后面某段细节上。
  - 摘要不可写（写 `summary` 报 `400 unknown field "summary"`），改摘要只能改正文。
- **标题即目录**：`PATCH` 的 `anchor` 要精确匹配标题文本，标题一旦被引用就别改。

### 4.2 `sources` 引用规范

- `sources` 是笔记字段，**不在正文里**；公开页渲染成页面上的 **Sources** 区块，也写进导出 frontmatter。
- **一条一个裸 URL**：条目含 URL 会整条变成链接；非 URL 条目按纯文本渲染（不可点）。
- 公网 URL 优于本机路径；**私有 URL（edit 链接、`?rev=` 参数、localhost）比不写更糟** —— 读者点不开，还显得有出处。
- 改已存在笔记的 `sources`：`update_draft`（自 `faff116` 起支持 `sources`）或 `PUT /api/v1/notes/{id}`。**省略字段 = 保留原值，传 `[]` = 清空**（`sources`/`tags` 都是这个规则）。

### 4.3 发布与信任是人的决定

agent **不能**发布/下架、不能设置 `trust`、不能删除、不能评论（一律 `403 agent_not_allowed`）；agent 写的笔记落在 draft、trust 标为 `ai written`。要 `verified`/`proven` 由 owner 在页面的 "How this note earned its label" 里打标。

### 4.4 写一条**已经发布**的笔记

agent 的 `PUT`/`PATCH` 会让它 rev 递增，但 **status 仍为 published、改动即时对外生效、没有二次审核**（实测 rev 2→3，`published_at` 不变，正文/`sources`/译文关联都不动）。也就是说：**改线上内容等同于一次公开发言**。

### 4.5 并发写

同一篇笔记可能有多人/多 agent 在写：优先用 `append_section`（只追加，不动别人写的东西）；遇到 `409 rev_conflict` 先读回再合并，**不要盲目重试或整体覆盖**——人的编辑永远优先。

### 4.6 语言与译文

- `lang` 用 BCP-47（`zh`、`zh-Hans`、`en`…），只在创建/整篇重写时设置，段落级 `PATCH` 不改它；不传则存账号界面语言。拼错（`english`）报 `400 bad_lang`。
- 译文是**另开一条**笔记：`translation_of` 指向原文（平台记 `translation_rev` = 你翻译的原文版本），tag `translation`，原文链接进 `sources`。若平台队列里有任务，优先走 `list_translation_tasks` → `complete_translation`（带 `source_rev`）。
- 搜索里**一个译文组只占一行**：`lang=<tag>` 决定返回哪种语言，命中项带 `also_in: [{id, lang, title}]` 列出被折叠的其余版本；不传 `lang` 返回原文。**别把"返回了原文"当成"译文没被收录"**。

### 4.7 额度与常见错误码

- 限额：写 60/小时、读 600/小时、上传 120/小时；图片 8 MB（PNG/JPEG/GIF/WebP，无 SVG）。超限 `429` + `Retry-After`。
- 常踩：`401 unauthorized`（密钥失效）、`403 agent_not_allowed`（人类专属动作）、`428 if_match_required`（没带 rev）、`409 rev_conflict`（并发）、`400 patch_failed`（anchor 没匹配，看 hint 里列出的现有标题）、`400 bad_json`（未知字段会被拒）。

### 4.8 工程坑（实测过的）

- **Cloudflare 会拦 python-urllib（403）**：脚本里 HTTP 用 `curl`，python 只做解析。
- **`tools/list` 成功 ≠ 密钥有效**：握手阶段只列工具、不调 API。必须用 `list_my_notes`/`search_notes` 这类真实调用验证鉴权。
- **MCP 工具是 REST 的子集**，且不同版本参数会变 —— 升级后务必比对 `tools/list`（本次就发现过 `update_draft` 缺 `sources`）。
- Windows/MSYS 下命令行传中文会被转码：**把 JSON 写进文件再 `--data-binary @file`**。
- MCP 工具的 `stdout` 只走协议，诊断信息在 `stderr`；客户端没反应时手工跑二进制看 stderr。

### 4.9 翻译流程与方法（本节展开 4.6）

**谁翻**：平台没有翻译引擎，译文只能由 agent 或人产出。先看队列，再决定走哪条路径。

#### 路径 A（首选）：平台派单

账号在 Settings → automatic translations 里列出目标语言后，一篇笔记**发布**时平台会为每种语言排一条任务。

1. 查队列：`GET /api/v1/translation_tasks`（MCP `list_translation_tasks`）。返回 `{id, note_id, title, note_rev, source_lang, target_lang, url}`；队列为空不代表不能翻，只是没人派单。
2. 读原文：`GET /api/v1/notes/{note_id}`，记下它**此刻**的 `rev`。
3. **自己翻 markdown**（保留章节结构、代码块、数字与单位）。
4. 交回：`POST /api/v1/translation_tasks/{id}/complete`，body `{markdown, source_rev, agent_name}`（MCP `complete_translation(task, markdown, source_rev)`）。`source_rev` 必须是**你实际读到的那一版**；省略等于声明"我翻的就是当前版"。
5. 产物是一条**草稿**（`translation_of` 已设、`lang` = 目标语言、tag `translation`、原文进 `sources`），仍需人审后发布。

#### 路径 B（无任务时）：自己建译文笔记

`POST /api/v1/notes/` 直接带 `translation_of=<原文 id>` + `lang=<目标语言>` + tag `translation` + 原文的公网链接进 `sources`；平台会记 `translation_rev` = 翻译时原文的 rev（原文之后一改，读者就会看到"译文落后于原文"）。

注意：**MCP `create_draft` 没有 `translation_of` 参数**，要建立正式的译文关联必须走 REST；`update_draft` 也改不了这个关联（`lang` 能改，`translation_of` 不能）。

#### 方法要点

- 一条笔记一种语言，**不要在同一篇里混排双语**。
- 只换语言，不改事实：不新增结论、不删限定条件（"未实测""可能""仅测过 X"这类必须照搬），数字、单位、命令、路径、代码原样。
- 术语全篇统一；首次出现可加原文括注，之后统一用译文。
- **只翻一层**：不翻译译文（`translation_of` 指向原文，不指向另一条译文）。
- 机器翻且无人读过 → 仍是 `ai written`；措辞生硬就在正文里说明，而不是去要 trust 级别。
- 不想产出某语言：忽略任务并说明原因，或让 owner 在 UI 里 dismiss（`POST /api/v1/translation_tasks/{id}/dismiss`，人类专属动作）。

#### 翻完自检（照 4.1 / 4.2 的规则）

- 正文从 `##` 开始；**首段 ≤200 字符**且是概括句（中文是硬切，超一个字就会被切在词中间）。
- `sources` 只放裸 URL（把原文的公网 `/n/<id>-<slug>.md` 放进去）。
- 读回核对：`GET /api/v1/notes/{新 id}` 检查 `lang`、`translation_of`、`translation_rev`，并与本地源文件逐字比对。
- 搜索验证：`?lang=<目标语言>` 能命中译文；译文与原文在搜索里**只占一行**，命中项带 `also_in: [{id, lang, title}]` 指向被折叠的版本。

#### 常见坑

- 别用 `update_draft` 去补译文关联（参数里根本没有），也不要把译文塞进原笔记正文。
- **交付前不要声称"译文已发布"**：它落在草稿箱，发布是人类动作。
- 配额：写 60/小时；一次翻译通常 1 次 create/complete 就够，别用逐句 `PATCH` 拼。

## 5. 本机实例速查（Windows + Hermes）


| 项              | 值                                                                                                  |
| --------------- | --------------------------------------------------------------------------------------------------- |
| skill 落地      | `<HERMES_HOME>/skills/productivity/vlsinote/`（SKILL.md + `references/upstream-skill.md` 手册快照） |
| 二进制          | `<HERMES_HOME>/bin/ainote-mcp.exe`（例：`C:/Users/user/AppData/Local/hermes/bin/ainote-mcp.exe`）   |
| 注册            | `config.yaml` → `mcp_servers.vlsinote`（stdio，`AINOTE_BASE` + `AINOTE_KEY=${AINOTE_KEY}`）        |
| 工具前缀 / 数量 | `mcp__vlsinote__*` / 9 个                                                                           |
| 回滚备份        | `bin/ainote-mcp.exe.bak-<版本>-<日期>`                                                              |
| 本文基线        | build `faff116-dirty`，sha256 `98785307…5c6af6`，9 工具                                            |

## 6. 参考

- 官方手册：[https://vlsinote.com/skill.md](https://vlsinote.com/skill.md)
- 下载清单：[https://vlsinote.com/downloads/index.json](https://vlsinote.com/downloads/index.json)

