Skip to main content
Glama

prawo-pl-mcp

One MCP server for Polish legal data.

Ten independent connectors - case law, legislation, the company register, tax rulings, public procurement, data-protection decisions, plus EU extras - behind five tools. Each connector stays a separate package with its own repository; this server spawns them on demand and translates between their conventions.

"Prawo" is Polish for "law"; the name follows yargi-mcp's move of naming a country server in its own language.

Why an aggregator

The connectors cover Polish legal data well, but as separate MCP servers, one per database - ten installs and ten config entries before the first question. yargi-mcp showed the fix for Turkish law: 16 institutions, one server. prawo-pl-mcp does the same for Poland:

  • Five tools instead of 42. pl_list_sources, pl_search, pl_get_document, pl_call and pl_coverage - fewer tool schemas means less context spent before the LLM starts working.

  • One convention. The connectors grew organically: dateFrom here, date_from there, pages counted from 0 or from 1 depending on the repo. Here page is always 1-based and dates are always date_from/date_to (YYYY-MM-DD); the translation happens inside. One name per concept applies to the aggregator's own tools too: page is the page parameter of both pl_search and pl_get_document (page_number still works as a deprecated alias), and pl_call takes arguments or its alias args.

  • Native names on the table. Every catalog entry carries native_params - the native parameter name each unified one maps to for that source, so article_number (eu-compliance) is visible without spawning the connector or reading an upstream error. pl_call checks the names it was given against the connector's own schema and says which one it meant.

  • Nothing preinstalled. Connectors run as subprocesses via npx -y / uvx, downloaded on the first call to a given source and kept alive afterwards. A source that cannot start reports a readable error while the rest keep working.

  • Paginated documents. Full judgment and act texts are chunked at ~5000 characters per page (yargi-mcp's pattern), so a 200-page ruling does not flood the context window. Sources that paginate server-side (isap) get the page forwarded natively and are reported as pagination: "native" - the aggregator does not re-cut an already-cut page and does not invent a total_pages it cannot know.

Related MCP server: mcp-nsa

Coverage

Source id

What

Institution / database

Connector

Runtime

saos

Case law: common courts, Supreme Court, Constitutional Tribunal + citator

SAOS

mcp-saos

npx

nsa

Case law: administrative courts (NSA + 16 WSA)

CBOSA

mcp-nsa

npx

isap

Legislation: Dziennik Ustaw + Monitor Polski, 96k+ acts

Sejm ELI API

mcp-isap

npx

krs

Company register: extracts, history, board composition

KRS API (Ministry of Justice)

mcp-krs

npx

eureka

Tax rulings: 550k+ individual interpretations

EUREKA (Ministry of Finance)

mcp-eureka

npx

kio

Public procurement: National Appeal Chamber rulings

orzeczenia.uzp.gov.pl

kio-orzeczenia-mcp

uvx

uodo

GDPR enforcement: Polish DPA decisions, fines, stats

orzeczenia.uodo.gov.pl

uodo-orzeczenia-mcp

uvx

eu-sparql

EU law: EUR-Lex by CELEX, CJEU by ECLI, GDPRhub

Cellar SPARQL

mcp-eu-sparql

npx

eu-compliance

14 EU regulations offline (GDPR, AI Act, DORA, NIS2...)

Local SQLite corpus

mcp-eu-compliance

npx

legalize

Law-as-git: 32 jurisdictions, versioned by commit

legalize-dev

legalize-mcp

uvx

Polish sources are tagged group: pl, EU extras group: eu - filter with pl_list_sources(group="pl").

Tools

Tool

What it does

pl_list_sources

Catalog of sources (no subprocess spawned). With source_id: live tool schemas of one connector.

pl_search

Search any source with normalized parameters; source-specific filters via extra.

pl_get_document

Full document by identifier (judgment id, ELI, KRS number, signature...), paginated.

pl_call

Any native tool of any connector: citator, DPA statistics, board composition, regulation comparison...

pl_coverage

Declare what this connector covers, when each family was captured, and - explicitly - what it does NOT cover. Every gap carries a fallback.

The typical flow an LLM follows (spelled out in the server's instructions): pl_list_sources → pl_search → pl_get_document, with pl_call for the specialized tools each connector brings.

Install

Requires Python ≥ 3.11 plus the runtimes of the sources you use: Node.js ≥ 18 for npx sources, uv for uvx sources. If a runtime is missing, its sources report source_unavailable and the rest work.

Claude Code

claude mcp add pl-legal -- uvx prawo-pl-mcp

Claude Desktop / any MCP client (stdio)

{
  "mcpServers": {
    "pl-legal": {
      "command": "uvx",
      "args": ["prawo-pl-mcp"]
    }
  }
}

Remote (Streamable HTTP)

uvicorn prawo_pl_mcp.asgi:app --host 0.0.0.0 --port 8000

Open by default (public, read-only data). Set PRAWO_PL_MCP_API_KEY to require X-API-Key: <key> or Authorization: Bearer <key> on every request.

Configuration

Env

Default

Meaning

PRAWO_PL_MCP_CMD_<ID>

-

Override the spawn command for a source, e.g. PRAWO_PL_MCP_CMD_SAOS="node C:/dev/mcp-saos/dist/index.js" for a local checkout. <ID> = source id, uppercase, - → _.

PRAWO_PL_MCP_INIT_TIMEOUT

180

Seconds allowed for a connector's first start (includes package download).

PRAWO_PL_MCP_TIMEOUT

90

Seconds per tool call after startup.

PRAWO_PL_MCP_AUDIT_DIR

~/.matematic/audit

Where the JSONL audit log goes.

PRAWO_PL_MCP_API_KEY

-

ASGI mode only: require this API key (dual-channel).

Architecture

The aggregator is a thin proxy (ADR 0001). Connectors are not imported, vendored or forked; the aggregator speaks MCP to them over stdio the same way any client would. The whole layer is a source registry (one dataclass entry per connector), a lazy subprocess pool, a parameter translator and a paginator. Adding a source means adding a registry entry.

LLM client ──MCP──▶ prawo-pl-mcp ──MCP/stdio──▶ npx @matematicsolutions/mcp-saos
                        │         ──MCP/stdio──▶ uvx kio-orzeczenia-mcp
                        │         ──MCP/stdio──▶ ... (spawned on first use)
                        └─ registry + param mapping + 5000-char pagination

Every call lands in a JSONL audit log (timestamp, tool, source, parameter hash, latency - never document content).

Development

git clone https://github.com/matematicsolutions/prawo-pl-mcp && cd prawo-pl-mcp
uv sync --extra dev
uv run pytest          # offline tests: registry, dispatch mapping, pagination, drift
uv run prawo-pl-mcp    # stdio server

License

Apache-2.0. Individual connectors carry their own licenses (MIT or Apache-2.0), their own rate limits and their own terms toward upstream databases - the aggregator adds no caching and no transformation beyond pagination, so each connector's constraints apply unchanged.

Available Tools

4 tools
pl_callA
Read-onlyIdempotent

Escape hatch: call any native tool of any registered source.

For operations the unified tools do not cover: citator (saos_cite_check), DPA statistics (uodo_stats), procurement-article search (kio_by_pzp_article), board composition (get_board), regulation comparison (eu_compare), tax category dictionary (list_categories)... arguments (alias: args - both accepted, pass only one) is an object keyed by the NATIVE parameter names of that tool, passed verbatim. The native names of the common ones are in native_params from pl_list_sources; for everything else read the live schema via pl_list_sources(source_id=...). Wrong parameter names are caught here against the connector's schema, before the call leaves the aggregator.

Errors: unknown_source, unknown_tool (not exposed by that source), invalid_arg (missing required or misspelled parameter name), source_unavailable, upstream_error (connector's own error code in message).

Inputs: tool (required), source (required), args, arguments. Returns: result

pl_get_documentA
Read-onlyIdempotent

Fetch the full document (judgment, act text, register extract...) from a source.

document_id format depends on the source - numeric id (saos), hex doc_id (nsa), ELI (isap), KRS number (krs), signature (kio, uodo)... - see the document_id field in pl_list_sources. Long documents are paginated at ~5000 characters per page: response is JSON with content, page, total_pages, has_more - iterate page while has_more (page is 1-based, exactly as in pl_search; page_number is a deprecated alias kept for backward compatibility, and is echoed in the response for the same reason). Sources that paginate the document themselves (isap) report pagination: "native" and carry their own page footer inside content. Some sources need extra (e.g. eu-compliance: {"regulation": "GDPR"}).

Errors: unknown_source, invalid_arg (missing required extra, page < 1, conflicting page/page_number), source_unavailable, upstream_error (e.g. connector's not_found - check the identifier or search first).

Inputs: source (required), document_id (required), page, extra, +1 more. Returns: result

pl_list_sourcesA
Read-onlyIdempotent

List available Polish/EU legal data sources, or inspect one source's live tool schemas.

Without arguments: full catalog (id, coverage, capabilities, native tool names, and native_params - the NATIVE parameter name each unified parameter maps to, e.g. eu-compliance pl_get_document -> {"tool": "eu_article", "document_id": "article_number"}) - reads a local registry, spawns nothing. With source_id: connects to that connector (lazy spawn on first use) and returns its live tools/list with input schemas - needed for native tools that the unified tools do not cover. Optional group filter: 'pl' or 'eu'.

Errors: unknown_source (bad source_id), source_unavailable (connector could not start).

Inputs: group, source_id. Returns: result

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.8/5.0

    Scored across 4 tools

    Disambiguation5/5

    Each tool occupies a distinct layer: discovery (pl_list_sources), search (pl_search), retrieval (pl_get_document), and native access (pl_call). No overlap; pl_call is explicitly for operations not covered by the unified tools.

    Naming Consistency5/5

    All tools share a consistent 'pl_' prefix with clear verb-based names (list_sources, search, get_document, call). The pattern is uniform and predictable, with no mixing of conventions.

    Tool Count5/5

    Four tools is well-scoped for an aggregator server: it provides the essential operations plus an escape hatch for source-specific features, without bloat.

    Completeness5/5

    The tool set covers the full lifecycle of legal data access: discover sources, search, fetch documents, and call any native tool for specialized operations. The escape hatch ensures no dead ends.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      MCP server that provides access to Polish individual tax interpretations (KIS) via the official EUREKA API, enabling search and retrieval of rulings with signatures, theses, and full text.
      4
      5
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables semantic search over Polish court judgments and legislative acts via MCP. Allows LLMs to retrieve legal documents using natural language queries.
      Apache 2.0
    • A
      license
      A
      quality
      C
      maintenance
      MCP server for searching and retrieving over 550,000 Polish tax documents from the EUREKA system, providing verifiable citations with signatures and URLs.
      4
      103 npm
      5
      MIT