termdat-mcp について
termdat-mcpは、スイス連邦行政機関のTERMDAT用読み取り専用MCPサーバーです。エージェントが4つの公用語で、機関名や肩書、略称を出典と検証状況付きで調べられます。
主な機能
- 01フィールドや分類による用語検索
- 02行政用語の公式訳の取得
- 031回に最大25語を照合
- 04コレクションと分類の一覧
- 05stdioとHTTPに対応する読み取り専用機能
活用シーン
- 01機関の公式フランス語名の確認
- 02公開前の訳語チェック
- 03特定分野の所管機関の検索
MCP 対応
TERMDAT項目の検索・翻訳・照合、統制語彙の閲覧、API状態の確認を行う7つの読み取り専用ツールを公開します。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 | いいえ |
詳細
🇨🇭 Swiss Public Data MCP Portfolio の一部です。AI エージェントをスイスの公共データおよびオープンデータに接続するオープンソースの MCP サーバーを提供しています。 これは個人プロジェクトです。雇用主や機関との提携関係はありません。
🏷️ termdat-mcp
DE/FR/IT/EN における、公式に検証済みのスイス行政機関の名称。出典情報と検証状況を付記しています。
概要
スイス連邦首相府が管理するスイス連邦行政機関の用語データベース TERMDAT 用の MCP サーバーです。スイスの当局、部局、法令について、DE/FR/IT/EN で公式に検証済みの名称を、出典情報と検証状況とともに AI エージェントに提供します。
TERMDAT をデータサービス ff0c37eb-2f7c-4ff6-996e-d22b77bf52fc としてカタログ化している i14y-mcp を通じて発見されました。
このツールの用途と、用途ではないもの。 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 |
「Bildung」の13件は、教育局、教育部門、経済・教育部門といった組織名であり、教育学上の概念ではありません。用途に応じて使い分けてください。このサーバーは当局の名称、公式名称、略称に強く、分野固有の用語はほとんど扱っていません。
機能
- 公式 TERMDAT パブリック v2 API を利用する、読み取り専用ツールを7つ提供します。
- DE/FR/IT/EN の公式名称を提供し、すべての応答に出典情報と検証状況を付記します。
- コミュニケーションの品質確認:1回の呼び出しで最大25件の用語を検証済みの名称と照合できます。
- 140のコレクションと23の分類に対応する語彙キャッシュ(TTL 24時間)。キャッシュが古い場合もフォールバックして提供します。
- 指数バックオフ(2/4/8秒)による再試行。応答の予期しない切り詰めを防ぐため、
MaxEntryCountを明示的に指定します。 - 2種類のトランスポートに対応:
stdio(ローカル)と Streamable HTTP(クラウド)。どちらも MCP プロトコルリビジョン2026-07-28を提供します。 - 認証は不要です。パブリックで認証不要の API を使用します(No-Auth-First)。
🎯 代表的なデモクエリ
「ドイツ語圏の州にある教育局の、フランス語およびイタリア語での公式名称は何ですか?」
list_classifications → search_terms → translate_term の順に実行して解決します。
デモ
前提条件
- Python 3.10+
uv/uvx(推奨)またはpipapi.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 のログレベル(stderr に JSON を出力) |
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 もありません。7つのツールは小規模で相互に関連しているため、tools/ パッケージではなく、単一の server.py にまとめています。
アーキテクチャ
┌─────────────────┐ 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 のみ) を採用し、キャッシュは 2 つの管理対象の統制語彙に限定しています。
理由(2026-07-19 にライブで確認済み):
- API は
/swagger/v2/swagger.jsonで完全な OpenAPI 3.0.4 仕様を公開しており、セキュリティスキームを宣言していません。認証なしでアクセスでき、No-Auth-First の要件を満たしています。 - サーバー側の検索は適切に機能し、11 個のフィールドフラグと、コレクションおよび分類によるフィルターに対応しています。データベースをローカルに複製する理由はなく、一括ダンプも提供されていません。
/Collection(140 件)と/Classification(23 件)は変更頻度が低く、エージェントがフィルター引数を理解するのに必要なため、24 時間の TTL と、古いキャッシュを提供するフォールバックを設定してキャッシュします。
結果:
- すべての検索でライブ呼び出しが行われます。語彙の検索を除き、
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 からは最大再現率で 7 件返されます(全言語、11 個すべてのフィールド、部分一致ワイルドカード、すべての分類とコレクションを指定)。重複するのはエントリー 447912 の1 件だけです。不足している ID を
/v2/Entryで直接取得しようとすると、HTTP 200 と空の本文が返されます。つまり、それらのエントリーはまったく提供されていないため、どのようなクエリでも取得できません。そのため、このサーバーで見つからない場合は、TERMDAT ではなくAPIに存在しないということであり、回避策はありません。2026-07-30 に、@dfch が issue #11 で提供したエントリー ID を使って確認済みです。 - I14Y のカタログレコードでは
license: nullとなっていますが、利用条件は別の場所にあります。 TERMDAT のコンテンツの再利用および再公開は、出典としてwww.termdat.chを明記し、事前に利用目的と方法を連邦官房の用語部門に知らせる場合に限り認められます([email protected]、2026-08-21 の回答)。このサーバーの実行は、その回答を得るきっかけとなった問い合わせの対象です。再公開する場合は、独自に通知する必要があります。すべてのレスポンスで、利用条件がsourceに再掲されます。 - エントリーごとに対応言語は異なります。 すべてのエントリーが 4 言語すべてに対応しているわけではありません。
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 | ❌ | 大文字と小文字を区別しない、2 文字の ISO コードのみです |
プローブに関する注記:記録に値する訂正。 以前のプローブでは、OutLanguageCode は結果セットを絞り込むと結論づけていました。追加するとすべてのヒットがなくなったように見えたためです。しかし、そうではありません。パラメーターと検索語という 2 つの変数を同時に変更しており、検索語自体(「Volksschule」)のヒット数が本当に 0 件でした。その後、広範な 4 つの検索語で検証したところ、OutLanguageCode の有無による結果数は同じであり、このパラメーターは結果に追加されるだけであることが確認されました。これを保護する回帰テスト(test_out_language_is_additive_not_filtering)を追加しました。
経験則: プローブ呼び出しごとに変更する変数は 1 つにしてください。そうしないと、API は身に覚えのない罪を自白します。
ライブプローブの調査結果(2026-07-27)— 検索範囲
issue #11 で報告された事例では、「Quellensteuer」の検索結果が返されなかった一方、TERMDAT のウェブサイトでは 12 件のヒットがありました。影響が大きい順に、独立した原因が 3 つありました。エントリー数、InLanguageCode=DE:
| 検索語 | ID なし(=VARIA) | 23 の分類すべて | + フリーテキストフィールド | + * ワイルドカード |
|---|---|---|---|---|
Quellensteuer | 0 | 1 | 3 | 6 |
Pensionskasse | 1 | 4 | 22 | 27 |
最初の列は、修正前にこのサーバーが送信していた内容です。VARIA のデフォルト設定が主な原因で、FINANZWESEN、RECHT、その他 20 の主題分野が、確信に満ちた 0 件という回答の陰に隠れていました。
プローブに関する注記。 記録すべき失敗の本質は件数ではなく、検索範囲が狭すぎる状態と、実際に該当項目が存在しない状態とを区別できないことです。報告されたセッションでは、モデルは空の結果を、このサーバー独自の「該当しない場合は通常、検索範囲外です」という注意書きとあわせて読み、実際にはデータベースに存在していた用語について、もっともらしい説明を作り上げました。検索範囲を黙って狭めるツールは、黙って信じられてしまいます。そのため、結果が空の場合は hint を返し、モデルが結論を出すのではなく再試行するよう促す注意書きに変更しました。
プロジェクトのフェーズ
このサーバーは**フェーズ 1(読み取り専用)**です。すべてのツールに readOnlyHint: true / destructiveHint: false が注釈されており、公開されている TERMDAT v2 API にクエリを送信するだけです。書き込み、送信、ファイルシステム操作の機能はありません。
| フェーズ | 対象範囲 | ステータス |
|---|---|---|
| 1 — 読み取り専用 | 検索、翻訳、行政上の名称の確認 | ✅ 現在 |
| 2 — 書き込み対応 | (予定なし) | — |
| 3 — マルチエージェント | (予定なし) | — |
後続フェーズへの移行には、書き込み対応のツールを追加する前に、再監査と人間参加型の制御が必要です。
MCP プロトコルのバージョン
このサーバーは、両方のトランスポートで同じエンドポイントを介して、2 つのプロトコル時代に対応しています。接続時のクライアントの最初のリクエストによって、どちらを適用するかが決まります。後からもう一方の時代を申告しても拒否されます。
| 時代 | リビジョン | 対象 |
|---|---|---|
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 の更新で、どちらかが気付かないうちに変更されることはありません。
この固定だけでは、かつて実際のギャップを見逃していました。 2026 年 9 月 18 日までは、ネットワークトランスポートとして、モダンな時代への分岐を持たない SDK の SSE アプリを使っていました。そのため HTTP では 2026-07-28 に到達できませんでしたが、定数は対応していると示し、stdio では問題なく利用できていました。/mcp で Streamable HTTP に切り替えて以降、両方の時代は実測でも確認されています。つまり、tests/test_streamable_http.py は、組み立て済みの ASGI スタックに両方の種類の実リクエストを送り、ネゴシエートされたリビジョン、必須の resultType、および不明なモダンリビジョンに対する -32022 の拒否を検証します。
SDK の LATEST_PROTOCOL_VERSION は、ハンドシェイク時代ではなくモダンな時代の別名であることに注意してください。これだけを基準に固定すると、現在のクライアントが実際にネゴシエートする時代が変更されても気付けません。
更新ポリシー。 ゲートに失敗しても、定数をそのまま編集しないでください。2 つのリビジョン間の仕様の変更履歴を読み、サーバーが引き続き正しく動作することを確認したうえで、定数、このセクション、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 を参照してください。
コントリビューション
Issue とプルリクエストを歓迎します。ツールは読み取り専用に保ち、提出前に 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 サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。