Skip to main content
Glama
jsCanvas

Docs MCP Server

by jsCanvas
README.md
# Docs MCP Server

将网页选中区域以 **HTML 快照**方式捕获,保留渲染后的 DOM 结构与内联样式,并通过 MCP 服务供 AI 智能体读取。

## 演示

### 使用 — 选中并捕获

在飞书文档中拖选内容(含代码块),点击 Chrome 扩展一键捕获为 HTML 快照。

![选中飞书文档内容并捕获](asset/images/选择网页内容演示.gif)

### 效果 — 智能体精准读取

将 content id 粘贴给 Cursor / Claude,智能体通过 MCP 读取完整快照并理解文档内容。

![AI 智能体通过 MCP 读取快照](asset/images/智能体获取内容演示.gif)

## 方案调研结论:**可行**

| 能力 | 可行性 | 说明 |
|------|--------|------|
| 保留 DOM 结构 | ✅ 完全可行 | 克隆选区 DOM 树,独立 HTML 文件可离线打开 |
| 内联样式 | ✅ 完全可行 | 将 `getComputedStyle()` 转为 inline style,还原视觉布局 |
| 代码块 | ✅ 可行 | 飞书代码块是 DOM 渲染,样式可内联保留 |
| SVG 流程图 | ✅ 可行 | 直接序列化 SVG 节点 |
| Canvas 图表 | ✅ 可行 | 扩展端栅格化为 data URL 传输,服务端保存为 PNG 文件 |
| 图片 | ✅ 可行 | 扩展保留原始 URL;服务端下载并保存为 `assets/` 文件,HTML 引用本地链接 |
| Web 字体 | ⚠️ 部分可行 | 离线打开可能 fallback 到系统字体,不影响文字内容 |
| iframe 嵌入内容 | ❌ 不可行 | 跨文档内容无法捕获 |
| Shadow DOM | ⚠️ 有限 | 开放 shadow root 可遍历,closed shadow 无法访问 |

**结论**:对于飞书文档这类 DOM 渲染的富文本(标题、段落、代码块、图片、SVG 流程图),HTML 快照方案比纯文本提取更可靠,**推荐采用**。

## 架构

```
选中区域 → clone DOM + 内联 computed styles
         → HTML + 图片 URL(blob/canvas 临时 data URL 传输)
         → 服务端 externalize:data URL / 远程图片 → data/snapshots/{id}/assets/*
         → data/snapshots/{id}/index.html
         → MCP Resource: docs://snapshot/{id}
```

## 快速开始

```bash
cd docs-mcp-server
npm install
npm run build
npm run server          # HTTP API :3847
# MCP 由 Cursor 通过 stdio 启动
```

### Chrome 扩展

1. `chrome://extensions/` → 开发者模式 → 加载 `extension/` 目录
2. 在页面**先选中内容**,再打开弹窗点击「捕获当前选区」
3. 或使用右键 → **Capture selection to MCP**

### Cursor MCP 配置

```json
{
  "mcpServers": {
    "docs-mcp-server": {
      "command": "node",
      "args": ["/Users/bryan.ren/faco/office/docs-mcp-server/dist/server/mcp.js"],
      "env": {
        "DOCS_MCP_DATA_DIR": "/Users/bryan.ren/faco/office/docs-mcp-server/data"
      }
    }
  }
}
```

> **重要**:HTTP 服务与 MCP 必须指向同一 `data/` 目录。在 MCP 配置的 `env.DOCS_MCP_DATA_DIR` 中填写**绝对路径**后,智能体可通过 Read 工具直接读取 `{DOCS_MCP_DATA_DIR}/snapshots/{id}/` 下的 HTML 与图片,无需走 HTTP。

## MCP 能力

| 类型 | 名称 | 说明 |
|------|------|------|
| Resource | `docs://index` | 文档索引(含本地文件路径) |
| Resource | `docs://document/{id}` | 元数据 + 文本预览 + 本地路径 |
| Resource | `docs://snapshot/{id}` | **完整 HTML 快照** |
| Resource | `docs://snapshot/{id}/paths` | **本地文件绝对路径** |
| Tool | `list_documents` | 列出所有快照(含 `localPaths`) |
| Tool | `get_snapshot_paths` | 获取 HTML / 图片目录绝对路径 |
| Tool | `get_snapshot_html` | 获取完整 HTML 内容 |
| Tool | `get_document` | 获取元数据 |
| Tool | `search_documents` | 关键词搜索 |

### 智能体推荐用法

扩展「复制 ID」或粘贴以下内容给 Cursor / Claude:

```
docs-mcp-server content id: 052cb4c2-401e-4c1b-b816-a085ae567b98

请优先用 Read 直接读取本地文件(路径由 MCP 配置 env.DOCS_MCP_DATA_DIR 决定;必须阅读快照的 assets 目录里资源):
- 数据目录: /Users/you/docs-mcp-server/data
- HTML: /Users/you/docs-mcp-server/data/snapshots/052cb4c2-.../index.html
- 图片目录: /Users/you/docs-mcp-server/data/snapshots/052cb4c2-.../assets/
- 图片文件 (3):
  - /Users/you/docs-mcp-server/data/snapshots/052cb4c2-.../assets/img-001-abc.png
  ...

或通过 MCP 读取完整 HTML:
- Resource: docs://snapshot/052cb4c2-401e-4c1b-b816-a085ae567b98
- Tool: get_snapshot_html({ "id": "052cb4c2-401e-4c1b-b816-a085ae567b98" })

浏览器预览: http://127.0.0.1:3847/api/snapshots/052cb4c2-401e-4c1b-b816-a085ae567b98.html
```

说明:
- **本地文件 / Read** — 在 MCP 配置中设置 `DOCS_MCP_DATA_DIR` 绝对路径后,复制内容会包含 HTML 与图片的本地路径,智能体可直接 Read
- **MCP Resource / Tool** — 也可通过 MCP 读取 HTML,或用 `get_snapshot_paths` / `docs://snapshot/{id}/paths` 获取路径
- **浏览器预览** — 本地 HTTP 服务运行时人工打开查看

## 捕获内容说明

每次捕获生成 **HTML + 图片资源目录**:

- 选区 DOM 完整克隆
- 所有可见样式内联化(`getComputedStyle` → `style=""`)
- **图片**:扩展端保留 `https://` 原始链接;服务端尝试下载并保存为独立文件,HTML 中 `src` 指向 `/api/snapshots/{id}/assets/...`;下载失败则**保持原链接**(`data-original-src` 备份)
- **blob / Canvas**:扩展端临时转为 data URL 传输,服务端落盘为 PNG 等资源文件
- SVG:保留原始 markup
- 移除 script / 事件处理器

目录结构:

```
data/snapshots/{id}/
├── index.html          # 快照 HTML(图片引用本地 asset URL)
└── assets/
    ├── img-000-a1b2c3d4.png
    └── img-001-e5f6g7h8.webp
```

预览:

```
http://127.0.0.1:3847/api/snapshots/{id}.html
http://127.0.0.1:3847/api/snapshots/{id}/assets/img-000-xxxx.png
```

> 旧版单文件 `{id}.html` 仍可正常读取;新捕获均使用目录结构。

## HTTP API

| Method | Path | 说明 |
|--------|------|------|
| GET | `/api/health` | 健康检查 |
| POST | `/api/documents` | 上传 HTML 快照 |
| GET | `/api/documents` | 列出文档 |
| GET | `/api/documents/:id` | 文档元数据 |
| GET | `/api/snapshots/:id.html` | **下载 HTML 快照** |
| GET | `/api/snapshots/:id/assets/:filename` | 快照图片资源 |
| DELETE | `/api/documents/:id` | 删除文档及快照 |

## 开发

```bash
npm run dev:server
npm run dev:mcp
npm run build
```

## 环境变量

| 变量 | 默认值 | 说明 |
|------|--------|------|
| `DOCS_MCP_PORT` | `3847` | HTTP API 端口 |
| `DOCS_MCP_PUBLIC_URL` | `http://127.0.0.1:3847` | 写入 HTML 中的图片 asset 绝对 URL 前缀 |
| `DOCS_MCP_DATA_DIR` | `{project}/data` | **数据目录(MCP 配置必填绝对路径)**,快照存于 `{dir}/snapshots/{id}/` |

## 产品演示幻灯片

HTML 演示文稿见 [`ppt/index.html`](ppt/index.html)。

```bash
cd docs-mcp-server
python3 -m http.server 8765
open http://127.0.0.1:8765/ppt/index.html
```