Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7adb4a9
feat: refactor uniparser-mcp to single uniparser_parse tool with CLI-…
LeoMY1 Jul 9, 2026
e6e7a91
Merge pull request #9 from LeoMY1/feat/lmy/skill-cli
LeoMY1 Jul 11, 2026
997dd37
feat: clarify MCP Cursor setup must replace API key and local path.
LeoMY1 Jul 11, 2026
4c9885a
feat: merge uniparser-mcp parse-only server into main.
LeoMY1 Jul 11, 2026
98d6ae4
feat: add pdf2vqa extraction.
LeoMY1 Jul 29, 2026
b93dd43
feat: simplify pdf2vqa package documentation.
LeoMY1 Jul 29, 2026
66b52f3
Merge pull request #10 from LeoMY1/feat/lmy/pdf2vqa-to-main
LeoMY1 Jul 29, 2026
983abbc
bug: harden pdf2vqa output handling and package isolation.
LeoMY1 Jul 29, 2026
c51051d
Merge pull request #11 from LeoMY1/feat/lmy/pdf2vqa-to-main
LeoMY1 Jul 29, 2026
7f14ee6
feat: refactor uniparser-mcp to single uniparser_parse tool with CLI-…
LeoMY1 Jul 9, 2026
7cf77d4
feat: clarify MCP Cursor setup must replace API key and local path.
LeoMY1 Jul 11, 2026
b0bc9e3
feat: add pdf2vqa extraction.
LeoMY1 Jul 29, 2026
5e7a525
feat: simplify pdf2vqa package documentation.
LeoMY1 Jul 29, 2026
ffde216
bug: harden pdf2vqa output handling and package isolation.
LeoMY1 Jul 29, 2026
09359b7
fix: align PR22 integrations with v1.3
LeoMY1 Jul 30, 2026
b1f76cd
fix(pdf2vqa): preserve LaTeX answer text
LeoMY1 Aug 2, 2026
fb52014
fix: replace overwrite with unique output directories
LeoMY1 Aug 2, 2026
0080f5d
chore: remove unused uv lock files
LeoMY1 Aug 4, 2026
c353b7c
Merge branch 'feat/lmy/pr22-align-v1.3' into main
LeoMY1 Aug 4, 2026
4f53888
chore: remove tracked uv lock files
LeoMY1 Aug 4, 2026
d77ee32
Merge upstream/main into main
LeoMY1 Aug 4, 2026
f7cc8de
fix(api): distinguish candidate tokens from confirmed task tokens
LeoMY1 Aug 16, 2026
6ed28c5
fix(cli): add resilient uploads and bound undefined polling
LeoMY1 Aug 16, 2026
357bfbf
docs(skill): restrict token recovery to confirmed tasks
LeoMY1 Aug 16, 2026
81c5d7b
fix(cli): use direct uploads for local PDFs
LeoMY1 Aug 16, 2026
fd8c2c8
docs(skill): use direct upload workflow
LeoMY1 Aug 16, 2026
29bf5a0
fix(mcp): align direct upload token recovery
LeoMY1 Aug 18, 2026
feb4f8c
docs(skill): align MCP token recovery guidance
LeoMY1 Aug 18, 2026
6920e3b
fix(project): use server tokens in CLI MCP and Skill
LeoMY1 Aug 19, 2026
a110285
fix(project): preserve diagnostic tokens without recovery
LeoMY1 Aug 19, 2026
2e06d38
Merge pull request #13 from LeoMY1/fix/lmy/tos-token-recovery
LeoMY1 Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ jobs:
cache-dependency-path: |
requirements.txt
pyproject.toml
mcp_server/pyproject.toml
uniparser_agent/pyproject.toml

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -e ".[test]"
pip install -e "./mcp_server[dev]"
pip install -e "./uniparser_agent[dev]"

- name: Ruff lint
run: ruff check .
Expand All @@ -41,3 +45,9 @@ jobs:

- name: Unit tests
run: pytest tests/unit/ -v -m "not live"

- name: MCP tests
run: pytest mcp_server/tests/ -v

- name: Agent tests
run: pytest uniparser_agent/tests/ -v
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*.vscode
*.txt
*.__pycache__
.venv/

# compilation and distribution
__pycache__
Expand Down Expand Up @@ -68,5 +69,8 @@ deploy
# env
.env

# uv project lock files (generated locally; CI installs with pip)
uv.lock

*.bak
.cursor/
46 changes: 24 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,33 +563,37 @@ UniParser 提供了基于 [Model Context Protocol](https://modelcontextprotocol.

### 可用工具

| 工具 | 说明 |
|------|------|
| `uniparser_health` | 检查服务健康状态 |
| `uniparser_version` | 获取服务版本信息 |
| `uniparser_parse_file` | 解析本机 PDF(传入绝对路径),返回 `content` 文本 |
| `uniparser_parse_url` | 解析公网 PDF URL,返回 `content` 文本 |

| 工具 | 说明 |
| ----------------- | ---------------------------------------------------------------- |
| `uniparser_parse` | 解析本地 PDF / 图片或公网 PDF URL;落盘 Markdown 与 `pages_tree.json`;返回路径与预览 |


健康检查、版本查询、按 token 恢复请使用 CLI(`uniparser health` / `version` / `fetch`)。详见 [mcp_server/README.md](./mcp_server/README.md)。

### 快速启动

```bash
cd mcp_server
uv sync # 安装依赖(与主项目隔离)
uv run python -m uniparser_mcp # 启动 MCP 服务(stdio 模式)
uv sync
uv run python -m uniparser_mcp
```

运行时必须设置以下环境变量:

| 变量 | 说明 |
|------|------|
| `UNIPARSER_BASE_URL` | UniParser 用户服务根 URL,例如 `http://127.0.0.1:40001` |
| `UNIPARSER_API_KEY` | API 密钥,对应请求头 `X-API-Key` |
| 变量 | 说明 |
| -------------------- | --------------------------------- |
| `UNIPARSER_API_KEY` | 必填 |
| `UNIPARSER_BASE_URL` | 可选,默认 `https://uniparser.dp.tech` |



默认解析参数和输出格式见 `mcp_server/config.yaml`。

### 接入 Cursor / Claude Code

在 MCP 配置文件中添加(将路径替换为本机实际路径):
先克隆本仓库并在 `mcp_server/` 下执行 `uv sync`,再在 MCP 配置中增加如下内容。**必须**将两处占位符改成你的本机值,否则 MCP 无法启动:

1. `"--directory"` 后的路径:把 `/path/to/UniParser-Tools/mcp_server` 替换为克隆到本机后的 `mcp_server` **绝对路径**(例如 macOS:`/Users/<you>/UniParser-Tools/mcp_server`)。
2. `UNIPARSER_API_KEY`:把 `your-api-key` 替换为你在 [https://uniparser.dp.tech/](https://uniparser.dp.tech/) 申请的真实 API Key。

```json
{
Expand All @@ -605,17 +609,16 @@ uv run python -m uniparser_mcp # 启动 MCP 服务(stdio 模式)
"uniparser_mcp"
],
"env": {
"UNIPARSER_BASE_URL": "http://127.0.0.1:40001",
"UNIPARSER_API_KEY": "your-api-key"
}
}
}
}
```

传输模式默认为 `stdio`,可通过 `UNIPARSER_MCP_TRANSPORT` 环境变量切换为 `sse` 或 `streamable-http`。
传输模式默认为 `stdio`,可通过 `UNIPARSER_MCP_TRANSPORT` 切换为 `sse` 或 `streamable-http`。

详细文档见 [`mcp_server/README.md`](./mcp_server/README.md)。
详细文档见 [mcp_server/README.md](./mcp_server/README.md)。

## 项目结构

Expand All @@ -631,10 +634,9 @@ uniparser_tools/
├── utils/ # 工具函数
└── order/ # 排序算法

mcp_server/ # MCP 服务(独立子项目)
├── uniparser_mcp/ # MCP server 实现
├── config.yaml # 默认解析参数配置
└── pyproject.toml # 独立依赖管理
mcp_server/ # MCP 服务(独立子项目,仅 uniparser_parse tool)
├── uniparser_mcp/
└── pyproject.toml

playground/
├── 01.quick_start.ipynb # 快速开始教程
Expand Down
123 changes: 38 additions & 85 deletions mcp_server/README.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,56 @@
# UniParser MCP Server

基于 [Model Context Protocol](https://modelcontextprotocol.io/) 的 **stdio** 服务,通过 MCP `tools` 调用 UniParser 用户面 HTTP API。不在 MCP 进程内加载解析栈,仅做 HTTP 转发,并与 [`config.yaml`](./config.yaml) 中的默认参数合并。
基于 [Model Context Protocol](https://modelcontextprotocol.io/) 的 MCP 服务,通过单一工具 `uniparser_parse` 调用 [UniParser](https://uniparser.dp.tech/) API(经 `uniparser-tools` 的 `UniParserClient`)。

## 前置条件
## Tool

1. **已运行 UniParser 用户服务**(提供 `GET /health`、`POST /trigger-file-async`、`POST /trigger-url-async`、`POST /get-formatted` 等)。若在 UniParser 主仓库中开发,典型启动方式为(路径以主仓库为准):

```bash
python services/server_user.py
```

默认监听 **40001** 端口;本地联调时请将 `UNIPARSER_BASE_URL` 设为 `http://127.0.0.1:40001`。

2. **Python 3.10+**,推荐使用 [uv](https://github.com/astral-sh/uv) 在 **本目录**(`src/mcp_server`)安装依赖,避免与主项目其它依赖版本冲突:

```bash
cd mcp_server
uv sync
```

## 环境变量

MCP 进程通过 [`UniParserClient`](../uniparser_tools/api/clients.py) 访问服务,**以下两项在运行 MCP 时均为必填**(未设置时工具会返回明确错误,而非静默使用默认 URL):

| 变量 | 说明 |
| Tool | 说明 |
|------|------|
| `UNIPARSER_BASE_URL` | 用户服务根 URL,**无代码内默认值**。本地示例:`http://127.0.0.1:40001` |
| `UNIPARSER_API_KEY` | 对应请求头 `X-API-Key`;与服务端鉴权配置一致 |
| `uniparser_parse` | 解析本地 PDF、本地图片或公网 PDF URL;落盘 Markdown + `pages_tree.json`;返回路径与 `content_preview` |

密钥仅通过环境变量注入,勿写入 MCP 工具参数或提交到版本库。
### `uniparser_parse` 参数

**集成测试**(`pytest` 标记 `integration`)还会读取:
提供 **三选一** 输入:`file_path`、`image_path`、`pdf_url`。

| 变量 | 说明 |
| 参数 | 说明 |
|------|------|
| `UNIPARSER_BASE_URL` | 未设置时,[`tests/conftest.py`](./tests/conftest.py) 默认使用 `https://uniparser.dp.tech`(与 MCP 手动配置本地 URL 的行为不同,请注意) |
| `UNIPARSER_API_KEY` | 未设置时跳过全部集成测试 |
| `UNIPARSER_INTEGRATION` | 设为 `0` / `false` / `no` / `skip` 时,不探测服务并跳过集成测试(适用于 CI 无后端场景) |
| `output_dir` | 可选的首选目录;默认 `~/Uni-Parser-Skill/<stem>/`;已存在时自动使用同级 `<name>_1`、`<name>_2` 等新目录 |
| `async_mode` | `sync=false` 提交后轮询直至完成 |
| `textual` … `molecule` | 7 个语义字段,默认 scientific-paper preset |

## 配置文件 `config.yaml`
成功返回 JSON(Pydantic):`markdown_path`、`pages_tree_path`、`content_preview`(默认 2000 字)、`message` 等。
调用方应以返回的 `output_dir` 为准;服务不会复用或删除已有目录。

与 `uniparser_mcp` 包同级目录下的 [`config.yaml`](./config.yaml) 控制 **trigger** 与 **get-formatted** 的默认字段(与 HTTP API 一致):
本地 PDF 固定通过 `trigger_file` 直接 multipart 上传,不使用 TOS 或自动回退。MCP 触发时固定发送
`token=None` 和 `server_generated_token=True`;同步请求使用 `(60, 1860)`,异步请求使用 `(60, 60)`,
只有服务端确认的 token 才能进入结果轮询。

| 段名 | 用途 |
|------|------|
| `default_trigger_file` | `uniparser_parse_file` → `POST /trigger-file-async` 的默认参数(如 `lang`、`sync`、`textual`、`table` 及各模态解析档位) |
| `default_trigger_url` | `uniparser_parse_url` → `POST /trigger-url-async` 的默认参数 |
| `default_get_result` | 解析完成后 `POST /get-formatted` 的默认参数(如各类型输出为 `markdown`、`content: true` 等) |
> 出于安全原因,根目录、HOME、当前工作目录及 Git 元数据目录不能作为首选输出目录。

修改解析行为时,优先编辑该文件;若需在工具层暴露更多 MCP 参数,需扩展 [`uniparser_mcp/server.py`](./uniparser_mcp/server.py)。
健康检查、版本查询、按 token 手动恢复请使用 CLI:`uniparser health`、`uniparser version`、`uniparser fetch`。

## Tools
失败返回的 `error.code` 可能为 `CONFIG_ERROR`、`INPUT_ERROR`、`UPLOAD_ERROR`、`PARSE_ERROR` 或
`TOKEN_NOT_FOUND`。触发失败时可能保留标准 `token` 供诊断,但 MCP 不会使用它或进入恢复流程;只有
成功触发返回的 token 才会被轮询。`undefined` 最多检查三次,不会持续等待 1800 秒。

| Tool | 说明 |
|------|------|
| `uniparser_health` | `GET /health`,返回服务健康状态字符串 |
| `uniparser_version` | `GET /version`,返回版本信息 |
| `uniparser_parse_file` | 参数:本机 PDF **绝对路径**。流程:`POST /trigger-file-async` → 成功后再 `POST /get-formatted`,返回合并后的 `content` 文本 |
| `uniparser_parse_url` | 参数:公网可访问的 PDF **URL**。流程:`POST /trigger-url-async` → `POST /get-formatted`,返回 `content` |
## 环境变量

`uniparser_parse_file` 仅适合**同机**或**体积较小**的 PDF;大文件或带宽受限时,建议将文件放到公网可访问地址后使用 `uniparser_parse_url`。
| 变量 | 说明 | 默认 |
|------|------|------|
| `UNIPARSER_API_KEY` | 必填,`X-API-Key` | — |
| `UNIPARSER_BASE_URL` | API 根 URL | `https://uniparser.dp.tech` |
| `OUTPUT_DIR` | 输出根目录 | `~/Uni-Parser-Skill` |
| `UNIPARSER_PREVIEW_CHARS` | `content_preview` 长度 | `2000` |
| `UNIPARSER_MCP_TRANSPORT` | `stdio` / `sse` / `streamable-http` | `stdio` |

## 本地运行
## 安装与运行

```bash
cd mcp_server
uv sync
uv run python -m uniparser_mcp
```

或:

```bash
uv run uniparser-mcp
```

## 测试

```bash
Expand All @@ -82,25 +59,12 @@ uv sync --extra dev
uv run pytest tests/ -v
```

调试时要看 `print` 或中间步骤输出,可关闭输出捕获:

```bash
uv run pytest tests/ -v -s
# 或
uv run pytest tests/ -v --capture=no
```

需要把 **logging** 打到终端时:
## Cursor / Claude Code 接入示例

```bash
uv run pytest tests/ -v -s -o log_cli=true -o log_cli_level=DEBUG
```

- **集成测试**:[`tests/test_client_integration.py`](./tests/test_client_integration.py)(标记 `integration`)。会话开始时用 `httpx` 请求 `{UNIPARSER_BASE_URL}/health`;不可达或非正常状态则**整会话 skip**。仅跑集成:`uv run pytest tests/test_client_integration.py -v`。CI 无服务:`UNIPARSER_INTEGRATION=0 uv run pytest tests/ -v`。

## Cursor / Claude 等 MCP 接入示例
先克隆本仓库并在本目录执行 `uv sync`,再在 MCP 配置中增加如下内容。**必须**将两处占位符改成你的本机值,否则 MCP 无法启动:

在 MCP 配置中增加(将 `/path/to/UniParser-Tools/src/mcp_server` 换为你的本机路径):
1. `"--directory"` 后的路径:把 `/path/to/UniParser-Tools/mcp_server` 替换为克隆到本机后的 `mcp_server` **绝对路径**(例如 macOS:`/Users/<you>/UniParser-Tools/mcp_server`)。
2. `UNIPARSER_API_KEY`:把 `your-api-key` 替换为你在 [https://uniparser.dp.tech/](https://uniparser.dp.tech/) 申请的真实 API Key。

```json
{
Expand All @@ -110,31 +74,20 @@ uv run pytest tests/ -v -s -o log_cli=true -o log_cli_level=DEBUG
"args": [
"run",
"--directory",
"/path/to/UniParser-Tools/src/mcp_server",
"/path/to/UniParser-Tools/mcp_server",
"python",
"-m",
"uniparser_mcp"
],
"env": {
"UNIPARSER_BASE_URL": "http://127.0.0.1:40001",
"UNIPARSER_API_KEY": "your-api-key"
}
}
}
}
```

`UNIPARSER_BASE_URL`、`UNIPARSER_API_KEY` 均需在 `env` 中配置(示例仅作占位)。

## 故障排查

| 现象 | 可能原因 |
|------|----------|
| 工具返回「未设置 UNIPARSER_BASE_URL」或「未设置 UNIPARSER_API_KEY」 | MCP 的 `env` 未注入或拼写错误 |
| `uniparser_health` 失败 | 用户服务未启动、端口/防火墙不一致、或 `UNIPARSER_BASE_URL` 与真实监听地址不符 |
| `uniparser_parse_file` 失败 | 路径不存在、无读权限,或服务端无法访问该路径(远程部署时常见) |
| `uniparser_parse_url` 长时间无结果 | PDF 较大或 URL 较慢;可通过 `UniParserClient` 的 `request_timeout` / `sync_request_timeout` 区分普通请求与同步解析请求超时 |

## 与主仓库的关系
`UNIPARSER_BASE_URL` 可省略(默认云服务);本地自托管时设置为 `http://127.0.0.1:40001` 等。

本目录为**独立**子项目(自有 [`pyproject.toml`](./pyproject.toml)),通过 `[tool.uv.sources]` 以可编辑方式依赖上一级 [`uniparser_tools`](../)。主仓库根 `pyproject.toml` 中若有可选依赖组 `mcp-server`,仅作文档引用;**推荐**始终在 `src/mcp_server/` 下执行 `uv sync`,以保证 MCP 与主服务依赖隔离。
本地 PDF 同步直传显式使用 `(60, 1860)`,异步直传显式使用 `(60, 60)`;轮询和结果获取使用
`UniParserClient.request_timeout`。这些都是客户端 HTTP 超时,与服务端解析预算相互独立。
39 changes: 0 additions & 39 deletions mcp_server/config.yaml

This file was deleted.

17 changes: 5 additions & 12 deletions mcp_server/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
[project]
name = "uniparser-mcp"
version = "0.1.0"
description = "MCP server exposing UniParser HTTP API (stdio)"
version = "0.2.0"
description = "MCP server exposing UniParser parse via uniparser-tools"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"mcp>=1.10.0",
"httpx>=0.27.0",
"pyyaml>=6.0",
"mcp>=1.10.0,<2",
"pydantic>=2.0",
"uniparser-tools",
]

Expand All @@ -28,12 +27,6 @@ build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["uniparser_mcp"]
# 将仓库根目录的 config.yaml 打进 wheel 的 uniparser_mcp/,供已安装包加载默认参数
[tool.hatch.build.targets.wheel.force-include]
"config.yaml" = "uniparser_mcp/config.yaml"

[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"integration: 需要可访问的 UniParser HTTP 服务(默认探测失败则 skip,可用 UNIPARSER_INTEGRATION=0 关闭)",
]
Empty file added mcp_server/tests/conftest.py
Empty file.
Loading
Loading