pdf-capture-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pdf_capture_mcp-0.1.0/.github/workflows/ci.yml +38 -0
- pdf_capture_mcp-0.1.0/.github/workflows/publish.yml +34 -0
- pdf_capture_mcp-0.1.0/.gitignore +35 -0
- pdf_capture_mcp-0.1.0/LICENSE +33 -0
- pdf_capture_mcp-0.1.0/PKG-INFO +39 -0
- pdf_capture_mcp-0.1.0/README.md +347 -0
- pdf_capture_mcp-0.1.0/pyproject.toml +69 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/__init__.py +3 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/__main__.py +35 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/classifier.py +125 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/config.py +78 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/engines/__init__.py +75 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/engines/base.py +47 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/engines/marker_engine.py +162 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/engines/mineru_engine.py +351 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/engines/pymupdf_engine.py +141 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/extractors/__init__.py +1 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/extractors/formulas.py +140 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/extractors/tables.py +126 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/extractors/tatr.py +118 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/llm_client.py +376 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/processors/__init__.py +1 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/processors/images.py +115 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/processors/layout.py +139 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/quality/__init__.py +1 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/quality/qc_gate.py +165 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/server.py +794 -0
- pdf_capture_mcp-0.1.0/src/pdf_capture_mcp/types.py +65 -0
- pdf_capture_mcp-0.1.0/tests/conftest.py +9 -0
- pdf_capture_mcp-0.1.0/tests/e2e_manual.py +134 -0
- pdf_capture_mcp-0.1.0/tests/test_server.py +66 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- run: uv sync --extra dev
|
|
16
|
+
- run: uv run ruff check src/ tests/
|
|
17
|
+
- run: uv run ruff format --check src/ tests/
|
|
18
|
+
|
|
19
|
+
typecheck:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: astral-sh/setup-uv@v5
|
|
24
|
+
- run: uv sync --extra dev
|
|
25
|
+
- run: uv run mypy src/pdf_capture_mcp/ --ignore-missing-imports
|
|
26
|
+
|
|
27
|
+
test:
|
|
28
|
+
runs-on: ${{ matrix.os }}
|
|
29
|
+
strategy:
|
|
30
|
+
matrix:
|
|
31
|
+
os: [ubuntu-latest, macos-latest]
|
|
32
|
+
python-version: ["3.11", "3.12"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: astral-sh/setup-uv@v5
|
|
36
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
37
|
+
- run: uv sync --extra dev --python ${{ matrix.python-version }}
|
|
38
|
+
- run: uv run pytest tests/ -v --tb=short
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
- name: Build package
|
|
14
|
+
run: uv build
|
|
15
|
+
- name: Upload artifacts
|
|
16
|
+
uses: actions/upload-artifact@v4
|
|
17
|
+
with:
|
|
18
|
+
name: dist
|
|
19
|
+
path: dist/
|
|
20
|
+
|
|
21
|
+
publish:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
id-token: write # Required for PyPI trusted publishing
|
|
26
|
+
steps:
|
|
27
|
+
- name: Download artifacts
|
|
28
|
+
uses: actions/download-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
- uses: astral-sh/setup-uv@v5
|
|
33
|
+
- name: Publish to PyPI
|
|
34
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
uv.lock
|
|
13
|
+
|
|
14
|
+
# Testing & tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.idea/
|
|
23
|
+
.vscode/
|
|
24
|
+
.qoder/
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
|
|
29
|
+
# Local artifacts
|
|
30
|
+
*.log
|
|
31
|
+
tmp/
|
|
32
|
+
._*
|
|
33
|
+
.pytest_cache/
|
|
34
|
+
.ruff_cache/
|
|
35
|
+
.venv/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Third-party license notices:
|
|
26
|
+
|
|
27
|
+
- marker-pdf (Apache 2.0): https://github.com/datalab-to/marker
|
|
28
|
+
- MinerU (AGPL-3.0): https://github.com/opendatalab/MinerU
|
|
29
|
+
MinerU is invoked as a separate subprocess and is not linked into this package.
|
|
30
|
+
- pdfplumber (MIT): https://github.com/jsvine/pdfplumber
|
|
31
|
+
- Table Transformer (MIT): https://github.com/microsoft/table-transformer
|
|
32
|
+
- DePlot (Apache 2.0): https://github.com/google-research/google-research/tree/master/deplot
|
|
33
|
+
- pymupdf4llm (Apache 2.0): https://github.com/pymupdf/pymupdf4llm
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdf-capture-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-phase PDF capture pipeline as an MCP server. Convert PDF to high-quality structured Markdown.
|
|
5
|
+
Project-URL: Repository, https://github.com/ChenHongYu2026/pdf-capture-mcp
|
|
6
|
+
Project-URL: Issues, https://github.com/ChenHongYu2026/pdf-capture-mcp/issues
|
|
7
|
+
Author: ChenHongYu2026
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: extraction,markdown,mcp,ocr,pdf
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: fastmcp<4,>=3.2.0
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Requires-Dist: numpy>=1.26
|
|
21
|
+
Requires-Dist: pdfplumber>=0.11.0
|
|
22
|
+
Requires-Dist: pillow>=10.0
|
|
23
|
+
Requires-Dist: pymupdf4llm>=0.0.17
|
|
24
|
+
Requires-Dist: pypdf>=5.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: marker-pdf>=2.0; extra == 'all'
|
|
27
|
+
Requires-Dist: torch>=2.0; extra == 'all'
|
|
28
|
+
Requires-Dist: transformers>=4.40; extra == 'all'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.15; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
34
|
+
Provides-Extra: marker
|
|
35
|
+
Requires-Dist: marker-pdf>=2.0; extra == 'marker'
|
|
36
|
+
Provides-Extra: ml
|
|
37
|
+
Requires-Dist: torch>=2.0; extra == 'ml'
|
|
38
|
+
Requires-Dist: transformers>=4.40; extra == 'ml'
|
|
39
|
+
Provides-Extra: vlm
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
<a id="english"></a>
|
|
2
|
+
|
|
3
|
+
# pdf-capture-mcp
|
|
4
|
+
|
|
5
|
+
**English** | [中文](#chinese)
|
|
6
|
+
|
|
7
|
+
Multi-phase PDF capture pipeline as an [MCP](https://modelcontextprotocol.io) server.
|
|
8
|
+
Convert PDF documents into high-quality structured Markdown — with formula recognition,
|
|
9
|
+
table extraction, layout cleaning, and a built-in quality gate.
|
|
10
|
+
|
|
11
|
+
[](https://github.com/ChenHongYu2026/pdf-capture-mcp/actions/workflows/ci.yml)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
[](https://www.python.org/downloads/)
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Triple extraction engines**
|
|
18
|
+
- [pymupdf4llm](https://github.com/pymupdf/pymupdf4llm) (built-in) — zero setup, fast, always available
|
|
19
|
+
- [marker](https://github.com/datalab-to/marker) (recommended) — highest quality for complex layouts
|
|
20
|
+
- [MinerU](https://github.com/opendatalab/MinerU) (optional) — best for multi-column/InDesign PDFs, auto-managed in an isolated venv
|
|
21
|
+
- **7 MCP tools** — `pdf_to_markdown`, `extract_tables`, `classify_document`, `pdf_info`, `setup_vlm`, `check_environment`, `install_engine`
|
|
22
|
+
- **Optional VLM enhancement** — plug in any vision-capable model (Qwen-VL, GLM-4V, MiniMax, Moonshot, OpenAI, local Ollama…) for better table/formula extraction. **No extra dependencies needed** — works out of the box with the base install.
|
|
23
|
+
- **Quality gate** — multi-dimensional QC (text completeness, heading structure, formula integrity, table coverage)
|
|
24
|
+
- **Progressive setup** — works immediately with zero config; enhance with marker/VLM on demand
|
|
25
|
+
- **Privacy-first** — API keys are stored locally with `chmod 600` and never echoed back in responses
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Base package (pymupdf engine + VLM support, ~80MB) — works immediately
|
|
33
|
+
pip install pdf-capture-mcp
|
|
34
|
+
|
|
35
|
+
# With marker engine (recommended for complex PDFs, includes PyTorch, ~2.5GB)
|
|
36
|
+
pip install "pdf-capture-mcp[marker]"
|
|
37
|
+
|
|
38
|
+
# Everything (marker + TATR table detection + DePlot charts, ~3GB)
|
|
39
|
+
pip install "pdf-capture-mcp[all]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Add to your MCP client
|
|
43
|
+
|
|
44
|
+
For Qoder / Claude Desktop / Cursor, add to your `mcp.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"pdf-capture": {
|
|
50
|
+
"command": "uvx",
|
|
51
|
+
"args": ["pdf-capture-mcp"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
With marker engine (recommended):
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"pdf-capture": {
|
|
63
|
+
"command": "uvx",
|
|
64
|
+
"args": ["pdf-capture-mcp[marker]"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or if installed via pip:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"pdf-capture": {
|
|
76
|
+
"command": "pdf-capture-mcp"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. Use it
|
|
83
|
+
|
|
84
|
+
Ask your AI agent things like:
|
|
85
|
+
|
|
86
|
+
> "Convert ~/Downloads/paper.pdf to Markdown"
|
|
87
|
+
> "Extract all tables from this report"
|
|
88
|
+
> "Is this PDF a scanned document?"
|
|
89
|
+
|
|
90
|
+
The server works immediately with the built-in pymupdf engine. For higher quality on
|
|
91
|
+
complex layouts, the agent can install marker on demand (or you can pre-install it).
|
|
92
|
+
|
|
93
|
+
## Tools
|
|
94
|
+
|
|
95
|
+
| Tool | Description |
|
|
96
|
+
|------|-------------|
|
|
97
|
+
| `pdf_to_markdown` | Full pipeline: extract → clean → QC → structured Markdown |
|
|
98
|
+
| `extract_tables` | Table extraction (pdfplumber rules + optional TATR deep learning) |
|
|
99
|
+
| `classify_document` | Document type detection (academic paper, consulting report, …) |
|
|
100
|
+
| `pdf_info` | Fast metadata: page count, text layer, scanned detection |
|
|
101
|
+
| `setup_vlm` | Configure optional VLM enhancement (any vision-capable provider) |
|
|
102
|
+
| `check_environment` | Verify engines, dependencies, and filesystem compatibility |
|
|
103
|
+
| `install_engine` | Install marker/ml engines on behalf of the user |
|
|
104
|
+
|
|
105
|
+
## VLM Enhancement (Optional)
|
|
106
|
+
|
|
107
|
+
VLM re-extracts complex tables and broken formulas from page images.
|
|
108
|
+
Works with **any provider whose model supports image input**:
|
|
109
|
+
|
|
110
|
+
| Provider | Example model | API base |
|
|
111
|
+
|----------|--------------|----------|
|
|
112
|
+
| Alibaba Qwen | `qwen-vl-max` | `https://dashscope.aliyuncs.com/compatible-mode/v1` |
|
|
113
|
+
| Zhipu AI | `glm-4v` | `https://open.bigmodel.cn/api/paas/v4` |
|
|
114
|
+
| MiniMax | `minimax-m3` | `https://api.minimaxi.com/v1` |
|
|
115
|
+
| Moonshot | `moonshot-v1-vision` | `https://api.moonshot.cn/v1` |
|
|
116
|
+
| OpenAI | `gpt-4o` | `https://api.openai.com/v1` |
|
|
117
|
+
| Ollama (local) | `llama3.2-vision` | `http://localhost:11434/v1` |
|
|
118
|
+
|
|
119
|
+
Set your key via environment variable (recommended — never typed into chat):
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
export PDF_CAPTURE_VLM_API_KEY=your_key_here
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Then tell your agent: *"Enable VLM with qwen-vl-max"* — it validates vision capability
|
|
126
|
+
before saving. **Note: using VLM consumes your API tokens.**
|
|
127
|
+
|
|
128
|
+
## MinerU Engine (Optional)
|
|
129
|
+
|
|
130
|
+
For the highest extraction quality on complex layouts (multi-column, InDesign PDFs):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pdf-capture-mcp setup-mineru # requires Python 3.11 on PATH
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Models (~2GB) auto-download from ModelScope on first extraction.
|
|
137
|
+
|
|
138
|
+
## Environment Variables
|
|
139
|
+
|
|
140
|
+
| Variable | Default | Description |
|
|
141
|
+
|----------|---------|-------------|
|
|
142
|
+
| `PDF_CAPTURE_ENGINE` | `auto` | Default engine: `marker` / `mineru` / `pymupdf` / `auto` |
|
|
143
|
+
| `PDF_CAPTURE_VLM_API_KEY` | — | VLM API key (preferred over passing in chat) |
|
|
144
|
+
| `PDF_CAPTURE_CACHE_DIR` | `~/.cache/pdf-capture-mcp` | Model & config cache |
|
|
145
|
+
| `PDF_CAPTURE_MINERU_VENV` | `<cache>/venv-mineru` | MinerU venv location |
|
|
146
|
+
| `PDF_CAPTURE_LOG_LEVEL` | `INFO` | Logging level |
|
|
147
|
+
| `MINERU_MODEL_SOURCE` | `modelscope` | MinerU model source: `modelscope` / `huggingface` / `local` |
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git clone https://github.com/ChenHongYu2026/pdf-capture-mcp.git
|
|
153
|
+
cd pdf-capture-mcp
|
|
154
|
+
uv sync --extra dev
|
|
155
|
+
uv run pytest tests/ -v
|
|
156
|
+
uv run ruff check src/ tests/
|
|
157
|
+
uv run mypy src/pdf_capture_mcp/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Troubleshooting
|
|
161
|
+
|
|
162
|
+
| Problem | Solution |
|
|
163
|
+
|---------|----------|
|
|
164
|
+
| `Operation not supported` during install | External/exFAT drive: `export UV_LINK_MODE=copy` then retry |
|
|
165
|
+
| `uvx: command not found` | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` |
|
|
166
|
+
| MCP server not appearing in tools | Restart your MCP client; check `mcp.json` syntax |
|
|
167
|
+
| marker engine slow on first run | Downloads ~1GB models on first use; cached afterwards |
|
|
168
|
+
| Python version too low | Requires 3.11+: `uv python install 3.11` |
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT — see [LICENSE](LICENSE).
|
|
173
|
+
|
|
174
|
+
Third-party notices: MinerU (AGPL-3.0, invoked as a separate subprocess, not linked),
|
|
175
|
+
marker (Apache-2.0), pdfplumber (MIT), Table Transformer (MIT), pymupdf4llm (Apache-2.0).
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
<a id="chinese"></a>
|
|
180
|
+
|
|
181
|
+
# pdf-capture-mcp(中文)
|
|
182
|
+
|
|
183
|
+
[English](#english) | **中文**
|
|
184
|
+
|
|
185
|
+
多阶段 PDF 捕获管线,以 [MCP](https://modelcontextprotocol.io) 服务器形式提供。
|
|
186
|
+
将 PDF 文档转换为高质量结构化 Markdown —— 支持公式识别、表格提取、版面清洁和内置质量门控。
|
|
187
|
+
|
|
188
|
+
## 功能特性
|
|
189
|
+
|
|
190
|
+
- **三提取引擎**
|
|
191
|
+
- [pymupdf4llm](https://github.com/pymupdf/pymupdf4llm)(内置)—— 零配置、快速、始终可用
|
|
192
|
+
- [marker](https://github.com/datalab-to/marker)(推荐)—— 复杂版面提取质量最高
|
|
193
|
+
- [MinerU](https://github.com/opendatalab/MinerU)(可选)—— 多栏/InDesign 排版最佳,自动管理独立虚拟环境
|
|
194
|
+
- **7 个 MCP 工具** —— `pdf_to_markdown`、`extract_tables`、`classify_document`、`pdf_info`、`setup_vlm`、`check_environment`、`install_engine`
|
|
195
|
+
- **可选 VLM 增强** —— 接入任何具备视觉能力的模型(通义千问 Qwen-VL、智谱 GLM-4V、MiniMax、月之暗面 Moonshot、OpenAI、本地 Ollama 等),提升表格/公式提取质量。**无需额外依赖**,基础安装即可使用。
|
|
196
|
+
- **质量门控** —— 多维度 QC 评估(文本完整度、标题结构、公式完好率、表格覆盖率)
|
|
197
|
+
- **渐进式配置** —— 零配置即可工作;按需增强 marker/VLM
|
|
198
|
+
- **隐私优先** —— API Key 以 `chmod 600` 权限本地存储,绝不在响应中回显
|
|
199
|
+
|
|
200
|
+
## 快速开始
|
|
201
|
+
|
|
202
|
+
### 1. 安装
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# 基础包(pymupdf 引擎 + VLM 支持,约 80MB)—— 安装即可用
|
|
206
|
+
pip install pdf-capture-mcp
|
|
207
|
+
|
|
208
|
+
# 含 marker 引擎(推荐复杂 PDF,包含 PyTorch,约 2.5GB)
|
|
209
|
+
pip install "pdf-capture-mcp[marker]"
|
|
210
|
+
|
|
211
|
+
# 完整安装(marker + TATR 表格检测 + DePlot 图表提取,约 3GB)
|
|
212
|
+
pip install "pdf-capture-mcp[all]"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 2. 添加到 MCP 客户端
|
|
216
|
+
|
|
217
|
+
Qoder / Claude Desktop / Cursor 用户,在 `mcp.json` 中添加:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"mcpServers": {
|
|
222
|
+
"pdf-capture": {
|
|
223
|
+
"command": "uvx",
|
|
224
|
+
"args": ["pdf-capture-mcp"]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
含 marker 引擎(推荐):
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"mcpServers": {
|
|
235
|
+
"pdf-capture": {
|
|
236
|
+
"command": "uvx",
|
|
237
|
+
"args": ["pdf-capture-mcp[marker]"]
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
如果已通过 pip 安装:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"mcpServers": {
|
|
248
|
+
"pdf-capture": {
|
|
249
|
+
"command": "pdf-capture-mcp"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 3. 开始使用
|
|
256
|
+
|
|
257
|
+
直接对你的 AI 助手说:
|
|
258
|
+
|
|
259
|
+
> “把 ~/Downloads/论文.pdf 转成 Markdown”
|
|
260
|
+
> “提取这份报告里的所有表格”
|
|
261
|
+
> “这个 PDF 是扫描件吗?”
|
|
262
|
+
|
|
263
|
+
服务器使用内置 pymupdf 引擎即可立即工作。对于复杂版面,助手可按需安装 marker 引擎。
|
|
264
|
+
|
|
265
|
+
## 工具列表
|
|
266
|
+
|
|
267
|
+
| 工具 | 说明 |
|
|
268
|
+
|------|------|
|
|
269
|
+
| `pdf_to_markdown` | 完整管线:提取 → 清洁 → QC → 结构化 Markdown |
|
|
270
|
+
| `extract_tables` | 表格提取(pdfplumber 规则 + 可选 TATR 深度学习) |
|
|
271
|
+
| `classify_document` | 文档类型检测(学术论文、咨询报告等) |
|
|
272
|
+
| `pdf_info` | 快速元数据:页数、文本层、扫描件检测 |
|
|
273
|
+
| `setup_vlm` | 配置可选的 VLM 增强(支持任何具备视觉能力的供应商) |
|
|
274
|
+
| `check_environment` | 校验引擎、依赖和文件系统兼容性 |
|
|
275
|
+
| `install_engine` | 代用户安装 marker/ml 引擎 |
|
|
276
|
+
|
|
277
|
+
## VLM 增强(可选)
|
|
278
|
+
|
|
279
|
+
VLM 会从页面图像中重新提取复杂表格和损坏的公式。
|
|
280
|
+
支持**任何模型具备图片输入能力的供应商**:
|
|
281
|
+
|
|
282
|
+
| 供应商 | 示例模型 | API 地址 |
|
|
283
|
+
|--------|----------|----------|
|
|
284
|
+
| 阿里通义千问 | `qwen-vl-max` | `https://dashscope.aliyuncs.com/compatible-mode/v1` |
|
|
285
|
+
| 智谱 AI | `glm-4v` | `https://open.bigmodel.cn/api/paas/v4` |
|
|
286
|
+
| MiniMax | `minimax-m3` | `https://api.minimaxi.com/v1` |
|
|
287
|
+
| 月之暗面 | `moonshot-v1-vision` | `https://api.moonshot.cn/v1` |
|
|
288
|
+
| OpenAI | `gpt-4o` | `https://api.openai.com/v1` |
|
|
289
|
+
| Ollama(本地) | `llama3.2-vision` | `http://localhost:11434/v1` |
|
|
290
|
+
|
|
291
|
+
推荐通过环境变量设置 Key(避免在对话中输入):
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
export PDF_CAPTURE_VLM_API_KEY=你的密钥
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
然后告诉助手:"启用 VLM,用 qwen-vl-max" —— 系统会先验证模型的视觉能力再保存配置。
|
|
298
|
+
**注意:使用 VLM 功能会消耗你的 Token。**
|
|
299
|
+
|
|
300
|
+
## MinerU 引擎(可选)
|
|
301
|
+
|
|
302
|
+
针对复杂版面(多栏、InDesign 排版 PDF)获得最高提取质量:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
pdf-capture-mcp setup-mineru # 需要 PATH 中有 Python 3.11
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
首次提取时会从 ModelScope 自动下载模型(约 2GB)。
|
|
309
|
+
|
|
310
|
+
## 环境变量
|
|
311
|
+
|
|
312
|
+
| 变量 | 默认值 | 说明 |
|
|
313
|
+
|------|--------|------|
|
|
314
|
+
| `PDF_CAPTURE_ENGINE` | `auto` | 默认引擎:`marker` / `mineru` / `pymupdf` / `auto` |
|
|
315
|
+
| `PDF_CAPTURE_VLM_API_KEY` | — | VLM API Key(推荐方式,避免对话中传递) |
|
|
316
|
+
| `PDF_CAPTURE_CACHE_DIR` | `~/.cache/pdf-capture-mcp` | 模型与配置缓存目录 |
|
|
317
|
+
| `PDF_CAPTURE_MINERU_VENV` | `<cache>/venv-mineru` | MinerU 虚拟环境位置 |
|
|
318
|
+
| `PDF_CAPTURE_LOG_LEVEL` | `INFO` | 日志级别 |
|
|
319
|
+
| `MINERU_MODEL_SOURCE` | `modelscope` | MinerU 模型源:`modelscope` / `huggingface` / `local` |
|
|
320
|
+
|
|
321
|
+
## 本地开发
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
git clone https://github.com/ChenHongYu2026/pdf-capture-mcp.git
|
|
325
|
+
cd pdf-capture-mcp
|
|
326
|
+
uv sync --extra dev
|
|
327
|
+
uv run pytest tests/ -v
|
|
328
|
+
uv run ruff check src/ tests/
|
|
329
|
+
uv run mypy src/pdf_capture_mcp/
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## 故障排查
|
|
333
|
+
|
|
334
|
+
| 问题 | 解决方案 |
|
|
335
|
+
|------|----------|
|
|
336
|
+
| 安装时报 `Operation not supported` | 外置/exFAT 磁盘:`export UV_LINK_MODE=copy` 后重试 |
|
|
337
|
+
| `uvx: command not found` | 安装 uv:`curl -LsSf https://astral.sh/uv/install.sh \| sh` |
|
|
338
|
+
| MCP 服务器未出现在工具列表 | 重启 MCP 客户端;检查 `mcp.json` 格式 |
|
|
339
|
+
| marker 引擎首次运行慢 | 首次使用需下载约 1GB 模型,后续使用缓存 |
|
|
340
|
+
| Python 版本过低 | 需要 3.11+:`uv python install 3.11` |
|
|
341
|
+
|
|
342
|
+
## 许可证
|
|
343
|
+
|
|
344
|
+
MIT —— 详见 [LICENSE](LICENSE)。
|
|
345
|
+
|
|
346
|
+
第三方声明:MinerU(AGPL-3.0,通过独立子进程调用,未链接)、
|
|
347
|
+
marker(Apache-2.0)、pdfplumber(MIT)、Table Transformer(MIT)、pymupdf4llm(Apache-2.0)。
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pdf-capture-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Multi-phase PDF capture pipeline as an MCP server. Convert PDF to high-quality structured Markdown."
|
|
9
|
+
license = "MIT"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
authors = [{ name = "ChenHongYu2026" }]
|
|
12
|
+
keywords = ["pdf", "markdown", "mcp", "extraction", "ocr"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Text Processing :: Markup :: Markdown",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"fastmcp>=3.2.0,<4",
|
|
23
|
+
"pymupdf4llm>=0.0.17",
|
|
24
|
+
"pdfplumber>=0.11.0",
|
|
25
|
+
"pypdf>=5.0",
|
|
26
|
+
"Pillow>=10.0",
|
|
27
|
+
"numpy>=1.26",
|
|
28
|
+
"httpx>=0.27",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
marker = ["marker-pdf>=2.0"]
|
|
33
|
+
ml = ["torch>=2.0", "transformers>=4.40"]
|
|
34
|
+
vlm = [] # VLM uses OpenAI-compatible HTTP API via httpx (already a base dep)
|
|
35
|
+
all = ["pdf-capture-mcp[marker,ml]"]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=8.0",
|
|
38
|
+
"pytest-asyncio>=0.24",
|
|
39
|
+
"ruff>=0.15",
|
|
40
|
+
"mypy>=1.15",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
pdf-capture-mcp = "pdf_capture_mcp.__main__:main"
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Repository = "https://github.com/ChenHongYu2026/pdf-capture-mcp"
|
|
48
|
+
Issues = "https://github.com/ChenHongYu2026/pdf-capture-mcp/issues"
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.wheel]
|
|
51
|
+
packages = ["src/pdf_capture_mcp"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 100
|
|
55
|
+
target-version = "py311"
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
59
|
+
|
|
60
|
+
[tool.mypy]
|
|
61
|
+
# No python_version pin: numpy's type stubs use PEP 695 syntax (3.12+),
|
|
62
|
+
# which mypy rejects when targeting 3.11. Checks run against the active interpreter.
|
|
63
|
+
warn_return_any = true
|
|
64
|
+
warn_unused_configs = true
|
|
65
|
+
ignore_missing_imports = true
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
69
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Entry point: python -m pdf_capture_mcp"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
"""Start the PDF Capture MCP server."""
|
|
10
|
+
from pdf_capture_mcp.config import setup_logging
|
|
11
|
+
from pdf_capture_mcp.server import mcp
|
|
12
|
+
|
|
13
|
+
setup_logging()
|
|
14
|
+
|
|
15
|
+
# Handle setup-mineru subcommand
|
|
16
|
+
if len(sys.argv) > 1 and sys.argv[1] == "setup-mineru":
|
|
17
|
+
from pdf_capture_mcp.engines.mineru_engine import ensure_mineru_env
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
python_exe = ensure_mineru_env()
|
|
21
|
+
print(f"MinerU environment ready: {python_exe}")
|
|
22
|
+
except RuntimeError as exc:
|
|
23
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
# Determine transport
|
|
28
|
+
if "--sse" in sys.argv:
|
|
29
|
+
mcp.run(transport="sse")
|
|
30
|
+
else:
|
|
31
|
+
mcp.run(transport="stdio")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
main()
|