@vandoor2/mineru-mcp
v0.4.0
Published
MinerU document parsing MCP server — PDF / Office / image / URL to Markdown, via local MinerU client + cloud API hybrid.
Maintainers
Readme
mineru-mcp
基于 MinerU 的文档解析 MCP Server —— 把 PDF、Office 文档、图片和网页 URL 解析为 Markdown 与图片文件,直接接入 Claude Code 等 MCP 客户端。
功能特点
- 多格式解析:PDF(
.pdf)、Word(.doc/.docx)、PPT(.ppt/.pptx)、Excel(.xls/.xlsx)、图片(.png/.jpg/.jpeg/.jp2/.webp/.gif/.bmp) - URL 解析:支持
http(s)网页链接与文件直链- 网页链接 → MinerU 云端直连解析(云端自动抓取页面,无需本地浏览器或 Playwright)
- 文件直链 → 自动下载后按本地文件流程解析
- 自动路由:根据页数 / 是否含图 / 文件大小自动选择解析方式
- 无图片且 ≤20 页、≤10MB → Agent 轻量解析 API(免 token,仅输出 Markdown)
- 有图片且页数 ≤200(或页数未知)→ Precision Extract API(输出 Markdown + 图片 + 中间 JSON)
- 页数 >200 → 自动按 ≤200 页切分,分别解析后合并 Markdown
- 异步任务:
document_parse提交后立即返回task_id,解析在后台执行;用get_task_progress查询进度、是否完成、输出路径,以及未完成时的剩余时间与预计完成时间 - 凭证自动获取:优先使用项目配置中的 token → MinerU 客户端本地
config.json→ 静默唤起客户端 → 内置访客额度 - TUN 环境可用:解析请求以
trust_env=False直连,并在被 Clash Verge 等 TUN 模式劫持到 fake-ip 时,用 DoH 解析出的真实 IP 重试(证书校验全程开启) - 零中断:MCP 工具调用不干扰主对话上下文
安装
方式一:npm 包(推荐,客户端零配置)
npx -y @vandoor2/mineru-mcpMCP 客户端里直接这样写即可,不需要克隆仓库、不需要手动装依赖。
launcher(bin/cli.js)按以下优先级选择运行时:
- uv(若已安装):
uv run --directory <包目录> mcp_server.py,自动创建虚拟环境并安装依赖(首次较慢,之后复用缓存) - python(兜底):在
~/.mineru-mcp/venv建一个持久虚拟环境并安装依赖,之后复用
两者至少要有其一:
- uv:Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex";macOS / Linuxcurl -LsSf https://astral.sh/uv/install.sh | sh - 或 Python 3.10+(自带
venv与pip)
launcher 环境变量:
| 变量 | 说明 |
|---|---|
| MINERU_MCP_VENV | python 兜底时持久 venv 的位置(默认 ~/.mineru-mcp/venv) |
| MINERU_MCP_PIP_INDEX | python 兜底安装依赖时的 pip 源,如 https://pypi.tuna.tsinghua.edu.cn/simple;不设则沿用 pip 自身配置(PIP_INDEX_URL / pip.ini) |
| MINERU_MCP_UV | 手动指定 uv 可执行文件路径 |
| MINERU_MCP_CONFIG_PATH | 配置目录。npm 安装时默认 ~/.mineru-mcp/config(避免 npx 缓存清理 / 升级丢配置),显式设置时以显式值为准 |
方式二:克隆源码
git clone https://gitee.com/vandoor2/mineru-mcp.git
cd mineru-mcp
uv sync在 MCP 客户端中接入
npm 包方式(推荐):
{
"mcpServers": {
"mineru-mcp": {
"command": "npx",
"args": ["-y", "@vandoor2/mineru-mcp"]
}
}
}Windows 上 npx 实际是 npx.cmd,部分 MCP 客户端不会自动过 shell,需要显式包装:
{
"mcpServers": {
"mineru-mcp": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@vandoor2/mineru-mcp"]
}
}
}⚠️ 已知限制:npx 方式不能在「本仓库源码目录内」使用。 若客户端的工作目录是本仓库(或它的任意子目录),
npx -y @vandoor2/mineru-mcp会失败并报'mineru-mcp' is not recognized as an internal or external command。原因:npm 发现当前目录
package.json的name就是@vandoor2/mineru-mcp, 判定「本地已有这个包」,于是跳过安装、直接去跑本地 bin;而本仓库不含node_modules/.bin,就退化成裸命令名交给cmd,必然找不到。影响范围:仅当工作目录是该 npm 包的源码目录(含子目录)时发生;在其它任何项目中都正常。
两种规避方式(择一):
在本仓库内改用下面的「源码方式」配置;
强制换到中立目录再跑 —— 注意这会把进程的工作目录一并改掉, 因此相对路径的
output_dir会解析到%TEMP%,产物会落到临时目录:{ "mcpServers": { "mineru-mcp": { "command": "cmd", "args": ["/c", "cd /d %TEMP% && npx -y @vandoor2/mineru-mcp"] } } }实测环境:npm 11.12.1 / node v24.14.1 / Windows(2026-09-30)。
源码方式:
{
"mcpServers": {
"mineru-mcp": {
"command": "uv",
"args": [
"--directory",
"<你的项目路径>",
"run",
"mcp_server.py"
]
}
}
}也可以直接运行 uv run mcp_server.py(stdio 协议),或用 uv run mcp dev mcp_server.py 调试。
在项目中给模型的使用约定示例:
## 文档解析
- 解析 PDF、Office 文档、图片、网页 URL 时,只使用 `mineru-mcp` MCP 中的 `document_parse` 工具。
- `document_parse` 是异步提交,立即返回 `task_id`;不要因为没看到结果就重复提交。
- 提交后优先用 `wait_for_task(task_id="...")` 阻塞等待完成(自动转后台、完成即唤醒,不必轮询);
- 用 `get_task_progress(task_id="...")` 查询进度、是否完成、输出路径与剩余时间;
不传 `task_id` 可一次列出所有未完成任务。MCP 工具说明
本服务提供 4 个工具。
document_parse
提交一个解析任务,立即返回任务编号(不等待解析完成)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input_path | string | 是 | 待解析文件路径或 URL |
| output_dir | string | 是 | 输出目录,结果写入 <文件名>.md 与 images/ |
| keep_intermediate | bool | 否 | 是否保留中间文件(layout.json、content_list.json、原始文档副本等)。默认使用 config/features.json 中的全局设置 |
| timeout_seconds | int | 否 | 后台解析的总超时(秒)。默认使用 config/features.json 中的全局设置(默认 0 = 不超时)。超时会把已完成部分落盘,任务状态记为 timeout |
返回字段(JSON 字符串,通常 < 0.1 秒返回):
status:固定为submittedtask_id:任务编号,后续用它查询进度与取结果input_path/output_dir/keep_intermediate/timeout_seconds:本次任务实际采用的参数hint:下一步怎么做(优先wait_for_task等待完成,或用get_task_progress查询)
⚠️ 返回值里没有 Markdown 正文。解析结果要通过
wait_for_task/get_task_progress(task_id=...)获取。
wait_for_task
阻塞等待一个解析任务进入终态(completed / failed / timeout / cancelled),
返回与 get_task_progress(task_id=...) 同形的单条结果。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| task_id | string | 是 | 任务编号(document_parse 提交时返回) |
| timeout_seconds | int | 否 | 最长等待秒数(夹取到 1-3600),默认 1800 |
| include_text | bool | 否 | 已完成任务是否返回完整 Markdown 正文(text_content) |
三种返回:
- 已终态:立即返回,
wait_timed_out=false,条目结构与get_task_progress完全一致 - 等待超时:返回当前进度快照,
wait_timed_out=true;任务仍在后台运行,可再次调用续等 - 任务不存在 / 等待中被清理:
count=0+error,不抛异常
等待期间每 30 秒上报一次进度用于保活。与 Claude Code 的配合:工具调用跑过客户端的
自动转后台阈值(默认 2 分钟,属 Claude Code 侧配置 CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS,
不是本项目的配置)会自动转为后台任务,模型立即继续其它工作,调用落定时以 task
notification 唤醒——所以提交后直接 wait_for_task 即可,不需要轮询。
该工具只受自身 timeout_seconds 约束,没有全局超时(agent_timeout_seconds 机制已移除)。
get_task_progress
查询解析任务的进度:是否完成、输出路径、剩余时间与预计完成时间。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| task_id | string | 否 | 任务编号。留空则返回所有未完成(pending / running)任务 |
| limit | int | 否 | 最多返回条数(1-100),默认 20 |
| include_finished | bool | 否 | 不传 task_id 时,是否把已结束任务也列出来 |
| include_text | bool | 否 | 已完成任务是否返回完整 Markdown 正文(text_content) |
每条任务的关键字段:
| 字段 | 含义 |
|---|---|
| status | pending / running / completed / failed / timeout / cancelled |
| finished | 是否已结束 |
| output_md、images_dir | 产物路径(完成后有值) |
| summary | 一句话进度描述,如 第 2/3 分片 \| 共 500 页 |
| elapsed_seconds | 已用时(秒) |
| remaining_seconds、eta_at | 剩余时间(秒)与预计完成时间(本地时间文本),未完成时给出 |
| eta_basis | 估算依据:chunk(分片进度,最准)/ pages(云端页数进度)/ history(同类型历史均值)/ heuristic(页数经验值)/ deadline(超时上限)/ finished |
| progress | 原始进度字典(state、chunk、total_chunks、page_count、extract_progress、updated_at…)。注意其中的 mineru_task_id / mineru_batch_id 是 MinerU 云端单号,与工具返回的任务编号无关 |
| progress_updated_at | 最近一次进度心跳时间(服务端每 5 秒刷新) |
| text_content / text_content_preview | Markdown 正文(include_text=true 时给全文,否则给前 500 字预览) |
| stale_warning | 运行中任务超过 90 秒没有进度更新时的提示(可能已中断) |
get_recent_logs
查询最近的解析任务与调用记录,用于排查状态和历史。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | int | 否 | 返回记录数量,默认 10 |
| tool_name | string | 否 | 按工具名过滤(如 document_parse),为空返回全部 |
返回 {"logs": [...]},每条记录包含 source(task=进行中任务 / usage=已完成调用)、time、tool、status、latency、progress、progress_updated_at、task_id、error 等。
task_id只在document_parse的调用记录里非空(提交时生成的任务编号), 可以用它反查任务结果:get_task_progress(task_id=...)。
进行中的任务永远排在前面且不会被截断(它们的
time取最近一次进度心跳时间), 因此解析进行中也能在日志里直接看到该任务。
配置
配置位于项目根目录的 config/ 目录(已被 .gitignore 忽略)。文件不存在时会自动使用默认值。
config/features.json
{
"document_parse": {
"enabled": true,
"keep_intermediate": false,
"timeout_seconds": 0,
"allow_private_hosts": false,
"task_retention_hours": 24
}
}| 字段 | 说明 |
|---|---|
| enabled | 文档解析总开关。关闭后 MCP 不再暴露 document_parse,直接调用会返回「未启用」错误 |
| keep_intermediate | 是否保留中间文件(layout.json、content_list.json、原始文档副本等),默认 false |
| timeout_seconds | 后台解析的总超时(秒),0 表示不设超时,默认 0。MCP 工具与 cli.py parse 都读这个值 |
| allow_private_hosts | 是否允许解析指向内网 / 回环 / 云元数据地址的 URL,默认 false(SSRF 防护开启)。确有内网自建服务时才打开 |
| task_retention_hours | 任务与调用记录的保留小时数,默认 24;0 表示永不清理。服务端每小时维护一次:清理结束超过该时长的任务与同样超期的调用记录。心跳静默超过 2 小时的僵尸任务会被置为 failed(不受此值影响) |
config/app_settings.json
{
"mineru_client_token": ""
}| 字段 | 说明 |
|---|---|
| mineru_client_token | 从 MinerU 客户端提取的 client_api_token;留空则回退到访客额度 |
环境变量
| 变量 | 说明 |
|---|---|
| MINERU_MCP_CONFIG_PATH | 覆盖配置目录位置(默认 <项目根>/config;npm 安装方式下由 launcher 默认到 ~/.mineru-mcp/config,显式设置时以显式值为准) |
MinerU 凭证获取顺序
config/app_settings.json中手动填写的mineru_client_token- MinerU 客户端本地配置
~/MinerU/config.json中的client_api_token(在客户端登录一次即可) - 静默唤起 MinerU 客户端,等待其写回新 token 后自动关闭
- 内置访客 JWT(兜底,额度有限)
如需完整额度:在 mineru.net 下载并登录一次桌面客户端;或自行把客户端配置里的 client_api_token 复制到 config/app_settings.json。
命令行工具(可选)
删掉 GUI / HTTP 配置后端之后,凭证管理与排查入口就落在 cli.py 上。它不改变 MCP 工具面,对外始终只有 document_parse、wait_for_task、get_task_progress、get_recent_logs 四个工具。
# 查看客户端是否安装、token 来自哪一层、配置目录与功能开关(纯只读,不会唤起 MinerU 客户端)
uv run python cli.py status # 加 --json 便于脚本读取
# 手工管理 MinerU 凭证
uv run python cli.py token set <TOKEN>
uv run python cli.py token clear
# 不走 MCP,在终端直接跑一次解析,看到 status / method / error / progress 原始字段
uv run python cli.py parse <输入> <输出目录> [--json] \
[--keep-intermediate|--no-keep-intermediate] [--timeout N]执行过 uv sync 后也可以直接用 mineru-mcp-cli ...(由 pyproject.toml 的 [project.scripts] 注册)。
⚠️ 非 ASCII 项目路径下 console script 会启动失败。 若项目目录含中文等非 ASCII 字符(例如
D:\文档\...),mineru-mcp与mineru-mcp-cli都会以ModuleNotFoundError: No module named 'mcp_server'(或cli)结束。 原因:可编辑安装生成的.pth由 uv 以 UTF-8 写入,而 CPython 的site.py按 系统区域编码(中文 Windows 为 GBK)读取它,路径解码成D:\鑲愬銆\...这类乱码, 因目录不存在被静默丢弃,于是sys.path里根本没有项目根。 规避方式二选一:
- 用
uv run python cli.py ...代替(始终可用,推荐);uv sync --no-editable—— 把文件实际复制进site-packages,不再依赖.pth路径。代价是之后改源码需重新uv sync才会被 console script 看到。MCP 链路不受影响:客户端配置走的是
uv --directory <目录> run mcp_server.py, 直接执行脚本文件,不经过 console script。
| 子命令 | 用途 |
|---|---|
| status | 客户端路径与版本、当前 token 的来源层级与前 12 位预览、配置目录、功能开关 |
| token set <TOKEN> | 写入 config/app_settings.json 的 mineru_client_token |
| token clear | 清空,退回自动获取(客户端本地配置 → 静默唤起 → 内置访客 JWT) |
| parse <输入> <输出目录> | 直接调用解析核心,输出原始字段 |
退出码(parse):0 completed、1 failed(或配置/IO 错误)、2 timeout(任务仍在后台跑)、3 cancelled、64 参数用法错误。
MCP 与 CLI 的一致与差异:两条链路都读
config/features.json的全局设置 (keep_intermediate/timeout_seconds)。差别只在是否等待:document_parse工具立即返回task_id,解析在服务端后台继续;cli.py parse则同步等待到解析结束再打印结果。
常见问题
解析请求失败但浏览器能正常访问?
解析请求以 trust_env=False 直连 MinerU 服务,不走系统代理环境变量(HTTP_PROXY / HTTPS_PROXY)。
注意 trust_env=False 绕不过 Clash Verge 等工具的 TUN 模式:TUN 在网络层把域名劫持到 fake-ip 段(198.18.0.0/15),表现为 TLS 握手失败或上传中断。本项目的对策是 DoH 解析真实 IP 后重试(_request_with_tun_bypass),并保持证书校验开启。
解析超时了?
timeout_seconds 到期不会中断已完成的产出:任务状态记为 timeout,已完成分片的结果照常落盘
(output_md 指向部分产物)。用 get_task_progress(task_id=...) 查看 progress 与 output_md。
提交后怎么拿结果?
document_parse 只返回 task_id。优先 wait_for_task(task_id="...") 阻塞等待(自动转后台、
完成即唤醒,无需轮询);要随时看进度用 get_task_progress(task_id="..."):finished=true 时
output_md 就是产物路径;正文可用 include_text=true 取回,或直接读取该文件。
任务一直卡在 running?
后台解析跑在 MCP 服务进程里,客户端退出会终止它。若 get_task_progress 返回 stale_warning
(超过 90 秒没有进度心跳),说明任务很可能已中断,需要重新提交。
URL 解析被拒绝了?
URL 输入会做 SSRF 校验(拒绝内网 / 回环 / 云元数据地址,重定向逐跳复检)。
确有内网自建服务需要解析时,在 config/features.json 打开 document_parse.allow_private_hosts。
200 页以上的 PDF 会不会解析不了?
不会:会自动按 ≤200 页切分,逐片解析后合并为一份 Markdown,耗时随页数增长,建议适当调大 timeout_seconds。
任务和调用记录会不会一直堆积?
不会。服务端每小时自动维护一次:清理结束超过 task_retention_hours(默认 24 小时)的任务与调用记录,
并把心跳静默超过 2 小时的僵尸任务置为 failed。要长期保留就把该值调大,设为 0 则永不清理。
任务一直卡在 running?
后台解析跑在 MCP 服务进程里,进程退出会留下僵尸任务。超过 2 小时没有心跳的任务会被维护动作置为
failed(原因写明「任务已中断」);在此之前查询会带 stale_warning 提示。
需要注册页面/开关文件? 不需要。本项目是纯 stdio MCP Server,无 GUI、无后台常驻服务。
开发
# 安装依赖
uv sync
# 调试 MCP Server
uv run mcp dev mcp_server.py
# 运行测试
uv run pytest tests/测试用配置目录已通过 MINERU_MCP_CONFIG_PATH 隔离到临时目录,不会污染 config/ 下的运行数据。
发布(维护者)
npm 包与 Python 包共用同一个仓库根目录:package.json 的 files 字段声明打进 npm 包的文件(Python 源码 + launcher),.npmignore 排除 __pycache__ 等本地产物。
版本号必须三处一致:pyproject.toml、package.json、mcp_server.py 的 version=。
方式一:一键发布脚本(本机,推荐)
uv run python scripts/release.py --check-only # 体检:版本 + 测试 + 打包清单
uv run python scripts/release.py --bump patch # 提版本 → 测试 → 提交 → 推送
uv run python scripts/release.py --bump minor --publish --yes # 连发布一起(会要 npm 2FA)脚本按顺序执行:版本三处一致性校验 → uv run pytest → npm pack --dry-run 清单体检
(拒收 __pycache__ / tests/ / config/ / .venv / output/)→ git add/commit/push →
(带 --publish 时)npm publish --access public → 发布后核验 registry 版本与 tarball
integrity 是否与本地打包逐字节一致。--dry-run 演练、--no-push 只提交、--check-only 只体检。
方式二:GitHub Actions 自动发布(OIDC / Trusted Publishing,零密钥)
.github/workflows/release.yml 在推送 v* tag 时触发(也可手动
workflow_dispatch,支持只演练)。它跑完测试与清单体检后,用 npm Trusted Publishing(OIDC)
发布,不需要任何 token 或密码。
启用前提(缺一不可):
- 仓库在 GitHub 上有一份 —— npm 的 Trusted Publisher 只支持 GitHub Actions / GitLab CI, 本项目主仓库在 Gitee,Gitee Go 走不了 OIDC;
- npmjs.com → 该包 → Settings → Trusted Publisher → 选 GitHub Actions,填
organization/user、repository、workflow filename = release.yml(必须逐字一致); - npm CLI ≥ 11.5.1(trusted publishing 自该版本起支持)。
之后发布变成:git tag v0.4.0 && git push origin v0.4.0(或 push 到 GitHub 触发)→ Actions
自动发布并附带 provenance 签名(用户可看到"由哪个 commit 构建")。tag 必须等于三处版本号,
workflow 会先校验再发布。
首次登录与 2FA
npm login # 只需一次
npm publish --access public # 开了 2FA 的账号会要求 OTP 或弹网页认证npm 已开始收紧 bypass-2FA 令牌:账号/包管理操作与(约 2027-01 起)直接发布都会逐步 受限,官方推荐的长期方案就是上面的 Trusted Publishing。
发布前自检清单
- 版本三处一致(
scripts/release.py会校验) npm pack --dry-run输出中不包含__pycache__、.venv、tests/、config/、output/- 本地验证:
npm pack后把 tarball 装进临时目录,用node <安装目录>/bin/cli.js跑一次 MCP initialize 握手 - npx 冷启动验证:在包源码目录之外的任意目录执行
npx -y @vandoor2/mineru-mcp, 跑一次initialize+tools/list(期望恰好 4 个工具)。不要在本仓库内验证,见上方「已知限制」
License
MIT
