Skip to main content
Glama
matematicsolutions

kio-orzeczenia-mcp

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.

Related MCP server: mcp-saos

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

Available Tools

5 tools
kio_by_pzp_articleA
Read-onlyIdempotent

Orzeczenia KIO cytujace konkretny artykul PZP.

UWAGA: filtr post-process (brak server-side article filter w UZP). Realizowane przez phrase search "art. {article}" + post-filter na pzp_articles.

ParametersJSON Schema
NameRequiredDescriptionDefault
articleYesnp "226" lub "224 ust. 1 pkt 1"
limitNolimit wynikow (default 20)

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior5/5

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

The description discloses that the tool uses a phrase search combined with a post-filter due to lack of server-side support, which adds significant behavioral context beyond the readOnlyHint annotation. This helps the agent understand potential limitations.

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

Conciseness4/5

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

The description is concise (three short sentences) and front-loaded with the purpose. The warning about post-filtering is useful, though the formatting could be slightly more structured.

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 100% schema coverage, presence of an output schema, and the behavioral detail provided (post-filtering), the description fully equips an agent to select 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 description adds value by providing concrete examples for the 'article' parameter (e.g., '226' or '224 ust. 1 pkt 1'), and the schema already has full coverage. The limit parameter is adequately described in the 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 KIO rulings citing a specific PZP article. It distinguishes from sibling tools like kio_search by focusing on article-based filtering.

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 clear context about the tool's purpose and the post-filtering limitation, implying use when article-specific results are needed. However, it does not explicitly mention when to prefer alternatives like kio_search.

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

kio_get_orzeczenieA
Read-onlyIdempotent

Pobiera pelne orzeczenie KIO.

ParametersJSON Schema
NameRequiredDescriptionDefault
signature_or_idYes"KIO 2924/21" (string) albo 15903 (internal int ID). Sygnatura wymaga +1 req aby ustalic internal_id przez search.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior3/5

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

Annotations already declare readOnly, idempotent, and non-destructive behavior. The description adds 'full decision' context but does not disclose other traits like error handling, rate limits, or return format. The description is consistent with annotations, adding minimal extra value.

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

Conciseness4/5

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

The description is a single sentence, concise and front-loaded. It could be slightly improved by adding brief context about what a 'full decision' entails, but it is efficient and easy to parse.

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?

Given the tool's simplicity, one parameter, and the presence of an output schema (which likely describes the return structure), the description is adequate. It states the core action clearly, so the agent can infer the rest from the schema.

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?

The tool description itself does not discuss parameters. However, the input schema's parameter description covers both string and integer types and explains the extra request needed for signatures. Since schema description coverage is 100%, baseline is 3, and the tool description adds nothing beyond that.

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 full KIO decision (orzeczenie), matching the name. It distinguishes from sibling tools that search, get recent, or get PDF URLs, as it specifically retrieves the full decision by signature or ID.

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

Usage Guidelines3/5

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

The main description provides no usage context or when to use this versus alternatives. However, the parameter description hints that using a signature requires an extra search step, implying a workflow. This is implicit guidance but not explicit about when-not or alternatives.

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

kio_get_pdf_urlA
Read-onlyIdempotent

Zwraca URL do PDF orzeczenia (NIE pobiera bytes).

ParametersJSON Schema
NameRequiredDescriptionDefault
signature_or_idYes"KIO 2924/21" lub 15903

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. The description adds that the tool returns a URL rather than the content bytes, which is important behavioral context beyond the 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?

Single, front-loaded sentence with no wasted words. Every part adds value: it states the action, resource, and key limitation (no bytes).

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 tool with one parameter, rich annotations, and an output schema, this description fully covers the essential details: what it returns (URL) and what it does not (bytes). No gaps.

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 coverage is 100% and includes a description. The tool description provides example formats for the parameter ('KIO 2924/21' or 15903), adding practical guidance beyond the schema's 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?

Description uses a specific verb ('Zwraca URL') and clearly identifies the resource ('PDF orzeczenia'). It also distinguishes itself by stating it does not download bytes, which differentiates from siblings like kio_get_orzeczenie.

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 clearly states that this tool returns a URL and does not download bytes, implicitly indicating when not to use it (if bytes are needed). However, no explicit alternative tool is named, but the context of sibling tools provides some guidance.

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

kio_recentA
Read-onlyIdempotent

Najnowsze orzeczenia KIO z ostatnich N dni.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNoile dni wstecz (default 30)
limitNoile orzeczen zwrocic (default 20, max 100)

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, destructiveHint, idempotentHint, and openWorldHint, covering the basic behavioral profile. The description adds the time-window constraint (last N days) but does not disclose additional behaviors like sorting order, pagination, or return format beyond what annotations imply.

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 a single, front-loaded sentence with no unnecessary words. It efficiently conveys the core purpose without any fluff.

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?

Given the tool's simplicity (2 optional parameters, output schema present), the description adequately explains what the tool does. It is implicitly clear that it returns a list of decisions, though explicitly stating 'list of recent decisions' would improve completeness slightly. The presence of an output schema reduces the need to describe return values.

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 100%, with both 'days' and 'limit' parameters having clear descriptions in the schema. The tool description does not add any extra parameter meaning beyond what the schema already provides, so the baseline score of 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?

The description clearly states that the tool retrieves recent KIO decisions from the last N days, using a specific verb and resource. It distinguishes itself from siblings by focusing on recency, whereas siblings deal with specific articles, individual decisions, PDFs, or general search.

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

Usage Guidelines2/5

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

No guidance is provided on when to use this tool versus its siblings (e.g., kio_search). The description lacks explicit when-to-use, when-not-to-use, or alternative recommendations, leaving the agent to infer usage context.

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. 5 tool updatesv0.2.0
    • First observedkio_by_pzp_article
    • First observedkio_get_orzeczenie
    • First observedkio_get_pdf_url
    • First observedkio_recent
    • First observedkio_search

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool serves a distinct purpose: search by article, fetch full decision, get PDF URL, recent decisions, and general search. There is no overlap or ambiguity.

Naming Consistency4/5

All names use snake_case and start with 'kio_', but the verbs are inconsistent: 'get' and 'search' are actions, while 'recent' is an adjective and 'by_pzp_article' is a prepositional phrase.

Tool Count5/5

With 5 tools covering search, retrieval, and specialized queries, the set is well-scoped for accessing procurement decisions without being too sparse or cluttered.

Completeness4/5

The toolset covers core operations: search, get full decision, get PDF URL, recent decisions, and article-specific search. Minor gaps like advanced filters or bulk listing are acceptable.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers