内容
# harvester
一款多格式的MCP **获取 + 搜索** 服务器。它可以将网页、PDF、Office文档、存档和本地文件转换为干净的Markdown;解析学术标识符(DOI/ISBN/标题)为免费、合法的全文本副本;将每个工件缓存在磁盘上的按类型分区的`.fetch/`树下;并在出现错误之前通过四级墙绕过梯子。
> [!CAUTION]
> 此服务器可以访问本地文件和本地/内部IP地址,并可以访问开放的网络。它可能代表安全风险。请以最小权限运行,并确保它不会暴露敏感数据。(凭证和密钥文件被拒绝 - 请参见[本地路径支持](#本地路径支持和拒绝路径)。)
- **仓库:** <https://github.com/mreza0100/harvester-web-mcp>
- **包名:** `harvester-mcp`(尚未发布到PyPI - 从源代码或GitHub安装)
- **命令/模块:** `harvester`(`python -m harvester`)
---
## 功能
**一个API,多种格式。** 传递一个URL、本地路径或学术标识符 - `harvester` 检测格式,将其路由到正确的转换器,缓存Markdown结果,并返回全文内容。第二次调用同一个输入时,直接从缓存中获取,无需重新获取。
**墙绕过梯子。** 对于HTML页面,服务器尝试四种方法,按顺序停止,一旦获取到丰富的内容:
1. **httpx** - 标准HTTP,使用`trafilatura`提取(`favor_recall`);文章图片被本地下载,``链接被重写为本地缓存路径。
2. **curl_cffi Chrome伪装** - 重现真实的Chrome TLS/JA3指纹;绕过许多机器人检测CDN。
3. **Jina Reader** (`https://r.jina.ai/<url>`) - 外部无头提取;跳过私有/内部主机。
4. **开放访问镜像** - 从URL中提取DOI(或从阻止的登陆页面抓取`citation_pdf_url` / `citation_doi`元标签),然后通过Europe PMC PDF → PMC文章HTML → 全开放访问链(Unpaywall,OpenAlex,Semantic Scholar,CORE,DOAJ,arXiv/OSF)→ Wayback Machine快照。这是针对墙学术出版商的主要路径。
PDF/Office/存档下载共享梯子的精神:空或403下载重试使用`curl_cffi`伪装,并且带有可提取DOI的墙出版商PDF URL跳转到开放访问镜像,然后放弃。
硬IP声誉块(出版商DRM,需要身份验证的付费墙)无法在没有住宅出口节点的情况下解决 - 超出范围,服务器显示清晰的错误消息。
---
## 格式矩阵
| 输入 | 转换器 | 缓存子目录 |
|---|---|---|
| 网页/HTML | trafilatura(`favor_recall`,本地图片) | `.fetch/html/` |
| PDF URL或文件(无扩展名的URL头部嗅探) | pymupdf4llm | `.fetch/pdf/` |
| DOCX,XLSX,PPTX | Docling | `.fetch/docx/` `.fetch/xlsx/` `.fetch/pptx/` |
| CSV | MarkItDown | `.fetch/csv/` |
| JSON | literal pretty-print在` ```json ````块中 | `.fetch/json/` |
| 图片(JPG/PNG/GIF/WebP/BMP/SVG/TIFF) | 保存本地,返回路径以供视觉使用 | `.fetch/<ext>/` |
| 存档(.zip / .tar(.gz/.bz2/.xz) / .7z / .rar) | 安全列表,成员按需 | `.fetch/<kind>/` |
| 存档成员(`source::path/to/member`) | 按成员扩展路由 | `.fetch/archive_member/` |
| 本地路径或`file://` URL | 相同路由,无网络 | 相同树 |
**图片本地化:** trafilatura提取``链接;每个图片并发下载(上限:50张图片,10MB每张)到`.fetch/<ext>/`,链接被重写为本地路径,使图片可被视觉工具读取。
**头部嗅探路由:** 内容类型和魔术字节覆盖URL扩展,因此`https://arxiv.org/pdf/1706.03762`(无`.pdf`)被正确路由到pymupdf4llm。实际提供HTML墙的`.pdf` URL被拒绝,而不是作为静默错误文档馈送到PDF解析器。
**DOI / ISBN快速路径:** 裸DOI(`10.xxxx/...`),`doi:`前缀,`doi.org` URL,裸ISBN和`isbn:`字符串跳过HTTP梯子,直接进入开放访问解析器。裸PMID/PMCID标识符通过Europe PMC解析。
---
## 工具
服务器暴露六种工具(`search`仅在配置了搜索后端时出现 - 请参见下方)。
### `fetch`
将一个或多个**来源**转换为干净的Markdown,按输入顺序返回全文内容。
- `sources`(字符串数组,**必填**,1-50项)- 每个项是一个**位置**(网页URL,本地路径或`file://` URL → 网页/PDF/DOCX/XLSX/PPTX/CSV/JSON)或一个**明确标识符**(DOI为`10.xxxx/…`,`doi:`或`doi.org` URL;书籍为`isbn:9780262300988`或裸ISBN)。标识符被解析为免费、合法的副本,然后转换。可以混合多种类型;失败项返回描述性逐项错误,其余项仍返回。
`fetch`仅返回文档Markdown。传递图片、存档或裸标题,它指向正确的 sibling 工具(`downloadImage`、`archive`、`find`)而不是猜测。每个结果带有简短头部(来源、`cache_status`、`method`、`bytes`、`cache path`),后跟内容。
### `find`
学术对应网页搜索:标题或自由文本书目查询 → 候选作品排名列表(论文+书籍),每个作品都有一个可用的`fetch:`句柄。它不下载任何内容 - 您选择一个候选并将其句柄传递给`fetch`(`find → fetch`模式)。论文来自OpenAlex;书籍来自Open Library + Project Gutenberg。
- `query`(字符串,**必填**)- 论文/书籍标题或自由文本查询。
- `limit`(整数,可选,默认**8**,范围1-25)- 返回候选作品的最大数量。
### `search`
开放网络搜索 - 标题 · 网址 · 摘要 · 引擎 - 用于分诊;使用`fetch`获取您想要的网址(`search → fetch`模式)。返回链接和摘要,不清洁内容。
- `query`(字符串,**必填**)- 网络搜索查询。
- `count`(整数,可选,默认**8**,范围1-20)- 最大结果数。
- `lang`(字符串,可选,默认`""`)- 偏向搜索的语言/地区(例如`zh`、`ja`、`pt-BR`);将查询路由到该语言的原生引擎,以获取非英语文献。
- `engines`(字符串,可选,默认`""`)- 以逗号分隔的SearXNG引擎列表,限制搜索(例如`google,brave`或`naver,yahoo`)。省略默认聚合集。
**后端(参见[网页搜索](#网页搜索)):** 仅当配置了后端时显示工具。
### `downloadImage`
下载一个或多个图片并返回它们的本地文件路径以供视觉阅读 - 图表、照片、图表、扫描页。图片被保存,而不是OCR;打开返回的路径以查看内容。
- `sources`(字符串数组,**必填**,1-50项)- 图片URL或本地图片路径。每个图片被保存到`.fetch/<ext>/`(例如`.fetch/png/`,`.fetch/jpg/`),并按顺序返回其路径。
### `archive`
安全浏览单个存档 - `.zip` / `.tar(.gz/.bz2/.xz)` / `.7z` / `.rar` - 通过URL或本地路径。两步,如`find → fetch`。
- `source`(字符串,**必填**)- 存档的URL或本地路径。
- `member`(字符串,可选,默认`null`)- 省略以获取安全成员列表(名称+大小;无内容提取到磁盘)。从该列表中提供成员名称以获取该成员,转换为Markdown。路径遍历和符号链接成员被拒绝;成员数量/大小上限被强制执行;存档永远不会自动提取。
### `grep_cache`
在`.fetch/`下缓存的每个页面中搜索正则表达式模式 - 回忆您已经获取的内容而无需重新爬取。返回匹配的源URL/路径、匹配计数和示例行。
- `pattern`(字符串,**必填**)- 正则表达式模式。
- `max_results`(整数,可选,默认**50**,范围1-1000)- 最大匹配页面数。
- `ignore_case`(布尔值,可选,默认**true**)- 不区分大小写的搜索。
---
## 提示
- `fetch` - 获取URL或本地路径并返回其完整提取的Markdown。参数:`url`(必填)。
---
## 网页搜索
`search`工具有两个后端,当配置了一个时**完全隐藏**(模型永远看不到一个死的“配置我”工具)。
- **`SEARXNG_URL`**(主要)- 自托管的[SearXNG](https://searxng.org)的基准URL(例如`http://127.0.0.1:8888`)。免费、自托管、聚合200+引擎(比普通谷歌搜索少的单引擎/SEO偏见),并按语言路由到区域引擎 - `lang` / `engines`参数的多语言杠杆。
- **`BRAVE_API_KEY`**(托管回退)- Brave Search API令牌,当SearXNG未配置、关闭或对某个地区屏蔽时使用。
- **`HARVESTER_DISABLE_SEARCH`** - 设置为`1`/`true`/`yes`/`on`以强制隐藏`search`工具,即使配置了后端。
工具在`SEARXNG_URL`和/或`BRAVE_API_KEY`设置时出现(并且`HARVESTER_DISABLE_SEARCH`不为真)。优先尝试SearXNG,然后是Brave。
---
## 本地路径支持和拒绝路径
本地文件和`file://` URL得到完全支持,通过相同的检测器路由,无需网络访问。凭证和密钥文件被拒绝 - 拒绝列表涵盖:
- **目录:** `.ssh`、`.gnupg`、`.aws`、`.password-store`、`.docker`。
- **名称:** `id_rsa`、`id_ed25519`、`id_dsa`、`id_ecdsa`、`credentials`、`.netrc`、`.pgpass`、`.htpasswd`、`shadow`、`master.key`。
- **后缀:** `.pem`、`.key`、`.p12`、`.pfx`、`.keystore`、`.jks`、`.asc`、`.gpg`、`.kdbx`、`.ppk`、`.env`(和`.env.*`),以及任何包含私有密钥标记的字符串(`_rsa`、`_ed25519`、`_dsa`、`_ecdsa`)。
检查在路径及其符号链接目标上运行,因此一个 innocently 命名的符号链接不能走私秘密。 outbound 网络获取也应用了SSRF防护,拒绝私有/内部/链路本地主机(包括混淆的IP表单)。
---
## 缓存
缓存位于项目根目录(包含`pyproject.toml`的目录)的`.fetch/`下,可通过`WEBFETCH_DIR`覆盖。每个工件按类型分区:
```
.fetch/
html/<slug>__<sha1>.md
pdf/<slug>__<sha1>.md (以及源.pdf文件)
docx/ xlsx/ pptx/ csv/ json/
png/ jpg/ gif/ webp/ … (图片和图片类)
archive_member/<slug>__<sha1>.md
```
斜杠是文件系统安全的,以10个字符的SHA-1哈希后缀以防止冲突。失败的获取被保存在一个短期的内存否定缓存中(默认120秒,见`HARVESTER_NEG_TTL`),因此不会重新锤打一个死亡的源。
---
## 安装
该软件包**尚未发布到PyPI**。从源代码或直接从GitHub安装。
### 从源代码
```bash
git clone https://github.com/mreza0100/harvester-web-mcp
cd harvester-web-mcp
uv sync # 安装依赖到本地venv
uv run harvester # 运行服务器(stdio)
```
### 从GitHub
```bash
# 不需要克隆就运行
uvx --from git+https://github.com/mreza0100/harvester-web-mcp harvester
# 或安装到环境中
pip install git+https://github.com/mreza0100/harvester-web-mcp
python -m harvester
```
### 发布到PyPI后
```bash
uvx --from harvester-mcp harvester # 或:pip install harvester-mcp
```
---
## 配置
添加到MCP客户端配置(`git+https`表单无需克隆):
```json
{
"mcpServers": {
"harvester": {
"command": "uvx",
"args": ["--from", "git+https://github.com/mreza0100/harvester-web-mcp", "harvester"]
}
}
}
```
要重新定位缓存并启用网页搜索,请传递环境变量:
```json
{
"mcpServers": {
"harvester": {
"command": "uvx",
"args": ["--from", "git+https://github.com/mreza0100/harvester-web-mcp", "harvester"],
"env": {
"WEBFETCH_DIR": "/path/to/cache",
"SEARXNG_URL": "http://127.0.0.1:8888"
}
}
}
}
```
### 环境变量
| 变量 | 默认值 | 效果 |
|---|---|---|
| `WEBFETCH_DIR` | `.fetch/` 项目根目录 | 重新定位磁盘缓存。 |
| `HARVESTER_MAX_INLINE_CHARS` | `50000` | 内联返回的Markdown上限;缓存文件始终包含全文(读取返回的路径以获取所有内容)。 |
| `HARVESTER_NEG_TTL` | `120` | 失败获取的超时时间,保存在否定缓存中。 |
| `HARVESTER_PDF_OCR` | 关闭 | 对无文本PDF页面运行Tesseract OCR(慢;默认关闭以进行快速文本层提取)。 |
| `HARVESTER_PDF_LAYOUT` | 关闭 | 为更高的表格/标题保真度运行pymupdf4llm布局模型(慢)。 |
| `HARVESTER_LOG_FILE` | 未设置 | 将日志写入此文件。 |
| `HARVESTER_LOG_LEVEL` | `INFO` | 日志级别。 |
| `SEARXNG_URL` | 空 | 自托管SearXNG基准URL - 主要`search`后端(启用工具)。 |
| `BRAVE_API_KEY` | 空 | Brave Search API令牌 - `search`后端备用(启用工具)。 |
| `HARVESTER_DISABLE_SEARCH` | 关闭 | 即使配置了后端,也强制隐藏`search`工具。 |
| `HARVESTER_CONTACT_EMAIL` | **空** | 学术API的礼貌池联系人(Crossref / Unpaywall / NCBI)。默认为空,因此发布的工具发送**无**联系人;操作员选择加入。设置后,它作为`mailto:` UA后缀和`?email=` / `?mailto=`参数 - 更好的速率限制,从不要求。 |
| `CORE_API_KEY` | 空 | 可选[CORE](https://core.ac.uk) API密钥。 |
| `SEMANTIC_SCHOLAR_API_KEY` | 空 | 可选Semantic Scholar API密钥。 |
| `GOOGLE_BOOKS_API_KEY` | 空 | 可选Google Books API密钥。 |
每个学术来源都**无密钥**工作 - 可选的API密钥仅提高速率限制和全文覆盖;无硬性限制。
### CLI标志
| 标志 | 效果 |
|---|---|
| `--user-agent <UA>` | 覆盖默认User-Agent(`Mozilla/5.0 (compatible; harvester/1.0)`)。 |
| `--proxy-url <URL>` | 通过此代理路由所有出站请求(例如`http://proxy:8080`)。 |
| `--ignore-robots-txt` | 无操作,保持兼容性 - 永远不强制执行robots.txt。 |
---
## 开发
```bash
uv sync
uv run pytest -q
uv run ruff check .
uv run pyright src/
---
## 许可证
MIT. 请参见[LICENSE](LICENSE). 该项目构建在MIT许可的fetch-server脚手架上;原始版权通知保留在许可证文件中。