⚠️ Tome-DEPRECATED
此软件包已弃用。使用 precis-mcp 相反。
precis-mcp 使用4个工具将论文阅读(以前称为tome mcp/acatome mcp)和手稿编辑统一到一个mcp服务器中: search(), get(), put(), move().
pip install precis-mcp______________________________________________________________________
Tome(已存档)
](https://pypi.org/project/tome-mcp/)   
一个管理研究论文库的Python MCP服务器:PDF、参考书目、, 语义搜索、图形跟踪和语义学者集成。
内部没有LLM——纯确定性代码。AI客户端提供智能; Tome提供工具。
使用开发和测试 帆板运动 + 克劳德作品4.6(思考). 应该与任何具有MCP能力的客户端和足够能力的模型一起工作, 但这种组合才是奇迹发生的地方。
安装
pip install tome-mcp开发(测试、梳理):
git clone https://github.com/retospect/tome-mcp.git
cd tome-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"依赖项
chromadb--用于语义搜索的矢量数据库(包括内置all-MiniLM-L6-v2嵌入,无需外部服务器)PyMuPDF(fitz)--PDF文本提取bibtexparser≥2.0——BibTeX解析和序列化httpx--用于CrossRef、语义学者、Unpaywall API的HTTP客户端mcp--模型上下文协议SDKPyYAML--配置文件解析
MCP配置
最快的设置--使用 uvx 要在没有手动venv的情况下运行:
{
"mcpServers": {
"tome": {
"command": "uvx",
"args": ["tome-mcp"],
"env": {
"TOME_ROOT": "/path/to/your/project",
"SEMANTIC_SCHOLAR_API_KEY": "optional"
}
}
}
}或者将您的MCP客户端指向本地安装:
{
"mcpServers": {
"tome": {
"command": "/path/to/tome/.venv/bin/python",
"args": ["-m", "tome.server"],
"env": {
"TOME_ROOT": "/path/to/your/project",
"SEMANTIC_SCHOLAR_API_KEY": "optional"
}
}
}
}或者,使用 set_root(path='...') 在每次会话开始时。
快速开始:您的第一次会议
安装Tome并配置MCP客户端后,打开项目 在IDE中,按顺序键入以下提示:
1.东方
这是一个使用Tome MCP服务器进行纸张管理的LaTeX项目。 呼叫guide('getting-started')查看工具索引,然后set_root('/path/to/my/project')连接。
2.描述你的项目 (因此LLM构建上下文)
这本书/论文是关于\[你的主题\]的。主文件是main.tex. 跑toc()查看文档结构和paper()参观图书馆。
3.吸收你的第一篇论文
我放了一个PDF tome/inbox/.摄入并验证DOI。4.搜索和引用
在我们的图书馆里找到关于\[主题\]的论文,并向我展示相关的引文。
5.编译
编译文件并检查警告。
就是这样。法学硕士通过以下方式发现了Tome的工具 guide() 并学习你的 文件系统中的项目结构。从这里,探索内置 导游-呼叫 guide() 无需争辩即可查看所有主题。
环境变量(全部可选)
| 变量 | 默认值 | 用途 |
|---|---|---|
TOME_ROOT | (无) | 项目根目录(替代 set_root() 或 cwd) |
SEMANTIC_SCHOLAR_API_KEY | (无) | S2速率限制更高 |
UNPAYWALL_EMAIL | (无) | 用于Unpaywall开放获取PDF查找的电子邮件 |
目录布局
面向用户(git跟踪)
project-root/
├── tome/
│ ├── references.bib # AUTHORITATIVE bibliography
│ ├── inbox/ # Drop PDFs here for processing
│ ├── figures/ # Source figure screenshots
│ └── notes/ # LLM-curated paper notes (authorYYYY.yaml)缓存(gitignored,可通过以下方式完全再生 tome:rebuild)
project-root/
├── .tome/
│ ├── tome.json # Derived metadata cache
│ ├── staging/ # Ingest prep area (transient)
│ ├── raw/ # Extracted text: raw/xu2022/xu2022.p1.txt
│ ├── chroma/ # ChromaDB persistent storage (embeddings + index)
│ ├── corpus_checksums.json # Checksum manifest for .tex/.py files
│ └── tome.json.bak # Safety backup before each write数据模型
耐久性等级
| 层 | 数据 | 位置 | 恢复 |
|---|---|---|---|
| 真相来源 | PDF、图片截图 | 保险库(~/.tome-mcp/pdf/), tome/figures/ | 无法恢复 |
| 独立档案 | .tome HDF5文件 | 保险库(~/.tome-mcp/tome/) | 不可恢复(包含文本+嵌入) |
| 权威元数据 | 参考文献 | tome/references.bib | Git回滚 |
| 派生缓存 | 其他所有内容 | .tome-mcp/ | 可重建自 .tome 档案 |
.tome 存档--HDF5,不是zip
每份摄入的纸张都会产生 .tome vault中的文件。这些是 HDF5档案 (以…开头) h5py,不 zipfile).每个档案都是完全独立的:
import h5py, json
f = h5py.File('~/.tome-mcp/tome/x/xu2022.tome', 'r')
meta = json.loads(f['meta'][()]) # key, title, authors, year, doi, ...
pages = f['pages'][:] # extracted page text (one string per page)
chunks = f['chunks/texts'][:] # chunked text for search
embeds = f['chunks/embeddings'][:] # (N, 384) float32 vectors
f.attrs['content_hash'] # SHA256 of the source PDF
f.attrs['embedding_model'] # "all-MiniLM-L6-v2"
f.close()所有数据库(catalog.db、ChromaDB)都可以从 .tome 文件单独。
references.bib --权威
bib文件是纸质元数据的唯一真实来源。汤姆解析它 和 bibtexparser 并使用完整的解析-修改-序列化(不是正则表达式)写回 手术)。往返测试(解析→ 序列化→ 解析→ 比较)之前运行 每一篇文章;如果发生意外更改,写入将中止。
A. .bak 每次写作前都要复印。
x场(策划,生存 .tome/ 重建)
| 字段 | 值 | 含义 |
|---|---|---|
x-pdf | true/false | PDF已被摄入(存储在保险库中) |
x-doi-status | valid/unchecked/rejected/missing | DOI验证状态 |
x-tags | 逗号分隔 | 用于搜索筛选的自由形式标签 |
密钥格式
authorYYYY[a-c]? --第一作者姓氏+出版年份。碰撞得到 字母后缀。数据表使用 manufacturer_partid.专利使用专利 号码。
tome.json --派生缓存
重建自 references.bib +文件系统打开 rebuild。包含昂贵的- 导出操作状态:
{
"version": 1,
"papers": {
"xu2022": {
"title": "...",
"authors": ["Xu, Y.", "..."],
"year": 2022,
"doi": "10.1038/s41586-022-04435-4",
"s2_id": "CorpusId:12345678",
"s2_fetched": "2026-02-13",
"citation_count": 47,
"cited_by_in_library": ["chen2023"],
"references_in_library": ["lambert2015"],
"abstract": "...",
"file_sha256": "a1b2c3...",
"pages_extracted": 12,
"embedded": true,
"doi_history": [],
"crossref_fetched": "2026-02-13T19:29:00Z",
"figures": {
"fig3": {
"status": "captured",
"file": "figures/xu2022_fig3.png",
"page": 3,
"reason": "QI transfer diagram",
"requested": "2026-02-13",
"captured": "2026-02-13",
"_caption": "Conductance measurements...",
"_context": [{"page": 1, "text": "As shown in Fig. 3..."}],
"_attribution": "Reproduced from Xu et al. (2022), Figure 3"
}
}
}
},
"requests": {
"ouyang2025": {
"doi": "10.1063/5.0xxx",
"tentative_title": "Fano interference...",
"reason": "PDF behind paywall",
"added": "2026-02-13",
"resolved": null
}
}
}前缀为的字段 _ 是派生的(可从原始文本提取中再生)。
DOI生命周期
| 状态 | 含义 | doi 现场 |
|---|---|---|
valid | CrossRef解析,标题/作者匹配 | 存在,已验证 |
unchecked | DOI存在,尚未验证 | 存在,未验证 |
rejected | 错误或产生幻觉,DOI被删除 | 缺席 |
missing | 从未缺席 |
过渡:
- 添加DOI→
unchecked - 未添加DOI→
missing unchecked+check_doi成功→validunchecked+check_doi失败→rejected(DOI已删除,历史记录在tome.json)rejected+set_paper新DOI→uncheckedmissing+set_paper与DOI→unchecked
不变性:if x-doi-status = valid,DOI是值得信赖的。
摄入管道
两阶段提交
第一阶段:准备 (只写给 .tome/staging/,可逆)
- 将PDF从收件箱复制到
.tome/staging/{key}/ - 提取PDF元数据(标题、作者)
doc.metadata) - 提取第一页文本(DOI正则表达式,标题启发式)
- 如果DOI发现→ 查询交叉引用→ 结构化元数据
- 如果没有DOI但找到标题→ 查询语义学者→ 元数据
- 逐页提取文本
- Chunk(500个字符,100个重叠,句子边界)
- 将建议返回到LLM(建议的密钥,提取与API元数据)
法学硕士审查提案并确认或更正。
第二阶段:承诺 (快速,为确保碰撞安全而订购)
- 将围兜条目写入
tome/references.bib(通过bibtexparser) - 将PDF复制到vault(
~/.tome-mcp/pdf/),写.tome档案 - 移动暂存工件→
.tome-mcp/raw/,.tome-mcp/cache/ - 进入ChromaDB
- 更新
.tome/tome.json - 清理暂存目录
如果提交中途失败:暂存目录仍然存在,收件箱文件可能已经存在 消失了,但围兜条目仍然存在。 rebuild 和解。
验证
LLM执行标题/作者验证(不是Tome)。Tome提取元数据 来自PDF和API的数据都返回给LLM。LLM处理模糊 匹配(编码变体,如ç/c、缩写、重新排序)。
语料库索引(.tex/.py文件)
与纸张分开。经常更改的动态文档。
同步模型
sync_corpus 或延迟同步 search_corpus:
- 扫描球体图案(例如。
sections/*.tex) - 对每个文件进行校验和(SHA256)
- 对比
.tome/corpus_checksums.json - 更改文件:删除旧的ChromaDB条目,重新分块,重新嵌入,插入
- 已删除的文件:从ChromaDB中删除
- 新文件:添加到ChromaDB
- 未更改的文件:跳过
ChromaDB系列: paper_pages, paper_chunks, corpus_chunks (单独)。
MCP工具
许多以前独立的工具已经统一为多动作工具。 呼叫 guide() 对于完整的主题索引,或 guide('getting-started') 为了定向。
纸张管理
| 工具 | 说明 |
|---|---|
paper | 统一:获取/设置/列表/删除/请求/统计。无参数=库统计信息。 key =元数据+注释。 action='list' =浏览。 |
ingest | 处理收件箱PDF。没有 confirm:建议密钥+元数据。随着 confirm=True:提交到库+保管库。 |
notes | 读/写/清除纸质笔记或文件元数据。纸质笔记 tome/notes/,文件元数据 % === FILE META 阻碍。 |
link_paper | 将vault图纸链接/取消链接到当前项目。No args=列出链接的论文。 |
搜索和导航
| 工具 | 说明 |
|---|---|
search | 统一搜索: scope (所有/论文/语料库/笔记)× mode (语义/精确)。过滤器: key, keys, tags, paths. |
toc | 文档结构: locate (标题/引用/标签/索引/树)。替换旧 doc_tree, find_cites, list_labels. |
文档分析
| 工具 | 说明 |
|---|---|
doc_lint | 结构问题:未定义的引用、孤立标签、浅引用、跟踪模式。 |
dep_graph | 标签、参考文献、引用 .tex 文件。 |
review_status | 跟踪标记计数 tome/config.yaml 模式。 |
validate_deep_cites | 根据ChromaDB中的源PDF文本验证深度引用。 |
发现与探索
| 工具 | 说明 |
|---|---|
discover | 统一:联合搜索(S2+OpenAlex)、引用图、共享引用、刷新、统计、查找。 |
cite_graph | S2引用图(谁引用了这篇论文,引用了什么)。图书馆报纸上的旗帜。 |
explore | LLM引导的引文束搜索——获取、分类、扩展、撤销。 |
DOI和数字
| 工具 | 说明 |
|---|---|
doi | 统一的DOI管理:验证、拒绝、列出拒绝、获取开放访问PDF(通过Unpaywall→ 收件箱)。 |
figure | 请求、注册或列出数字。No args=列出所有参数。 |
任务跟踪
| 工具 | 说明 |
|---|---|
needful | 列出N个最紧急的任务,或将任务标记为已完成。按从未完成>更改>逾期排名。 |
file_diff | Git diff用LaTeX节标题注释。 |
维护
| 工具 | 说明 |
|---|---|
set_root | 切换项目根目录。脚手架目录。表面存在未解决的问题。 |
reindex | 重新索引论文、语料库文件或两者。从vault档案中重建。 |
guide | 按需使用指南。主题索引的调用不带参数。 |
report_issue | 将工具问题记录到 tome/issues.md (git跟踪)。 |
工具说明
每个工具都有一个精心编写的MCP描述(约100字),使用一致的 术语。工具响应包括 next_steps 采取后续行动时的字段 需要。
术语(用于所有描述)
| 术语 | 含义 |
|---|---|
| 图书馆 | 收藏的论文 tome/references.bib |
| key | 围兜键,例如。 miller1999.与 \cite{miller1999} |
has_pdf | PDF是否已被摄取(存在于vault中) |
| 收件箱 | tome/inbox/ --将PDF放到此处进行处理 |
错误处理
所有错误都是特定的异常类,其消息告诉LLM什么 出了问题,该怎么办。
TomeError (base)
├── PaperNotFound — key not in library
├── PageOutOfRange — page N requested, paper has M pages
├── DuplicateKey — key already exists
├── DOIResolutionFailed — CrossRef error (404, 429, 5xx)
├── IngestFailed — could not identify paper from PDF
├── BibParseError — bib file could not be parsed
├── BibWriteError — roundtrip test failed, write aborted
├── ChromaDBError — search index init/query failed
├── ConfigError (base) — project configuration issue
│ ├── ConfigMissing — no tome/config.yaml found
│ ├── RootNotFound — named root not in config
│ ├── RootFileNotFound — root .tex file doesn't exist on disk
│ ├── NoBibFile — no references.bib yet
│ ├── NoTexFiles — tex_globs matched no files
│ └── UnpaywallNotConfigured — no email for Unpaywall API
├── APIError — external API error (CrossRef, S2, Unpaywall)
├── TextNotExtracted — paper exists but no raw text yet
├── FigureNotFound — no such figure for paper
└── UnsafeInput — path traversal or unsafe characters每个错误消息都包括:发生了什么、为什么以及下一步该做什么。
测试
- 每个模块都有一个对应的
test_*.py - 测试使用小型夹具(2个入口围兜,1页PDF模型)
- 明确测试错误路径(对MCP来说比快乐路径更重要)
- 外部服务(CrossRef,S2)被模拟
- 需要标记实时服务的集成测试
@pytest.mark.integration pytest无标记运行所有单元测试(无需网络)
封装结构
~/repos/tome/
├── pyproject.toml
├── README.md
├── LICENSE # AGPL-3.0
├── .gitignore
├── examples/
│ └── config.yaml # Full config example (all features)
├── src/
│ └── tome/
│ ├── __init__.py
│ ├── __main__.py # python -m tome.server entry point
│ ├── py.typed # PEP 561 type marker
│ ├── server.py # MCP server + tool handlers
│ ├── errors.py # Exception hierarchy
│ ├── config.py # Project config (config.yaml parsing)
│ ├── manifest.py # tome.json read/write (atomic, backup)
│ ├── bib.py # BibTeX parser + writer (bibtexparser)
│ ├── extract.py # PDF text extraction (PyMuPDF)
│ ├── chunk.py # Sentence-boundary overlapping chunker
│ ├── store.py # ChromaDB management (built-in embeddings)
│ ├── checksum.py # SHA256 file checksumming
│ ├── identify.py # PDF identification + key generation
│ ├── crossref.py # CrossRef API client
│ ├── semantic_scholar.py # Semantic Scholar API client
│ ├── openalex.py # OpenAlex API client
│ ├── unpaywall.py # Unpaywall open-access PDF lookup
│ ├── http.py # Shared HTTP client utilities
│ ├── figures.py # Figure request/registration + caption extraction
│ ├── notes.py # Paper notes (YAML + ChromaDB indexing)
│ ├── issues.py # Issue tracking (tome/issues.md)
│ ├── analysis.py # LaTeX document analysis (labels, refs, cites)
│ ├── latex.py # LaTeX parsing utilities
│ ├── toc.py # Table of contents parsing
│ ├── index.py # Back-of-book index (.idx parsing)
│ ├── find_text.py # Normalized .tex source search
│ ├── grep_raw.py # Normalized PDF raw text grep
│ ├── validate.py # Path traversal + input validation
│ ├── git_diff.py # Git diff with LaTeX section annotations
│ ├── cite_tree.py # Citation tree (S2 graph caching)
│ ├── s2ag.py # Local S2AG database (offline citations)
│ ├── s2ag_cli.py # S2AG CLI utilities
│ ├── needful.py # Recurring task tracking
│ ├── summaries.py # File content summaries
│ ├── guide.py # On-demand usage guide loader
│ ├── filelock.py # Cross-process file locking
│ └── docs/ # Built-in guide markdown files (11)
└── tests/
├── conftest.py # Shared fixtures
├── test_analysis.py
├── test_bib.py
├── test_checksum.py
├── test_chunk.py
├── test_cite_tree.py
├── test_concurrent_bib.py
├── test_config.py
├── test_crossref.py
├── test_discovery.py
├── test_errors.py
├── test_extract.py
├── test_figures.py
├── test_filelock.py
├── test_git_diff.py
├── test_grep_raw.py
├── test_guide.py
├── test_http.py
├── test_identify.py
├── test_index.py
├── test_issues.py
├── test_latex.py
├── test_manifest.py
├── test_needful.py
├── test_notes.py
├── test_openalex.py
├── test_semantic_scholar.py
├── test_store.py
├── test_summaries.py
├── test_toc.py
├── test_unpaywall.py
└── test_validate.py