# MCP 服务器

基于 K2F SDK 的本地 Model Context Protocol 适配器。工具名称和 JSON schema 来自 `engine/k2f_mcp/mcp_tools.json`（在 `@openk2f/k2f` 中镜像为 `public/mcp_tools.json`）。

> **MCP：stdio 已提供** — 在 k2f 检出目录中运行 `cargo run -p k2f_mcp --release`。**远程 HTTP MCP 尚未提供**（[项目状态](/status)）。不使用 MCP 时，请使用 [K2F 技能](/skills/k2f)。

## Agent 工作流

K2F MCP：复制作者目录（create），或 open/markdown_to_k2f → Editor。在 Editor 上使用 insert_node/replace_text/set_role。用 form_field 节点 + replace_text 填充空白；绝不要在正文中画 ____。save 写入 .K2F（重新锁定）；export_pdf 需要 path。sign 不是工具。

## 工具（16）

每个工具都封装一个现有 SDK 调用。`sign` 不是工具。

### 打开会话

返回 session_id 和 outline — 绝不返回 lock JSON。

| 工具 | 描述 | SDK | 文档 |
| :--- | :--- | :--- | :--- |
| `create` | 将作者目录复制到 dest_dir 并打开 Editor 会话。dest_dir 必须不存在。 | `copy author dir + Editor.open_dir` | [k2f](/skills/k2f) |
| `open` | 将本地 .K2F 打开为 Editor 会话。返回 session_id + outline — 绝不返回 lock JSON。 | `Editor.open(bytes)` | [k2f](/skills/k2f) |
| `markdown_to_k2f` | 将 Markdown 转换为 Editor 会话（可变）。返回 session_id、outline 和 report.warnings。 | `markdownToK2f(md, opts)` | [k2f](/skills/k2f) |

### 读取与校验

仅语义树和完整性；不含布局坐标。

| 工具 | 描述 | SDK | 文档 |
| :--- | :--- | :--- | :--- |
| `outline` | Editor 树摘要：id、role、约 80 字符预览、子节点 id。需要 Editor 会话。 | `Editor.outline()` | [k2f](/skills/k2f) |
| `get_node` | 按稳定 id 获取一个语义节点（agent JSON，不含布局坐标）。 | `getNode(id)` | [k2f](/skills/k2f) |
| `search` | 搜索节点文本；返回匹配的节点 id。需要 Editor 会话。 | `search(query)` | [k2f](/skills/k2f) |
| `validate` | 校验作者源（语义树 + 主题词表）。需要 Editor 会话 — 导出前使用 diff + save + verify。 | `Editor.validate_package()` | [k2f](/skills/k2f) |
| `diff` | 与打开/创建时的基线进行语义 diff。需要 Editor 会话。 | `diff()` | [k2f](/skills/k2f) |
| `verify` | 针对 .K2F 路径或带 source_path 的打开会话，返回完整性横幅和信任元数据。 | `Viewer.banner() / status_code()` | [完整性](/integrity) |

### 编辑树

对打开的 Editor 会话进行变更。

| 工具 | 描述 | SDK | 文档 |
| :--- | :--- | :--- | :--- |
| `replace_text` | 替换 Text/Math 节点上的文本（相同 id+text 幂等）。需要 Editor 会话。 | `replaceText(id, text)` | [k2f](/skills/k2f) |
| `set_role` | 设置节点的语义 role（以及可选 variant）。Role 必须存在于包主题中。 | `setRole(id, role, variant?)` | [k2f](/skills/k2f) |
| `insert_node` | 在 parent_id 下的 index 处插入子节点。node 是 JSON 对象（不是字符串），且不含 x/y。需要 Editor 会话 — 不是 create()。 | `insertNode(parentId, index, node)` | [k2f](/skills/k2f) |
| `delete_node` | 按 id 删除节点（不能删除根节点）。需要 Editor 会话。 | `deleteNode(id)` | [k2f](/skills/k2f) |

### 保存、导出、发布

save 和 export_pdf 需要 path；响应返回路径和大小，绝不返回文件字节。

| 工具 | 描述 | SDK | 文档 |
| :--- | :--- | :--- | :--- |
| `save` | 将会话重新锁定为 path（必填）处的 UNSIGNED .K2F。移除签名；如果 diff 非空则追加 changelog。返回 {path,size,banner} — 绝不返回包字节。传入 expected_content_hash，以与打开时的哈希进行乐观锁比较。 | `save()` | [k2f](/skills/k2f) |
| `export_pdf` | 从当前锁导出 PDF 到 path（必填）。未保存的 Editor 会绘制旧锁 — 请先保存。返回 {path,size} — 绝不返回 PDF 字节。 | `exportPdf(bytes)` | [k2f](/skills/k2f) |
| `publish` | 重新锁定会话并 POST 到 K2F 发布 API；返回 /v/{appearance_hash}。设置 dry_run=true 可在不 POST 的情况下预览哈希。环境变量 K2F_PUBLISH_ORIGIN。 | `POST /api/publish → /v/{appearance_hash}` | [k2f](/skills/k2f) |

## MCP 资源

- `k2f://session/{session_id}`
- `k2f://session/{session_id}/node/{node_id}`

## 规则

- **上下文中不得包含完整 lock JSON**。返回节点 JSON、横幅和文件路径 — 绝不返回 document.K2F.lock 几何或绘制操作。
- **sign 需人工确认**。sign 不是 MCP 工具。save 写入 UNSIGNED。签名仍是单独的人工/组织步骤。
- **幂等写入**。replace_text、save 和 validate 可安全重试。相同的 (id, text) 写入不得追加第二个节点。
