关于 Gramps Evidence
Gramps Evidence 是供助手操作自托管 Gramps Web 家谱的 MCP 服务。写入事实时要求附带引文,并提供审计工具;旧版 GEDCOM 文件仅作为只读线索。
主要功能
- 01读写 Gramps Web 记录
- 02事实写入必须附带引文
- 03审查无来源事实与重复项
- 04只读 GEDCOM 参考层
- 05默认过滤在世者信息
适用场景
- 01向家谱记录添加有来源的事实
- 02查找无来源事实和重复记录
- 03将旧版 GEDCOM 导出作为线索
MCP 相关
向 MCP 客户端提供 97 个工具,用于读写 Gramps Web 家谱,并提供只读 GEDCOM 参考层。它通过 REST API 连接 Gramps Web。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx gramps-evidence-mcpGRAMPS_MCP_API_URLBase URL of your Gramps Web API, e.g. http://gramps.example.org:5000.GRAMPS_MCP_USERNAMEA dedicated Gramps Web user with at least the editor role.GRAMPS_MCP_PASSWORD密钥That user's password.GRAMPS_MCP_CONFIGAbsolute path to the TOML file listing reference GEDCOMs and the privacy setting.GRAMPS_MCP_EXPOSE_PRIVATESet true to turn off living-person and private-record filtering of bulk output.GRAMPS_MCP_TRANSKRIBUS_USERNAMEOptional. A Transkribus account (Scholar plan or above), for ocr_media's handwriting routes. Every page costs credits.GRAMPS_MCP_TRANSKRIBUS_PASSWORD密钥That Transkribus account's password.GRAMPS_MCP_TRANSKRIBUS_PAGE_BUDGETPages a calendar month ocr_media may send to Transkribus without the caller's per-call spend_credits. 0 asks every time.GRAMPS_MCP_TRANSKRIBUS_API_URLTranskribus' processing API.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
97收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| birth | any | 否 | 可选的出生事件。除非记录为无来源信息,否则请附上引用。事件类型默认为 'Birth'。 |
| death | any | 否 | 可选的死亡事件;类型默认为 'Death'。 |
| given | string | 是 | 名字/名,例如 'John Robert'。 |
| gender | any | 否 | female、male 或 unknown。 |
| surname | string | 否 | 姓/家族名。 |
| name_prefix | string | 否 | 姓氏前缀,例如 'van'、'de'。 |
| name_suffix | string | 否 | 后缀,例如 'Jr.'、'III'。 |
| require_citation | boolean | 否 | 如果为 True(默认值),任何出生/死亡事件都必须带有引用,否则调用会失败。设置为 False 可直接记录事件,并为其添加 UNSOURCED 属性,以便之后通过 list_unsourced_facts 查找。 |
详细介绍
gramps-evidence-mcp
一个 MCP 服务,为 AI 助手提供 对 Gramps 家谱树的读写权限,并在旧版 GEDCOM 导出文件上提供一个只读 “参考层”。
97 个工具,以证据规范为核心构建。 其前提是,如果任由助手操作家谱树,它很可能会编造一个看似合理的祖先,因此这里的写入路径经过设计,可确保每项主张都附有来源:创建事实时会附上引文,uncite 会删除因此变成孤立项的内容,亲子关联会分别附上引文,因为单个引文对象无法同时承载两种置信度;此外,还提供了一组审计工具——list_unsourced_facts、get_backlinks、find_duplicates,以及通过 query_objects 和 query_records 执行的服务端查询——用于找出未遵守这些规范的地方。
通过只读参考层查询旧版家谱树(Ancestry / FamilySearch 导出文件),将其作为不可信的提示,而非事实来源。
该服务是 Gramps Web 的 REST 客户端
(gramps-webapi)。它绝不会直接接触 Gramps 数据库文件——参见
连接方式。
默认情况下启用在世人士隐私过滤——参见隐私。
这是一个独立项目,并非由 Gramps 项目开发、认可或支持。
目录
- 连接方式
- 设置
- 配置
- 客户端配置
- 远程访问(Claude 网页版 / 移动端)
- 证据模型与事务
- 隐私
- 工具参考
- 阅读文档:
ocr_media - 参考层(旧版 GEDCOM 文件)
- 示例:附有出生证明引文的人物
- 开发
- 限制
- 仓库布局
连接方式
该服务通过 HTTP 与 gramps-webapi 通信,绝不会打开 Gramps 数据库文件。API 服务是数据库的唯一所有者,因此此服务和 Gramps Web 前端都可以写入数据库,而不会争用桌面应用的独占锁。该软件包不会导入任何 gramps.gen.* 模块,也无需修改 sys.path。
需要注意的是,“并行使用”是指 Gramps Web 前端和此服务都连接到同一个 gramps-webapi。这不表示让 Gramps 桌面应用指向 gramps-webapi 正在提供服务的同一个数据库文件——这样会重新引发双写入者问题。请将 Gramps Web 家谱树视为事实来源,并通过导出/导入与桌面应用往返同步。
分层说明请参阅 docs/ARCHITECTURE.md。
设置
你需要一个正在运行的 Gramps Web 实例,以及用于运行服务的
uv。
选项 A — 你已经在运行 Gramps Web
如果你已在某个可访问的位置运行 Gramps Web,无需使用随附的 docker-compose。只需让 MCP 连接到它:
- 记下 API 基础 URL,例如
http://gramps.example.org:5000。REST API 位于…/api下。 - 创建一个专用 MCP 用户,至少授予编辑者角色(写入需要编辑者角色;所有者角色也可以)。在 Gramps Web 主机 / 容器的 shell 中运行:
(在官方镜像中,包装命令是gramps-web user add mcp 'A_STRONG_PASSWORD' --fullname 'MCP client' --role 3gramps-web;底层命令为python3 -m gramps_webapi user add …。) - 将 URL 和凭据放入环境变量中(参见 配置)。
选项 B — 使用捆绑的 compose 在本地运行 Gramps Web
docker/docker-compose.yml 中提供了开箱即用的堆栈:
SQLite 后端、绑定挂载的数据和媒体目录(普通目录树,可像其他目录一样复制或备份),以及主机端口 5555 → 容器端口
5000。端口 5555 可避开占用 5000 的 macOS AirPlay Receiver。
docker compose -f docker/docker-compose.yml up -d
然后创建第一个所有者(通过 http://localhost:5555 上的浏览器向导,或使用 CLI),以及专用的 MCP 编辑器用户:
docker compose -f docker/docker-compose.yml exec grampsweb \
gramps-web user add owner 'OWNER_PW' --fullname 'Owner' --role 4
docker compose -f docker/docker-compose.yml exec grampsweb \
gramps-web user add mcp 'MCP_PW' --fullname 'MCP client' --role 3
GRAMPSWEB_TREE 指定的树(默认为 MyTree)会在首次启动时创建。
安装 MCP 服务
uvx gramps-evidence-mcp
这会通过 stdio 运行服务,这是 MCP 客户端启动服务的方式。通常你会将其添加到客户端配置(如下),而不是自己运行。
配置
密钥保存在环境中;其他所有内容都放在 TOML 文件中。 因此,TOML 文件永远不会包含凭据。
环境变量
在客户端配置中设置这些变量,或在服务启动目录下的 .env 文件中设置(参见 .env.example)。实际环境变量的优先级高于文件中的变量。
| 变量 | 含义 |
|---|---|
GRAMPS_MCP_API_URL | gramps-webapi 的基础 URL,例如 http://gramps.example.org:5000 |
GRAMPS_MCP_USERNAME | 专用 MCP 用户 |
GRAMPS_MCP_PASSWORD | 该用户的密码 |
GRAMPS_MCP_CONFIG | TOML 配置文件的路径(默认为 ./gramps_mcp.toml,相对于工作目录) |
GRAMPS_MCP_EXPOSE_PRIVATE | 覆盖 TOML 中的 expose_private 标志 |
GRAMPS_MCP_TRANSPORT | stdio(默认)或 http — 参见 远程访问 |
GRAMPS_MCP_HOST、GRAMPS_MCP_PORT | http 的监听地址(默认为 127.0.0.1:8090) |
GRAMPS_MCP_TRANSKRIBUS_USERNAME、GRAMPS_MCP_TRANSKRIBUS_PASSWORD | 可选。用于 ocr_media 手写识别路由的 Transkribus 账户(Scholar 计划或更高)。必须同时设置,或都不设置。参见 阅读文档 |
GRAMPS_MCP_TRANSKRIBUS_PAGE_BUDGET | 每个日历月内,ocr_media 在无需调用方逐次调用时指定 spend_credits 的情况下,可发送给 Transkribus 的页数。默认为 0:每一页都会询问 |
GRAMPS_MCP_TRANSKRIBUS_API_URL | Transkribus 的处理 API(默认为 https://transkribus.eu/processing/v1,Metagrapho v1) |
TOML 文件(参见 gramps_mcp.example.toml)
[server]
expose_private = false # see Privacy below
[[reference]]
path = "~/gedcoms/export_one.ged"
label = "Ancestry (one branch)"
trust = "User-built; many hints unsourced. Verify everything."
桌面客户端会在自行选择的目录中启动服务,因此请将 GRAMPS_MCP_CONFIG 设置为绝对路径。
客户端配置
Claude Desktop — 添加到 claude_desktop_config.json(在 macOS 上,路径为
~/Library/Application Support/Claude/):
{
"mcpServers": {
"gramps": {
"command": "uvx",
"args": ["gramps-evidence-mcp"],
"env": {
"GRAMPS_MCP_API_URL": "http://gramps.example.org:5000",
"GRAMPS_MCP_USERNAME": "mcp",
"GRAMPS_MCP_PASSWORD": "MCP_PW",
"GRAMPS_MCP_CONFIG": "/path/to/gramps_mcp.toml"
}
}
}
}
Claude Code:
claude mcp add gramps \
-e GRAMPS_MCP_API_URL=http://gramps.example.org:5000 \
-e GRAMPS_MCP_USERNAME=mcp -e GRAMPS_MCP_PASSWORD=MCP_PW \
-e GRAMPS_MCP_CONFIG=/path/to/gramps_mcp.toml \
-- uvx gramps-evidence-mcp
任何其他可启动 stdio 服务的 MCP 客户端也采用相同方式:命令为 uvx gramps-evidence-mcp,并在其环境中设置上述变量。编辑配置后,重启客户端。
若要从克隆的仓库运行,请将 uvx gramps-evidence-mcp 替换为
uv --directory /path/to/gramps-evidence-mcp run gramps-evidence-mcp。
远程访问(Claude 网页版 / 移动端)
以上配置使用 stdio 传输:客户端会将服务作为本地子进程启动。Claude 网页版(claude.ai)和移动应用无法这样做——它们会通过 Streamable HTTP 传输,经由 HTTPS 连接到远程服务,并将其添加为自定义连接器。需要提前考虑以下三点差异:
- 传输方式必须使用 HTTP,而不是 stdio。
- 可访问性——Anthropic 的服务器会主动连接到你的服务,因此需要一个公开的 HTTPS URL。像
http://192.168.1.50:5000这样的局域网地址无法使用。 - 身份验证——此服务可以写入包含在世亲属信息的数据库,因此必须置于身份验证保护之下。服务本身不提供身份验证。切勿在没有身份验证的情况下公开访问。
如果你并不特别需要浏览器或手机,桌面客户端已经可以使用相同的模型,并让此服务在本地运行(stdio,无需公开暴露)。以下步骤仅适用于确实需要远程访问的情况。
1. 通过 HTTP 提供服务
GRAMPS_MCP_TRANSPORT=http uvx gramps-evidence-mcp
这样会在 http://127.0.0.1:8090/mcp 提供 MCP 端点;GRAMPS_MCP_HOST 和 GRAMPS_MCP_PORT 可更改服务位置。其他 GRAMPS_MCP_* 变量仍与之前相同。
绑定到环回地址时,服务只接受 Host 为 localhost 或 127.0.0.1 的请求——这是 MCP SDK 用于防止 DNS 重绑定的保护机制。因此,同一台机器上的隧道或代理必须发送 Host: localhost(cloudflared 的 httpHostHeader 设置会执行此操作)。在容器中,请改为绑定到 0.0.0.0,如下所示,并通过容器网络进行隔离。
2. 将其部署在 Gramps Web 旁边
在与 Gramps Web 相同的主机上以容器运行,使其通过内部网络访问 API。以下是一个最精简的 Dockerfile:
FROM python:3.13-slim
RUN pip install uv
ENV GRAMPS_MCP_TRANSPORT=http GRAMPS_MCP_HOST=0.0.0.0 GRAMPS_MCP_PORT=8090
EXPOSE 8090
CMD ["uvx", "gramps-evidence-mcp"]
将其添加到 Gramps Web 的 compose 配置中(使用相同的网络),通过服务名称指向 API,并从环境中读取密钥:
gramps_evidence_mcp:
build: /path/to/dockerfile-dir
restart: unless-stopped
environment:
GRAMPS_MCP_API_URL: "http://grampsweb:5000" # internal service name
GRAMPS_MCP_USERNAME: "mcp"
GRAMPS_MCP_PASSWORD: "${MCP_PW}"
# no host port needed if the tunnel (below) runs in the same compose network
3. 使用 TLS 将其公开
反向隧道无需配置端口转发,还能提供 TLS 和稳定的主机名。以下任一种方式均可:
- Cloudflare Tunnel——与服务一同运行
cloudflared,并将类似https://gramps-mcp.example.org的主机名路由到http://gramps_evidence_mcp:8090。 - Tailscale Funnel——通过你自己的 tailnet 提供公开 HTTPS。
ngrok http 8090,用于快速临时测试。
也可以在 8090 端口前使用任意反向代理自行终止 TLS。
4. 在前面配置身份验证
claude.ai 自定义连接器使用 MCP OAuth 2.0 流程。在隧道主机名前配置一个身份代理——Cloudflare Access、Authelia、oauth2-proxy 等类似服务都可以接入连接器所需的 OAuth 流程。服务本身不会验证调用方;请参阅 docs/ROADMAP.md。
5. 在 claude.ai 中添加连接器
Settings → Connectors → Add custom connector → 粘贴
https://gramps-mcp.example.org/mcp,完成身份验证提示后,聊天中就会出现 97 个工具。(自定义连接器需要付费 Claude 计划;Team/Enterprise 用户可能需要管理员启用此功能。)
**安全提醒:**此端点可以在包含在世人士的家谱中创建、编辑和删除记录。请务必启用身份验证,优先使用私有隧道而不是开放端口;如果远程只需查询,可以考虑使用具有只读角色的 Gramps Web 用户——这样服务的写入工具会因权限错误而失败,不会写入数据。
证据模型与事务
服务器强制执行 Gramps 证据模型:
Repository → Source → Citation → fact (Event / Attribute)
**每次记录事实的写入都需要引文。**你可以引用现有的引文或来源,也可以直接创建一个。唯一无需引文即可记录事实的方式,是明确传入 require_citation=False;这会为事件添加 UNSOURCED=true 属性,以便稍后通过 list_unsourced_facts 找到它。置信度级别对应 Gramps 的 0–4 级(very_low, low, normal, high, very_high)。
**类型名称采用 Gramps 自己的名称。**Gramps 会将它不完全识别的类型名称(例如 "birth"、"Web Home Page")保存为新的自定义类型,与原本想用的标准类型并列,并将其永久保留在树的词汇表中。因此,每次写入前都会先匹配类型名称:先与 Gramps 的标准名称和树中的自定义名称匹配(忽略大小写和标点),再尝试几个明确无歧义的同义词(例如,“Born”对应 Birth,“microfilm”对应 Film)。如果仍然找不到匹配项,则会拒绝该名称并提供最接近的名称;当确实需要创建新的自定义类型时,可以使用 allow_new_type(例如“Land Grant”或类型为“Territory”的地点)。详情请参阅 docs/PITFALLS.md 第 26 节。
写入如何映射到 DbTxn 事务
gramps-webapi 会将每次对象创建或更新封装在单独的服务器端 DbTxn 中(标签为 New Person、Edit Event 等),因此所有写入都会通过适当的事务进行,并保留在 Gramps 的撤销历史中。复合操作(例如,添加带引文的出生信息的 add_person)会按顺序执行这些对象写入——来源 → 引文 → 事件 → 人物,并在过程中关联各对象。
设计说明(有记录的偏离):gramps-webapi 还提供了一个原始的 POST /api/transactions/ 端点,可将多个对象打包到单个 DbTxn 中,并使用自定义描述。我们有意不使用它,因为该端点会绕过服务器端辅助函数,而这些函数会(a)自动分配 handle 和 gramps_id,(b)将英文类型字符串("Birth")转换为 Gramps 内部的类型字典。使用该端点将迫使此客户端重新实现 Gramps 的 ID 分配和类型内部逻辑,并自行生成 gramps_id,从而有 ID 计数器不同步的风险。逐对象事务的做法更安全,也能保持撤销记录一致;代价是,一次逻辑上的添加会显示为多个撤销条目,而不是一个。
隐私
树中包含在世人士。设置 expose_private = false(默认值)时,批量输出会排除所有隐私记录或可能在世的人士:
- 可能在世是指出生时间距今不到 110 年且没有记录死亡信息的人士;如果既没有记录出生信息,也没有记录死亡信息,也会归入此类,以优先保护隐私。即使死亡事件没有日期,也算作已有死亡记录。110 年是家谱学中惯用的“推定已故”界限;这不会永久隐藏明确属于历史人物的人士。
- 隐私记录是指任何类型记录上的 Gramps 隐私标记。
各工具的具体处理方式如下:
| 输出 | 某人是隐私人士或可能仍在世…… |
|---|---|
search_people、get_ancestors、get_descendants、query_objects、query_records | ……会显示为已隐去信息的占位记录:仅包含 ID,不含姓名或事实,因此仍可主动获取。query_records 中的家族行会根据双方父母进行判断。 |
遍历整棵树的 list_unsourced_facts | ……其事实会被省略并计数,且只显示一次占位记录。 |
find_duplicates | ……会被省略。只有当同名群组中仍有两位历史人物时,才会报告该群组。 |
get_timeline、consolidated_timeline | ……作为亲属纳入时,其事件会被省略并计数。你指定的人会显示;家族自身成员也会按亲属进行判断,因此在世夫妇的家族时间线会返回空结果。 |
get_facts | ……会在服务器计算任何内容前被排除。 |
run_report | ……会从支持相关选项的报告中排除——Gramps 的 25 份报告中,有 21 份支持 living_people,有 24 份支持 incl_private:除非你传入任一选项,否则会将它们设为 false,并显示为“未包含”。Gramps 自身的默认设置会同时包含两者。结果中的 privacy_options 会显示实际应用的设置。 |
consult_reference | ……会从匹配结果中排除并计数。来自 Ancestry 等类似网站的导出数据不会将任何人设为隐私人士。 |
check_family_links | ……其发现的关系问题会被省略并计数。 |
按 ID 查询不会过滤。 get_person、get_object、其他类型化查询工具,以及针对某位指定人物的 list_unsourced_facts,都会完整返回结果。这是树的所有者针对自己的数据使用的个人工具:目的是防止意外的大规模泄露——例如搜索结果转储、遍历整棵树、共享报告——而不是阻止所有者访问数据。按 ID 查询单条记录是有意为之。
写入操作绝不会过滤。 添加、引用、编辑、合并和删除操作对在世人士和隐私人士一视同仁。
只需提出请求,一次调用就能显示所有人。 表中的每个工具都接受 include_private。将其设为 true 后,该次调用会完整返回结果——包括在世人士、隐私记录和 Gramps 自身的报告默认项——之后的调用会再次进行过滤。这适用于你按姓名请求在世亲属的情况(例如“列出我 1950 年后出生的表亲”);工具说明会告知助手这一点。每次使用都会记录工具名称,但不会记录相关记录。
出于设计或限制原因,不会过滤的内容:
export_backup会创建无损备份;如果备份丢弃了在世人士,树就无法恢复。verify_tree会按 Gramps 自身的措辞返回其检查结果。get_dna_matches会返回你指定人物的匹配结果,但每个匹配对象都是另一个人——通常是在世人士——并以句柄标识,同时包含片段数据。- 事件、引用、备注和媒体只根据各自的隐私标记进行判断。事件行不包含指向所属人物的链接,因此,除非事件本身设为隐私,否则事件查询会显示在世人士的出生事件。
list_research_tasks根据任务自身的隐私标记判断任务,根据任务备注的隐私标记判断其描述。任务标题可能提及在世人士;此类任务应设为隐私。- 时间线条目根据所属人物进行判断,而不根据事件自身的隐私标记判断;时间线端点不会报告该标记。
ocr_media会读取你指定的媒体对象,和按 ID 查询一样,不论其是否为隐私对象。它绝不会将隐私对象发送给第三方 Transkribus;但在世人士记录中的公开图像会被发送,因此此类记录应设为隐私。- 占位记录仍会表明匹配到了一条记录。按姓名和出生年份查询并返回占位记录,便能确认存在一位符合这两个条件的人。过滤只能防止意外披露,无法阻止蓄意搜索。
若要改为关闭所有调用的过滤功能,请在 TOML 文件中设置 expose_private = true,或在客户端配置中设置 GRAMPS_MCP_EXPOSE_PRIVATE=true——这适用于全面审计,或不包含在世人士的树。但这会降低助手读取所有内容时的隐私保护。
日志绝不会包含记录内容,只包含句柄、ID 和操作名称。
请求 URL 也不会写入日志,因为查询筛选条件会包含在其中。
工具参考
97 个工具。 每个会修改树的工具都会在 PUT 回去之前重新获取整个对象——通过 service._mutate() 编辑——参见
keys= 陷阱。
每个工具都会声明 MCP 注解,说明它是只读、添加,还是会更改和删除内容,因此客户端可以自动批准读取,并在执行其他操作前询问用户。45 个工具只读。ocr_media 会读取内容,但其注解既未标记为只读,也未标记为封闭世界:它可以从国会图书馆和 Internet Archive 获取内容、消耗 Transkribus 积分,并存储转录文本注释。
未知参数会被拒绝。 拼写错误或虚构的参数会触发错误,并列出该工具接受的参数。参数不会被静默丢弃;过去这会导致 query_objects(query=...)——正确的参数是 gql——变成不带筛选条件地列出前 200 个对象。
创建
| 工具 | 用途 |
|---|---|
add_person | 创建一个人,可选择添加有引文的出生/死亡事件;每个事件的类型可以省略。 |
add_family | 将父母与子女关联起来,可选择添加有引文的婚姻信息。添加子女时可以指定其与每位父母的关系——例如在同一次调用中添加继子女。 |
add_event_to_person | 为某人添加有引文的事件(居住、人口普查、职业……)。 |
add_event_to_family | 为某个家庭添加有日期/地点的事件(婚姻、离婚……)。 |
add_child_to_family | 将已有人员添加为已有家庭的子女。 |
add_event_ref | 以某种角色(证人、信息提供者、教父母……)将已有事件关联到另一人:一条人口普查记录或一条埋葬记录,共用一组引文。属于此人本人的信息——例如表格中的对应行、其年龄——应作为属性添加到该引用上(As enumerated、Age)。 |
add_alternate_name | 为某人添加非主要姓名(别名、婚后姓名、昵称),并附上该姓名形式的引文。 |
add_source | 创建来源(记录集、书籍、证书),可选择将其归入某个资料库。 |
add_citation | 在某个来源上创建/复用独立引文。 |
add_repository | 创建资料库(档案馆、图书馆、墓园、网站)。 |
add_place | 有意创建一个地点,并指定其类型和上级地点,而不是让它仅因在事件中被命名而作为副作用出现。 |
add_note | 创建一条注释,可选择将其附加到某个对象。它会重新读取目标对象;如果无法证明注释已附加,就返回 verified: false,而不会声称已成功附加。一次调用可处理任意长度的注释;服务器错误会说明是否已写入任何内容,若附加失败,则会撤回新建的注释。 |
add_media | 将图像、PDF、音频或视频文件上传为独立的 Media 对象;如果文件的 md5 相同,则会复用已有对象。 |
attach_media | 将文件(上传文件)或已有 Media 对象关联到某个对象。 |
add_attribute | 添加带类型的键/值属性(对象上的 Attribute,来源/引文上的 SrcAttribute)。 |
add_url | 为某个人、地点或资料库添加网页 URL。 |
add_dna_match | 将 DNA 匹配记录为证据:按 Gramps Web 的存储方式保存,并引用发现该匹配的测试。若片段数据无法解析,或该配对已有记录,则会拒绝添加。 |
引用——将证据附加到主张
| 工具 | 用途 |
|---|---|
cite_event | 为已存在的事件附加引用。 |
cite_object | 为任何带有引用的对象附加引用——尤其是 家族,以及某人的 姓名(object_type="name"),无论是主要姓名还是其他姓名。 |
cite_child_link | 为亲子关系本身添加引用。始终为该关系新建专属引用,因为一个引用对象无法承载两种不同的置信度。 |
uncite | 移除引用;如果移除后引用成为孤立对象,则将其删除。始终删除——不删除而仅移除引用的操作已两次产生孤立残留。若某条引用是某条注释或图片的唯一承载者,则保留该引用,除非 carry_to 指定了要将它们移至的引用。 |
编辑——更正已有内容
| 工具 | 用途 |
|---|---|
update_citation | 定位信息、置信度、日期,或将引用重新指向其他来源。 |
update_citations | 一次调用批量更新多个引用——进行一轮检查。expect_page_prefix 会报告页面自计划检查以来发生变化的行,而不是覆盖它。 |
update_event | 原地更新类型(根据家谱树的词汇表检查)、日期、地点、描述,同时保留事件的 ID 及其所有关联内容。若来源未说明地点或日期,则清除对应信息。 |
update_source | 标题、作者、出版信息、缩写。 |
update_media | 描述、日期、路径。 |
update_person | 性别、主要姓名(旧姓名会保留为备用姓名;如果旧姓名是数据录入错误,则会原地更正,并在备注中记录原因)、隐私标记。 |
update_alternate_name | 原地更正、更改类型或移除一个备用姓名,同时保留其引用。带引用的姓名不会被移除。 |
update_child_ref | 原地更新孩子与父亲或母亲的关系——亲生、继子女、收养……,同时保留关联的引用和出生顺序。 |
move_child | 将孩子移至另一个家庭,同时保留关联的引用、备注和隐私设置,并按出生顺序排列;指向旧家庭的所有关联都会移除,包括重复关联。 |
set_family_parent | 为现有家庭设置、更换(按要求)或移除父亲或母亲,同时保留此人与家庭双方的关联。 |
update_event_ref | 原地更新某人对共享事件的引用:角色,或按名称添加、移除属性集,同时保留其引用。 |
update_attribute | 原地设置或移除人物、家庭、事件、媒体对象、来源或引用上的一个属性,同时保留其引用;按名称选择,若名称重复,则还会按部分属性值选择。被移除属性的引用会被列出。 |
update_object_fields | 更新其他对象(地点、备注、存储库)的标量字段。不接受结构化列表。 |
update_place | 地点类型、上级区域、名称、标题、坐标。上级地点必须已存在(不会根据名称创建新地点);拒绝形成循环,以及拒绝将包含多个条目的有日期区域扁平化。 |
update_url | 按子字符串匹配并编辑或移除人物、地点或存储库上的一个现有 URL 条目——必须恰好匹配一个。这适用于链接归入错误类型的情况。 |
link_repository | 将现有来源关联到现有存储库,并指定索书号和介质。 |
link_repositories | 一次调用批量执行相同操作,逐行报告结果。 |
tag_object | 为对象附加指定名称的标签;若标签不存在则创建。 |
set_private | 为对象设置或清除 Gramps 隐私标记。 |
merge_objects | 在一个事务中通过服务端自身的合并功能合并重复对象。默认先试运行;试运行会列出 Gramps 还会合并的其他对象——例如合并人物可能会合并两个家庭,合并家庭可能会合并两位父亲——以及拒绝执行的内容。对于地点,会确定保留地点的上级区域,而不是同时保留两个地点的上级区域。 |
detach_object | 移除事件、媒体、备注、标签、孩子或存储库关联,或移除地点的额外上级区域;可选择在对象变为孤立对象时将其删除。可以指定标签名称;存储库关联可以限定到一个索书号。 |
delete_object | 按句柄或 Gramps ID 永久删除对象。如果删除会导致某条备注或图片仅由该对象持有而无处归属,或者来源仍有引用,则拒绝删除。 |
读取
| 工具 | 用途 |
|---|---|
get_person | 完整详情:姓名、所有别名(含引用次数)、性别、事件、属性(含引用次数)、家庭、媒体。 |
get_family | 关系、父母、子女、事件数。 |
get_event | 类型、日期(含修饰语:“1882 年至 1883 年之间”,绝不只写“1882 年”)、地点、描述、引用次数。 |
get_source | 标题、作者、出版信息、缩写,以及实际引用次数。 |
get_repository | 名称、类型、URL,以及其持有的来源。 |
get_object | 任意类型的原始记录。返回存储的记录原样,这正是编辑所需的内容。 |
get_place | 名称、标题、类型、包含地链、坐标、URL。 |
get_citation | 页码、可信度、日期、来源,以及 cited_by_count;值为零表示孤立的残留数据。 |
get_note | 类型和全文。注释承载推理过程,因此不会截断。 |
get_media | 路径、MIME 类型、校验和,以及引用该媒体的对象数量。 |
consolidated_timeline | 合并多个人物或家庭的时间线,例如同一家庭共同迁居、参与人口普查的历程。 |
get_timeline | 人物或家庭的按时间顺序排列的事件,每项均含年龄、引用次数和最佳可信度,另含 uncited_count。 |
get_relationship | 用文字和代际距离说明两人之间的关系。如果答案不唯一,则使用树状结构中的 all_paths。 |
assess_living | 服务自身对生死状态的判断,并追溯亲属;可选择提供估算日期和推理过程。 |
event_span | 两个事件之间经过的时间,即每项事件时年龄核查背后的计算。 |
get_facts | 查找家谱树中的记录保持者——死亡时年龄最大者、最年轻的父母、子女最多者;也可仅查某个人的祖先或后代。若记录保持者不合常理,通常意味着数据有误。 |
get_dna_matches | DNA 匹配结果,包括共享厘摩总数和最大值,以及已识别的共同祖先。unattributed_count 表示尚待研究的部分。 |
get_ydna | Y-DNA 单倍群,从最宽泛的分支到末端分支。仅反映父系直系血统。 |
parse_dna_segments | 将粘贴的共享片段数据解析为片段和总数。若解析失败,会明确报告解析失败,而不会将其当作没有共享 DNA。 |
get_researcher | 研究者详情,随每次导出一同包含。 |
list_reports | Gramps 可生成的 25 种报告:Ahnentafel、后代、家庭组、亲属关系、扇形图和关系图、统计、世系终止。 |
get_report_options | 某个报告的默认选项。报告接收完整的选项字典,因此覆盖选项时应以此为基础合并。 |
run_report | 生成报告。默认不包含在世人物和私密记录,除非你明确要求。 |
search_people | 姓名子字符串 + 可选的出生年份范围(已按隐私规则过滤)。 |
get_ancestors / get_descendants | 遍历家谱树的 N 代(已按隐私规则过滤)。 |
list_tags | 所有标签及其句柄、名称和颜色。 |
list_object_types | 写入工具接受的类型名称:Gramps 标准类型和家谱树的自定义类型。 |
审计
| 工具 | 用途 |
|---|---|
query_objects | 使用 GrampsQL 在服务端筛选任意集合。 审核的主力工具——请阅读下方的语法陷阱。 |
get_backlinks | 查看哪些对象引用了此对象——询问“此来源是否被引用?”的唯一正确方式。 |
find_duplicates | 按策略查找疑似重复项:media_checksum、source_title、citation_page、vital_events、person_name。 |
list_unsourced_facts | 没有引用或标记为 UNSOURCED 的事件。 |
check_family_links | 双向检查个人↔家庭关联:家庭被列出两次、一侧缺少的关联、指向不存在对象的关联。每项检查结果都会说明如何修复。 |
db_stats | 人物/家庭/事件/引用等的数量。 |
query_records | 结构化查询引擎。 支持索引列、通过 json_path 查询存储对象、关系遍历、regex/like/in、排序、键集分页。筛选事件类型的唯一方式。它会拒绝服务器无法正确响应的查询形式——例如将 type 用作列、查询日期的 year,或使用 in 以外的运算符比较列表——并说明可用的查询方式。 |
list_event_types | 树中的事件类型词汇及其存储的整数值。这里出现意外名称通常意味着 Gramps 将拼写错误接受为了自定义类型。 |
list_filter_rules | Gramps 的筛选规则词汇——“是……的后代”“与……有共同祖先”——这些条件既无法通过 GrampsQL 表达,也无法通过 query_records 表达。 |
list_custom_filters | 此实例中按名称可重复使用的已保存筛选器。 |
create_filter | 使用这些规则创建并保存可重复使用的筛选条件。 |
delete_filter | 删除已保存的筛选器。只影响筛选器定义。 |
verify_tree | Gramps 自身的家谱合理性检查——例如母亲只有 9 岁、婚姻持续 120 年、日期无法解析。这与引用检查不同。 |
ocr_media | 使用合适的引擎读取文档图像:印刷文本使用现有 OCR 文本或 Tesseract,英文手写使用图像本身,德文和挪威文手写使用 Transkribus,人口普查表使用 FamilySearch 的索引。这只是查找线索,不是证据——引用前请先阅读图像。参见阅读文档。 |
研究任务——Gramps Web 自带的任务列表
| 工具 | 用途 |
|---|---|
add_research_task | 在 Gramps Web 的 Tasks 视图中添加任务,写入方式与其 New Task 表单相同:创建一个标记为 ToDo 的来源,包含 Status 和 Priority 来源属性,并将描述作为 To Do 备注。 |
list_research_tasks | 按 Tasks 视图中的顺序列出任务,包含状态、优先级、标签、属性和描述;可按状态、标签或属性值筛选。私密任务会显示为已脱敏的占位项。 |
update_research_task | 设置任务的状态、优先级、隐私设置或属性——每项都会替换旧值,而不是保留旧值并追加新值,因为 Gramps Web 不会显示这种方式;也可向描述中添加段落。私密任务的描述备注也为私密内容;将任务设为公开不会自动公开私密备注,除非设置 private_note=false。 |
在此创建的任务与在 Gramps Web 中创建的任务是同一个对象:两者都会显示在 Tasks 视图中,也会通过备注显示在桌面版 Gramps 的 To Do 小程序中。任务是待办事项,不是证据,因此永远不会被引用。这些任务不是 list_jobs 报告的后台作业。
运维
| 工具 | 用途 |
|---|---|
export_backup | 将整个树导出为 Gramps XML 文件;不会覆盖现有文件。任何批量写入前都要运行此工具。 |
list_transactions | 查看近期写入:更改内容、时间和执行用户。也可用于检查是否有其他会话正在写入。 |
undo_transaction | 检查冲突后撤销事务。默认先进行试运行。返回一个 task_id。 |
list_jobs | 查看此树的近期后台任务,按时间从新到旧排列。 |
get_transaction | 查看完整事务,包括其更改的对象。撤销前请先读取。 |
get_record_history | 查看谁在何时添加、编辑或删除了一条记录,以及每次更改对应的事务。已删除的记录通过其句柄查找。field 可缩小范围,仅显示更改某个字段的写入及更改前后的值。在 gramps-webapi 3.21 上,此工具会改为读取整个事务日志,因此速度较慢,并会说明这一点。 |
get_job | 查看后台任务是否已完成,以及是否成功。撤销、验证和重新索引都会分派给工作进程。 |
reindex_search | 重建 Gramps Web 搜索框背后的全文索引。服务器会在每次写入后更新索引;大量导入后,或搜索结果遗漏树中已有内容时,请重建索引。 |
参考层(只读,不会触及树)
| 工具 | 用途 |
|---|---|
consult_reference | 在旧版 GEDCOM 导出文件中查找人员,作为不可信的线索。参见参考层:旧版 GEDCOM 文件。 |
使用 GrampsQL 查询
query_objects 会在数据库中过滤,而不是先提取整个集合再用 Python
筛选。其语法有一些易踩的陷阱;我们已在每次 CI 运行中,针对
gramps-webapi 3.21.1、3.22.3 和 3.23.1 进行验证:
- 相等判断使用单个
=。page == ""会导致解析错误。 ~表示子串:description ~ "1871"。.length可用于任何列表:media_list.length = 0。- 对象没有的字段不会匹配任何内容,也不会报错。 结果为零可能意味着字段拼写错误。
- 事件也有这样的字段
type。 在包含数百条出生记录的树中,type = "Birth"会静默地匹配不到任何内容。请改用query_records和event_type。 - 来源没有
citation_list——引文指向来源。请使用get_backlinks查找没有引文的来源。在来源上查询citation_list不会匹配任何内容,无论来源是否有引文。 - 布尔值按整数比较:使用
private = 1,而不是private = true。 - 列表会通过其条目进行搜索。 对列表本身使用
~,表示检查该值 是否为列表中的一个条目,因此urls ~ "http"不会匹配任何内容。query_objects会拒绝此用法,并指出正确的形式:.any.(或.all.)可访问列表条目,get_<type>可通过句柄继续查询。 - 每个条件都会逐个对象读取整个集合,因此 GrampsQL 读取允许使用 120 秒:对 6,000 条引文执行包含两个列表条件的 OR 查询,耗时 29 秒。
常用查询:
citations confidence >= 3 AND page = "" high-confidence claims with no locator
sources media_list.length = 0 documents with no image attached
media desc = "" media nothing identifies
person urls.any.path ~ "findagrave" a URL anywhere among a person's links
citation note_list.any.get_note.text.string ~ "x" a citation whose note mentions x
每个工具都提供面向 LLM 的文档字符串,说明何时使用、参数语义以及 良好的引文实践。
阅读文档:ocr_media
Gramps Web 自带的 OCR 使用 Tesseract,适用于印刷文字。家族史中至关
重要的文档都是手写的——遗嘱、契约、信件、用 Kurrent 体书写的德国教
会簿、挪威教区登记册;该信任哪种识读工具,取决于笔迹和语言。
ocr_media 会根据调用者提供的文档类型(doc_type)和语言(lang)
进行路由:
| 文档 | 读取方式 | 原因 |
|---|---|---|
print | 媒体已有的文本——Transcript 注释、PDF 的文本层——或美国国会图书馆或 Internet Archive 为来源中 URL 所指页面提供的 OCR;否则通过 Gramps Web 使用 Tesseract | 现有文本无需花费;Tesseract 擅长读取印刷体 |
hand,英语 | 将图像作为 MCP 图像内容返回,并向调用模型提供外交式转录指令:按原文拼写,保留换行和缩写,不确定处标记为 [?]。second_witness 会额外使用 Transkribus,并要求结果列出两种读取结果中不同的所有姓名和数字 | 视觉模型读取 18 世纪和 19 世纪英语手写体时,字符错误率为 5.7–7 %,优于专用引擎(Humphries 等人) |
hand,德语 | 仅使用 Transkribus(其德语 Kurrent 模型)。没有 Transkribus 时拒绝处理——绝不使用视觉模型读取,即使有人要求也不行,德语表格或整卷也不例外 | 视觉模型读取历史德语时的字符错误率为 48.8 %(METATR,2026 年 5 月,READ-2016) |
hand,挪威语 | 使用 Transkribus(NorHand 1820–1940),并提供图像以供核对;没有 Transkribus 时,返回图像并警告其错误率 | 视觉模型读取挪威语时的错误率约为 10 %(METATR,NorHand) |
hand,其他语言 | 使用 Transkribus 的通用模型(Text Titan II),并提供图像;没有该模型时,返回图像并发出警告 | 暂无已知的视觉模型读取基准 |
table | 不读取:使用 FamilySearch 的索引(familysearch-mcp 中的 get_records_on_image) | 引擎会跨行读取,无法保留每个单元格与对应人员之间的关系 |
volume | 先使用 FamilySearch 全文搜索,再用 Transkribus 逐页处理 | 搜索整卷胜过通读整卷 |
engine 可直接指定一种读取方式:existing、tesseract、vision(德语手写体会拒绝处理)或 transkribus。返回的图像会缩放到当前 Claude 模型无需再次缩放即可读取的尺寸(长边 2576 像素,3.75 百万像素);以百分比表示的 region=[x1, y1, x2, y2] 会以完整细节返回页面的一部分。PDF 按页读取(page):读取其文本层和嵌入页面中的扫描图像;没有扫描图像的页面由 Gramps Web 渲染,而它只渲染第一页。
每个结果都包含来源信息——{engine, model, date}——并附带相同的提醒:转录内容绝不是证据;引用仍应指向图像。store=true 会将机器读取结果作为 Transcript 注释保存在媒体对象中,标题注明所用引擎、模型和日期;还会将 Transkribus 的 PAGE XML 保存在第二条预格式化注释中,因为 Transkribus 会在任务完成一天后删除它。视觉模型的读取结果由调用模型自行保留,可使用 add_note 保存。之后调用以读取印刷体时,如果发现已存储的读取结果,就会返回该结果,而不会再次读取。
**Transkribus 为可选服务,且需要付费。**API 任务按应用费率的一半计费——每页约 0.5 个点数,约合 €0.12——因此,只有当调用传入 spend_credits=true,或本月的 GRAMPS_MCP_TRANSKRIBUS_PAGE_BUDGET 尚有额度时,才会发送页面。每个结果都会说明花费金额和本月已使用的页面数;计数保存在缓存目录中的 transkribus-usage.json 文件里;页面会在发送前计数,若无法计数则不会发送。同一文件、页面和模型在一天内提交的任务会重新获取结果,不会再次付费;失败的任务会被丢弃,因此下次调用可以重新尝试。绝不会发送私密媒体对象,也不会发送此处没有模型支持语言的页面。图像会发送给位于奥地利的 READ-COOP;其文档说明图像仅用于执行任务,不会被存储;其条款允许使用提交的材料来改进产品。
**Transkribus 路径是根据已发布的 API 构建的,尚未进行过实时测试。**它遵循 Metagrapho v1 OpenAPI 文档和 READ-COOP 记录的登录流程,并使用符合这些格式的固定测试数据进行测试;构建时未使用任何账号。Library of Congress 和 Internet Archive 路径、图像和 PDF 处理,以及通过任务队列运行的 Tesseract,均已在 2026-10-06 针对实时树和在线档案进行检查。
参考层(旧版 GEDCOM)
consult_reference 会搜索 TOML 配置中列出的 GEDCOM 文件,并逐个文件返回符合条件的个人及其声称的事实。关键是,每条事实都会标明旧版树是否附有来源;如果有,还会提供来源文本——这样助手就能区分“Ancestry 引用了真实的死亡证明”和“没有来源的猜测”。
- 解析 GEDCOM 5.5.1,包括 Ancestry 方言(
_APID、_TREE、……)。自定义下划线标签会原样保留,不会导致解析失败。 - 文件会按需加载,解析后的内容会缓存在磁盘上(以路径/大小/修改时间为键),因此大型导出文件只需解析一次。
- 每个文件都有一条会显示在结果中的可信度说明。
这些只是未经验证的提示。预期的工作流程是:参考提示 → 判断要寻找哪份真实记录 → 在树中创建事实并引用该记录——绝不要把提示当作已有来源的内容直接复制进去。
你的真实 GEDCOM 文件绝不会被提交(.gitignore 会排除 *.ged,合成测试样本除外)。
示例:添加一个人并引用其出生记录
**你:**添加 Martha Ellery,女性,出生于 1890 年 1 月 12 日,地点为俄亥俄州哥伦布市。我有她的出生证明(俄亥俄州证明编号 #12345);请以极高置信度引用。
助手会调用一个工具:
add_person({
"given": "Martha", "surname": "Ellery", "gender": "female",
"birth": {
"type": "Birth",
"date": "12 Jan 1890",
"place": "Columbus, Ohio, USA",
"citation": {
"source_title": "Ohio Birth Certificate #12345",
"page": "certificate no. 12345",
"confidence": "very_high"
}
}
})
在幕后,服务会:创建来源“Ohio Birth Certificate #12345” → 在其上创建引用(页码 + 极高置信度)→ 查找或创建地点“Columbus, Ohio, USA”→ 创建出生事件(带日期、地点和引用)→ 创建引用该事件作为其主要出生事件的个人。它会返回新的 gramps_id(例如 I0001)。
随时调用 list_unsourced_facts,查看哪些事实仍需要来源。如果你添加的事实暂时无法找到来源,请传入 require_citation=false;该事实会被标记为 UNSOURCED,列入审计清单。
开发
git clone https://github.com/ianderso/gramps-evidence-mcp
cd gramps-evidence-mcp
uv sync --extra dev
uv run pytest
测试套件通过 respx 调用 gramps-webapi 的内存模拟,因此无需 Gramps Web 实例,并且任何试图建立真实连接的测试都会失败。它会像客户端一样调用工具接口进行测试:每个工具的架构、对未知参数的拒绝、所有编辑工具的完整对象写入规则、每种批量输出的隐私过滤,以及每个工具返回而非抛出的错误封装。
第二个测试套件 tests/live 会针对真实的一次性 gramps-webapi 运行——CI 中使用 3.21.1、3.22.3 和 3.23.1,通过 PyPI 安装,无需 Docker。它会检查 docs/PITFALLS.md 中描述的所有服务行为,端到端地调用工具,并对模拟服务和真实服务运行相同场景并要求得到相同答案,确保模拟服务不会偏离它所模拟的对象。CONTRIBUTING.md 说明了如何运行测试。
CONTRIBUTING.md 说明了每项更改应包含哪些内容。
限制
- 需要 gramps-webapi 3.21 或更高版本(Gramps 6.0);已在每次 CI
运行中针对 3.21.1、3.22.3 和 3.23.1 进行测试。在较旧的服务上,每个工具都会返回一个
unsupported_server错误,并指出其版本:3.20 缺少搜索和隐私筛选器所用的查询 端点。实例的/api/openapi.json是权威依据——如果调用行为 异常,请检查该文件,并参阅 docs/PITFALLS.md,了解 API 以往有哪些令人意外的行为。 - 没有树选择器。 在 gramps-webapi 上,树与用于 身份验证的账户绑定;没有数据端点接受树参数。要对其他树进行操作,请使用属于该树的 凭据。
- 类型名称(
"Birth"、"Married")会与 Gramps 的英文 标准名称和树的自定义名称进行匹配,且不区分大小写和标点符号, 也支持少数明确的同义词(“Born”就是 Birth)。其他名称都会被拒绝,并提供最接近的名称,除非allow_new_type要求创建新的自定义类型;因此,其他语言的类型名称会被拒绝,而不会存储:请提供 英文名称。参阅 docs/PITFALLS.md 第 26 节。 search_people、list_unsourced_facts和find_duplicates会读取整个 集合。这适用于拥有几千人的个人族谱树;它们并非为超大型数据库 设计。get_facts每次调用都会由服务端计算;即使族谱树中的人数不到一千,也需要几十秒 才能完成。该调用的超时时间为三分钟。ocr_media的 Transkribus 路径尚未在实际服务上运行过。 Gramps Web 只会渲染 PDF 的第一页,因此,后续页面若未嵌入扫描图像,就无法作为图像读取。
仓库布局
src/gramps_evidence_mcp/ the MCP server
server.py tool definitions (the 97 tools) and the entry point
service.py genealogy operations; edits go through _mutate()
client.py gramps-webapi REST client
mapping.py Gramps object <-> JSON shapes
models.py Pydantic input models shared by the tools
privacy.py living-person assessment and redaction
gedcom_ref.py read-only reference layer over legacy GEDCOMs
ocr.py ocr_media's routing, images, archives and Transkribus
config.py env vars + TOML
tests/ against an in-memory fake; no live server needed
live/ against a throwaway gramps-webapi (CI: 3.21.1, 3.22.3, 3.23.1)
docker/ docker-compose for a local Gramps Web
docs/
ARCHITECTURE.md how the server is put together
PITFALLS.md gramps-webapi behaviours that have cost real data
ROADMAP.md what is planned (nothing), and what is not
直接针对 gramps-webapi 编写脚本前,必须阅读 docs/PITFALLS.md。 其中的每一项经验都伴随着损失:一次 keys= PUT 清除了 65 个家族的父母关联;一个引文句柄在两条主张间重复使用,导致 107 条亲子关联的评分有误;还有两个并发会话让写入路径瘫痪了约 18 小时。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。