关于 openlex-mcp
openlex-mcp 为 MCP 客户端提供苏黎世州约 970 部法律的查询与条文提取,结合本地全文索引和 zh.ch 实时元数据。适用于法律检索及教育法规查询。
主要功能
- 01全文检索并按相关性排序
- 02按编号或简称获取法规
- 03提取条文并搜索条文内容
- 04专用教育法规检索
- 05获取实时有效状态和 PDF 链接
适用场景
- 01查找有关数据保护的法规
- 02检索 Volksschulgesetz 指定条文
- 03查找提及特定词语的教育法规条文
MCP 相关
以 MCP 服务形式向客户端提供法规搜索、获取、条文提取、元数据和缓存管理工具,支持 stdio 与 Streamable HTTP。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
⚖️ openlex-mcp
苏黎世州立法(ZH-Lex)的 MCP 服务——为约 970 部州级法律提供全文搜索、条文提取和教育法工具
概述
openlex-mcp 提供对苏黎世州全部法律汇编(Zürcher Gesetzessammlung)的 AI 原生访问。它结合来自 HuggingFace 的全文数据与瑞士联邦网站 zh.ch 的实时元数据,并将所有内容存储在本地 SQLite 数据库中,通过 FTS5 全文索引实现低于 50 ms 的搜索性能。
| 来源 | 数据 | 访问方式 |
|---|---|---|
| HuggingFace | 974 部 ZH 法律——全文(PDF 提取内容) | 在本地缓存为 SQLite + FTS5 |
| zh.ch ZH-Lex | 当前元数据、PDF 链接、有效状态 | 实时 HTTP 请求 |
本项目为苏黎世市教育局(Schulamt)打造,但涵盖州级法律的所有领域——从税法到建筑法规。
演示示例查询: “《义务教育法》(Volksschulgesetz)对家长参与有何规定?请展示 VSG 第 55 条,并找出所有提及‘Elternrat’的条文。”
功能
- ⚖️ 8 个工具,涵盖搜索、检索、条文提取和缓存管理
- 🔍 FTS5 全文搜索,覆盖约 970 部州级法律,并采用 BM25 排序
- 📑 条文提取——解析单条条文(Art. / §),并检测段落
- 🏫 教育法快捷工具——针对 LS 412.x 系列提供专门搜索(《义务教育法》、教师条例等)
- 🌐 来自 zh.ch 的实时元数据,提供当前有效状态和 PDF 链接
- 💾 混合架构——缓存全文(HuggingFace)+ 实时元数据(zh.ch)
- 🔓 无需 API 密钥——所有数据均采用开放许可(CC-BY-SA 4.0)
- ☁️ 双传输方式——stdio(Claude Desktop)+ Streamable HTTP(云端)
开发阶段
当前阶段:第 1 阶段——只读。 所有工具均为只读(readOnlyHint: true);不会向外部系统写入数据。有关阶段计划及添加任何写入或多 Agent 功能前的转换门槛,请参阅 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 中试用:
“什么是《义务教育法》(VSG)?” “查找所有关于数据保护的苏黎世法律” “请展示《义务教育法》第 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,或在 GitHub 上派生仓库
- 在 render.com 上:新建 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_CORS_ORIGINS=https://claude.ai,以便浏览器读取Mcp-Session-Id标头(以逗号分隔的列表;不能使用通配符——默认值为 空,即不允许跨域访问) - 在 claude.ai 的“Settings → MCP Servers”下,添加:
https://your-app.onrender.com/sse
💡 “面向开发者笔记本电脑使用 stdio,面向浏览器使用 SSE。”
可用工具
搜索与浏览
| 工具 | 描述 |
|---|---|
openlex__zhlaw_search_laws | 在全部约 970 部 ZH 法律中进行全文搜索(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 |
| “显示 Art. 55 VSG” | 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 部 ZH 法律(全文) | 无 | CC-BY-SA 4.0 |
| zh.ch ZH-Lex | HTTP/HTML | 当前元数据、PDF | 无 | 公共 |
| LexFind.ch | HTTP | 跨州链接 | 无 | 公共 |
设计决策:仅使用工具(不使用 MCP 资源)
全部 8 个端点都作为 工具 暴露,而不是 MCP 资源。原因如下:
- 每次查询都是参数化的——查询内容、缩写和条文编号会因调用而异。静态资源(每份文档对应一个 URI)无法自然地适应这一点。
- 语料库包含 974 部法律 × 众多条文——若将每部法律都注册为资源 URI,资源列表会大到不切实际。
- MCP 资源模板(
zhlex://laws/{sr_number})可作为 Phase 2 的未来考虑,前提是客户端能从资源级缓存或订阅中受益。
扩展限制
限制取决于客户端使用的协议时代:
2026-07-28(现代)——每个请求都是一个独立的 POST,携带自己的_meta信封;服务端无需发送Mcp-Session-Id即可响应,并且不会在请求之间保留会话。任意副本都可以处理任意请求;无需共享存储,也无需固定路由。(subscriptions/listen流会留在开启它的副本上——此服务的列表在导入时即已固定,因此无需向其他副本转发。)- 握手时代(
initialize,截至2025-11-25)——会话状态保存在进程内。多个副本会破坏活动会话,因为没有共享会话存储(Redis、Durable Objects 等)。单副本 Render 部署会将所有请求路由到同一进程,因此目前不需要粘性会话负载均衡器。
在将握手时代的客户端扩展到多个实例之前:要么添加共享会话存储,要么配置边缘负载均衡器,根据 Mcp-Session-Id 标头使用粘性表和适当的 TTL 进行路由。
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 文本提取有时会合并条文边界;复杂的嵌套条文可能无法完美解析
- 首次加载: 首次启动需要约 25 秒从 HuggingFace 下载并索引 974 部法律(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不会更新法律。 它会重新下载同一份冻结的数据集。其文档字符串之前写着“仅在法律搜索结果似乎过时时调用”,这恰好暗示了它并不具备的效果。
安全与限制
| 方面 | 详情 |
|---|---|
| 访问权限 | 只读(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 开放访问 |
| 安全态势(致命三要素) | 得分 1 / 3:仅公开数据(无私人或敏感数据)✓ · 仅向 *.zh.ch 发起 GET 出站请求——无 POST、无 Webhook、无电子邮件 ✓ · 不执行代码 ✓。从结构上而言,设计安全。 |
| 会话处理 | Mcp-Session-Id 由 MCP SDK 生成和管理(使用密码学安全的 UUID)。不绑定用户身份——对于公开的只读数据,auth_model=none 是正确的。如果将来添加身份验证,请在部署前将会话绑定到经过验证的 OAuth sub 声明。 |
| 机密信息 | 不持有机密信息——所有数据源均为公开数据。参见 docs/secret-management.md。 |
| 许可协议 | 法律数据:CC-BY-SA 4.0(rcds/swiss_legislation);zh.ch 元数据:公开 |
| 服务条款 | 受 HuggingFace 和 苏黎世州 的服务条款约束 |
| 免责声明 | 本服务仅出于信息目的提供法律文本,不构成法律建议 |
如需报告漏洞,请参阅安全政策。
测试
# 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 项实时测试。八个工具,八项实时测试:这是本项目组合中覆盖率最高的,因此这里发现的问题与运行机制无关,而在于混淆了两个问题。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
贡献
安全
参见 SECURITY.md
许可证
MIT License——参见 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 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。