内容
# vcf-mcp
一款 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,允许 AI 助手查询 bgzipped VCF 文件 — 个人基因组、群体样本、任何 VCF 格式文件 — 通过自然语言提示。不需要 `.tbi` 索引文件;服务器在注册时在内存中构建索引。
## 免责声明
vcf-mcp 是一个研究/工程工具。**它不是医疗设备**,不得用于临床诊断、治疗决策或涉及患者护理的任何其他目的。输出可能不完整、不正确或相对于底层 VCF 数据过期。始终在得出生物学结论之前,将结果与源文件和权威注释进行验证。使用该软件的风险由用户自行承担;请参阅 [LICENSE](LICENSE) 以获取完整的保修免责声明。
## 功能
向 MCP 客户端公开十种工具:
| 工具 | 描述 |
|---|---|
| `add_sample` | 在运行时注册单个 VCF 文件。验证 BGZF 魔术、头结构,并运行 tabix 探针,然后再接受。自动从 VCF 头检测基因组构建;自动从文件名派生样本名称。默认情况下,还扫描目录中的变异类别兄弟(参见下文)。 |
| `add_samples_from_folder` | 扫描文件夹中的 `*.vcf.gz` 并注册每个通过验证的文件。可选递归遍历。默认上限 50 个文件,硬上限 200 个文件。 |
| `remove_sample` | 按名称注销样本(不删除文件)。 |
| `reset_samples` | 删除所有已注册的样本及其缓存;将状态文件重写为空。具有破坏性 — 需要 `confirm: true`。 |
| `list_samples` | 列出当前已注册的样本。 |
| `server_info` | 报告 vcf-mcp 版本、嵌入的 Ensembl 版本号以及已注册样本数量。 |
| `query_region` | 返回重叠染色体区域的变异调用。 |
| `lookup_rsids` | 按 dbSNP rsid 查找变异;延迟每个样本的缓存。 |
| `query_gene` | 使用嵌入的 Ensembl 115/87 基因表返回基因坐标内的变异。 |
| `compare_samples` | 跨多个样本运行相同的查询并合并每个样本的结果。 |
工具返回结构化 JSON。MCP 客户端(如 Claude Desktop)按需调用它们,模型会根据提示进行推理。**支持零配置启动** — 将 Claude Desktop 指向二进制文件,然后在聊天中粘贴 VCF 路径(或 VCF 文件夹),并要求 Claude 注册它们。服务器自动检测基因组构建并将注册保存在状态文件中,以便在重启后生存。
## 快速入门
```bash
git clone <repo> vcf-mcp && cd vcf-mcp
cargo build --release
# (可选)生成 tabix 索引,如果 VCF 没有:
cargo run --release --example index_vcf -- /path/to/sample.vcf.gz
# 将二进制文件连接到 MCP 客户端(参见下面的“Claude Desktop”)。
# 不需要配置文件 — 将 MCP 客户端指向二进制文件,并在提示时提供 VCF 路径;服务器通过 add_sample 注册它。
```
## 先决条件
- **bgzipped VCF** (`.vcf.gz`),或多个文件。不需要 `.tbi` 索引文件 — vcf-mcp 在注册时在内存中构建索引。(如果 `.tbi` 已存在于文件旁边,则使用它作为快速加载快捷方式,但它是可选的。)
- **Rust ≥ 1.85** (传递依赖使用 Rust 2024 版本特性)。从 [rustup.rs](https://rustup.rs/) 安装。
- **支持 MCP 的客户端**。在 Windows 上使用 Claude Desktop 进行测试。
在 Windows 上,需要 MSVC 构建工具或 GNU 工具链(`rustup target add x86_64-pc-windows-msvc` 是默认值)。
## 构建
```bash
cargo build --release
```
二进制文件位于 `target/release/vcf-mcp` (或 Windows 上的 `.exe` )。
> 如果全局设置了 `CARGO_TARGET_DIR`,则项目的 [`.cargo/config.toml`](.cargo/config.toml) 尝试将其覆盖为 `./target/` 。但是,Cargo 的环境变量仍然优先于配置文件,因此在设置了环境变量的 shell 中,需要清除它(`unset CARGO_TARGET_DIR` / `$env:CARGO_TARGET_DIR=''`) 或在命令行上传递 `--target-dir target` 。
## 配置(可选)
有三种方法可以注册样本;请选择适合您工作流程的方法:
**1. 在聊天中(推荐)** — 说“添加此 VCF:D:\path\file.vcf.gz” ,Claude 调用 `add_sample` 。服务器验证文件并记住它。注册在服务器重启后通过小型自动管理的状态文件持续存在:
| 操作系统 | 默认状态文件 |
|---|---|
| Linux | `~/.local/share/vcf-mcp/state.json` |
| macOS | `~/Library/Application Support/vcf-mcp/state.json` |
| Windows | `%LOCALAPPDATA%\vcf-mcp\data\state.json` |
此文件由服务器写入,不会直接编辑。可以使用 `--state-file <path>` 覆盖位置,或使用 `--ephemeral` 完全禁用持久性。
**2. TOML 引导** — 对于现有设置,或一次性启动多个样本。传递 `--config /path/to/config.toml` ;文件将在启动时导入到注册表中。导入后,正常的 `add_sample` / `remove_sample` 操作仍然有效,并持续到状态文件。示例(另见 [`config.example.toml`](config.example.toml) ):
```toml
[[samples]]
name = "me" # prompt 中使用的短标识符
vcf_path = "person_genome_001.snp-indel.vcf.gz" # 绝对路径,或相对于此配置文件
build = "GRCh38" # 或 "GRCh37"
description = "Whole-genome sequencing, 2025-06-24"
```
**3. 文件夹扫描** — 说“注册 D:\cohort\ 中的所有文件” ,Claude 调用 `add_samples_from_folder` 。默认上限 50 个文件,硬上限 200 个文件;参见下面的工具参考。
无论选择哪种路径,每个 VCF 必须 **bgzipped** (`.vcf.gz`)。不需要 `.tbi` — 服务器在注册样本时在内存中构建索引。
要验证启动而无需服务:
```bash
vcf-mcp serve --check
```
### 构建 `.tbi` 索引(可选)
vcf-mcp 在注册时在内存中构建 tabix 索引,如果没有 `.tbi`,因此预先编制索引通常是多余的。要将 `.tbi` 写入磁盘(例如,用于 `tabix` 或 `bcftools`):
```bash
cargo run --release --example index_vcf -- /path/to/sample.vcf.gz
```
这会流式传输文件并写入 `<sample.vcf.gz>.tbi` 。吞吐量约为 300 万条记录/秒;在典型的台式机上,30 倍 WGS 文件(含约 1200 万个变异)索引时间不到 5 秒。vcf-mcp 将直接使用该文件进行下一次 `add_sample` ,而不是重建。
## 连接到 Claude Desktop
找到 `claude_desktop_config.json` 。最干净的方法是 **设置 → 开发者 → 编辑配置** 从 Claude Desktop 中打开 — 该按钮会打开文件,路径根据安装位置而有所不同(在 Windows 上,它与独立下载和 Microsoft Store / MSIX 构建版本不同)。
在 `mcpServers` 下添加 `vcf-mcp` 条目(合并现有条目 — 不要替换整个文件):
```json
{
"mcpServers": {
"vcf-mcp": {
"command": "/absolute/path/to/vcf-mcp",
"args": ["serve"]
}
}
}
```
最简形式的 `serve` 使用状态文件持久性 — 聊天中进行的注册在重启后存活。要从 TOML 配置中启动,请添加 `"--config", "/path/to/config.toml"` 到参数中。要完全不持久运行(每个会话都是空白的),请添加 `"--ephemeral"` 。
在 Windows 上,请转义反斜杠(`"D:\\code\\vcf-mcp\\target\\release\\vcf-mcp.exe"`)或使用正斜杠。
**完全重启 Claude Desktop** — 右键单击托盘图标 → 退出。仅关闭窗口会使进程保持运行,而运行的进程不会拾取配置更改。在 Windows Store 版本中,可能需要手动终止任何剩余的 `claude.exe` 进程:
```powershell
Get-Process -Name claude -ErrorAction SilentlyContinue |
Where-Object { $_.Path -like '*WindowsApps*' } |
Stop-Process -Force
```
然后打开一个新的聊天窗口并尝试:
```
已配置哪些样本?
chr19:44905000-44910000 有哪些变异?
查找 rs429358 和 rs7412。
APOE 基因中有哪些变异?
我的 ApoE 基因型是什么?
```
最后一个提示很有趣 — 它需要将工具输出与生物学知识相结合(VCF 中没有 rs429358 / rs7412 意味着两个位点都是纯合参考,即 ε3/ε3 基因型)。
## 工具参考
所有工具都接受 `sample` 参数(配置中的 `name`)并以 MCP `CallToolResult` 的 `text` 字段返回 JSON。坐标约定在整个过程中为 **1-based inclusive** ,与 VCF 规范和命令行工具(如 `tabix`)匹配。
### `add_sample`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `path` | 字符串 | ✓ | bgzipped VCF (`.vcf.gz`)的绝对路径 |
| `name` | 字符串 | | 如果省略,则从文件名自动派生 |
| `build` | 字符串 | | “GRCh37” 或 “GRCh38”;如果省略,则从 VCF 头自动检测 |
| `description` | 字符串 | | 人类可读的注释 |
| `scan_adjacent` | 布尔值 | | 扫描目录中的变异类别兄弟。默认为 `true` |
验证链(按顺序短路,最便宜的优先):
1. 路径规范化(解析符号链接)
2. 路径存在,是常规文件,可读
3. 文件名以 `.vcf.gz` 结尾(不区分大小写)
4. 前 4 个字节匹配 BGZF 魔术 `1F 8B 08 04` (拒绝纯 gzip)
5. 获取 tabix 索引:`.tbi` 如果存在则从磁盘加载,否则 **在内存中构建** — 永远不会在 VCF 旁边写入 `.tbi`
6. `noodles_vcf::io::IndexedReader` 使用获取的索引打开文件
7. 头部有 `#CHROM` 行,≥1 个样本列,≥1 个 `##contig`
8. 对第一个索引的 contig 进行 tabix 探针以证明数据 ↔ 索引一致性
内存中的 tabix 索引缓存在服务器(每个样本)上,直到进程 lifetime 为止,因此后续查询不会重建。索引不会在重启后跨重启持续存在 — 它们会在每个样本上首次使用时重建,这很快(30 倍 WGS 文件需要 ~3.5 秒;rsID 缓存也需要几秒钟)。为了避免将此小成本转移到第一个用户查询,服务器在启动时会在后台预热每个已注册样本的缓存,并在 `add_sample` 后台预热新添加样本的 rsID 缓存。参见 [KNOWN_ISSUES.md](KNOWN_ISSUES.md) 以获取测量时间和 OneDrive 液压警告。
构建检测级联:`##reference=` 子字符串 → `##contig=<...assembly=...>` 字段 → chr1 长度启发式(GRCh38:248,956,422;GRCh37:249,250,621)。
重新添加相同的规范路径 **而没有** 显式名称会返回现有的条目(幂等)。重新添加 **具有** 显式名称会注册一个新条目 — 对于通过多个标签查看相同文件很有用。
**相邻文件自动检测** (`scan_adjacent`,默认为 `true`):变异调用者通常会发出一个群体作为多个文件共享一个词干并且仅由变异类别令牌区分 — 例如 `person.snp-indel.genome.vcf.gz` 、 `person.cnv.vcf.gz` 、 `person.sv.vcf.gz` 、 `person.mitochondrial.vcf.gz` 。在注册主文件后,vcf-mcp 剥离识别的类令牌以查找词干并在 **同一目录** 中查找兄弟姐妹。每个姐妹文件都会经过完整的验证链并注册为单独的样本,命名为 `{primary_name}-{class}` (`-cnv` 、 `-sv` 、 `-mt`)。扫描不会致命:失败的姐妹不会阻止主要文件。
识别的类令牌(不区分大小写,剥离前 `.vcf.gz`):`.snp-indel.genome` 、 `.snp-indel` 、 `.snv` 、 `.small-variants` (视为主要类,无后缀)、 `.cnv` → `-cnv` 、 `.sv` / `.structural` → `-sv` 、 `.mitochondrial` / `.mt` → `-mt` 。如果主文件的名称不属于这些令牌,则不会运行扫描。扫描是幂等的 — 已注册的姐妹不会重复。
响应扩展主摘要为两个数组 — `adjacent` (注册的姐妹)和 `adjacent_skipped` (找到但拒绝的姐妹,每个都有 `reason`):
```json
{
"name": "person", "build": "GRCh38", "description": "", "vcf_path": "...person.snp-indel.genome.vcf.gz",
"variant_class": "small-variants",
"adjacent": [
{"name": "person-cnv", "build": "GRCh38", "variant_class": "cnv", "vcf_path": "...person.cnv.vcf.gz"},
{"name": "person-sv", "build": "GRCh38", "variant_class": "sv", "vcf_path": "...person.sv.vcf.gz"}
],
"adjacent_skipped": [
{"path": "...person.mitochondrial.vcf.gz", "reason": "BGZF magic mismatch ..."}
]
}
```
### `add_samples_from_folder`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `folder` | 字符串 | ✓ | 目录的绝对路径 |
| `recursive` | 布尔值 | | 遍历子目录。默认为 `false` |
| `max_files` | 整数 | | 注册的最大文件数;默认为 50,硬上限 200 |
如果文件夹包含的 `.vcf.gz` 文件超过 `max_files` ,则调用会错误,并提示调用者确切的 `max_files` 值允许扫描继续。每个文件的验证失败 **不会** 终止扫描 — 它们会收集到 `skipped` 数组中,每个文件都有文件原因:
```json
{
"folder": "D:\\cohort",
"scanned": 12,
"registered": [
{"name": "person_genome_001", "build": "GRCh38", "description": "", "vcf_path": "..."}
],
"skipped": [
{"path": "D:\\cohort\\malformed.vcf.gz", "reason": "BGZF magic mismatch ..."}
]
}
```
### `remove_sample`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `name` | 字符串 | ✓ | 已注册的样本名称 |
从注册表中删除样本(以及状态文件)。返回已删除样本的摘要,或 `{"removed": false, "name": "..."}` 如果没有注册这样的样本。
### `list_samples`
无参数。
```json
[
{"name": "me", "build": "GRCh38", "description": "...", "vcf_path": "..."},
{"name": "spouse", "build": "GRCh38", "description": "...", "vcf_path": "..."}
]
```
### `query_region`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `sample` | 字符串 | ✓ | 配置样本名称 |
| `chrom` | 字符串 | ✓ | 带或不带 `chr` 前缀;服务器标准化 |
| `start` | 整数 | ✓ | 1-based inclusive |
| `end` | 整数 | ✓ | 1-based inclusive |
最大 10 Mb 区域。结果限制为 500 条记录;如果截断,则 `truncated` 标志为 `true`。
```json
{
"sample": "me",
"chrom": "chr19",
"start": 44905000,
"end": 44910000,
"count": 10,
"truncated": false,
"variants": [
{
"chrom": "19",
"pos": 44905579,
"ref": "T",
"alt": "G",
"rsid": "rs405509",
"genotype": "1/1",
"genotype_alleles": "GG",
"depth": 33,
"gq": 73,
"filter": "PASS"
}
]
}
```
- `alt` 为多等位基因记录以逗号分隔。
- gVCF 参考调用记录具有 `alt: "."` 和 `genotype: "0/0"`。
- `filter` 为 `PASS` 表示所有过滤器通过的变异,`.` 为未过滤的记录,或以分号连接的过滤器名称列表。
### `lookup_rsids`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `sample` | 字符串 | ✓ | |
| `rsids` | 字符串数组 | ✓ | 1-100 个条目;空白/空格条目被拒绝 |
空白或仅包含空格的 rsid 被视为调用错误并返回输入错误(而不是静默产生 `found: false`,这看起来像实际的非样本结果)。
对样本的第一次调用会触发一次性扫描 VCF 以构建内存 `rsid → position` 缓存(几秒钟,适用于 30 倍 WGS 文件)。后续调用重用缓存以实现 O(1) 查找。缓存在服务器进程 lifetime 内保持。
```json
{
"sample": "me",
"count": 3,
"found_count": 2,
"results": [
{"rsid": "rs405509", "found": true, "variant": {/* 与 query_region 相同 */}},
{"rsid": "rs9999999", "found": false},
{"rsid": "rs440446", "found": true, "variant": {...}}
]
}
```
结果按输入顺序排列。未找到的条目产生 `{rsid, found: false}`。
**注意“未找到”**:VCF 通常只包含样本在位点与参考不同时的记录。未找到的 rsid 通常意味着样本在该位点是纯合参考 **而不是** 查找损坏。常见的变异如 rs429358 / rs7412 对于具有常见单倍型的每个人都不存在。
### `query_gene`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `sample` | 字符串 | ✓ | |
| `gene` | 字符串 | ✓ | HGNC 符号,不区分大小写 |
| `flank_bp` | 整数 | | 每个基因坐标两侧添加的碱基数。默认为 0。 |
基因坐标来自与样本 `build` 绑定的嵌入 Ensembl 版本:**release 115** 用于 GRCh38,**release 87** (GRCh37 存档冻结)用于 GRCh37。只有蛋白质编码基因。多个符号解析为一个 Ensembl ID 的符号将被丢弃,以避免歧义。
```json
{
"gene": "APOE",
"ensembl_id": "ENSG00000130203",
"chrom": "19",
"start": 44903787,
"end": 44909396,
"build": "GRCh38",
"flank_bp": 0,
"count": 9,
"truncated": false,
"variants": [/* 与 query_region 相同 */]
}
```
未知符号返回错误,并附有 Levenshtein-1 距离建议:
```
gene "BRCA22" not found in GRCh38. Did you mean: BRCA2?
```
### `compare_samples`
| 参数 | 类型 | 必需 | 备注 |
|---|---|---|---|
| `samples` | 字符串数组 | ✓ | 最少2个已配置的样本名称 |
| `query` | 对象 | ✓ | Either `{"rsids": [...]}` or `{"chrom": ..., "start": ..., "end": ...}` |
跨多个样本合成 `lookup_rsids` 或 `query_region`,并将每个样本的结果合并为一个按位置键控的表格。
```json
{
"samples": ["me", "spouse"],
"query_type": "rsids",
"count": 1,
"truncated": false,
"results": [
{
"rsid": "rs405509",
"chrom": "19",
"pos": 44905579,
"ref": "T",
"alt": "G",
"results": [
{"sample": "me", "found": true, "genotype": "1/1", "genotype_alleles": "GG", "depth": 33, "gq": 73, "filter": "PASS"},
{"sample": "spouse", "found": false}
]
}
]
}
```
每个合并条目的 `results` 数组包含一个输入样本的项目,按输入顺序排列。没有匹配 `(chrom, pos, ref, alt)` 的样本将获得 `{found: false}`。
## 架构
- [`src/main.rs`](src/main.rs): clap 命令行界面;`serve` 子命令启动 tracing(仅限 stderr — stdout 保留给 MCP JSON-RPC)并在 stdio 上运行 MCP 服务器。
- [`src/config.rs`](src/config.rs): TOML 配置加载器;在启动时验证 VCF + 索引存在性;将相对 `vcf_path` 解析为配置文件目录。
- [`src/vcf.rs`](src/vcf.rs): 查询实现。所有阻塞 VCF I/O 操作都在 `tokio::task::spawn_blocking` 中进行,超时时间为 30 秒。`RsidCache` 是 `Arc<RwLock<HashMap<sample, Arc<RsidIndex>>>>`,用于在每个样本缓存预热后进行无锁读取。
- [`src/genes.rs`](src/genes.rs): 通过 `include_str!` 嵌入基因表格,在首次查找每个构建时通过 `OnceLock` 进行惰性解析。
- [`src/tools.rs`](src/tools.rs): rmcp 工具定义。每个工具都是 `VcfServer` 上的 `async` 方法,并使用 `#[tool]` 进行注解;`#[tool_router]` 和 `#[tool_handler]` 宏将它们连接到 `ServerHandler` 实现。
构建于:
- [`rmcp`](https://crates.io/crates/rmcp) — 官方 Rust MCP SDK。
- [`noodles`](https://crates.io/crates/noodles) — 生物信息学 I/O(VCF、bgzf、tabix、csi)。
- [`tokio`](https://crates.io/crates/tokio) — 异步运行时。
## 开发
```bash
cargo test --release
cargo clippy --release --all-targets --all-features -- -D warnings
cargo fmt --all -- --check
```
> **Windows 11 上的智能应用控制**: SAC 可以静默阻止新编译的未签名可执行文件。`target/release/` 中的发布二进制通常是允许的;`target/debug/build/.../build-script-build` 中的调试模式构建脚本可能会被阻止,这会破坏 `cargo test`,除非使用 `--release`。要么在发布模式下运行测试(CI 隐式执行此操作),要么关闭 SAC — 请注意,禁用 SAC 是一种单向更改,无法在不重新安装 OS 的情况下撤销。
### 示例
- [`examples/index_vcf.rs`](examples/index_vcf.rs) — 为现有的 bgzipped VCF 构建 tabix `.tbi` 索引。
- [`examples/slice_vcf.rs`](examples/slice_vcf.rs) — 使用 `noodles` 提取 bgzipped VCF(及其索引)的子区域。
- [`examples/annotate_rsids.rs`](examples/annotate_rsids.rs) — 使用综合确定性 rsids 重写 VCF 的 ID 列(用于在 SAC 允许的宿主上构建 `lookup_rsids` 测试夹具)。
### 重新生成基因表格
已提交的 `data/genes_grch3{7,8}.tsv` 文件是根据 Ensembl 版本 115 / 87 预先构建的。要从不同版本重新生成:
1. 下载新鲜的 GTF 文件到 `data/ensembl/`(gitignored)。源注释中编码的 URL 是稳定的。
2. 运行预处理器:
```bash
cargo test --release -- --ignored generate_gene_tables
```
这会重新生成 `data/genes_grch3{7,8}.tsv`,然后通过 `include_str!` 嵌入到二进制文件中,以便在下一次构建时使用。
## 数据源
- **基因坐标**: [Ensembl 版本 115](https://ftp.ensembl.org/pub/release-115/) (GRCh38)和 [GRCh37 存档版本 87](https://ftp.ensembl.org/pub/grch37/current/) 。仅限蛋白质编码基因。
- **测试夹具**: [GIAB NA12878 / HG001 v4.2.1 基准 VCF](https://ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/NA12878_HG001/latest/),切片到 chr17:50100000-50300000(COL1A1 区域)。
## 联系方式
在仓库上打开问题,或发送电子邮件到 **`NahButch@users.noreply.github.com`** 。
## 许可证
[MIT](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器