mcpField
T
termdat-mcp

termdat-mcp

本地服务
github.com法律与政务15 周前更新

关于 termdat-mcp

termdat-mcp 是瑞士联邦行政部门 TERMDAT 术语数据库的只读 MCP 服务,供 Agent 查询和核验官方机构名称、职称及缩写,并查看四种国家语言的条目、来源和验证状态。

主要功能

  1. 01按字段和分类搜索术语
  2. 02翻译官方行政术语
  3. 03单次核验最多 25 个术语
  4. 04浏览术语集和分类
  5. 05支持 stdio 与 HTTP,只读

适用场景

  1. 01核实机构的官方法语名称
  2. 02发布前检查译名
  3. 03查找某主题的主管机构

MCP 相关

提供七种只读工具,用于搜索、翻译和核验 TERMDAT 条目、浏览受控词汇表及检查 API 状态。支持 stdio 和 Streamable HTTP。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"malkreide-termdat": {
"command": "uvx",
"args": [
"termdat-mcp"
]
}
}
}
启动命令uvx termdat-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypitermdat-mcp0.2.0

工具

7

收录的工具列表 · 更新于 2 周前

参数类型必填说明
detailboolean否
fieldsstring否
in_languagestring否
max_resultsinteger否
search_termstring是
out_languagestring否
collection_idsany否
classification_idsany否

详细介绍

🇨🇭 属于瑞士公共数据 MCP 组合 — 开源 MCP 服务,将 AI Agent 连接到瑞士公共数据和开放数据。 这是一个个人项目,与任何雇主或机构均无关联。

🏷️ termdat-mcp

Version CI License: MIT Python 3.10+ MCP Auth: none Portfolio

瑞士行政机构官方认证的名称,涵盖德语/法语/意大利语/英语,并附来源参考和验证状态。

🇩🇪 德语版

概述

面向瑞士联邦行政部门术语数据库 TERMDAT 的 MCP 服务,由联邦总理府维护。它为 AI Agent 提供瑞士主管部门、部门和法律文件经官方验证的德语/法语/意大利语/英语名称,并附来源参考和验证状态。

通过 i14y-mcp 发现;该项目将 TERMDAT 列为数据服务 ff0c37eb-2f7c-4ff6-996e-d22b77bf52fc。

它是什么——又不是什么。 TERMDAT 不是主题词典,而是一个认证名称档案库:它不会告诉你「Sonderpädagogik」是什么意思,但会告诉你负责该领域的主管部门的官方名称,以及该部门的法语名称。

实测覆盖范围(实时数据,2026-07-19,在 Terminus 字段中进行德语搜索):

搜索词命中数
Departement20
Bildung13
Verordnung8
Schule5
Behörde4
Sonderpädagogik3
Volksschule · Lehrperson · Schulleitung · Unterricht · Kindergarten0

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 查询得出。

演示

演示:Claude 使用 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_TRANSPORTstdio传输方式:stdio(本地)或 streamable-http / http(云端)。sse 作为已弃用的别名仍受支持,现在会在 /mcp 上提供 Streamable HTTP 服务,并在启动时发出警告
HOST127.0.0.1绑定主机(仅限 HTTP 传输)。默认绑定环回地址;仅在容器内设置 HOST=0.0.0.0
PORT8000绑定端口(仅限 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_LEVELINFOstructlog 日志级别(以 JSON 格式输出到 stderr)
TERMDAT_MCP_VOCAB_TTL86400词汇缓存 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.json200✅OpenAPI 3.0.4,132 KB,securitySchemes: []
/v2/Search200✅需要 SearchTerm、InLanguageCode、ReturnType
/v2/Entry200✅需要 EntryIds、InLanguageCode
/v2/Collection200✅140 个值
/v2/Classification200✅23 个值,包括 BILD(教育)
/v2/(根路径)404❌没有索引;I14Y 记录指向此处
InLanguageCode=deu / de-CH400❌仅接受两字母 ISO 代码,不区分大小写

探测备注:值得记录的一项修正。 早先的一次探测得出结论,OutLanguageCode 会筛选结果集,因为添加它后似乎所有命中结果都消失了。事实并非如此。有两个变量同时发生了变化——参数和搜索词——而该搜索词本身(«Volksschule»)确实没有任何命中。之后使用四个宽泛搜索词进行了验证:无论是否使用 OutLanguageCode,结果数量都相同;该参数只是附加信息。现在有一项回归测试(test_out_language_is_additive_not_filtering)对此进行防护。

经验法则: 每次探测调用只更改一个变量,否则 API 会承认自己并未犯下的罪行。

在线探测结果(2026-07-27)——搜索范围

问题 #11 中报告:「Quellensteuer」 未返回任何结果,而 TERMDAT 网站却返回了 12 条结果。存在三个彼此独立的原因, 按影响大小降序排列。条目数,InLanguageCode=DE:

查询无 ID(=VARIA)全部 23 个分类+ 自由文本字段+ * 通配符
Quellensteuer0136
Pensionskasse142227

第一列就是此服务修复前发送的结果。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

致谢与相关项目

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。