Skip to main content
Glama

πŸ‡¨πŸ‡­ Part of the Swiss Public Data MCP Portfolio

βš–οΈ openlex-mcp

Version License: MIT Python 3.11+ MCP No Auth Required

MCP Server for Canton Zurich legislation (ZH-Lex) β€” full-text search, article extraction, and education law tools for ~970 cantonal laws

πŸ‡©πŸ‡ͺ Deutsche Version


Overview

openlex-mcp provides AI-native access to the entire legal collection of Canton Zurich (ZΓΌrcher Gesetzessammlung). It combines full-text data from HuggingFace with live metadata from the official zh.ch website, storing everything in a local SQLite database with FTS5 full-text indexing for sub-50ms search performance.

Source

Data

Access

HuggingFace

974 ZH laws β€” full text (PDF extracts)

Cached locally as SQLite + FTS5

zh.ch ZH-Lex

Current metadata, PDF links, validity status

Live HTTP requests

Built for the Schulamt (school department) of the City of Zurich, but covers all areas of cantonal law β€” from tax law to building regulations.

Anchor demo query: "What does the Volksschulgesetz say about parental involvement? Show me Art. 55 VSG and find all articles that mention 'Elternrat'."


Related MCP server: swiss-courts-mcp

Features

  • βš–οΈ 8 tools covering search, retrieval, article extraction, and cache management

  • πŸ” FTS5 full-text search across ~970 cantonal laws with BM25 ranking

  • πŸ“‘ Article extraction β€” parse individual articles (Art. / Β§) with paragraph detection

  • 🏫 Education law shortcuts β€” specialized search for LS 412.x series (Volksschulgesetz, Lehrpersonalverordnung, etc.)

  • 🌐 Live metadata from zh.ch for current validity status and PDF links

  • πŸ’Ύ Hybrid architecture β€” cached full-text (HuggingFace) + live metadata (zh.ch)

  • πŸ”“ No API key required β€” all data under open licenses (CC-BY-SA 4.0)

  • ☁️ Dual transport β€” stdio (Claude Desktop) + Streamable HTTP (cloud)


Development Phase

Current phase: Phase 1 β€” Read-Only. All tools are read-only (readOnlyHint: true); no writes to external systems. See ROADMAP.md for the phase plan and transition gates before any write or multi-agent capability is added.


Prerequisites

  • Python 3.11+

  • uv (recommended) or pip

  • Internet connection (for initial data download and live metadata)


Installation

# 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 .

Quickstart

# 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

Network binding

By default the HTTP transport binds to 127.0.0.1 (localhost only). The host and port are configurable via the MCP_HOST / MCP_PORT environment variables (or the --host / --port CLI flags, which take precedence).

Never bind to 0.0.0.0 outside a container β€” it exposes the server to your local network (NeighborJack risk). For containerized/cloud deployments set MCP_HOST=0.0.0.0 explicitly; when that happens outside a detected container the server logs a warning.

Try it immediately in Claude Desktop:

"What is the Volksschulgesetz (VSG)?" "Find all Zurich laws about data protection" "Show me Art. 1 of the Volksschulgesetz" "Which education laws mention 'Schulleitung'?"


Configuration

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "openlex": {
      "command": "python",
      "args": ["-m", "openlex_mcp.server"]
    }
  }
}

Or with the installed entry point:

{
  "mcpServers": {
    "openlex": {
      "command": "openlex-mcp"
    }
  }
}

Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Cloud Deployment (SSE for browser access)

For use via claude.ai in the browser (e.g. on managed workstations without local software):

Render.com (recommended):

  1. Push/fork the repository to GitHub

  2. On render.com: New Web Service β†’ connect GitHub repo

  3. Set start command: python -m openlex_mcp.server --http --port 8000

  4. Set environment variable MCP_HOST=0.0.0.0 so the container is reachable (the code default is 127.0.0.1; Render sets the RENDER env var, so no NeighborJack warning is logged)

  5. Set MCP_CORS_ORIGINS=https://claude.ai so the browser can read the Mcp-Session-Id header (comma-separated list; no wildcard β€” defaults to empty, i.e. no cross-origin access)

  6. In claude.ai under Settings β†’ MCP Servers, add: https://your-app.onrender.com/sse

πŸ’‘ "stdio for the developer laptop, SSE for the browser."


Available Tools

Search & Browse

Tool

Description

openlex__zhlaw_search_laws

Full-text search across all ~970 ZH laws (FTS5 + BM25 ranking)

openlex__zhlaw_get_law

Retrieve a law by LS number (e.g. 412.100) or abbreviation (e.g. VSG)

openlex__zhlaw_list_laws

List and filter laws by legal area prefix

openlex__zhlaw_find_education_laws

Specialized search in education law (LS 412.x series)

Article Extraction

Tool

Description

openlex__zhlaw_get_article

Extract a specific article from a law (e.g. Art. 28 VSG)

openlex__zhlaw_search_articles

Search within all articles of a specific law

Metadata & Cache

Tool

Description

openlex__zhlaw_get_law_metadata

Get live metadata from zh.ch (PDF links, validity status)

openlex__zhlaw_update_cache

Refresh the local data cache from HuggingFace

Prefix

Legal Area

Example

131

Constitution and popular rights

Kantonsverfassung

170

Administrative procedure

Datenschutzgesetz

331

Tax law

Steuergesetz

412

Education and schools

Volksschulgesetz (VSG)

700

Spatial planning and building

Planungs- und Baugesetz

810

Health

Gesundheitsgesetz

Example Use Cases

Query

Tool

"What is the Volksschulgesetz?"

openlex__zhlaw_get_law

"Find laws about data protection"

openlex__zhlaw_search_laws

"Show me Art. 55 VSG"

openlex__zhlaw_get_article

"Which education laws mention Schulleitung?"

openlex__zhlaw_find_education_laws

"Find all articles about Elternrat in the VSG"

openlex__zhlaw_search_articles

"Is LS 412.100 still in force?"

openlex__zhlaw_get_law_metadata


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   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)            β”‚
                                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Source Characteristics

Source

Protocol

Coverage

Auth

License

HuggingFace rcds/swiss_legislation

Datasets API

974 ZH laws (full text)

None

CC-BY-SA 4.0

zh.ch ZH-Lex

HTTP/HTML

Current metadata, PDFs

None

Public

LexFind.ch

HTTP

Cross-cantonal links

None

Public

Design Decision: Tools-only (no MCP Resources)

All 8 endpoints are exposed as Tools rather than MCP Resources. Rationale:

  • Every lookup is parametric β€” queries, abbreviations, article numbers vary per call. Static Resources (one URI per document) don't capture this naturally.

  • The corpus is 974 laws Γ— many articles β€” registering each as a Resource URI would create an impractically large resource list.

  • MCP Resource templates (zhlex://laws/{sr_number}) are a future consideration for Phase 2 if clients benefit from resource-level caching or subscriptions.

Scaling Constraints

The Streamable-HTTP transport keeps session state in-process (FastMCP default). This has two implications:

  • Single-instance only β€” horizontal scaling (multiple replicas) breaks active sessions because there is no shared session store (Redis, Durable Objects, etc.).

  • No sticky-session LB needed today β€” a single-replica Render deployment naturally routes all requests to one process.

Before scaling beyond one instance: either add a shared session store or configure your edge load balancer to route on the Mcp-Session-Id header with a stick-table and an appropriate TTL.


MCP Protocol Version

Item

Value

Served via the initialize handshake

2024-11-05 … 2025-11-25 β€” the handshake ceiling

Served via the per-request envelope

2026-07-28

Who picks

The client's first request, once per connection. A request carrying the 2026-07-28 _meta envelope opens a modern connection; anything else opens a handshake connection.

SDK

mcp[cli]>=2.0.0,<3

Cache hints

tools/list and server/discover: ttlMs 300000, cacheScope public

Pinned in

src/openlex_mcp/server.py β€” MCP_PROTOCOL_VERSION constant

Update policy

  1. When mcp is upgraded (via Dependabot PR), verify the protocol version in the SDK release notes.

  2. If the protocol version changes, update MCP_PROTOCOL_VERSION in server.py, regenerate docs/tool-hashes.json (PYTHONPATH=src python scripts/gen_tool_hashes.py --write), and note the change in CHANGELOG.md.

  3. Run pytest tests/ -m "not live" to confirm compatibility before merging.


Project Structure

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       # Weekly 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

Tool output format

All tools return a structured response envelope (not Markdown text), so MCP clients receive structuredContent they can parse directly:

{
  "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 */ ]
}

Known Limitations

  • HuggingFace dataset: The html_content field is unreliable (cross-contaminated between laws); the server uses pdf_content instead, which is correct but has PDF extraction artefacts (hyphenation, layout artefacts)

  • Article parser: PDF text extraction sometimes merges article boundaries; complex nested articles may not parse perfectly

  • Initial load: First start requires ~25s to download and index 974 laws from HuggingFace (~38 MB SQLite database)

  • zh.ch metadata: No official API; metadata extraction relies on HTML patterns that may change

  • Offline mode: Full-text search works offline after initial load; live metadata requires internet

  • The corpus is frozen at 2023-01-01. This is the limitation that matters most for a legal server, and it was the one not stated. The newest version in the entire dataset carries version_active_since = 2023-01-01; the HuggingFace dataset itself was last touched 2024-10-10. The 24-hour cache TTL and provenance="cache" describe where an answer came from, not how old the laws in it are. Every response now carries corpus_as_of and corpus_note alongside provenance.

  • Repeals after the cut-off are invisible. All 974 entries carry is_active = True and not one has a version_inactive_since. That is not a server bug β€” the source lists only the statutes in force at snapshot time. The consequence is what matters: a law repealed since then still appears to be in force. Consult the ZH-Lex permalink for the operative text.

  • zhlaw_update_cache does not make the laws newer. It re-downloads the same frozen dataset. Its docstring previously read "only call when law search results seem outdated", which suggested exactly the effect it does not have.


Safety & Limits

Aspect

Details

Access

Read-only (readOnlyHint: true) β€” the server cannot modify or delete any data

Personal data

No personal data β€” all sources are aggregated, public legal texts

Rate limits

Built-in per-query caps (max 50 search results, 5000 chars content preview)

Timeout

30 seconds per HTTP call to zh.ch

Egress

Outbound requests are restricted to an allow-list (www.zh.ch over HTTPS, plus the HTTP-only legacy permalink host www.zhlex.zh.ch), with SSRF IP-blocking and DNS-pinning β€” see docs/network-egress.md

Authentication

No API keys required β€” HuggingFace dataset is public, zh.ch is open

Security posture (Lethal Trifecta)

Score 1 / 3: public data only (no private/sensitive data) βœ“ Β· GET-only egress to *.zh.ch β€” no POST, no webhooks, no email βœ“ Β· no code execution βœ“. Structurally safe by design.

Session handling

Mcp-Session-Id generated and managed by the MCP SDK (cryptographically secure UUIDs). No user-identity binding β€” auth_model=none is correct for public read-only data. If authentication is ever added, bind sessions to the validated OAuth sub claim before deployment.

Secrets

No secrets held β€” all data sources are public. See docs/secret-management.md.

Licenses

Law data: CC-BY-SA 4.0 (rcds/swiss_legislation); zh.ch metadata: public

Terms of Service

Subject to ToS of HuggingFace and Canton Zurich

Disclaimer

This server provides legal texts for informational purposes only β€” it does not constitute legal advice

To report a vulnerability, see the Security Policy.


Testing

# 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 tests β€” 142 offline, 8 live. Eight tools, eight live tests: the best coverage in this portfolio, which is why the finding here is not about mechanics but about a confusion between two questions. provenance="cache" answers where an answer came from; corpus_as_of answers how old the laws in it are. Only the first was ever answered, and the second is the one a user means when they ask "is this current?".

A measurement limit, deliberately not resolved by editing a test

test_live_get_law_metadata fails in the recording environment: zhlex.zh.ch is not reachable from it. Nothing follows from that. Public DNS resolves the host (NOERROR, 194.247.8.174) and an NXDOMAIN control shows the query discriminates β€” so the limit is the environment's, not the source's.

The test was therefore left untouched. A test you see red because your own network cannot get out is not a test to rewrite; rewriting it would leave you measuring your own environment instead of the source. PROVENANCE.md records this as open.


Changelog

See CHANGELOG.md


Roadmap

See ROADMAP.md


Contributing

See CONTRIBUTING.md


Security

See SECURITY.md


License

MIT License β€” see LICENSE


Author

Hayal Oezkan Β· malkreide


Installation

Run via uv's uvx β€” no clone or manual install needed. Add to your MCP client config (mcpServers for Claude Desktop, Cursor and Windsurf; use a top-level servers key for VS Code in .vscode/mcp.json):

{
  "mcpServers": {
    "openlex-mcp": {
      "command": "uvx",
      "args": [
        "openlex-mcp"
      ]
    }
  }
}

Available Tools

8 tools
openlex__zhlaw_find_education_lawsA
Read-onlyIdempotent

Sucht gezielt im ZΓΌrcher Bildungsrecht (Ordnungsnummern 412.x).

Bevorzuge dieses Tool gegenΓΌber openlex__zhlaw_search_laws wenn die Anfrage klar im Bildungsbereich liegt (Schule, Lehrpersonen, Kindergarten, SonderpΓ€dagogik, Tagesstrukturen). Schneller und prΓ€ziser als die allgemeine Suche, da nur die 412.x-Serie (VSG, VSV, LPG, LPVO, VSM u.a.) durchsucht wird.

Sucht ausschliesslich in aktiven 412.x-Gesetzen. Bei keinen Treffern: automatischer Fallback auf alle Rechtsgebiete mit Hinweis im message- Feld. FΓΌr Artikel innerhalb eines gefundenen Gesetzes: openlex__zhlaw_search_articles. Synergie: Fundstelle + swiss-courts-mcp β†’ Rechtsprechung finden.

query='Elternrat', limit=10

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as readOnlyHint, idempotentHint, and not destructive. The description adds valuable context: it searches only active 412.x laws and if no results, automatically falls back to all legal areas with a note in the message field. There is no contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (<use_case>, <important_notes>, <example>), front-loads the main purpose, and uses concise language. Every sentence adds value, and the example illustrates usage efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (search with fallback) and the presence of an output schema (though not shown), the description covers key aspects: scope, fallback behavior, related tools, and synergy with external MCP. It is comprehensive for a search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has a detailed description for the query parameter, but the tool description does not repeat it. However, the description provides an example (query='Elternrat', limit=10) and context for typical queries (e.g., 'Kindergarten', 'Schulleitungen'), which adds meaning beyond the schema. Given low schema description coverage (0% per signals), the description compensates adequately but could elaborate more.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it searches specifically in Zurich education law (Ordnungsnummern 412.x) and distinguishes itself from sibling tools like openlex__zhlaw_search_laws by being faster and more precise for education-related queries. It also lists specific laws covered (VSG, VSV, LPG, etc.), making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The <use_case> tag explicitly states when to prefer this tool over sibling openlex__zhlaw_search_laws (e.g., when query is education-related). It also provides conditions for use (school, teachers, kindergarten, etc.) and mentions fallback behavior to all legal areas with a message hint. Additionally, it advises using other tools for articles and synergy with swiss-courts-mcp.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_get_articleA
Read-onlyIdempotent

Extrahiert einen einzelnen Artikel aus einem ZΓΌrcher Gesetz.

Wenn der genaue Wortlaut eines bestimmten Artikels benΓΆtigt wird. Voraussetzung: Gesetz und Artikelnummer sind bekannt. Um zuerst relevante Artikel zu finden: openlex__zhlaw_search_articles nutzen.

UnterstΓΌtzt Standard-Artikelnummern (28), Buchstaben-Artikel (28a) und Bis-Artikel (28bis). Liefert Titel, alle AbsΓ€tze und Volltext des Artikels. Gibt count=0 zurΓΌck wenn der Artikel nicht existiert β€” Artikelnummer ohne 'Art.' angeben (nur die Zahl).

law_identifier='VSG', article_number='28'

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations (readOnlyHint=true, idempotentHint=true, destructiveHint=false) already indicate safety. Description adds specifics: supports standard/letter/bis article numbers, returns title/paragraphs/full text, returns count=0 if not found, and format requirements like omitting 'Art.' prefix.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Structured with XML tags (use_case, important_notes, example) that organize information efficiently. Every sentence is necessary, no fluff, and front-loaded with the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter retrieval tool, the description covers all needed context: what it retrieves, error behavior (count=0), and format requirements. Output schema exists, so return value details are not required.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema descriptions already cover parameter formats and examples. Tool description adds a concrete example but no new semantic information beyond the schema, which already specifies 'Ohne 'Art.' Prefix' and examples.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states 'Extrahiert einen einzelnen Artikel aus einem ZΓΌrcher Gesetz' with a use case that clarifies when to use it and distinguishes it from sibling tool openlex__zhlaw_search_articles for searching.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly includes a <use_case> block describing when to use (exact wording needed, known identifiers) and directs to openlex__zhlaw_search_articles for finding articles first.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_get_lawA
Read-onlyIdempotent

Ruft ein ZΓΌrcher Gesetz anhand der Ordnungsnummer oder AbkΓΌrzung ab.

Wenn die Ordnungsnummer oder AbkΓΌrzung eines Gesetzes bereits bekannt ist und Metadaten oder Volltext abgerufen werden sollen. Um ein Gesetz erst zu finden, openlex__zhlaw_search_laws oder openlex__zhlaw_find_education_laws vorschalten.

UnterstΓΌtzt LS-Nummern ('412.100') und AbkΓΌrzungen ('VSG'). include_content=True liefert den Volltext (bis 5000 Zeichen, dann truncated=True). Bei Truncation: openlex__zhlaw_get_article fΓΌr einzelne Artikel verwenden. Wichtige Gesetze: VSG, LPG, PBG, StG, KV.

identifier='VSG', include_content=False

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds behavioral details beyond annotations: include_content returns up to 5000 characters with truncation flag; lists important laws. Annotations already indicate readOnly and idempotent, no contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (use_case, important_notes, example). Every sentence adds value, no fluff. Purpose stated in first line.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (1 required param, no nested objects, output schema exists), the description covers all necessary aspects: input format, behavior of include_content, truncation handling, and related tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema descriptions already cover both parameters (identifier and include_content). Description adds practical examples and known abbreviations, enhancing usability beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves a Zurich law by identifier (Ordnungsnummer or Abbreviation), and distinguishes it from sibling tools like openlex__zhlaw_search_laws for finding laws.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit use case: when identifier is known. Provides when-not-to-use (use search_laws or find_education_laws to find) and alternatives for truncated content (openlex__zhlaw_get_article).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_get_law_metadataA
Read-onlyIdempotent

Ruft aktuelle Metadaten eines Gesetzes live von zh.ch ab.

Wenn der aktuelle Stand eines Gesetzes auf zh.ch geprΓΌft werden soll β€” z.B. ob es kΓΌrzlich geΓ€ndert wurde, welche PDF-Version aktuell gilt oder welche ZH-Lex URL direkt verlinkt werden kann. Einziges Tool mit Live-HTTP-Aufruf; alle anderen Tools lesen den lokalen Cache.

Macht einen echten HTTP-Request an www.zh.ch (ca. 1–3 s). Erfordert Netzwerkzugang. Liefert: Seitentitel, PDF-Links, Γ„nderungsdatum, ZH-Lex URL. Bei 404 oder Timeout: found=False mit Fehlerdetail im error-Feld. Nur Ordnungsnummern akzeptiert (kein AbkΓΌrzungs-Lookup).

sr_number='412.100'

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint, openWorldHint, idempotentHint, destructiveHint=false. Description adds details: real HTTP request (1-3s), required network access, return fields, error handling (404/Timeout). No contradictions. Slightly above baseline because description adds performance and error context not in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is well-structured with clear sections (use_case, important_notes, example). Every sentence adds value. No fluff. Two short paragraphs with XML-like tags make it easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one parameter and an output schema, the description covers purpose, usage, behavioral details, parameter format, error handling, and constraints. Complete and self-contained. Sibling tools are diverse, and this description clearly differentiates.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% per context, but the schema itself includes a description for the single parameter sr_number with example and format. The tool description includes an additional example. Since schema already provides parameter meaning, description adds minimal extra value. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states it retrieves current metadata of a law live from zh.ch. It distinguishes from siblings by noting it's the only tool with a live HTTP call, while others use local cache. Verb+resource is specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Use_case section explicitly describes when to use: checking current status, recent changes, PDF version, ZH-Lex URL. It states it's the only live-HTTP tool, implying alternatives. Important_notes specify input constraints (only Ordnungsnummer, no abbreviation lookup).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_list_lawsA
Read-onlyIdempotent

Listet ZΓΌrcher Gesetze auf mit optionalem Filter nach Rechtsgebiet.

Wenn eine strukturierte Übersicht aller Gesetze eines Rechtsgebiets benâtigt wird (z.B. alle aktiven Bildungsgesetze). Für gezielte Textsuche ist openlex__zhlaw_search_laws besser; für reines Bildungsrecht openlex__zhlaw_find_education_laws.

sr_prefix filtert nach Ordnungsnummer-Prefix (412=Bildung, 331=Steuern, 700=Bau, 131=Verfassung, 810=Gesundheit). active_only=True blendet aufgehobene Gesetze aus. UnterstΓΌtzt Paginierung via offset. Gibt Titel, AbkΓΌrzung, SR-Nummer und Status zurΓΌck β€” keinen Volltext.

sr_prefix='412', active_only=True, limit=50

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint, idempotentHint, destructiveHint. The description adds context about sr_prefix examples, active_only filtering, pagination via offset, and return fields (no full text), which is valuable beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with use_case, important_notes, and example tags. It is concise, front-loaded with the main purpose, and every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given complexity (4 sub-params), output schema exists, and annotations are present, the description covers when to use, parameter details, return fields, and sibling differentiation. It is complete for an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds concrete examples for sr_prefix (412=Bildung, etc.) and explains active_only and pagination. The schema itself has decent descriptions, but the description enhances understanding with real-world usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists Zurich laws with optional filter, using specific verb 'auflisten' and resource 'ZΓΌrcher Gesetze'. It distinguishes itself from siblings by referencing use cases for alternative tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The use_case tag explicitly states when to use (structured overview) and when not (targeted text search or pure education law), naming alternative tools search_laws and find_education_laws.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_search_articlesA
Read-onlyIdempotent

Durchsucht alle Artikel eines bestimmten Gesetzes nach einem Begriff.

Wenn bekannt ist, in welchem Gesetz gesucht werden soll, aber nicht welcher Artikel relevant ist. Liefert alle Treffer-Artikel mit Inhalt. FΓΌr einen einzelnen Artikel mit bekannter Nummer: openlex__zhlaw_get_article.

Parst das Gesetz in einzelne Artikel und durchsucht Titel und Inhalt. Suche ist case-insensitive, kein FTS5 (einfaches Substring-Match). Gibt count=0 wenn kein Artikel den Begriff enthΓ€lt. BenΓΆtigt Volltext im Cache β€” bei fehlendem Inhalt: Hinweis im message-Feld.

law_identifier='VSG', query='Elternrat'

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses important behavioral traits such as case-insensitive substring matching, no FTS5, parsing law into articles, and cache requirements. These add significant value beyond the annotations which only indicate read-only and idempotent hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured with clear sections (<use_case>, <important_notes>, <example>). Every sentence adds value without unnecessary verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema and the comprehensive description covering purpose, usage, behavior, and an example, the description is complete enough for an AI agent to understand and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides descriptions for both parameters. The description adds an example usage and reinforces the meaning, but does not introduce new semantics beyond what is in the schema. However, the example is helpful for clarifying the intended use.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool searches all articles of a specific law for a term, using a specific verb and resource. It distinguishes itself from sibling tools by noting when to use this tool vs. openlex__zhlaw_get_article.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly provides a use case ('when the law is known but the article is not') and an alternative ('for a specific article number, use openlex__zhlaw_get_article'). This gives clear guidance on when to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_search_lawsA
Read-onlyIdempotent

Volltextsuche in allen ZΓΌrcher Gesetzen mit FTS5-Ranking.

Allgemeine Suche nach einem Rechtsbegriff ΓΌber alle ~970 kantonalen Gesetze. WΓ€hle dieses Tool wenn das Rechtsgebiet unbekannt ist. FΓΌr das Bildungsrecht (412.x) ist openlex__zhlaw_find_education_laws schneller und prΓ€ziser. Um Artikel innerhalb eines bekannten Gesetzes zu finden, nutze openlex__zhlaw_search_articles.

FTS5-Syntax: AND, OR, NOT, Phrasensuche "...". Ergebnisse nach BM25-Relevanz sortiert. Max 50 Treffer pro Aufruf (limit-Parameter). sr_prefix filtert nach Rechtsgebiet (412=Bildung, 331=Steuern, 700=Bau). Sucht im lokalen Cache β€” AktualitΓ€t der Metadaten via openlex__zhlaw_get_law_metadata prΓΌfen.

query='Elternrat OR Elternmitwirkung', sr_prefix='412', limit=10

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint, idempotentHint, destructiveHint), the description adds crucial behavioral details: FTS5 syntax (AND, OR, NOT, phrase search), BM25 relevance sorting, max 50 results, local caching, and a note to check metadata freshness via openlex__zhlaw_get_law_metadata. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is succinct and well-structured using <use_case>, <important_notes>, and <example> tags. Each section adds necessary information without redundancy. The first sentence immediately conveys the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 parameters, an output schema, annotations), the description covers use cases, important notes, and an example. It explains caching behavior and when to check metadata freshness. The output schema is present, so return values need not be described.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema coverage at 0%, the description meaningfully explains the query, limit, and sr_prefix parameters, including FTS5 syntax examples and how to filter by legal area. It does not mention the active_only parameter, but the other parameters are well-covered with practical guidance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool does a full-text search across all ~970 Zurich cantonal laws using FTS5 ranking. It explicitly distinguishes itself from sibling tools: openlex__zhlaw_find_education_laws for the education law area and openlex__zhlaw_search_articles for finding articles within a known law.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The <use_case> tag explicitly states when to use this tool (when the legal area is unknown) and when to use alternatives (for education law or within a known law). It names specific sibling tools and provides clear context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

openlex__zhlaw_update_cacheA
Idempotent

Aktualisiert den lokalen Cache der ZΓΌrcher Gesetzesdaten.

Nur aufrufen wenn Gesetzes-Suchergebnisse veraltet wirken oder der Cache explizit neu geladen werden soll. Der Cache wird automatisch beim Start befΓΌllt und ist 24 Stunden gΓΌltig β€” manuelles Update ist selten nΓΆtig.

LΓ€dt ~970 Gesetze von HuggingFace (rcds/swiss_legislation) in die lokale SQLite-DB mit FTS5-Index (~25 s erster Lauf). force=False ΓΌberspringt den Download wenn Cache <24h alt (gibt status='cache_fresh' zurΓΌck). force=True erzwingt Neudownload. Erfordert Internetzugang zu HuggingFace.

force=False

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
sourceNo
messageNo
resultsNo
provenanceYes
result_typeNo

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (idempotent, not destructive), the description adds key behaviors: downloads ~970 laws from HuggingFace, takes ~25s first run, force=False skips if cache fresh returning 'cache_fresh', force=True forces download, requires internet. This fully informs the agent of side effects and state outcomes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured: short introductory sentence, then use_case, important_notes (with timings and behavior), and a clear example. Every sentence serves a purpose, and it's front-loaded with the core action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter update action, the description covers usage, behavior, and output snippet. An output schema exists, so detailed return structure is not needed. Minor gap: no mention of error handling or internet failure, but still complete enough for typical use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single boolean parameter 'force' is described in the schema as 'Cache auch wenn aktuell (<24h) erzwingen.' The tool description adds explicit return value behavior (status='cache_fresh' when skipped) and the time context. This adds meaningful nuance beyond the schema's brief description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it updates the local cache of Zurich law data. The use_case explicitly tells when to call: when search results seem outdated or cache needs reloading. Sibling tools are all query tools, so this stands out as a cache management action.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit context: cache auto-refreshes every 24h, manual update rarely needed, and condition for calling (outdated results). However, it does not name specific alternative tools for when the cache should not be called, though siblings are clearly different in purpose.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv0.2.3
    • First observedopenlex__zhlaw_find_education_laws
    • First observedopenlex__zhlaw_get_article
    • First observedopenlex__zhlaw_get_law
    • First observedopenlex__zhlaw_get_law_metadata
    • First observedopenlex__zhlaw_list_laws
    • First observedopenlex__zhlaw_search_articles
    • First observedopenlex__zhlaw_search_laws
    • First observedopenlex__zhlaw_update_cache

TDQS

A4.8/5.0

Scored across 8 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: full-text search across all laws, retrieval by number/abbreviation, article extraction, listing, specialized education search, article search within a law, live metadata, and cache update. The use case guidance in descriptions eliminates ambiguity.

Naming Consistency5/5

All tool names follow a consistent pattern: openlex__zhlaw_<verb>_<noun> (e.g., search_laws, get_law, get_article). The verb-noun structure is uniform across all tools, using standard verbs like search, get, list, find, update.

Tool Count5/5

8 tools is well-scoped for Zurich cantonal law: covers search, retrieval, listing, specialized search, metadata, and cache management. Neither too few nor too many, each tool earns its place.

Completeness5/5

The tool set covers the full lifecycle of legal retrieval: searching across laws, finding specific articles, retrieving full text, listing laws by area, getting live metadata, and managing cache. No obvious gaps for a read-only legal database.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers