Metadata-Version: 2.4
Name: kio-orzeczenia-mcp
Version: 0.3.0
Summary: MCP server for Krajowa Izba Odwolawcza (KIO) rulings from orzeczenia.uzp.gov.pl
Project-URL: Homepage, https://github.com/matematicsolutions/kio-orzeczenia-mcp
Project-URL: Repository, https://github.com/matematicsolutions/kio-orzeczenia-mcp
Project-URL: Issues, https://github.com/matematicsolutions/kio-orzeczenia-mcp/issues
Author-email: MateMatic <kontakt@matematic.co>
License: Apache-2.0
License-File: LICENSE
Keywords: kio,legal,mcp,polish-law,uzp,zamowienia-publiczne
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Legal Industry
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business
Requires-Python: >=3.11
Requires-Dist: diskcache>=5.6.3
Requires-Dist: fastmcp>=0.2.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: selectolax>=0.3.21
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.4.0; extra == 'dev'
Description-Content-Type: text/markdown

# kio-orzeczenia-mcp

<!-- mcp-name: io.github.matematicsolutions/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](https://matematicsolutions.com)

> **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

```powershell
# 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):

```json
{
  "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:

```bash
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`.

```json
{ "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.

## 5 MCP tools

### 1. `kio_search(query: SearchQuery) -> SearchResult`

Search over KIO case law. All fields optional.

```jsonc
// 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.

```jsonc
// 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.

```jsonc
// 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.

```jsonc
// 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.

```jsonc
// 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"
}
```

---

## 3 usage examples (natural language)

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

The agent calls `kio_search`:

```json
{
  "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`:

```json
{ "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`:

```json
"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.

## Legal disclaimer

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`](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`](https://github.com/matematicsolutions/mcp-saos) - SAOS (Supreme Court, Supreme Administrative Court, common courts)
- [`mcp-eu-sparql`](https://github.com/matematicsolutions/mcp-eu-sparql) - EU law via Cellar SPARQL
- [`mcp-isap`](https://github.com/matematicsolutions/mcp-isap) - Journal of Laws, Monitor Polski, ministerial gazettes (Sejm ELI API)

External catalog of legal sources: [`worldwidelaw/legal-sources`](https://github.com/worldwidelaw/legal-sources) (bulk harvest scripts, MIT).
