openlex-mcp について
openlex-mcpは、ローカル全文索引とzh.chの最新メタデータを組み合わせ、チューリッヒ州の約970件の法律を検索・条文抽出できるMCPサーバーです。一般的な法令調査や教育法令の検索に対応します。
主な機能
- 01関連度順の全文検索
- 02番号や略称による法令取得
- 03条文の抽出と検索
- 04教育法令の専用検索
- 05有効状況とPDFリンクの取得
活用シーン
- 01データ保護に関する法令の検索
- 02Volksschulgesetzの指定条文の取得
- 03語句を含む教育法令の条文検索
MCP 対応
法令検索・取得、条文抽出、メタデータ照会、キャッシュ管理のツールをMCPクライアントに提供します。stdioとStreamable HTTPに対応します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
🇨🇭 スイス公共データ MCP ポートフォリオの一部です
⚖️ openlex-mcp
チューリッヒ州の法令(ZH-Lex)向け MCP サーバー — 約970件の州法を対象とした全文検索、条文抽出、教育法ツール
概要
openlex-mcp は、チューリッヒ州の法令集(Zürcher Gesetzessammlung)全体へのAIネイティブなアクセスを提供します。HuggingFaceの全文データと、公式zh.chウェブサイトから取得する最新メタデータを組み合わせ、すべてをFTS5全文インデックス付きのローカルSQLiteデータベースに保存することで、50ミリ秒未満の検索性能を実現します。
| ソース | データ | アクセス |
|---|---|---|
| HuggingFace | 974件のZH法令 — 全文(PDF抽出) | SQLite + FTS5としてローカルにキャッシュ |
| zh.ch ZH-Lex | 最新メタデータ、PDFリンク、有効性ステータス | HTTPリクエストでライブ取得 |
チューリッヒ市の学校局向けに開発されていますが、税法から建築規制まで、州法のあらゆる分野を網羅しています。
デモのクエリ例: 「Volksschulgesetzでは保護者の関与について何と定めていますか?Art. 55 VSGを示し、『Elternrat』に言及するすべての条文を探してください。」
機能
- ⚖️ 8つのツール — 検索、取得、条文抽出、キャッシュ管理に対応
- 🔍 FTS5全文検索 — 約970件の州法を対象にBM25ランキングで検索
- 📑 条文抽出 — 段落を検出しながら個別条文(Art. / §)を解析
- 🏫 教育法のショートカット — LS 412.xシリーズ(Volksschulgesetz、Lehrpersonalverordnungなど)に特化した検索
- 🌐 zh.chの最新メタデータ — 現在の有効性ステータスとPDFリンクを取得
- 💾 ハイブリッド構成 — キャッシュされた全文(HuggingFace)+ 最新メタデータ(zh.ch)
- 🔓 APIキー不要 — すべてのデータはオープンライセンス(CC-BY-SA 4.0)で提供
- ☁️ デュアルトランスポート — stdio(Claude Desktop)+ Streamable HTTP(クラウド)
開発フェーズ
現在のフェーズ:フェーズ1 — 読み取り専用。 すべてのツールは読み取り専用(readOnlyHint: true)であり、外部システムへの書き込みは行いません。フェーズ計画と、書き込み機能またはマルチエージェント機能を追加する前の移行条件については、ROADMAP.mdを参照してください。
前提条件
- Python 3.11+
- uv(推奨)またはpip
- インターネット接続(初回のデータダウンロードと最新メタデータ取得用)
インストール
# Clone the repository
git clone https://github.com/malkreide/openlex-mcp.git
cd openlex-mcp
# Install
pip install -e .
# or with uv:
uv pip install -e .
クイックスタート
# stdio (for Claude Desktop)
python -m openlex_mcp.server
# Streamable HTTP — binds to 127.0.0.1:8000 by default (localhost only)
python -m openlex_mcp.server --http --port 8000
ネットワークバインド
デフォルトでは、HTTPトランスポートは 127.0.0.1(ローカルホストのみ)にバインドします。ホストとポートは、MCP_HOST / MCP_PORT環境変数(または優先される--host / --port CLIフラグ)で設定できます。
コンテナ外で 0.0.0.0 にバインドしないでください。ローカルネットワークにサーバーを公開することになります(NeighborJackのリスク)。コンテナまたはクラウドにデプロイする場合は、MCP_HOST=0.0.0.0を明示的に設定してください。コンテナ外でこの設定が行われた場合、サーバーは警告をログに記録します。
Claude Desktopですぐに試してみましょう。
「Volksschulgesetz(VSG)とは何ですか?」 「チューリッヒ州のデータ保護に関する法令をすべて探してください」 「VolksschulgesetzのArt. 1を表示してください」 「『Schulleitung』に言及している教育法令はどれですか?」
設定
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または %APPDATA%\Claude\claude_desktop_config.json(Windows)を編集します:
{
"mcpServers": {
"openlex": {
"command": "python",
"args": ["-m", "openlex_mcp.server"]
}
}
}
または、インストール済みのエントリーポイントを使用します:
{
"mcpServers": {
"openlex": {
"command": "openlex-mcp"
}
}
}
設定ファイルの場所:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
クラウドデプロイ(ブラウザーアクセス用の SSE)
ブラウザー上の claude.ai 経由で使用する場合(ローカルソフトウェアをインストールできない管理対象ワークステーションなど):
Render.com(推奨):
- リポジトリを GitHub にプッシュするか、フォークします
- render.comで、「New Web Service」→ GitHub リポジトリに接続します
- 起動コマンドを設定します:
python -m openlex_mcp.server --http --port 8000 - コンテナーにアクセスできるよう、環境変数
MCP_HOST=0.0.0.0を設定します (コードのデフォルト値は127.0.0.1です。Render はRENDER環境変数を設定するため、 NeighborJack の警告はログに記録されません) - ブラウザーが
Mcp-Session-Idヘッダーを読み取れるように、MCP_CORS_ORIGINS=https://claude.aiを設定します(カンマ区切りのリストです。ワイルドカードは使用できません。 デフォルトは空で、クロスオリジンアクセスは許可されません) - claude.ai の「Settings」→「MCP Servers」で、
https://your-app.onrender.com/sseを追加します
💡 「開発者のノート PC には stdio、ブラウザーには SSE。」
利用可能なツール
検索と閲覧
| ツール | 説明 |
|---|---|
openlex__zhlaw_search_laws | 約970件のチューリッヒ州法令を全文検索します(FTS5 + BM25 ランキング) |
openlex__zhlaw_get_law | LS 番号(例:412.100)または略称(例:VSG)で法令を取得します |
openlex__zhlaw_list_laws | 法分野の接頭辞で法令を一覧表示し、絞り込みます |
openlex__zhlaw_find_education_laws | 教育法(LS 412.x シリーズ)を専門的に検索します |
条文の抽出
| ツール | 説明 |
|---|---|
openlex__zhlaw_get_article | 法令から特定の条文を抽出します(例:Art. 28 VSG) |
openlex__zhlaw_search_articles | 特定の法令に含まれるすべての条文を検索します |
メタデータとキャッシュ
| ツール | 説明 |
|---|---|
openlex__zhlaw_get_law_metadata | zh.ch から最新のメタデータを取得します(PDF リンク、有効性の状態) |
openlex__zhlaw_update_cache | HuggingFace からローカルデータキャッシュを更新します |
主な法分野の接頭辞(LS 番号)
| 接頭辞 | 法分野 | 例 |
|---|---|---|
131 | 憲法および国民の権利 | Kantonsverfassung |
170 | 行政手続き | Datenschutzgesetz |
331 | 税法 | Steuergesetz |
412 | 教育および学校 | Volksschulgesetz (VSG) |
700 | 空間計画および建築 | Planungs- und Baugesetz |
810 | 健康 | Gesundheitsgesetz |
使用例
| 質問 | ツール |
|---|---|
| 「Volksschulgesetz とは何ですか?」 | openlex__zhlaw_get_law |
| 「データ保護に関する法令を探してください」 | openlex__zhlaw_search_laws |
| 「VSG の Art. 55 を表示してください」 | openlex__zhlaw_get_article |
| 「Schulleitung に言及している教育法はどれですか?」 | openlex__zhlaw_find_education_laws |
| 「VSG に含まれる Elternrat に関するすべての条文を探してください」 | openlex__zhlaw_search_articles |
| 「LS 412.100 は現在も有効ですか?」 | openlex__zhlaw_get_law_metadata |
アーキテクチャ
┌─────────────────┐ ┌──────────────────────────────┐ ┌──────────────────────────┐
│ Claude / AI │────▶│ OpenLex MCP │────▶│ HuggingFace │
│ (MCP Host) │◀────│ (MCP Server) │◀────│ rcds/swiss_legislation │
└─────────────────┘ │ │ │ (974 ZH laws, cached) │
│ 8 Tools │ ├──────────────────────────┤
│ SQLite + FTS5 Cache │────▶│ zh.ch ZH-Lex │
│ Stdio | HTTP │◀────│ (live metadata + PDFs) │
│ │ ├──────────────────────────┤
│ No authentication required │ │ LexFind.ch │
└──────────────────────────────┘ │ (links only) │
└──────────────────────────┘
データソースの特性
| ソース | プロトコル | 対象範囲 | 認証 | ライセンス |
|---|---|---|---|---|
HuggingFace rcds/swiss_legislation | Datasets API | チューリッヒ州法974件(全文) | なし | CC-BY-SA 4.0 |
| zh.ch ZH-Lex | HTTP/HTML | 最新のメタデータ、PDF | なし | Public |
| LexFind.ch | HTTP | 州をまたぐリンク | なし | Public |
設計上の決定:ツールのみ(MCP リソースなし)
8 つのエンドポイントはすべて、MCP リソースではなく ツール として公開されています。その理由は次のとおりです。
- すべての検索は パラメーター化されています。クエリ、略語、条文番号は呼び出しごとに変わります。静的リソース(文書ごとに 1 つの URI)では、この特性を自然に表現できません。
- コーパスは 974 件の法律 × 多数の条文 で構成されています。それぞれをリソース URI として登録すると、実用的でないほど大規模なリソース一覧になります。
- MCP リソーステンプレート(
zhlex://laws/{sr_number})は、クライアントがリソースレベルのキャッシュやサブスクリプションの恩恵を受けられる場合に備え、フェーズ 2 の将来的な検討事項です。
スケーリングの制約
制約は、クライアントが使用するプロトコルの世代によって異なります。
2026-07-28(モダン) — 各リクエストは、独自の_metaエンベロープを含む自己完結型の POST です。サーバーはMcp-Session-Idを発行せずに応答し、リクエスト間でセッションを保持しません。どのレプリカでも、どのリクエストにも応答できます。共有ストアもスティッキールーティングも不要です。(subscriptions/listenのストリームは、開始したレプリカ上で動作します。このサーバーの一覧はインポート時に固定されるため、配信先の振り分けは不要です。)- ハンドシェイク方式(
initialize、2025-11-25まで) — セッション状態は プロセス内 に保持されます。共有セッションストア(Redis、Durable Objects など)がないため、複数のレプリカを使用するとアクティブなセッションが壊れます。Render の単一レプリカデプロイでは、すべてのリクエストが 1 つのプロセスにルーティングされるため、現時点ではスティッキーセッション対応の LB は不要です。
ハンドシェイク方式のクライアントを 1 インスタンス以上にスケールする前に、共有セッションストアを追加する か、エッジロードバランサーがスティックテーブルと適切な TTL を使って Mcp-Session-Id ヘッダーに基づいてルーティングするように設定してください。
MCP プロトコルバージョン
| 項目 | 値 |
|---|---|
initialize ハンドシェイク経由で提供 | 2024-11-05 … 2025-11-25 — ハンドシェイクの上限 |
| リクエストごとのエンベロープ経由で提供 | 2026-07-28 |
| 選択するもの | クライアントが接続ごとに最初のリクエストで選択します。2026-07-28 の _meta エンベロープを含むリクエストはモダン接続を開始し、それ以外はハンドシェイク接続を開始します。 |
| SDK | mcp[cli]>=2.0.0,<3 |
| キャッシュヒント | tools/list、prompts/list、resources/list、resources/templates/list、server/discover:ttlMs 300000、cacheScope public |
serverInfo.version | パッケージのバージョン(openlex_mcp.__version__)。すべての server/discover の結果に含まれます |
2026-07-28 で非推奨、未使用 | ロギング(ctx.info/ctx.warning、SEP-2577)。ステータスはツールの結果に、診断情報はサーバーログに記録されます |
| 固定箇所 | src/openlex_mcp/server.py — MCP_PROTOCOL_VERSION 定数 |
更新ポリシー
mcpをアップグレードした場合(Dependabot PR 経由)、SDK のリリースノートでプロトコルバージョンを確認します。- プロトコルバージョンが変わった場合は、
server.pyのMCP_PROTOCOL_VERSIONを更新し、docs/tool-hashes.jsonを再生成して(PYTHONPATH=src python scripts/gen_tool_hashes.py --write)、変更内容をCHANGELOG.mdに記載します。 - マージ前に
pytest tests/ -m "not live"を実行して、互換性を確認します。
プロジェクト構成
openlex-mcp/
├── src/openlex_mcp/
│ ├── __init__.py # Package
│ ├── __main__.py # Entry point for python -m
│ ├── server.py # 8 MCP tool definitions (FastMCP) + Settings
│ ├── responses.py # Typed structured response envelopes (SDK-002)
│ ├── logging_config.py # structlog JSON logging setup (OBS-003)
│ ├── net.py # SSRF/egress-hardened outbound HTTP
│ ├── api_client.py # zh.ch HTTP client + metadata extraction
│ ├── data_cache.py # SQLite + FTS5 cache management
│ └── law_parser.py # Article extraction from law texts
├── tests/ # 89 unit tests (parser, cache, net, tools…)
├── scripts/gen_tool_hashes.py # Tool-definition hash snapshot (SEC-022)
├── docs/ # network-egress, secret-management, tool-hashes
├── .github/workflows/ci.yml # GitHub Actions (Python 3.11/3.12/3.13)
├── .github/dependabot.yml # Monthly grouped dependency PRs (ARCH-012)
├── Dockerfile # Hardened multi-stage build (SEC-007/SCALE-004)
├── compose.yml # Resource limits for local testing (SCALE-006)
├── pyproject.toml
├── claude_desktop_config.json # Example config for Claude Desktop
├── CHANGELOG.md
├── ROADMAP.md # Phase plan + accepted-risk register
├── CONTRIBUTING.md # Contribution guide (English)
├── CONTRIBUTING.de.md # Contribution guide (German)
├── SECURITY.md # Security policy (English)
├── SECURITY.de.md # Security policy (German)
├── LICENSE
├── README.md # This file (English)
└── README.de.md # German version
ツールの出力形式
すべてのツールは、Markdown テキストではなく 構造化レスポンスエンベロープ を返すため、MCP
クライアントは直接解析できる structuredContent を受け取ります。
{
"source": "Kanton Zürich Rechtssammlung — HuggingFace … & zh.ch",
"provenance": "cache", // cache | live | parser | cache+parser | none
"result_type": "law_summaries", // law_summaries | law_detail | articles | metadata | cache_status
"count": 2,
"message": null, // human-readable guidance for empty/edge results
"results": [ /* typed items */ ]
}
既知の制限事項
- HuggingFace データセット:
html_contentフィールドは信頼性がありません(法律間で内容が混在しています)。サーバーでは代わりにpdf_contentを使用します。こちらは正確ですが、PDF の抽出に伴うアーティファクト(単語のハイフン分割、レイアウトの乱れ)があります。 - 条文パーサー: PDF のテキスト抽出では、条文の境界が結合されることがあります。複雑な入れ子構造の条文は、正しく解析できない場合があります。
- 初回読み込み: 初回起動時には、HuggingFace から974件の法律をダウンロードしてインデックス化するため、約25秒かかります(SQLite データベースは約38 MB)。
- zh.ch のメタデータ: 公式 API がありません。メタデータの抽出は、変更される可能性のある HTML パターンに依存しています。
- オフラインモード: 初回読み込み後は、オフラインでも全文検索を利用できます。リアルタイムのメタデータを取得するには、インターネット接続が必要です。
- コーパスは2023-01-01時点で固定されています。 これは法律関連サーバーにとって最も重要な制限ですが、これまで明記されていませんでした。データセット全体で最も新しいバージョンでも
version_active_since = 2023-01-01となっており、HuggingFace データセット自体の最終更新日は2024-10-10です。24時間のキャッシュ TTL とprovenance="cache"が示すのは回答の取得元であり、回答に含まれる法律がどれほど古いかではありません。現在、すべてのレスポンスにprovenanceと併せてcorpus_as_ofおよびcorpus_noteが含まれています。 - 基準日以降の廃止は反映されません。 974件すべてに
is_active = Trueが設定されており、version_inactive_sinceが設定されたものは一つもありません。これはサーバーの不具合ではありません。ソースには、スナップショットの時点で施行されていた法令だけが掲載されています。その結果が重要です。それ以降に廃止された法律も、現在有効であるかのように表示されます。現在施行されている本文については、ZH-Lex のパーマリンクを確認してください。 zhlaw_update_cacheを実行しても、法律の内容は新しくなりません。 同じ固定データセットを再ダウンロードするだけです。以前は docstring に「法律の検索結果が古いように見える場合にのみ呼び出してください」と記載されており、実際には起こらない効果を示唆していました。
安全性と制限事項
| 項目 | 詳細 |
|---|---|
| アクセス | 読み取り専用(readOnlyHint: true)— サーバーはデータを変更または削除できません |
| 個人データ | 個人データはありません — すべてのソースは集約された公開法令テキストです |
| レート制限 | クエリごとの上限を組み込み済みです(検索結果は最大50件、コンテンツのプレビューは最大5000文字) |
| タイムアウト | zh.ch への HTTP 呼び出しごとに30秒 |
| 送信先制限 | アウトバウンドリクエストは許可リストに制限されています(HTTPS 経由の www.zh.ch と、HTTP 専用の旧式パーマリンクホスト www.zhlex.zh.ch)。SSRF の IP ブロックと DNS ピンニングも行います。詳細は docs/network-egress.md を参照してください |
| 認証 | API キーは不要です — HuggingFace データセットは公開されており、zh.ch はオープンです |
| セキュリティ態勢(Lethal Trifecta) | スコア 1 / 3:公開データのみ(非公開データ・機密データなし)✓ · *.zh.ch への GET のみの送信 — POST、Webhook、メールなし ✓ · コード実行なし ✓。設計上、構造的に安全です。 |
| セッション管理 | Mcp-Session-Id は MCP SDK によって生成・管理されます(暗号学的に安全な UUID)。ユーザー ID との紐付けはありません — 公開の読み取り専用データでは auth_model=none が適切です。認証を追加する場合は、デプロイ前にセッションを検証済み OAuth sub クレームに紐付けてください。 |
| シークレット | シークレットは保持していません — すべてのデータソースは公開されています。詳細は docs/secret-management.md を参照してください。 |
| ライセンス | 法律データ:CC-BY-SA 4.0(rcds/swiss_legislation);zh.ch のメタデータ:公開 |
| 利用規約 | HuggingFace およびチューリッヒ州の利用規約が適用されます |
| 免責事項 | このサーバーは情報提供のみを目的として法令テキストを提供するものであり、法律上の助言を行うものではありません |
脆弱性の報告については、Security Policy を参照してください。
テスト
# Unit + contract tests (no network) — this is what CI runs
PYTHONPATH=src pytest tests/ -m "not live"
# Live tests against zh.ch and HuggingFace
PYTHONPATH=src pytest tests/ -m "live"
# Re-measure the corpus date and the live hosts
PYTHONPATH=src python scripts/record_fixtures.py
150 件のテスト — オフラインが 142 件、ライブが 8 件です。8 つのツールに対してライブテストが 8 件あり、このポートフォリオで最も優れたカバレッジです。だからこそ、ここでの指摘は仕組みの問題ではなく、2 つの問いの混同についてです。provenance="cache" は回答の「出所」を示し、corpus_as_of はその中の法律が「どれくらい古いか」を示します。答えられていたのは前者だけで、ユーザーが「これは最新ですか?」と尋ねるときに意味するのは後者です。
テストの編集では意図的に解決しない測定上の制限
記録環境では test_live_get_law_metadata が失敗します。この環境から zhlex.zh.ch に接続できないためです。ここから何も結論づけられません。 パブリック DNS ではホストが解決され(NOERROR、194.247.8.174)、NXDOMAIN の対照テストでクエリが識別能力を示しているため、制限はソースではなく環境にあります。
そのため、このテストには手を加えていません。自分のネットワークから外部に接続できないために赤くなるテストは、書き換えるべきテストではありません。書き換えると、ソースではなく自分の環境を測定することになります。PROVENANCE.md では、これを未解決事項として記録しています。
変更履歴
CHANGELOG.md を参照してください。
ロードマップ
ROADMAP.md を参照してください。
貢献
CONTRIBUTING.md を参照してください。
セキュリティ
SECURITY.md を参照してください。
ライセンス
MIT ライセンス — LICENSE を参照してください。
作者
Hayal Oezkan · malkreide
クレジットと関連プロジェクト
- データ: rcds/swiss_legislation — HuggingFace データセット(CC-BY-SA 4.0)
- ZH-Lex: zh.ch Gesetzessammlung — チューリッヒ州の公式法令集
- LexFind: lexfind.ch — 州をまたぐ法令データベース
- プロトコル: Model Context Protocol — Anthropic / Linux Foundation
- 関連: swiss-courts-mcp — 法令本文 + 判例法 = 完全な法律調査
- 関連: zurich-opendata-mcp — 法令本文 + 市議会の決定 = 全体像
- ポートフォリオ: Swiss Public Data MCP Portfolio
インストール
uv の uvx を使って実行できます。クローンや手動でのインストールは不要です。MCP クライアントの設定に追加してください(Claude Desktop、Cursor、Windsurf では mcpServers を使用し、VS Code では .vscode/mcp.json のトップレベルキー servers を使用します)。
{
"mcpServers": {
"openlex-mcp": {
"command": "uvx",
"args": [
"openlex-mcp"
]
}
}
}
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。