关于 termdat-mcp
termdat-mcp 是瑞士联邦行政部门 TERMDAT 术语数据库的只读 MCP 服务,供 Agent 查询和核验官方机构名称、职称及缩写,并查看四种国家语言的条目、来源和验证状态。
主要功能
- 01按字段和分类搜索术语
- 02翻译官方行政术语
- 03单次核验最多 25 个术语
- 04浏览术语集和分类
- 05支持 stdio 与 HTTP,只读
适用场景
- 01核实机构的官方法语名称
- 02发布前检查译名
- 03查找某主题的主管机构
MCP 相关
提供七种只读工具,用于搜索、翻译和核验 TERMDAT 条目、浏览受控词汇表及检查 API 状态。支持 stdio 和 Streamable HTTP。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
7收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| detail | boolean | 否 | |
| fields | string | 否 | |
| in_language | string | 否 | |
| max_results | integer | 否 | |
| search_term | string | 是 | |
| out_language | string | 否 | |
| collection_ids | any | 否 | |
| classification_ids | any | 否 |
详细介绍
🇨🇭 属于瑞士公共数据 MCP 组合 — 开源 MCP 服务,将 AI Agent 连接到瑞士公共数据和开放数据。 这是一个个人项目,与任何雇主或机构均无关联。
🏷️ termdat-mcp
瑞士行政机构官方认证的名称,涵盖德语/法语/意大利语/英语,并附来源参考和验证状态。
概述
面向瑞士联邦行政部门术语数据库 TERMDAT 的 MCP 服务,由联邦总理府维护。它为 AI Agent 提供瑞士主管部门、部门和法律文件经官方验证的德语/法语/意大利语/英语名称,并附来源参考和验证状态。
通过 i14y-mcp 发现;该项目将 TERMDAT 列为数据服务 ff0c37eb-2f7c-4ff6-996e-d22b77bf52fc。
它是什么——又不是什么。 TERMDAT 不是主题词典,而是一个认证名称档案库:它不会告诉你「Sonderpädagogik」是什么意思,但会告诉你负责该领域的主管部门的官方名称,以及该部门的法语名称。
实测覆盖范围(实时数据,2026-07-19,在 Terminus 字段中进行德语搜索):
| 搜索词 | 命中数 |
|---|---|
| Departement | 20 |
| Bildung | 13 |
| Verordnung | 8 |
| Schule | 5 |
| Behörde | 4 |
| Sonderpädagogik | 3 |
| Volksschule · Lehrperson · Schulleitung · Unterricht · Kindergarten | 0 |
13 条「Bildung」命中结果都是组织名称——Bildungsdirektion、Erziehungsdepartement、Departement für Volkswirtschaft und Bildung——而不是教育学概念。请据此安排:此服务擅长主管部门命名、官方名称和缩写,但基本不涉及领域词汇。
功能
- 通过官方 TERMDAT 公共 v2 API 提供 7 个只读工具。
- 提供德语/法语/意大利语/英语官方名称,每条响应均附来源参考和验证状态。
- 通信质量检查:一次调用最多可根据经验证名称检查 25 个术语。
- 词汇缓存(TTL 为 24 小时),涵盖 140 个集合和 23 个分类,并支持在缓存过期时使用旧数据作为回退。
- 指数退避重试(2/4/8 秒);明确设置
MaxEntryCount,避免静默截断。 - 双传输方式:
stdio(本地)和 Streamable HTTP(云端)。两者均提供 MCP 协议修订版2026-07-28。 - 无需身份验证——使用公开、无需身份验证的 API(优先无认证)。
🎯 演示查询
「德语区各州教育局的官方法语和意大利语名称是什么?」
通过 list_classifications → search_terms → translate_term 查询得出。
演示
前置条件
- Python 3.10+
uv/uvx(推荐)或pip- 可访问
api.termdat.bk.admin.ch的网络连接——无需 API 密钥
安装
uvx termdat-mcp
Claude Desktop(claude_desktop_config.json):
{
"mcpServers": {
"termdat": {
"command": "uvx",
"args": ["termdat-mcp"]
}
}
}
快速开始
# Run locally over stdio (default transport)
uvx termdat-mcp
# From a checkout, without installing
PYTHONPATH=src python -m termdat_mcp
配置
所有配置均通过环境变量完成。默认值适合本地使用。
| 变量 | 默认值 | 用途 |
|---|---|---|
TERMDAT_MCP_TRANSPORT | stdio | 传输方式:stdio(本地)或 streamable-http / http(云端)。sse 作为已弃用的别名仍受支持,现在会在 /mcp 上提供 Streamable HTTP 服务,并在启动时发出警告 |
HOST | 127.0.0.1 | 绑定主机(仅限 HTTP 传输)。默认绑定环回地址;仅在容器内设置 HOST=0.0.0.0 |
PORT | 8000 | 绑定端口(仅限 HTTP 传输) |
TERMDAT_MCP_CORS_ORIGINS | [] | 仅限 HTTP:明确允许的浏览器来源(默认拒绝;生产环境中绝不使用通配符)。以逗号分隔,例如 https://a.example,https://b.example |
TERMDAT_MCP_ALLOWED_HOSTS | [] | 仅限 HTTP:入站 Host 允许列表。以逗号分隔,例如 mcp.example.ch,mcp.example.ch:443。绑定到非环回地址时必须设置;否则完全不会检查 Host 标头 |
TERMDAT_MCP_LOG_LEVEL | INFO | structlog 日志级别(以 JSON 格式输出到 stderr) |
TERMDAT_MCP_VOCAB_TTL | 86400 | 词汇缓存 TTL,单位为秒 |
配置会一次性加载到类型化的 Settings 对象中(pydantic-settings)。
列表变量。 TERMDAT_MCP_CORS_ORIGINS 和 TERMDAT_MCP_ALLOWED_HOSTS
接受以逗号分隔的列表——推荐使用这种写法,Swiss Public Data MCP 产品组合中的其他项目也采用这种写法:
TERMDAT_MCP_ALLOWED_HOSTS="mcp.example.ch,mcp.example.ch:443"
JSON 列表(["mcp.example.ch"])仍然有效。两种格式都会去除首尾空白和空项,因此末尾的逗号不会造成问题。单个值无需加括号。
TERMDAT_MCP_ALLOWED_HOSTS 在云端至关重要。 未配置允许列表时,SDK 会关闭 DNS 重绑定保护。在容器中绑定到 0.0.0.0 却未设置此变量时,永远不会验证 Host 标头——服务器无法根据绑定地址推导出自己的公网名称,而猜测的列表会拒绝所有真实请求并返回 HTTP 421。若必须退回到这种状态,服务器会在启动时发出警告。
云端(Render / Railway):
TERMDAT_MCP_TRANSPORT=streamable-http PORT=8000 termdat-mcp # exposes /mcp
可用工具
| 工具 | 用途 |
|---|---|
search_terms | 使用字段标志、集合和分类筛选条件搜索 TERMDAT |
translate_term | 查询行政术语在另一种国家语言中的官方对应词 |
check_terms | 通信质量检查:根据经验证的名称检查最多 25 个术语 |
get_entries | 按数字 ID 获取已知条目 |
list_collections | 列出约 140 个术语集合(筛选值) |
list_classifications | 列出 23 个主题分类,例如 BILD = 教育 |
api_status | 检查可用性;不会静默返回空结果 |
所有工具均标注为 readOnlyHint: true、destructiveHint: false。
MCP 原语。 此服务仅使用 Tools 原语。TERMDAT 的回答来自实时查询,没有可供公开为 Resources 的稳定资源层级,也没有由服务端编写的 Prompts。这七个工具规模小且关系紧密,因此都放在单个 server.py 中,而不是单独放在 tools/ 包里。
架构
┌─────────────────┐ stdio / HTTP ┌──────────────────────────┐
│ MCP host │ ───────────────► │ termdat-mcp │
│ (Claude, IDE) │ ◄─────────────── │ │
└─────────────────┘ │ vocabulary cache (24 h) │
│ 140 collections │
│ 23 classifications │
└────────────┬─────────────┘
│ httpx + retry (2/4/8 s)
▼
https://api.termdat.bk.admin.ch/v2
├── /Search (SearchTerm + InLanguageCode)
├── /Entry (EntryIds)
├── /Collection (140 values)
└── /Classification ( 23 values, incl. BILD)
架构决策
此服务采用 架构 A(仅实时 API),仅缓存两种受控词表。
理由(已于 2026-07-19 在线验证):
- API 在
/swagger/v2/swagger.json发布了完整的 OpenAPI 3.0.4 规范,且声明没有安全方案——无需身份验证,符合优先采用无身份验证的原则。 - 服务端搜索运行正常,支持 11 个字段标志,以及按集合和分类筛选。没有理由在本地镜像数据库,也没有提供批量数据转储。
/Collection(140 条)和/Classification(23 条)很少变更,而且 Agent 需要它们才能理解筛选参数,因此会缓存 24 小时,并在缓存过期时回退使用陈旧缓存。
后果:
- 每次搜索都会实时调用;除词表查询外,
provenance的值为live_api。 - 验证错误会以清晰的 RFC 9110 载荷返回,并直接呈现,不会被吞掉。
项目结构
termdat-mcp/
├── src/termdat_mcp/
│ ├── __init__.py
│ ├── __main__.py # entry point; dual transport (stdio / Streamable HTTP)
│ ├── client.py # httpx client, retry, vocabulary cache
│ ├── models.py # Pydantic models
│ └── server.py # MCP tool definitions
├── tests/
│ ├── test_client.py # offline, respx-mocked
│ └── test_live.py # hits the real TERMDAT API
├── README.md
├── README.de.md
├── CHANGELOG.md
├── LICENSE
└── pyproject.toml
安全性与限制
- 只读。 每个工具都标注为
readOnlyHint: true、destructiveHint: false;服务绝不会向 TERMDAT 写入数据。 - 不处理凭据。 API 无需身份验证;服务不会存储或转发任何机密信息。
- 不返回静默空结果。
api_status和错误处理路径会呈现故障,而不是返回看起来完整的空结果。 - 明确标示截断。 始终会发送
MaxEntryCount,并报告truncated(参见已知限制)。 - 明确说明使用条款,不作猜测。 每个响应都会在
source中重申使用条款:复用和再发布时必须注明来源www.termdat.ch,并事先告知联邦总理府术语处使用目的和方式(2026-08-21 的说明)。 - 出站允许列表。 请求只能访问
api.termdat.bk.admin.ch(HTTPS),每次调用前都会通过冻结的ALLOWED_HOSTS集合强制执行——任何用户输入都无法重定向出站流量。参见docs/network-egress.md。 - 默认绑定回环地址。 HTTP 传输绑定到
127.0.0.1;容器可显式选择绑定到0.0.0.0,此时会向 stderr 发出警告。它还会设置默认拒绝的 CORS,仅公开Mcp-Session-Id。 - 错误详情会被屏蔽。 上游或内部错误详情会记录到 stderr(structlog JSON),绝不会返回给模型。
- 接受的风险(ADR): DNS 固定(ADR 0001)和有状态负载均衡(ADR 0002)暂缓处理——对于单实例、单主机、无需身份验证的服务,风险较低。
- 容器。 提供了经过加固的非 root
Dockerfile,用于托管部署。
已知限制
- 仅限行政管理领域。 请参阅上方的覆盖范围表。
check_terms返回not_found,而不是“错误”,正是因为条目未收录于 TERMDAT 并不能证明有误。 MaxEntryCount的默认值约为 25,且不会提示。 省略此参数会让结果看起来像是完整结果集。本服务始终显式发送该参数,并报告truncated。CollectionIds/ClassificationIds的默认值是VARIA,且不会提示。 不带 ID 的/v2/Search仅覆盖 23 个主题领域中的 1 个——即剩余类别,并会将截断后的结果作为普通的空结果返回。除非你明确缩小范围,否则本服务会发送完整的分类集合。请参阅 issue #11。SearchTerm使用 Lucene,且按完整单词匹配。 «Quellensteuer» 不会匹配 «Quellensteuerverordnung»;«Quellensteuer*» 则会匹配。可使用*、?和~——若结果为空,请先尝试使用通配符重试,再判断该词条是否不存在。- 未发送时,
Field.*标志默认值为 true。 除非明确禁用,否则Terminus、Name、Abbreviation和Phraseology均处于启用状态,因此仅设置部分标志只会扩大搜索范围。本服务会显式发送全部 11 个标志,因此fields才能缩小范围。 - 多语言变体需显式启用。 未设置
OutLanguageCode时,条目仅返回德语名称。translate_term会自动为你设置该参数。 - 公共 API 有意提供比网站更少的内容。 这并非范围设置,也不是缺陷:联邦总理府术语部门在收到直接询问后,于 2026-08-21 确认,该 API 仅涵盖部分 TERMDAT 记录,所选内容依据联邦行政部门译员的需求,且暂无扩大覆盖范围的计划。此前测量结果显示:对于 «Quellensteuer»,网站列出 12 条不同记录,而 API 在最大召回率下(所有语言、全部 11 个字段、词内通配符、所有分类和集合)最多返回 7 条;两者仅有 1 条记录重合,即 447912。直接通过
/v2/Entry获取缺失的 ID 时,返回 HTTP 200 和空响应正文——这些记录根本未提供,因此任何查询都无法检索到。由此可见:此服务中没有结果,意味着该条目不在 API 中,而不代表它不在 TERMDAT 中,也无从绕过。已于 2026-07-30 使用 @dfch 在 issue #11 中提供的条目 ID 进行验证。 - I14Y 目录记录中的
license: null——使用条款另见他处。 仅当注明来源www.termdat.ch,并事先告知联邦总理府术语部门再利用和重新发布 TERMDAT 内容的目的及方式后,才允许再利用和重新发布([email protected];2026-08-21 的声明)。运行本服务已由促成该声明的询问所涵盖;你自行进行下游重新发布时,仍需另行告知。每个响应都会在source中重复列出这些条款。 - 各条目的语言覆盖范围不尽相同。 并非每条记录都提供全部四种语言;若某条记录没有目标语言变体,
translate_term会将其省略,而不会凭空生成。
在线探测结果(2026-07-19)
| 端点 | HTTP | 状态 | 备注 |
|---|---|---|---|
/swagger/v2/swagger.json | 200 | ✅ | OpenAPI 3.0.4,132 KB,securitySchemes: [] |
/v2/Search | 200 | ✅ | 需要 SearchTerm、InLanguageCode、ReturnType |
/v2/Entry | 200 | ✅ | 需要 EntryIds、InLanguageCode |
/v2/Collection | 200 | ✅ | 140 个值 |
/v2/Classification | 200 | ✅ | 23 个值,包括 BILD(教育) |
/v2/(根路径) | 404 | ❌ | 没有索引;I14Y 记录指向此处 |
InLanguageCode=deu / de-CH | 400 | ❌ | 仅接受两字母 ISO 代码,不区分大小写 |
探测备注:值得记录的一项修正。 早先的一次探测得出结论,OutLanguageCode 会筛选结果集,因为添加它后似乎所有命中结果都消失了。事实并非如此。有两个变量同时发生了变化——参数和搜索词——而该搜索词本身(«Volksschule»)确实没有任何命中。之后使用四个宽泛搜索词进行了验证:无论是否使用 OutLanguageCode,结果数量都相同;该参数只是附加信息。现在有一项回归测试(test_out_language_is_additive_not_filtering)对此进行防护。
经验法则: 每次探测调用只更改一个变量,否则 API 会承认自己并未犯下的罪行。
在线探测结果(2026-07-27)——搜索范围
问题 #11 中报告:「Quellensteuer」
未返回任何结果,而 TERMDAT 网站却返回了 12 条结果。存在三个彼此独立的原因,
按影响大小降序排列。条目数,InLanguageCode=DE:
| 查询 | 无 ID(=VARIA) | 全部 23 个分类 | + 自由文本字段 | + * 通配符 |
|---|---|---|---|---|
Quellensteuer | 0 | 1 | 3 | 6 |
Pensionskasse | 1 | 4 | 22 | 27 |
第一列就是此服务修复前发送的结果。VARIA 默认值是影响最大的因素:它隐藏了
FINANZWESEN、RECHT 和另外 20 个主题领域,使结果看起来像是一个肯定的零。
探测备注。 值得记录的故障模式不是结果数量,而是搜索范围过窄与确实没有结果无法区分。在报告的问题会话中,模型读到空结果后,又结合此服务自身的「没有结果通常意味着超出范围」警告,为一个一直存在于数据库中的词条编造了一个看似合理的解释。工具若悄然缩小范围,就会悄然被相信。因此,空结果中会包含 hint,警告也已更新,现在会提示模型重试,而不是直接下结论。
项目阶段
此服务处于阶段 1(只读)。所有工具都标注为
readOnlyHint: true / destructiveHint: false,且只会查询公开的
TERMDAT v2 API——没有写入、发送或文件系统功能。
| 阶段 | 范围 | 状态 |
|---|---|---|
| 1 — 只读 | 搜索、翻译和核查行政名称 | ✅ 当前阶段 |
| 2 — 可写入 | (暂无计划) | — |
| 3 — 多 Agent | (暂无计划) | — |
若要进入后续阶段,必须重新审计,并在人为介入环节加入控制措施,然后才能添加任何可写入工具。
MCP 协议版本
此服务通过同一个端点在两种传输方式上支持两个协议时代。客户端在连接上发送的第一个请求决定适用哪个时代;之后若声明来自另一个时代,则会被拒绝。
| 时代 | 修订版本 | 使用者 |
|---|---|---|
initialize 握手 | 2024-11-05 … 2025-11-25 | 当前客户端使用的协议。服务会以请求中的修订版本作答;若请求的是更新版本,则使用 2025-11-25 上限。 |
| 每请求信封 | 2026-07-28 | 携带 2026-07-28 _meta 信封的请求会开启现代连接。 |
这两个修订版本都固定在
tests/test_protocol_version.py 中,并与已安装的 SDK 进行断言核对,因此 Dependabot 更新 mcp 时不会悄然更改其中任何一个。
单靠这项固定曾掩盖了一个真实缺口。 直到 18.09.2026,网络传输使用的还是 SDK 的 SSE 应用,它完全没有通往现代时代的分支——通过 HTTP 无法使用 2026-07-28,尽管常量声称可以,stdio 却能正常提供该版本。切换到 /mcp 上的 Streamable HTTP 后,这两个时代也都经过了实际测量:tests/test_streamable_http.py 会通过组装好的 ASGI 堆栈发送两种类型的真实请求,并检查协商后的修订版本、必需的 resultType,以及对未知现代修订版本的 -32022 拒绝响应。
请注意,SDK 的 LATEST_PROTOCOL_VERSION 是现代时代的别名,而非握手时代的别名——仅针对它进行固定,会让当前客户端实际协商的时代仍可悄然漂移。
更新策略。 当检查失败时,不要盲目编辑常量:阅读两个修订版本之间的规范变更日志,验证服务仍正常运行,然后一并更新常量、本节、README.de.md 和
CHANGELOG.md。
测试
PYTHONPATH=src pytest tests/ -m "not live" # offline, respx-mocked
PYTHONPATH=src pytest tests/ -m live # hits the real API
python scripts/check_ruff_pin.py
ruff check src/ tests/ scripts/
ruff format --check src/ tests/ scripts/
python scripts/check_version_sync.py
更新日志
参见 CHANGELOG.md。
贡献
欢迎提交问题和拉取请求。请确保工具仅提供只读功能,在提交前运行 ruff check 和离线测试套件,并在面向用户的变更对应的 [Unreleased] 下添加一条 CHANGELOG.md 记录。
维护者:有关逐步进行的 PyPI 发布流程(通过 GitHub Release 进行 Trusted Publishing),请参见 PUBLISHING.md。
安全
有关安全态势、加固措施以及如何报告漏洞,请参见 SECURITY.md。
许可证
此服务采用 MIT 许可证,详见 LICENSE。TERMDAT 内容仍受联邦总理府条款约束:请注明来源 www.termdat.ch,并在任何重复使用或再发布前,告知术语部门其用途和方式(2026-08-21 声明)。
作者
Hayal Oezkan · github.com/malkreide
致谢与相关项目
- 数据:TERMDAT,瑞士联邦总理府(BK)。
- 目录条目:I14Y 数据服务
ff0c37eb… - 发现服务:i14y-mcp
- 作品集索引:swiss-public-data-mcp
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。