Skip to main content

kio-orzeczenia-mcp

MCP server (Model Context Protocol) for the case law of the Krajowa Izba Odwolawcza (KIO) (National Appeals Chamber) at the Urzad Zamowien Publicznych (Public Procurement Office) - the public database orzeczenia.uzp.gov.pl.

It lets Claude / Cursor / VS Code MCP agents consume KIO rulings with verifiable citations (signature + URL + date).

Status: POC v0.1.0 | License: Apache-2.0 | Maintainer: MateMatic

Preliminary warning. The v0.1.0 connector is a proof-of-concept release. It fetches data from the public UZP case law database via HTML (no official REST API). Full legal disclaimer - see the "Legal disclaimer" section below. A dedicated smoke test and notification to UZP are required before production deployment.


What KIO is

The Krajowa Izba Odwolawcza (National Appeals Chamber) is an administrative (quasi-judicial) body operating at the Urzad Zamowien Publicznych (Public Procurement Office) - KIO members are independent when adjudicating (art. 471 PZP) - which hears appeals against contracting authorities' decisions in public procurement proceedings (the Act of 11 September 2019 - Public Procurement Law). KIO rulings are made publicly available by UZP under the Act on access to public information.

KIO rulings are not a source of law within the meaning of art. 87 of the Constitution of the Republic of Poland - they are reference material widely used in the practice of law firms dealing with public procurement.

Quickstart

# Clone and enter the directory
git clone https://github.com/matematicsolutions/kio-orzeczenia-mcp.git
cd kio-orzeczenia-mcp

# Virtualenv
python -m venv .venv
.\.venv\Scripts\Activate.ps1

# Install
pip install -e ".[dev]"

# Offline test (signature parser)
pytest tests/test_signature.py -v

# Smoke test (online - hits UZP, rate-limited 1 req/s)
pytest tests/test_smoke.py -v -m smoke

# Run the server (stdio)
python -m kio_orzeczenia_mcp.server

Wiring into Claude Code

Add to ~/.claude.json (or .mcp.json in the project):

{
  "mcpServers": {
    "kio-orzeczenia": {
      "command": "python",
      "args": ["-m", "kio_orzeczenia_mcp.server"],
      "env": {
        "KIO_MCP_RATE_LIMIT": "1.0",
        "KIO_MCP_CACHE_DIR": "~/.matematic/cache/kio"
      }
    }
  }
}

Restart Claude Code. After startup, 5 tools should be visible.

Windows 11 with Smart App Control

Smart App Control blocks unsigned executables, which covers uvx.exe, pip.exe and the kio-orzeczenia-mcp.exe launcher that pip writes at install time. The python.exe and py.exe from the python.org installer are signed by the Python Software Foundation, so running the module through the interpreter works:

python -m pip install kio-orzeczenia-mcp
python -m kio_orzeczenia_mcp

pip.exe is blocked for the same reason, so install with python -m pip, not pip install. If python is not on PATH, use the Windows launcher: py -3 -m kio_orzeczenia_mcp.

{ "mcpServers": { "kio-orzeczenia-mcp": { "command": "python", "args": ["-m", "kio_orzeczenia_mcp"] } } }

Do not turn Smart App Control off to work around this - it cannot be re-enabled without reinstalling Windows.

6 MCP tools

1. kio_search(query: SearchQuery) -> SearchResult

Search over KIO case law. All fields optional.

// Arguments:
{
  "phrase": "razaco niska cena",
  "signature": null,
  "date_from": "2024-01-01",
  "date_to": "2024-12-31",
  "pzp_article": "226",
  "subject_index": null,
  "inflection": true,
  "page": 1,
  "size": 20
}

Returns {total, page, items: [OrzeczenieSummary]}.

2. kio_get_orzeczenie(signature_or_id: str | int) -> Orzeczenie

Fetches the full text of a single ruling.

// Arguments:
"KIO 2924/21"   // string -> first search by signature to resolve internal_id (+1 req)
15903           // int -> directly GET /Home/Details/15903 + /Home/ContentHtml/15903

Returns the full Orzeczenie with content_text, sentence, reasoning (if the parser can extract them), pzp_articles, subject_index, doc_type, outcome, chamber_composition, parties.

issue_date may be null - UZP has no issue date for some older records. We do not substitute a placeholder date.

3. kio_recent(days: int = 30, limit: int = 20) -> list[OrzeczenieSummary]

The most recent rulings from the last N days, sorted by date descending.

// Arguments:
{ "days": 30, "limit": 20 }

4. kio_by_pzp_article(article: str, limit: int = 20) -> list[OrzeczenieSummary]

Rulings citing a specific PZP article. Uses the UZP server-side Art filter, which matches the provisions dictionary and is format-sensitive ("art. 226 ust. 1 pkt 5" hits, plain "226" does not). On an empty result the tool falls back to a full-text phrase search.

// Arguments:
{ "article": "art. 226 ust. 1 pkt 5", "limit": 20 }
{ "article": "224 ust. 1", "limit": 50 }   // "art. " prefix added automatically

5. kio_get_pdf_url(signature_or_id: str | int) -> dict

Returns the URL to the PDF (rendered by UZP from .docx via Qt 4.8.7). Does not fetch bytes - we link to it.

// Returns:
{
  "pdf_url": "https://orzeczenia.uzp.gov.pl/Home/PdfContent/15903?Kind=KIO",
  "signature": "KIO 2924/21",
  "internal_id": 15903,
  "human_readable_citation": "Wyrok KIO z 2021-10-28, sygn. KIO 2924/21"
}

6. kio_coverage() -> Coverage

Declares what this connector covers, where it comes from, and what it does NOT cover. Every gap carries a stable id and a fallback. Call it before telling a user a ruling "does not exist" - the absence may be a gap in this connector, not in KIO case law.

3 usage examples (natural language)

Example 1: "KIO rulings on abnormally low price from the past year"

The agent calls kio_search:

{
  "phrase": "razaco niska cena",
  "date_from": "2025-05-20",
  "date_to": "2026-05-20",
  "inflection": true,
  "size": 50
}

Result: a list of OrzeczenieSummary with human_readable_citation ready to insert into a court filing.

Example 2: "KIO rulings citing art. 226 sec. 1 point 5 PZP"

The agent calls kio_by_pzp_article:

{ "article": "226 ust. 1 pkt 5", "limit": 30 }

Example 3: "Ruling KIO 2924/21 - who were the parties and was the appeal upheld"

The agent calls kio_get_orzeczenie:

"KIO 2924/21"

Result: the full Orzeczenie with parties, sentence, reasoning.


Limitations (POC)

  1. Mapping signature -> internal_id requires a search - if you query by signature, we make +1 req
  2. Server-side PZP article filter is dictionary-based - Art matches entries of the UZP provisions dictionary, so the format matters ("art. 226 ust. 1 pkt 5", not "226"); on a miss we fall back to a phrase search, which is broader and needs verification
  3. PDF not fetched - only a link to the UZP page (product decision)
  4. The sentence/reasoning parser is shallow - in the POC we return content_text as plain text. Splitting into sections -> v1.0
  5. Rate limit 1 req/s - large lists may be slow. A 7-day cache for rulings (immutable) mitigates this.
  6. UZP serves a fixed 10 results per page - size > 10 is stitched client-side from consecutive pages, i.e. +1 request per extra page
  7. Scraping, not an API - UZP rebuilt the search engine in July 2026 and every endpoint moved (see DISCOVERY.md). Run pytest -m smoke after any UZP-side change; tests/test_parser_regression.py guards the parser offline.

Cache

  • Rulings (immutable): 7 days
  • Search result lists: 6 hours
  • PZP dictionary (once implemented): 30 days

Cache location: ~/.matematic/cache/kio/ (configurable via KIO_MCP_CACHE_DIR).

Audit log

Location: ~/.matematic/audit/kio-orzeczenia-mcp.jsonl

JSONL format (one entry per tool call). See CONSTITUTION.md Art. 3.

What is NOT logged: the full text of a ruling (content_text, reasoning). We log only signatures and metadata.

The data comes from the public UZP case law database (orzeczenia.uzp.gov.pl), made available under the Act of 6 September 2001 on access to public information and the Act of 11 September 2019 - Public Procurement Law.

The connector:

  • does not modify the source data,
  • does not de-anonymize the parties to a proceeding beyond what UZP publishes,
  • does not circumvent any technical protections,
  • identifies itself with the User-Agent header matematic-kio-mcp/{version} (+https://matematic.co).

Pre-release blocker. Before publishing the repository on GitHub, a notification to UZP (kontakt@uzp.gov.pl) about launching the connector is planned - User-Agent, query limit, nature of access. Status: TODO.

In case of objections from UZP or other parties - contact: kontakt@matematic.co.

License

Apache-2.0. See LICENSE.

Project constitution

See CONSTITUTION.md - 4 governance principles (public data, rate limit, audit log, citations).

Other open connectors for Polish law

Open-source connectors by MateMatic that complement the scope of this repository:

  • mcp-saos - SAOS (Supreme Court, Supreme Administrative Court, common courts)
  • mcp-eu-sparql - EU law via Cellar SPARQL
  • mcp-isap - Journal of Laws, Monitor Polski, ministerial gazettes (Sejm ELI API)

External catalog of legal sources: worldwidelaw/legal-sources (bulk harvest scripts, MIT).

Metadata

Release files for kio-orzeczenia-mcp 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kio-orzeczenia-mcp 0.4.1
File Size Uploaded
kio_orzeczenia_mcp-0.4.1.tar.gz 166.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kio-orzeczenia-mcp 0.4.1
File Interpreter ABI Platform
kio_orzeczenia_mcp-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 202.7 kB

Release files / kio_orzeczenia_mcp-0.4.1.tar.gz

Download URL kio_orzeczenia_mcp-0.4.1.tar.gz
Size 166.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a6288b3b0fc632e3a78054e82a5e83fd11234203ec7be4d9513a82405046dd92
BLAKE2b-256 checksum
How to use checksums
84f55942ab14a146c3db6389d8349df49bda4c7b9a528d3579cec909bbd6f682
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / kio_orzeczenia_mcp-0.4.1-py3-none-any.whl

Download URL kio_orzeczenia_mcp-0.4.1-py3-none-any.whl
Size 36.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b311bf6eec872dd22a5408398dd38bc0303ea6b999df85f85491ac0e0a9c7dc
BLAKE2b-256 checksum
How to use checksums
5b1b00c1c8a826ce358571a4e3e9a402f6391a96a9c429d213273cf8f1511537
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page