
Termdat Mcp
io.github.malkreide由 GitHub 帳號 malkreide 發布v0.2.0更新於 Oct 24, 2026
MCP server for TERMDAT — the terminology database of the Swiss Federal Administration
概覽
讓助理查詢瑞士聯邦行政機關經官方確認的機關名稱及其德/法/義/英對應譯名,資料來自 TERMDAT 術語庫。
- 可以做什麼
- 將瑞士聯邦總理府的公開 TERMDAT v2 API 包裝成七個唯讀工具:search_terms、translate_term、check_terms、get_entries、list_collections、list_classifications 與 api_status(R25、R71-R79)。回傳結果包含德語、法語、義大利語與英語的官方名稱,並附來源引用與驗證狀態(R26)。約 140 個集合與 23 個分類有 24 小時快取,檢索為即時 API 呼叫,具重試機制並明確回報截斷(R28、R29、R93)。
- 適用情境
- 當助理需要瑞士機關、部門或法律文件的官方名稱,或需要其另一種國家語言的對應譯名時適用;也適合檢查行政文本中的術語一致性(R9、R27)。它是經認證的名稱檔案庫,而非專業詞典:擅長機關命名、官方頭銜與縮寫,對領域詞彙幾乎沒有涵蓋(R12、R22、R23)。
- 執行需求
- 以 stdio 方式在本機執行,使用 uvx termdat-mcp(或 pip)啟動。需要 Python 3.10+ 與 uv/uvx,並能連線 api.termdat.bk.admin.ch;不需要 API 金鑰(R37、R38、R39)。可選環境變數用於設定傳輸方式、主機、連接埠、CORS 來源、允許的主機、日誌層級與詞彙快取 TTL(R46-R60)。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Termdat Mcp,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
🇨🇭 Part of the Swiss Public Data MCP Portfolio — open-source MCP servers connecting AI agents to Swiss public and open data. This is a private project. It is independent of any employer or institutional affiliation.
🏷️ termdat-mcp
[Version] [CI] [License: MIT] [Python 3.10+] [MCP] [Auth: none] [Portfolio]
Official, validated Swiss administrative designations across DE / FR / IT / EN — with source references and validation status.
Overview
MCP server for TERMDAT, the terminology database of the Swiss Federal Administration, maintained by the Federal Chancellery. It gives an AI agent the officially validated designations of Swiss authorities, departments and legal acts across DE / FR / IT / EN — with source references and validation status.
Discovered through i14y-mcp, which catalogues TERMDAT as data service ff0c37eb-2f7c-4ff6-996e-d22b77bf52fc.
What this is — and what it is not. TERMDAT is not a subject dictionary. It is a certified name-plate archive: it will not tell you what «Sonderpädagogik» means, but it will tell you the official name of the authority responsible for it, and what that authority is called in French.
Measured coverage (live, 2026-07-19, German search over the Terminus field):
The thirteen «Bildung» hits are organisational names — Bildungsdirektion, Erziehungsdepartement, Departement für Volkswirtschaft und Bildung — not pedagogical concepts. Plan accordingly: this server is strong for authority naming, official titles and abbreviations, and largely silent on domain vocabulary.
Features
- Seven read-only tools over the official TERMDAT public v2 API.
- Official designations across DE / FR / IT / EN, with source reference and validation status on every response.
- Communication QA: check up to 25 terms in one call against validated designations.
- Vocabulary cache (24 h TTL) for the 140 collections and 23 classifications, with stale-serve fallback.
- Retry with exponential backoff (2/4/8 s); explicit
MaxEntryCountto avoid silent truncation. - Dual transport:
stdio(local) and Streamable HTTP (cloud). Both serve MCP protocol revision2026-07-28. - No authentication required — public, unauthenticated API (No-Auth-First).
🎯 Anchor demo query
«What are the official French and Italian names of the education directorates of the German-speaking cantons?»
Resolved with list_classifications → search_terms → translate_term.
Demo
[Demo: Claude using search_terms and translate_term]
Prerequisites
- Python 3.10+
uv/uvx(recommended) orpip- Network access to
api.termdat.bk.admin.ch— no API key needed
Installation
Claude Desktop (claude_desktop_config.json):
Quickstart
Configuration
All configuration is via environment variables. Defaults are safe for local use.
Configuration is loaded once into a typed Settings object (pydantic-settings).
List variables. TERMDAT_MCP_CORS_ORIGINS and TERMDAT_MCP_ALLOWED_HOSTS
take a comma-separated list — the recommended spelling, and the one the
rest of the Swiss Public Data MCP portfolio uses:
A JSON list (["mcp.example.ch"]) stays valid. Surrounding whitespace and
empty entries are dropped in both forms, so a trailing comma is harmless. A
single value needs no brackets.
TERMDAT_MCP_ALLOWED_HOSTS matters in the cloud. The SDK leaves
DNS-rebinding protection off while no allow-list is configured. Bind to
0.0.0.0 in a container without this variable and the Host header is never
validated — the server cannot derive its own public name from the bind address,
and a guessed list would reject every real request with HTTP 421. It warns on
startup when it has to fall back to that state.
Cloud (Render / Railway):
Available Tools
All tools are annotated readOnlyHint: true, destructiveHint: false.
MCP primitives. This server uses only the Tools primitive. TERMDAT answers
are live queries with no stable resource hierarchy to expose as Resources, and
there are no server-authored Prompts. The seven tools are small and closely
related, so they live in a single server.py rather than a tools/ package.
Architecture
Architecture decision
This server uses Architecture A (live API only), with caching limited to the two controlled vocabularies.
Rationale (verified live on 2026-07-19):
- The API publishes a complete OpenAPI 3.0.4 specification at
/swagger/v2/swagger.jsonand declares no security schemes — unauthenticated access, No-Auth-First satisfied. - Server-side search works properly, including 11 field flags and filters by collection and classification. There is no reason to mirror the database locally, and no bulk dump is offered.
/Collection(140 entries) and/Classification(23 entries) change rarely and are needed to make filter arguments legible to an agent, so they are cached with a 24-hour TTL and a stale-serve fallback.
Consequences:
- Every search is a live call;
provenanceislive_apiexcept for vocabulary lookups. - Validation errors arrive as clean RFC 9110 payloads and are surfaced rather than swallowed.
Project Structure
Safety & Limits
- Read-only. Every tool is annotated
readOnlyHint: true,destructiveHint: false; the server never writes to TERMDAT. - No credentials handled. The API is unauthenticated; the server stores and forwards no secrets.
- No silent empties.
api_statusand error paths surface failures instead of returning an empty result that looks complete. - Truncation is explicit.
MaxEntryCountis always sent andtruncatedis reported (see Known Limitations). - Terms of use are stated, not guessed. Every response repeats them in
source: reuse and republication require the sourcewww.termdat.chto be named, and the Federal Chancellery's Terminology Section to be informed of purpose and manner beforehand (statement of 2026-08-21). - Egress allow-list. Requests can only reach
api.termdat.bk.admin.ch(HTTPS), enforced before every call by a frozenALLOWED_HOSTSset — no user input can redirect egress. Seedocs/network-egress.md. - Loopback by default. The HTTP transport binds to
127.0.0.1;0.0.0.0is an explicit container opt-in that warns on stderr. It also sets default-deny CORS, exposing onlyMcp-Session-Id. - Errors are masked. Upstream/internal error detail is logged to stderr (structlog JSON) and never returned to the model.
- Accepted risks (ADRs): DNS pinning (ADR 0001) and stateful load balancing (ADR 0002) are deliberately deferred — low risk for a single-instance, single-host, no-auth server.
- Container. A hardened, non-root
Dockerfileis provided for hosted deployments.
Known Limitations
- Administrative scope only. See the coverage table above.
check_termsreturnsnot_found, never «incorrect», precisely because absence from TERMDAT is not evidence of error. MaxEntryCounthas a silent default of ~25. Omitting it looks like a complete result set. This server always sends the parameter explicitly and reportstruncated.CollectionIds/ClassificationIdshave a silent default ofVARIA. An ID-less/v2/Searchcovers one of 23 subject areas — the residual one — and reports the truncated result as a normal empty answer. This server sends the full classification set unless you narrow it explicitly. See issue #11.SearchTermis Lucene, and matching is on whole words. «Quellensteuer» does not match «Quellensteuerverordnung»; «Quellensteuer*» does.*,?and~are available — on an empty result, retry with a wildcard before concluding the term is absent.Field.*flags default to true where unsent.Terminus,Name,AbbreviationandPhraseologyare on unless explicitly disabled, so a partial flag set can only widen a search. This server sends all eleven flags explicitly, which is what makesfieldsable to narrow.- Multilingual variants are opt-in. Without
OutLanguageCode, entries return German designations only.translate_termsets it for you. - The public API exposes less than the website — deliberately. Not a scope setting, and not a defect: asked directly, the Federal Chancellery's Terminology Section confirmed on 2026-08-21 that the API covers only part of the TERMDAT records, that the selection follows the needs of the federal administration's translators, and that no fuller coverage is planned. Measured beforehand: for «Quellensteuer» the website lists 12 distinct entries and the API returns 7 at maximum recall (every language, all 11 fields, infix wildcard, all classifications and collections); the overlap is one entry, 447912. Fetching the missing IDs directly via
/v2/Entryreturns HTTP 200 with an empty body — they are not served at all, so no query can reach them. Consequence: absence from this server means absence from the API, not from TERMDAT, and there is nothing to work around. Verified 2026-07-30 with the entry IDs supplied by @dfch in issue #11. - The I14Y catalogue record carries
license: null— the terms are elsewhere. Reuse and republication of TERMDAT content are permitted only with the sourcewww.termdat.chnamed, and the Terminology Section of the Federal Chancellery informed of purpose and manner beforehand ([email protected]; statement of 2026-08-21). Running this server is covered by the enquiry that produced that statement; your own downstream republication needs its own notice. Every response repeats the terms insource. - Entry-level language coverage varies. Not every entry exists in all four languages;
translate_termomits entries without a target-language variant rather than inventing one.
Live probe findings (2026-07-19)
Probe note: a correction worth recording. An earlier probe concluded that OutLanguageCode filters the result set, because adding it appeared to drop all hits. It does not. Two variables had been changed at once — the parameter and the search term — and the term itself («Volksschule») genuinely has zero hits. Verified afterwards across four broad terms: result counts are identical with and without OutLanguageCode; the parameter is purely additive. A regression test (test_out_language_is_additive_not_filtering) now guards this.
Rule of thumb: change one variable per probe call, or the API will confess to a crime it did not commit.
Live probe findings (2026-07-27) — search scope
Reported in issue #11: «Quellensteuer»
returned nothing while the TERMDAT website returned twelve hits. Three independent causes,
in descending order of effect. Entry counts, InLanguageCode=DE:
The first column is what this server sent before the fix. The VARIA default is the
dominant term: it hid FINANZWESEN, RECHT and twenty other subject areas behind an
answer that looked like a confident zero.
Probe note. The failure mode worth recording is not the count — it is that an
under-scoped search is indistinguishable from a genuine absence. In the reported session
the model read the empty result together with this server's own «absence usually means out
of scope» caveat and invented a plausible explanation for a term that was in the database
all along. A tool that narrows silently will be believed silently. Hence hint on empty
results, and a caveat that now tells the model to retry rather than to conclude.
Project Phase
This server is in Phase 1 (read-only). All tools are annotated
readOnlyHint: true / destructiveHint: false and only ever query the public
TERMDAT v2 API — there are no write, send, or filesystem capabilities.
A transition to a later phase would require a re-audit and human-in-the-loop controls before any write-capable tool is added.
MCP Protocol Version
This server speaks two protocol eras over the same endpoint, on both transports. The client's first request on a connection decides which one applies; a later claim from the other era is refused.
Both revisions are pinned in
tests/test_protocol_version.py and asserted
against the installed SDK, so a Dependabot bump of mcp cannot move either one
silently.
That pin alone once hid a real gap. Until 18.09.2026 the network transport
was the SDK's SSE app, which has no branch into the modern era at all — over
HTTP, 2026-07-28 was unreachable, while the constants said otherwise and
stdio served it fine. Since the switch to Streamable HTTP on /mcp, both eras
are also measured: tests/test_streamable_http.py
sends real requests of both kinds through the assembled ASGI stack and checks
the negotiated revision, the mandatory resultType, and the -32022 rejection
of an unknown modern revision.
Note that the SDK's LATEST_PROTOCOL_VERSION is an alias for the modern
era, not for the handshake era — pinning against it alone would leave the era
that current clients actually negotiate free to drift.
Update policy. When the gate fails, do not edit the constant blindly: read
the spec changelog between the two revisions, verify the server still behaves,
then move the constant, this section, README.de.md and
CHANGELOG.md together.
Testing
Changelog
See CHANGELOG.md.
Contributing
Issues and pull requests are welcome. Please keep tools read-only, run ruff check and the offline test suite before submitting, and add a CHANGELOG.md entry under [Unreleased] for user-facing changes.
Maintainers: see PUBLISHING.md for the step-by-step PyPI release process (Trusted Publishing via GitHub Release).
Security
See SECURITY.md for the security posture, hardening controls, and how to report a vulnerability.
License
MIT for this server — see LICENSE. TERMDAT content remains subject to the Federal Chancellery's terms: name the source www.termdat.ch and inform the Terminology Section beforehand of purpose and manner of any reuse or republication (statement of 2026-08-21).
Author
Hayal Oezkan · github.com/malkreide
Credits & Related Projects
- Data: TERMDAT, Swiss Federal Chancellery (BK).
- Catalogue entry: I14Y data service
ff0c37eb… - Discovery server: i14y-mcp
- Portfolio index: swiss-public-data-mcp
來源:README.md,提交 59394ba
工具
0版本歷史
2- v0.2.0最新Sep 19, 2026
- v0.1.3Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分