Initial commit: book-mcp 教辅处理 MCP server + Arco Vue 管理端

后端 (book_mcp_server.py, 335 行, 15 个 MCP 工具):
- create_book / add_document / mark_html_ready / mark_json_ready / mark_failed
- update_book_status / soft_delete_book / retry_failed
- get_book / list_books / get_document / list_documents / get_document_url
- upload_to_rustfs / health
- 走 JSON-RPC + stateless_http + json_response(前端可直接调)
- launchd 守护 / 6 秒拉起(macOS)
- systemd 守护脚本(Linux/codelab)

存储:
- MongoDB (mongo:8.0 容器) - book/book_document/document_file 三表
- RustFS 对象存储(fs.code-lab.cn/scan/)- book/<id>/docs/<doc_id>/{docx,html,json}
- 幂等设计: zip_sha256 唯一 + (book_id+original_path) 唯一

前端 (Vue 3 + TS + Arco Design):
- 列表页 /book/list - 搜索 + 状态过滤 + 进度
- 详情页 /book/:id - 文档折叠 + 文件下载
- MCP 客户端 src/api/mcp.ts (JSON-RPC over fetch)

部署:
- deploy/install.sh (codelab 一键装 backend + systemd)
- deploy/install-frontend.sh (vite dev / 生产构建)
- docs/deploy.md (运维手册)

测试:
- tests/test_basic.py (sanity 7 项)
- tests/test_e2e.py (完整 e2e: docx→HTML→入库→上传→校验)
This commit is contained in:
2026-10-11 18:03:45 +08:00
parent f7fb571bce
commit 9b0910ccb1
160 changed files with 23628 additions and 3 deletions

213
docs/deploy.md Normal file
View File

@@ -0,0 +1,213 @@
# book-mcp 部署文档
## 架构
```
Agent (任意 Agent 客户端)
↓ HTTP/JSON-RPC
book-mcp (8100) ──┬─→ MongoDB (27018/book_mcp) ← docker container
└─→ RustFS (fs.code-lab.cn/scan/) ← 对象存储
└─→ wordparse (8089) ← docker container docx4j
```
## 组件
| 组件 | 位置 | 端口 | 启动方式 |
|---|---|---|---|
| **book-mcp** | `~/WorkSpace/book-mcp/book_mcp_server.py` | 8100 | launchd `ai.book-mcp.server` |
| **MongoDB** | docker container `book-mcp-mongo` | 27018 | `docker start book-mcp-mongo` |
| **RustFS** | `fs.code-lab.cn` | 443 | 远程 |
| **wordparse (docx4j)** | docker container `wordparse-test` | 8089 | `docker start wordparse-test` |
## 安装(一次性)
### 1. MongoDB 容器
```bash
docker run -d \
--name book-mcp-mongo \
-p 27018:27017 \
-e MONGO_INITDB_ROOT_USERNAME=book_admin \
-e MONGO_INITDB_ROOT_PASSWORD=book_mongo_2026 \
-e MONGO_INITDB_DATABASE=book_mcp \
-v book-mcp-mongo-data:/data/db \
--restart unless-stopped \
mongo:8.0
```
### 2. Python venv(一次性)
```bash
python3.13 -m venv /tmp/wordparse_venv
/tmp/wordparse_venv/bin/pip install -r ~/WorkSpace/book-mcp/requirements.txt
```
依赖:`fastmcp boto3 pymongo pyyaml`
### 3. MongoDB 索引
```bash
/tmp/wordparse_venv/bin/python ~/WorkSpace/book-mcp/book_mcp_server.py --init-db
```
输出:
```
✓ MongoDB 已就绪(db: book_mcp, collections: [...])
✓ 8 个索引创建完成
```
### 4. LaunchAgent
```bash
# 装(已装好:~/Library/LaunchAgents/ai.book-mcp.server.plist)
launchctl load -w ~/Library/LaunchAgents/ai.book-mcp.server.plist
```
## 日常运维
### 状态查询
```bash
# launchd 状态
launchctl print gui/501/ai.book-mcp.server | grep -E "state|pid"
# 端口
lsof -i :8100
# 健康
curl http://127.0.0.1:8100/mcp
# fastmcp 在 /mcp 走 HTTP
# 实时日志
tail -f ~/.hermes/services/book-mcp/logs/server.log
```
### 重启
```bash
# 软重启(SIGTERM,launchd 30s ThrottleInterval)
kill $(lsof -ti :8100)
# launchd 自动拉起
sleep 5
ps aux | grep book_mcp_server | grep -v grep
```
### 调试
```bash
# 1. 跑 e2e 测试
/tmp/wordparse_venv/bin/python ~/WorkSpace/book-mcp/tests/test_e2e.py
# 2. 直接调 MCP 工具(fastmcp client)
/tmp/wordparse_venv/bin/python << 'PYEOF'
import asyncio
from fastmcp.client import Client
async def main():
async with Client("http://127.0.0.1:8100/mcp") as c:
r = await c.call_tool('health', {})
print(r.data)
asyncio.run(main())
PYEOF
# 3. mongo 直连
/tmp/wordparse_venv/bin/python << 'PYEOF'
from db import get_collection, COL_BOOK
print(f"books count: {get_collection(COL_BOOK).count_documents({})}")
PYEOF
```
## 配置
`~/WorkSpace/book-mcp/config.yaml`:
```yaml
server:
port: 8100 # MCP server 端口
database:
url: mongodb://book_admin:book_mongo_2026@127.0.0.1:27018/book_mcp?authSource=admin
db_name: book_mcp
rustfs:
endpoint: https://fs.code-lab.cn
default_bucket: scan
wordparse:
url: http://127.0.0.1:8089/api/v3/server_office_file/file/parse
timeout: 600
```
## MCP 工具列表(15 个)
| # | 工具 | 类别 |
|---|---|---|
| 1 | create_book | 写入 |
| 2 | add_document | 写入 |
| 3 | mark_html_ready | 写入 |
| 4 | mark_json_ready | 写入 |
| 5 | mark_failed | 写入 |
| 6 | update_book_status | 写入 |
| 7 | get_book | 查询 |
| 8 | list_books | 查询 |
| 9 | get_document | 查询 |
| 10 | list_documents | 查询 |
| 11 | get_document_url | 查询 |
| 12 | upload_to_rustfs | 辅助 |
| 13 | soft_delete_book | 辅助 |
| 14 | retry_failed | 辅助 |
| 15 | health | 诊断 |
## 数据流
```
zip 上传 RustFS
↓ create_book(zip_sha256)
→ book.id
↓ 循环每 docx
→ add_document(filename, path)
→ doc.doc_id = sha1(book_id|path)[:16]
→ wordparse.convert_one (cleaner + docx4j + MathJax)
→ RustFS upload → mark_html_ready(url)
→ Agent 解析 HTML → structure.json
→ RustFS upload → mark_json_ready(url)
→ update_book_status("done")
```
## 故障排查
| 现象 | 原因 | 解决 |
|---|---|---|
| `mongo` health=False | 容器没起 | `docker start book-mcp-mongo` |
| `wordparse` health=False | docx4j 容器没起 | `docker start wordparse-test` |
| 8100 端口未 listen | 进程挂了 | `kill $(lsof -ti :8100)` 走 launchd 自动拉起 |
| `add_document` 报 NoneType | 数据库索引问题(已修复) | 重跑 `--init-db` |
| 上传 403 Forbidden | 桶不存在 | 检查 `default_bucket` 是否在 RustFS 实际存在 |
| e2e 测试报 `uk_name_version` | 同名教辅已存在 | 改 name 或 version 加时间戳 |
## 备份
- **MongoDB 数据**:`book-mcp-mongo-data` docker volume(在 docker 里)
- **RustFS 文件**:`https://fs.code-lab.cn/scan/book/<book_id>/...`
- **代码**:`~/WorkSpace/book-mcp/`
## 升级路径
```bash
# 1. 拉新代码
cd ~/WorkSpace/book-mcp
git pull # 假设推到 git
# 2. 升级依赖
/tmp/wordparse_venv/bin/pip install -r requirements.txt --upgrade
# 3. 重启 launchd
kill $(lsof -ti :8100)
sleep 5
ps aux | grep book_mcp_server | grep -v grep
```
## 相关链接
- 设计文档:`docs/book-mcp-design.md`
- Schema:`db/schema_doc.md`
- wordparse 工具:`services/wordparse/`
- 启动日志:`~/.hermes/services/book-mcp/logs/server.log`
- 错误日志:`~/.hermes/services/book-mcp/logs/server.error.log`