Skip to main content
Glama

Law Scrapper MCP

A comprehensive Model Context Protocol (MCP) server for accessing and analyzing Polish legal acts from the Sejm API, enabling AI-powered legal research and document analysis.

Python version License Version

Features

  • Comprehensive legal act access - Full access to Polish legal acts from Dziennik Ustaw (DU) and Monitor Polski (MP)

  • Advanced search and filtering - Multi-criteria search by date, type, keywords, publisher, and status

  • Result Store with chained filtering - Store search results and filter with regex, type/status/year match, date ranges, sorting

  • Document Store pattern - Load acts into memory for efficient section-level navigation and search

  • Detailed document analysis - Metadata, structure, references, and content retrieval

  • Content processing - Automatic PDF-to-text and HTML-to-Markdown conversion

  • Date calculations - Specialized date utilities for legal document analysis

  • System metadata - Keywords, statuses, document types, and institution data

  • Official MCP SDK - Built with mcp[cli]==2.0.0 and MCPServer; STDIO and stateless Streamable HTTP transports

  • Async HTTP client - Efficient httpx client with retry logic and connection pooling

  • TTL caching - Intelligent response caching with configurable TTL

  • Structured logging - JSON and text log formats for easy debugging

  • Docker support - Containerized deployment with docker-compose

  • Comprehensive documentation - Examples and clear parameter descriptions

Related MCP server: Slov-Lex MCP Server

Requirements

  • Python: 3.13 or higher

  • Package manager: uv (recommended) or pip

  • Internet connection: Required for accessing Sejm API endpoints

  • MCP-compatible tool: Cursor IDE, Claude Code, or other MCP clients

Installation

# Clone the repository
git clone https://github.com/numikel/law-scrapper-mcp.git
cd law-scrapper-mcp

# Install dependencies
uv sync

# Install with dev dependencies
uv sync --extra dev

Using pip

# Clone the repository
git clone https://github.com/numikel/law-scrapper-mcp.git
cd law-scrapper-mcp

# Install dependencies
pip install -e .

Using uvx (no installation required)

For quick testing without cloning the repository:

# Run the server directly from GitHub
uvx --from git+https://github.com/numikel/law-scrapper-mcp law-scrapper

Quick start

STDIO transport (default)

STDIO is the default transport for MCP communication. Start the server and connect from your MCP client:

# Run the server
uv run python -m law_scrapper_mcp

# Or use the installed script
law-scrapper

Configure in your MCP client (e.g., Cursor .cursor/mcp.json):

{
  "mcpServers": {
    "law-scrapper-mcp": {
      "command": "law-scrapper"
    }
  }
}

For Claude Code:

claude mcp add law-scrapper "uvx --from git+https://github.com/numikel/law-scrapper-mcp law-scrapper"

HTTP transport (streamable-http)

Run the server on HTTP with streamable-http transport:

# Run with HTTP transport on port 7683
LAW_MCP_TRANSPORT=streamable-http uv run python -m law_scrapper_mcp

# Or specify a custom port (the host stays on loopback unless you configure
# an authentication mode — see "Authenticated remote deployment" below)
LAW_MCP_TRANSPORT=streamable-http LAW_MCP_PORT=8080 uv run python -m law_scrapper_mcp

Configure in your MCP client:

{
  "mcpServers": {
    "law-scrapper-mcp": {
      "url": "http://localhost:7683/mcp",
      "transport": "streamable-http"
    }
  }
}

Note: The URL must include the /mcp path. The server exposes stateless Streamable HTTP at /mcp, not at the root. Using http://localhost:7683 without /mcp results in 404 (Not Found).

Docker

Build and run with Docker:

# Build the image
docker build -t law-scrapper-mcp .

# Run with STDIO transport (default)
docker run -it law-scrapper-mcp

# Run with HTTP transport on port 7683
docker run -it -p 7683:7683 -e LAW_MCP_TRANSPORT=streamable-http law-scrapper-mcp

Or use docker-compose:

# Run with STDIO transport
docker compose up

# Run with HTTP transport (set TRANSPORT=streamable-http in docker-compose.yml)
docker compose -f docker-compose.yml up

CI builds this image and smoke-tests both transports (/health over streamable-http, one initialize over STDIO) on every code change, so a Dockerfile that no longer builds fails the pull request rather than a downstream registry.

Configuration

All settings are configured via environment variables with the LAW_MCP_ prefix:

The list-valued settings — LAW_MCP_ALLOWED_HOSTS, LAW_MCP_ALLOWED_ORIGINS, LAW_MCP_TRUSTED_PROXIES, LAW_MCP_AUTH_REQUIRED_SCOPES and LAW_MCP_AUTH_ALGORITHMS — accept either a comma-separated value (a:*, b:*, the form used throughout this document) or a JSON array (["a:*", "b:*"]). Both are equivalent; surrounding whitespace is trimmed.

Variable

Default

Description

LAW_MCP_TRANSPORT

stdio

Transport: stdio or streamable-http

LAW_MCP_HOST

127.0.0.1

HTTP server host (when using streamable-http). Binding beyond loopback requires LAW_MCP_AUTH_MODE — see "Authenticated remote deployment"

LAW_MCP_PORT

7683

HTTP server port (when using streamable-http)

LAW_MCP_SHUTDOWN_GRACE

15

Graceful shutdown window in whole seconds for the HTTP server (a fractional value is rejected at startup rather than silently rounded). Keep stop_grace_period in docker-compose.yml at or above twice this value — nothing in the code enforces the relation.

LAW_MCP_API_TIMEOUT

30.0

HTTP request timeout in seconds

LAW_MCP_API_MAX_CONCURRENT

8

Concurrent light API requests (JSON metadata and search)

LAW_MCP_API_MAX_CONCURRENT_CONTENT

2

Concurrent heavy API requests (act HTML and PDF downloads)

LAW_MCP_API_RATE_PER_SECOND

5.0

Sustained outbound request rate towards the Sejm API; accepted range 0.1100, non-finite values rejected

LAW_MCP_API_RATE_BURST

10

Requests allowed back-to-back before the rate applies; accepted range 11000

LAW_MCP_API_MAX_SERVER_PAUSE

60.0

Longest pause, in seconds, that a Retry-After sent by the Sejm API may hold all outbound traffic; a larger header value is clamped to this and logged. Accepted range above 0 up to 600

LAW_MCP_API_MAX_ATTEMPTS

3

Attempts per operation, retries included; accepted range 120

LAW_MCP_API_RETRY_BUDGET

45.0

Seconds the retry sequence of one operation may plan to wait

LAW_MCP_CACHE_METADATA_TTL

86400

Metadata cache TTL (24 hours)

LAW_MCP_CACHE_SEARCH_TTL

600

Search results cache TTL (10 minutes)

LAW_MCP_CACHE_BROWSE_TTL

3600

Browse results cache TTL (1 hour)

LAW_MCP_CACHE_DETAILS_TTL

3600

Act details cache TTL (1 hour)

LAW_MCP_CACHE_CHANGES_TTL

300

Changes tracking cache TTL (5 minutes)

LAW_MCP_CACHE_MAX_ENTRIES

1000

Maximum cache entries

LAW_MCP_DOC_STORE_MAX_DOCUMENTS

10

Maximum documents in Document Store

LAW_MCP_DOC_STORE_MAX_SIZE_BYTES

5242880

Maximum Document Store size (5 MB). Also the conversion threshold: content whose HTML or PDF payload exceeds it is refused with an error naming the source PDF URL — the download is aborted as soon as the body passes the limit (or earlier, from Content-Length), so the server never holds a larger body in memory.

LAW_MCP_DOC_STORE_TTL

7200

Document Store TTL (2 hours)

LAW_MCP_CIRCUIT_BREAKER_THRESHOLD

5

Failures before circuit breaker opens

LAW_MCP_CIRCUIT_BREAKER_RECOVERY_TIMEOUT

60.0

Seconds before trying recovery

LAW_MCP_CIRCUIT_BREAKER_HALF_OPEN_MAX_CALLS

3

Test calls in half-open state

LAW_MCP_MAX_PATTERN_LENGTH

512

Max filter_results pattern length, clamped to 64-4096

LAW_MCP_FILTER_MAX_RECORDS

100

Max records filter_results processes per call; floor 1, no ceiling (very high values lengthen the synchronous but linear scan)

LAW_MCP_ALLOWED_HOSTS

127.0.0.1:*, localhost:*, [::1]:*

Host header allowlist for streamable-http (DNS-rebinding protection). Widening beyond loopback requires an auth mode — see "Authenticated remote deployment"

LAW_MCP_ALLOWED_ORIGINS

http://127.0.0.1:*, http://localhost:*, http://[::1]:*

Origin header allowlist for streamable-http. Same auth-mode requirement as LAW_MCP_ALLOWED_HOSTS

LAW_MCP_AUTH_JWKS_URI

unset

Override the JWKS URI discovered from LAW_MCP_AUTH_ISSUER's OIDC discovery document; needed only when a provider's discovery document omits or misreports it

LAW_MCP_AUTH_REQUIRED_SCOPES

[] (none)

Scopes a presented token must carry, checked by RequireAuthMiddleware. Meaningful in oauth mode only: under bearer the static verifier would grant exactly these scopes to every holder of the token, so the check could never restrict anything — a non-empty value with LAW_MCP_AUTH_MODE=bearer is therefore rejected at startup

LAW_MCP_AUTH_ALGORITHMS

RS256, ES256

JWT signature algorithm allowlist passed to the decoder; never read from the token header

LAW_MCP_AUTH_JWKS_CACHE_TTL

3600

Seconds a fetched JWKS key set is cached before re-fetching

LAW_MCP_RATE_LIMIT_ENABLED

true

Whether the per-client rate limiter wraps the HTTP app. /health is exempt only when the caller is a loopback peer (container healthchecks); any other peer is metered like every other request

LAW_MCP_RATE_LIMIT_REQUESTS

60

Requests allowed per LAW_MCP_RATE_LIMIT_WINDOW before throttling

LAW_MCP_RATE_LIMIT_WINDOW

60.0

Rate limit window in seconds

LAW_MCP_RATE_LIMIT_BURST

10

Token bucket capacity — how many requests can arrive back-to-back before 429

LAW_MCP_LOG_LEVEL

INFO

Log level: DEBUG, INFO, WARNING, ERROR or CRITICAL (case-insensitive). Aliases such as WARN are rejected at startup on both transports instead of working on one and crashing the other. httpx's own request lines, which carry the full query URL, never go below WARNING regardless of this setting

LAW_MCP_LOG_FORMAT

text

Log format: text or json

Logs go to stderr (stdout carries the MCP protocol on the STDIO transport). Every record carries a request_id correlating it with a single tool call — lifespan for records emitted outside one:

2026-08-24 12:00:00,123 - law_scrapper_mcp.tools.search - [a1b2c3d4] - INFO - Stored result set rs_1: 20 results (total 137)
{"timestamp": "2026-08-24T10:00:00.123456+00:00", "level": "INFO", "logger": "law_scrapper_mcp.tools.search", "request_id": "a1b2c3d4", "message": "Stored result set rs_1: 20 results (total 137)"}

Set LAW_MCP_LOG_LEVEL=DEBUG to recover the detail deliberately kept off INFO: search query text and the exception messages behind validation and upstream failures.

Example environment configuration:

export LAW_MCP_TRANSPORT=streamable-http
export LAW_MCP_PORT=7683
export LAW_MCP_LOG_LEVEL=DEBUG
export LAW_MCP_CACHE_METADATA_TTL=86400

Authenticated remote deployment

The HTTP transport binds 127.0.0.1 by default and refuses to start on any other address unless an authentication mode is configured. Two modes exist and neither falls back to the other.

Bearer token — local and simple deployments:

export LAW_MCP_AUTH_TOKEN=$(openssl rand -base64 32)   # min. 32 bytes
LAW_MCP_TRANSPORT=streamable-http \
LAW_MCP_HOST=0.0.0.0 \
LAW_MCP_AUTH_MODE=bearer \
  law-scrapper

In production prefer LAW_MCP_AUTH_TOKEN_FILE=/run/secrets/law_mcp_token — an environment variable is visible in docker inspect and /proc/<pid>/environ. Setting both sources is a startup error, not a precedence rule.

OAuth 2.1 / OIDC — corporate deployments. Works with any provider publishing OIDC discovery and JWKS:

Provider

LAW_MCP_AUTH_ISSUER

Microsoft Entra ID

https://login.microsoftonline.com/<tenant>/v2.0

Google

https://accounts.google.com

AWS Cognito

https://cognito-idp.<region>.amazonaws.com/<pool-id>

Okta

https://<org>.okta.com/oauth2/<server-id>

Auth0

https://<tenant>.auth0.com/

LAW_MCP_AUTH_MODE=oauth \
LAW_MCP_AUTH_ISSUER=https://login.microsoftonline.com/<tenant>/v2.0 \
LAW_MCP_AUTH_AUDIENCE=api://law-scrapper \
LAW_MCP_AUTH_RESOURCE_SERVER_URL=https://mcp.example.com/mcp \
  law-scrapper

Reachable through a reverse proxyLAW_MCP_ALLOWED_HOSTS and LAW_MCP_ALLOWED_ORIGINS default to loopback-only (F18): a request whose Host or Origin header doesn't match gets 421/403 from the SDK's DNS-rebinding protection, even with a valid token. This default is deliberate — widening the allowlist is only permitted once an auth mode is configured (enforced at startup, D6). A reverse proxy that preserves the original Host (nginx's proxy_set_header Host $host, Caddy's default) needs both variables set to the public name, for either auth mode above:

LAW_MCP_ALLOWED_HOSTS='mcp.example.com:*' \
LAW_MCP_ALLOWED_ORIGINS='https://mcp.example.com' \
  law-scrapper

Providers issuing opaque tokens (GitHub) are not supported — they would require RFC 7662 introspection. TLS termination stays with the reverse proxy. /health is intentionally unauthenticated so container healthchecks work; it exposes the server version and circuit-breaker state. It is exempt from the rate limiter only for loopback peers — a probe arriving from another host is metered like any other request.

Rate limiting is always on for HTTP: 60 requests per 60 s, burst 10. Behind a proxy, set LAW_MCP_TRUSTED_PROXIES (addresses or CIDRs) — otherwise X-Forwarded-For is ignored and every client shares one bucket.

Tools reference

Law Scrapper MCP provides 13 tools for legal research and analysis:

1. get_system_metadata(category)

Retrieve system metadata for filtering and searching legal acts.

Parameters:

  • category (string, default: "all") - Metadata category: "keywords", "publishers", "statuses", "types", "institutions", or "all"

Returns: Keywords, publishers, document types, statuses, and institutions available in the system

Examples:

- Get all available search keywords
- Retrieve all legal document types
- List all publishers (DU, MP)
- Get all document statuses
- Get complete system metadata

Search for legal acts with advanced filtering options.

Parameters:

  • publisher (string) - Publisher code: "DU" (Dziennik Ustaw) or "MP" (Monitor Polski)

  • year (integer) - Publication year (e.g., 2024)

  • keywords (string) - Search keywords (AND logic - use multiple searches for OR)

  • detail_level (string, default: "standard") - Response detail: "minimal", "standard", or "full"

  • status (string, optional) - Document status filter

  • type (string, optional) - Document type filter

Returns: List of matching legal acts with metadata

Search note: Multiple keywords use AND logic. Search one keyword at a time for OR behavior.

Examples:

- Search DU 2024 for "environment protection" acts
- Find all MP 2023 acts with status "active"
- Search for COVID-19 related legislation
- Find acts by specific type (e.g., "regulation")
- Get minimal detail results for quick scanning

3. browse_acts(publisher, year, detail_level)

Browse all legal acts published in a specific year by publisher.

Parameters:

  • publisher (string) - Publisher code: "DU" or "MP"

  • year (integer) - Publication year

  • detail_level (string, default: "standard") - Response detail: "minimal", "standard", or "full"

Returns: Complete list of acts published in the specified year

Examples:

- Browse all DU acts from 2024
- Get minimal details of all MP acts from 2023
- Browse full details of DU 2022 legislation
- Get an overview of acts by publisher and year
- Track legislation published in a specific year

4. filter_results(result_set_id, pattern, field, type_equals, ...)

Filter and narrow down previously retrieved search/browse/changes results.

Parameters:

  • result_set_id (string) - Result set ID from a previous search/browse/changes call (e.g., "rs_1")

  • pattern (string, optional) - Regex pattern for text search (supports OR: "podatek|VAT|akcyza")

  • field (string, default: "title") - Field to search: "title", "eli", "status", "type", "publisher"

  • type_equals (string, optional) - Exact match on document type (e.g., "Ustawa", "Rozporządzenie")

  • status_equals (string, optional) - Exact match on status (e.g., "akt obowiązujący", "akt uchylony")

  • year_equals (integer, optional) - Exact match on publication year

  • date_field (string, optional) - Date field for range filter: "promulgation_date" or "effective_date"

  • date_from / date_to (string, optional) - Date range (YYYY-MM-DD)

  • sort_by (string, optional) - Sort field: "title", "year", "pos", "promulgation_date", etc.

  • sort_desc (boolean, default: false) - Sort descending

  • limit (integer, optional) - Maximum results to return

Returns: Filtered results with a new result_set_id for chained filtering

Examples:

- Filter search results to only "Rozporządzenie" type
- Search titles with regex "zdrow|apteka|lekar"
- Filter by date range and sort by promulgation date
- Chain filters: first by type, then by regex pattern
- Get top 10 most recent results

Result-set scope. Every stored result set declares whether it is the whole answer (complete) or a window cut from a larger corpus (page), in the result_set_scope field of the response and in list_result_sets. The distinction matters because filter_results narrows the set, not the query: an empty filter over a page set means "no match among these twenty records", not "no such act exists". Responses say so explicitly — a filter that matches nothing over a window sets no_match_is_inconclusive. track_legal_changes is the one tool whose set is always complete, because it stores the whole fetched range.

5. get_act_details(eli, load_content, detail_level)

Retrieve detailed information about a specific legal act and optionally load its content.

Parameters:

  • eli (string) - Act identifier in format "PUBLISHER/YEAR/NUMBER" (e.g., "DU/2024/1")

  • load_content (boolean, default: false) - Load act content into Document Store for section reading

  • detail_level (string, default: "standard") - Response detail: "minimal", "standard", or "full"

Returns: Act metadata (title, publication date, status, type, etc.), table of contents if load_content=true

Examples:

- Get metadata for act DU/2024/1
- Load act content for section-level reading
- Get full details including table of contents
- Retrieve act status and publication information
- Load multiple acts for comparison

6. read_act_content(eli, section)

Read content from a specific section of a loaded legal act.

Parameters:

  • eli (string) - Act identifier (must be loaded first via get_act_details with load_content=true)

  • section (string) - Section to read (e.g., "Art. 1", "Chapter 2", "Preamble")

Returns: Content of the requested section

Workflow note: Must call get_act_details(eli="...", load_content=true) first, then use this tool.

Examples:

- Read Article 1 from loaded act
- Get Chapter 2 content
- Read the Preamble section
- Access specific numbered articles
- Navigate act by chapters

7. search_in_act(eli, query)

Search for specific terms within a loaded legal act.

Parameters:

  • eli (string) - Act identifier (must be loaded first via get_act_details with load_content=true)

  • query (string) - Search term or phrase

Returns: Matching sections with context and location

Examples:

- Find all mentions of "penalty" in loaded act
- Search for specific legal terms
- Locate articles containing "fine" or "punishment"
- Find definitional sections
- Search for specific references

8. analyze_act_relationships(eli, relationship_type)

Analyze legal relationships and references of an act (amendments, references, etc.).

Parameters:

  • eli (string) - Act identifier

  • relationship_type (string, default: "all") - Type: "amends", "amended_by", "references", "referenced_by", or "all"

Returns: List of related acts and their relationships

Examples:

- Find which acts amend this legislation
- See what acts this legislation amends
- Get all legal references in the act
- Find acts that reference this legislation
- Analyze complete act relationship network

Track legal changes and new acts within a date range.

Parameters:

  • date_from (string) - Start date (YYYY-MM-DD format)

  • date_to (string) - End date (YYYY-MM-DD format)

  • publisher (string, optional) - Filter by publisher: "DU" or "MP"

  • keywords (string, optional) - Filter by keywords

Returns: Legal acts published in the date range

Examples:

- Track changes from 2024-01-01 to 2024-12-31
- Find new DU acts from last month
- Get changes published in past 7 days
- Track legislation on specific topics over time
- Monitor legal changes by publisher and date range

Calculate legal dates with intuitive sign convention.

Parameters:

  • days (integer, default: 0) - Days offset (+future, -past)

  • months (integer, default: 0) - Months offset (+future, -past)

  • years (integer, default: 0) - Years offset (+future, -past)

  • base_date (string, optional) - Base date (YYYY, YYYY-MM, or YYYY-MM-DD format, defaults to today)

Returns: Calculated date and relative description

Sign convention: Positive = future, Negative = past

Examples:

- Get current date (call with no parameters)
- Calculate date 30 days in the future (+30)
- Calculate date 6 months in the past (-6 months)
- Calculate date 1 year from a specific date
- Calculate legal deadlines and periods

11. compare_acts(eli_a, eli_b)

Compare metadata of two legal acts.

Parameters:

  • eli_a (string) - ELI identifier of the first act (e.g., "DU/2024/1692")

  • eli_b (string) - ELI identifier of the second act (e.g., "DU/2024/1716")

Returns: Comparison of titles, types, statuses, dates, keywords overlap and differences

Examples:

- Compare two acts from the same year
- Compare old and new versions of legislation
- Identify metadata differences between related acts

12. list_result_sets()

Display active result sets stored in memory.

Returns: List of result sets with IDs, query summaries, counts, and creation times

13. list_loaded_documents()

Display documents loaded into the Document Store.

Returns: List of loaded documents with ELIs, sizes, section counts, and timestamps

Document Store workflow

The Document Store pattern enables efficient content navigation and search within legal acts:

Workflow steps

  1. Load an act - Call get_act_details(eli="DU/2024/1", load_content=true) to load the act into the Document Store

  2. Read sections - Use read_act_content(eli="DU/2024/1", section="Art. 1") to read specific sections

  3. Search within act - Use search_in_act(eli="DU/2024/1", query="penalty") to find terms

Benefits

  • Efficient memory usage (configurable max documents and TTL)

  • Fast section-level navigation without refetching

  • Search within loaded acts without API calls

  • Automatic content processing (PDF→text, HTML→Markdown)

Configuration

  • LAW_MCP_DOC_STORE_MAX_DOCUMENTS - How many acts to keep in memory (default: 10)

  • LAW_MCP_DOC_STORE_MAX_SIZE_BYTES - Maximum memory usage (default: 5 MB)

  • LAW_MCP_DOC_STORE_TTL - How long to keep acts in memory (default: 2 hours)

Project structure

law-scrapper-mcp/
├── src/law_scrapper_mcp/
│   ├── __init__.py
│   ├── __main__.py              # Entry point for python -m
│   ├── server.py                # MCPServer, lifespan, transport config
│   ├── config.py                # Pydantic settings (env vars)
│   ├── logging_config.py        # Structured logging setup
│   ├── models/                  # Pydantic models
│   │   ├── enums.py            # Enumerations
│   │   ├── api_responses.py    # Sejm API response models
│   │   ├── tool_inputs.py      # Tool input models
│   │   └── tool_outputs.py     # Tool output models
│   ├── client/                  # HTTP client
│   │   ├── sejm_client.py      # AsyncClient with retry and circuit breaker
│   │   ├── cache.py            # Async TTL cache implementation
│   │   ├── circuit_breaker.py  # Circuit breaker for API protection
│   │   └── exceptions.py       # Custom exceptions (Polish messages)
│   ├── services/                # Business logic
│   │   ├── metadata_service.py    # Metadata retrieval
│   │   ├── search_service.py      # Search and browse
│   │   ├── act_service.py         # Act details and content
│   │   ├── changes_service.py     # Change tracking
│   │   ├── document_store.py      # In-memory act storage
│   │   ├── result_store.py        # Search result persistence and filtering
│   │   ├── content_processor.py   # PDF/HTML processing
│   │   └── response_enrichment.py # Response hints
│   └── tools/                   # MCP tool definitions
│       ├── metadata.py          # get_system_metadata
│       ├── search.py            # search_legal_acts
│       ├── browse.py            # browse_acts
│       ├── act_details.py       # get_act_details
│       ├── act_content.py       # read_act_content
│       ├── act_search.py        # search_in_act
│       ├── relationships.py     # analyze_act_relationships
│       ├── filter_results.py    # filter_results, list_result_sets
│       ├── changes.py           # track_legal_changes
│       ├── compare.py           # compare_acts
│       ├── dates.py             # calculate_legal_date
│       └── error_handling.py    # Centralized @handle_tool_errors decorator
├── tests/
│   ├── unit/                    # Unit tests
│   └── integration/             # Integration tests with Sejm API
├── Dockerfile                   # Container image definition
├── docker-compose.yml           # Multi-service setup
├── pyproject.toml              # Project metadata and dependencies
├── uv.lock                      # Reproducible dependency lock
└── README.md                    # This file

Docker

Security and deployment

The server binds 127.0.0.1 by default; binding beyond loopback (as Docker port publishing requires) fails at startup unless an authentication mode is configured — see "Authenticated remote deployment" above. When exposing the HTTP transport (streamable-http) to a network, place the server behind a reverse proxy (nginx, Caddy, Traefik) with TLS termination — this project verifies bearer tokens and OAuth 2.1/OIDC access tokens, but does not terminate TLS itself. The /health endpoint is unauthenticated and intended for container healthchecks only — do not expose it publicly without access controls. Dependency versions are pinned in uv.lock with security overrides in pyproject.toml (cryptography, urllib3, idna, werkzeug, requests).

Host/Origin allowlist (DNS-rebinding protection): the official MCP SDK only auto-enables Host/Origin validation when the server binds to a literal loopback address (127.0.0.1, localhost, ::1). docker-compose.yml sets LAW_MCP_HOST=0.0.0.0 so Docker can publish the port, which would otherwise leave that validation disabled. server.py passes transport_security explicitly (build_transport_security()) so requests are still validated against the configured allowlist — Host outside it gets 421, Origin outside it gets 403 — independent of the bind address. The allowlist defaults to loopback only (127.0.0.1:* / localhost:* / [::1]:* and the matching http:// origins) and is widened through LAW_MCP_ALLOWED_HOSTS / LAW_MCP_ALLOWED_ORIGINS, which startup validation permits only once an authentication mode is configured. This restores the pre-3.0.0 FastMCP posture for /mcp. It is a defense-in-depth layer, not a substitute for the authentication mode required to bind beyond loopback in the first place.

One deliberate difference from the pre-3.0.0 server: the SDK applies this validation inside the Streamable HTTP app, not as whole-app middleware, so /health is not covered by the allowlist and answers any Host. That is what keeps container healthchecks working when they connect by container name or bridge IP, but it also means /health discloses the server name and version — and, since it now also reports the circuit breaker's circuit_state and failure_count, the health of the Sejm API integration — to anything that can reach the published port. FastMCP guarded /health too. Restrict the published port, or front it with a proxy, if that disclosure matters to you.

Health contract: the response body carries an upstream object. upstream.circuit_state is closed, open, half_open, or unknown before the lifespan has started (or after it has been torn down) — in that unknown case upstream.failure_count is omitted entirely; otherwise it sits alongside circuit_state. /health still answers 200 while the breaker is open: restarting the container cannot repair an outage of api.sejm.gov.pl, and under restart: unless-stopped a 503 would turn someone else's outage into a restart loop.

Dockerfile

The included Dockerfile builds a containerized Law Scrapper MCP server:

FROM python:3.13-slim
WORKDIR /app
COPY . .
RUN pip install -e .
EXPOSE 7683
CMD ["law-scrapper"]

Build and run:

# Build the image
docker build -t law-scrapper-mcp .

# Run with STDIO transport
docker run -it law-scrapper-mcp

# Run with HTTP transport, published on the host — the image no longer
# defaults LAW_MCP_HOST to 0.0.0.0, so publishing the port beyond loopback
# requires setting the host explicitly together with an authentication mode
docker run -it -p 7683:7683 \
  -e LAW_MCP_TRANSPORT=streamable-http \
  -e LAW_MCP_HOST=0.0.0.0 \
  -e LAW_MCP_AUTH_MODE=bearer \
  -e LAW_MCP_AUTH_TOKEN="$(openssl rand -base64 32)" \
  -e LAW_MCP_LOG_LEVEL=DEBUG \
  law-scrapper-mcp

docker-compose.yml

Deployment with docker-compose:

# LAW_MCP_AUTH_TOKEN is required — docker-compose.yml fails fast without it
export LAW_MCP_AUTH_TOKEN=$(openssl rand -base64 32)

# Start service
docker compose up -d

# View logs
docker compose logs -f

# Stop service
docker compose down

Migration guide (v1 to v2)

If upgrading from v1.0.2, note these breaking changes:

v1.0.2 (old)

v2.0.0 (new)

Notes

get_current_date

calculate_legal_date()

Call with no parameters for current date

calculate_date_offset

calculate_legal_date(days/months/years)

Use intuitive +future/-past sign convention

get_legal_keywords

get_system_metadata(category="keywords")

Consolidated into one tool

get_legal_publishers

get_system_metadata(category="publishers")

Consolidated into one tool

get_legal_statuses

get_system_metadata(category="statuses")

Consolidated into one tool

get_legal_types

get_system_metadata(category="types")

Consolidated into one tool

get_legal_institutions

get_system_metadata(category="institutions")

Consolidated into one tool

get_publisher_details

N/A

Use get_system_metadata(category="publishers")

search_legal_acts

search_legal_acts

Enhanced with detail_level parameter

get_publisher_year_acts

browse_acts

Renamed for clarity

get_act_comprehensive_details

get_act_details

Added load_content and detail_level

get_act_content

read_act_content

Requires pre-loading with get_act_details

get_act_table_of_contents

get_act_details

TOC included in details response

get_act_relationships

analyze_act_relationships

Renamed for clarity

ELI format

Single string "DU/2024/1"

Changed from separate parameters

Legacy event-stream transport

STDIO (default)

STDIO is default, HTTP via streamable-http

Port 7683

Port 7683

Same default HTTP port

What's new in v3.0.0

  • Official Python MCP SDK — Replaced the prior third-party framework with mcp[cli]==2.0.0 and MCPServer[AppContext]

  • Native structured responses — Tools return EnrichedResponse with outputSchema and object structuredContent (no JSON strings)

  • Protocol-visible errors — Tool failures surface as isError=true instead of in-body error fields

  • Stateless Streamable HTTP/mcp endpoint only; legacy event-stream transport removed

  • PaginationPageInfo model exposed as the page_info field with limit/offset on search, browse, metadata, changes, TOC, sections, and in-act search

  • Typed AppContext — Lifespan resources accessed via ctx.request_context.lifespan_context

  • Domain servicesComparisonService, RelationshipService, and DateService extracted from tool adapters

  • Transport tests — Real STDIO subprocess, loopback HTTP, and MCP conformance in CI

  • Explicit Host/Origin allowlist on HTTPtransport_security is passed explicitly so DNS-rebinding protection applies regardless of the configured bind host; see Security and deployment

Migration guide (v2 to v3)

v2.x (old)

v3.0.0 (new)

Notes

result.content JSON string

structuredContent object

Parse native MCP structured payload

EnrichedResponse.error in success body

isError=true

Check result.is_error before reading content

v2.x in-process client

Official mcp.Client

In-memory and transport tests use SDK client

v2.x direct lifespan access

ctx.request_context.lifespan_context

Typed AppContext dataclass

Legacy event-stream transport

Removed

STDIO or stateless Streamable HTTP only

What's new in v2.4.0

  • Security hardening — Framework upgrade and dependency overrides close 51 Dependabot alerts (cryptography, urllib3, pillow, starlette, and others)

  • Framework 3.x — Lifespan context API, app.run() for HTTP transport, @custom_route for /health

  • Integration tests — In-memory client tests for core tools (metadata, search, dates, act details)

  • CI and Dependabot — Automated quality gates and weekly dependency updates

What's new in v2.3.1

  • uvx / tool registration fix — Fixed NameError: name 'Annotated' is not defined when running via uvx --from "git+https://github.com/numikel/law-scrapper-mcp" law-scrapper. Removed from __future__ import annotations from compare.py so parameter type hints resolve correctly during tool registration.

What's new in v2.3.0

  • 3 new toolscompare_acts, list_result_sets, list_loaded_documents (total: 13 tools)

  • Circuit breaker — Protects against cascading failures when Sejm API is unavailable

  • Centralized error handling@handle_tool_errors decorator with error classification and full tracebacks

  • asyncio.Lock migration — All stores use asyncio.Lock for proper async compatibility

  • Default search limit — Search/browse return max 20 results by default to limit token usage

  • Health endpoint/health for Docker deployments with streamable-http transport

  • Polish error messages — All exception messages in Polish for consistent user experience

  • Decision tree docstrings — "When to use" / "When NOT to use" for all tools

Development

Setup

# Install dependencies
uv sync

# Install with dev dependencies
uv sync --extra dev

Running tests

# Run unit tests
uv run pytest tests/unit/ -v

# Run integration tests (mocked Sejm API via respx; no live network required)
uv run pytest tests/integration/ -v -m integration

# Run all tests with coverage
uv run pytest --cov=law_scrapper_mcp --cov-report=term-missing

# Run with timeout for slow tests
uv run pytest --timeout=10 -v

MCP integration testing

Law Scrapper MCP uses three automated test layers to verify all 13 tools and their MCP protocol interaction:

Layer 1 — unit tests (tests/unit/):

uv run pytest tests/unit/ -v

Layer 2 — in-memory integration (official mcp.Client, mocked Sejm API via respx):

uv run pytest tests/integration/test_tools_e2e.py \
  tests/integration/test_content_pagination.py \
  tests/integration/test_result_pagination.py -v -m integration

Layer 3 — transport integration (real STDIO subprocess and loopback Streamable HTTP):

uv run pytest tests/integration/test_stdio_transport.py \
  tests/integration/test_http_transport.py -v -m integration

Full integration suite:

uv run pytest tests/integration/ -v -m integration

What the tests verify:

  • All 13 tools registered with preserved public arguments

  • Native structuredContent payloads with outputSchema (not JSON strings)

  • isError=true on tool execution failures

  • page_info (PageInfo) pagination metadata on paginated outputs

  • Stateful workflows: search → filter → load act → read sections → search in content

  • Real STDIO and HTTP transports against a running server process

Architecture:

  • Test framework: pytest with asyncio (unit) and anyio (MCP client tests)

  • HTTP mocking: respx intercepts httpx calls in in-memory integration tests

  • MCP protocol: Official mcp.Client from the Python MCP SDK

  • Helpers: tests/mcp_helpers.py (parse_tool_result for structuredContent assertions)

  • CI: Legacy dependency gate, protocol transport tests, and MCP conformance against /mcp

See tests/TEST_SUITE_SUMMARY.md for the full file layout and patterns.

Code quality

The project follows MCP SDK best practices:

  • Modular architecture - Separated concerns (models, client, services, tools)

  • Type hints - Full type annotation with Pydantic models

  • Async throughout - Async/await for all I/O operations

  • Comprehensive examples - Minimum 5 examples per tool

  • Tagged tools - Organized by category for easy discovery

  • Annotated parameters - Clear descriptions for all inputs

  • Structured logging - Configurable JSON/text formats

Running the server

# STDIO transport (default)
uv run python -m law_scrapper_mcp

# HTTP transport
LAW_MCP_TRANSPORT=streamable-http uv run python -m law_scrapper_mcp

# With debug logging
LAW_MCP_LOG_LEVEL=DEBUG uv run python -m law_scrapper_mcp

Contributing

  1. Fork the repository

  2. Create your feature branch (git checkout -b feature/amazing-feature)

  3. Commit your changes using Conventional Commits format

  4. Add tests for new functionality

  5. Ensure all tests pass and coverage is maintained

  6. Push to the branch (git push origin feature/amazing-feature)

  7. Open a Pull Request

Development guidelines

  • Follow MCP SDK patterns for tool definitions and structured output

  • Include comprehensive examples and parameter descriptions

  • Add appropriate tags for tool categorization

  • Write async code throughout

  • Add tests for all new functionality

  • Update CHANGELOG.md with your changes

  • Use English for all code comments and documentation

License

This project is licensed under the MIT License. See the LICENSE file for details.

Author

@numikel

Developed with help from:

Cursor Claude Code

And with models:

Claude Opus 4.6 Claude Opus 4.5 Claude Sonnet 4.5 Claude Haiku 4.5


Legal disclaimer: This tool provides access to Polish legal documents for research purposes. Always consult with qualified legal professionals for legal advice and interpretation of laws.

Available Tools

13 tools
analyze_act_relationshipsA

Przeanalizuj powiązania i referencje między aktami prawnymi.

Zwraca informacje o aktach zmienionych, zmieniających, uchylonych, podstawie prawnej i tekstach jednolitych.

Przykłady:

  • analyze_act_relationships(eli="DU/2024/1716") - Wszystkie powiązania

  • analyze_act_relationships(eli="DU/2024/1716", relationship_type="Akty zmienione") - Jakie akty zmienił

  • analyze_act_relationships(eli="DU/2024/1716", relationship_type="Podstawa prawna") - Na jakiej podstawie powstał

  • analyze_act_relationships(eli="DU/2024/1716", relationship_type="Akty zmieniające") - Co go zmienia

  • analyze_act_relationships(eli="DU/2024/1", relationship_type="Akty uznane za uchylone") - Uchylone akty

ParametersJSON Schema
NameRequiredDescriptionDefault
eliYesIdentyfikator ELI aktu. Format: "{wydawca}/{rok}/{pozycja}". Wydawcy: DU (Dziennik Ustaw), MP (Monitor Polski). Przykłady: "DU/2024/1716", "MP/2023/500", "DU/2024/1".
relationship_typeNoFiltruj po typie powiązania (dokładne dopasowanie do klucza z API). Dostępne typy: 'Akty zmienione', 'Akty zmieniające', 'Akty uchylone', 'Akty uchylające', 'Akty uznane za uchylone', 'Podstawa prawna', 'Podstawa prawna z art.', 'Teksty jednolite'. None = zwróć wszystkie powiązania.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A3.9/5.0
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It states that the tool 'returns information' but does not explicitly mention that it is read-only or has no side effects. For a query tool, this is acceptable but could be more explicit about impact (e.g., no state changes).

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, with a brief summary followed by illustrative examples. The structure is clear, though the examples partially replicate the schema's parameter list, making it slightly redundant.

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 that an output schema exists, the description need not explain return values. It sufficiently covers the tool's purpose and usage context. Minor gaps: no mention of error handling or edge cases like invalid ELI formats, but these are not critical for basic usage.

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 schema provides 100% coverage of parameter descriptions, including the valid relationship_type values and the ELI format. The tool description adds usage examples but no additional semantic detail beyond what the schema already contains.

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's function: analyzing relationships and references between legal acts. It names the specific resource (powiązania i referencje) and provides examples of distinct relationship types, distinguishing it from sibling tools like get_act_details or search_legal_acts.

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 includes concrete examples for different relationship_type values, making the intended use implicit. However, it lacks explicit guidance on when to prefer this tool over alternatives (e.g., 'use this when you need to see amendments' or 'not for full act content').

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

browse_actsA

Przeglądaj wszystkie akty prawne wydane przez wydawcę w danym roku.

Kiedy użyć: Gdy chcesz przeglądać cały rocznik wydawcy bez filtrowania. Kiedy NIE używać: Gdy szukasz po słowach kluczowych lub tytule → użyj search_legal_acts.

Zwraca listę aktów (domyślnie max 20). Wyniki zapisywane są do result_set_id — użyj filter_results aby je zawęzić.

Przykłady:

  • browse_acts(publisher="DU", year=2024) - Wszystkie akty DU z 2024

  • browse_acts(publisher="MP", year=2024) - Wszystkie akty MP z 2024

  • browse_acts(publisher="DU", year=2024, detail_level="full") - Ze szczegółami

  • browse_acts(publisher="DU", year=2024, detail_level="minimal") - Tylko podstawowe info

  • browse_acts(publisher="DU", year=2000) - Akty z roku 2000

  • browse_acts(publisher="DU", year=2024, limit=20, offset=20) - Druga strona rocznika

ParametersJSON Schema
NameRequiredDescriptionDefault
yearYesRok publikacji (np. 2024).
limitNoMaksymalna liczba wyników do zwrócenia. Domyślnie 20, maksymalnie 100 — wartości powyżej są przycinane, tak samo jak w pozostałych narzędziach listujących. Podawaj najmniejszą wystarczającą wartość.
offsetNoLiczba wyników do pominięcia (paginacja). Użyj razem z limit. Domyślnie 0.
publisherYesKod wydawcy: 'DU' (Dziennik Ustaw) lub 'MP' (Monitor Polski).
detail_levelNoPoziom szczegółowości: 'minimal' (ELI, tytuł, status), 'standard' (+ typ, daty, obowiązywanie), 'full' (wszystkie pola). Domyślnie 'standard'.standard

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.7/5.0
Behavior4/5

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

With no annotations provided, the description carries the behavioral disclosure burden. It discloses the default maximum of 20 results and the side effect that results are saved to result_set_id for later filtering. It does not mention permissions or edge cases, but for a read-only browse tool this is reasonable.

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 a one-line purpose, explicit usage rules, a behavior note, and examples. The examples are numerous but each illustrates a distinct parameter combination, and the overall length is justified by the clarity it provides.

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?

The description covers purpose, when to use and avoid, return behavior, result_set_id handling, pagination, and parameter variants via examples. An output schema exists, so the return structure is already specified. Nothing critical is missing for an agent to invoke this 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?

Schema coverage is 100%, so the baseline is 3. The description adds value through concrete examples showing valid publisher codes (DU/MP), detail_level values, pagination with limit/offset, and year usage. These examples clarify parameter combinations beyond the schema descriptions.

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 opens with a specific verb and resource: 'Przeglądaj wszystkie akty prawne wydane przez wydawcę w danym roku' (browse all legal acts issued by a publisher in a given year). It clearly differentiates from the sibling search_legal_acts by contrasting browsing a full annual set with searching by keywords or title.

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 'Kiedy użyć' and 'Kiedy NIE używać' sections state exactly when to use this tool versus search_legal_acts. It also advises using filter_results when narrowing is needed, giving the agent clear routing and follow-up guidance.

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

compare_actsA

Porównaj metadane dwóch aktów prawnych.

Porównuje tytuły, typy, statusy, daty (ogłoszenia, promulgacji, wejścia w życie), słowa kluczowe dwóch aktów. Identyfikuje różnice i wspólne elementy. Nie wymaga wcześniejszego załadowania treści (load_content).

Kiedy użyć: Aby porównać metadane dwóch aktów (np. wersji, powiązanych aktów). Kiedy NIE używać: Do porównania treści → załaduj oba akty i użyj read_act_content.

Przykłady:

  • compare_acts(eli_a="DU/2024/1692", eli_b="DU/2024/1716") - Porównaj dwa akty z 2024

  • compare_acts(eli_a="DU/2020/1444", eli_b="DU/2024/1692") - Porównaj starą i nową wersję

  • compare_acts(eli_a="DU/2024/1", eli_b="DU/2023/2000") - Porównaj akty z różnych lat

  • compare_acts(eli_a="DU/2024/100", eli_b="MP/2024/200") - Porównaj DU vs MP

  • compare_acts(eli_a="DU/2021/1500", eli_b="DU/2021/1600") - Porównaj podobne akty

ParametersJSON Schema
NameRequiredDescriptionDefault
eli_aYesIdentyfikator ELI pierwszego aktu. Format: "{wydawca}/{rok}/{pozycja}". Przykłady: "DU/2024/1716", "MP/2023/500".
eli_bYesIdentyfikator ELI drugiego aktu do porównania. Format: "{wydawca}/{rok}/{pozycja}". Przykłady: "DU/2024/1692", "DU/2020/1444".

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations present, the description carries the full behavioral burden. It clearly states what the tool compares, that it identifies differences and common elements, and that it does not require prior content loading. It does not explicitly label the operation as read-only, but comparing metadata strongly implies a non-mutating action, and the output schema covers return structure.

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 well-structured: summary line, detailed scope, usage guidance, exclusions, and examples. It is front-loaded with the most important information. The five examples are somewhat repetitive, but they demonstrate diverse comparison scenarios, so they earn their place without excessive bloat.

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 two-parameter comparison tool with an output schema, the description is complete: it states purpose, inputs, scope, prerequisites, exclusions, and examples. An agent has everything needed to select and invoke this tool correctly and to avoid confusing it with content-reading siblings.

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 input schema already documents both parameters with format and examples, achieving 100% coverage. The description adds use-case examples that pair eli_a and eli_b in meaningful ways (e.g., versions, different publishers), but this is illustrative rather than necessary parameter semantics. Baseline 3 is appropriate since the schema does the heavy lifting.

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 opens with a specific verb and resource: 'Porównaj metadane dwóch aktów prawnych' (compare metadata of two legal acts). It enumerates the exact metadata dimensions (titles, types, statuses, dates, keywords) and explicitly differentiates itself from content comparison via read_act_content, so the tool's role among siblings is 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 description provides explicit 'Kiedy użyć' and 'Kiedy NIE używać' sections, directing the agent to read_act_content for content comparison instead. It also notes that loading content beforehand is not required, which removes a possible mistaken prerequisite.

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

filter_resultsA

Filtruj i zawężaj wyniki wcześniejszego wyszukiwania aktów prawnych.

Działa jak grep/filtr na wynikach z search_legal_acts, browse_acts lub track_legal_changes. Wymaga result_set_id zwróconego przez te narzędzia. Przefiltrowane wyniki zapisywane są jako nowy zestaw (nowe result_set_id), który można filtrować dalej.

Limit rozmiaru wejścia: pojedyncze wywołanie przetwarza maksymalnie 100 rekordów (wartość domyślna, konfigurowalna przez operatora). Większy zestaw kończy się błędem, a nie wynikiem częściowym — dzięki temu brak dopasowania zawsze oznacza przeszukanie całego zestawu. Zawęź wyszukiwanie przed filtrowaniem.

Kiedy użyć: Po search_legal_acts/browse_acts/track_legal_changes aby zawęzić wyniki. Kiedy NIE używać: Gdy potrzebujesz nowych wyników z API → użyj search_legal_acts. Kiedy NIE używać: Do dowodzenia, że akt nie istnieje, dopóki pole source_scope odpowiedzi ma wartość 'page' — filtrowane jest wtedy okno, a nie cały zbiór, więc pusty wynik nie rozstrzyga. Odpowiedź sygnalizuje to polem no_match_is_inconclusive.

Przykłady:

  • filter_results(result_set_id="rs_1", type_equals="Rozporządzenie") - Tylko rozporządzenia

  • filter_results(result_set_id="rs_1", pattern="zdrow|apteka|lekar") - Akty związane ze zdrowiem

  • filter_results(result_set_id="rs_1", pattern="podatek|VAT", type_equals="Ustawa") - Ustawy podatkowe

  • filter_results(result_set_id="rs_1", date_field="promulgation_date", date_from="2024-01-01", date_to="2024-06-30") - Ogłoszone w I połowie 2024

  • filter_results(result_set_id="rs_1", sort_by="promulgation_date", sort_desc=True, limit=10) - 10 najnowszych na pierwszej stronie

  • filter_results(result_set_id="rs_1", pattern="\p{L}+ o ochronie") - Wzorzec z klasą unikodową

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldNoPole do przeszukania wzorcem RE2. Dostępne: 'title' (domyślne), 'eli', 'status', 'type', 'publisher'.title
limitNoMaksymalna liczba wyników na stronie odpowiedzi (domyślnie 20, maks. 100).
offsetNoNieujemne przesunięcie strony wyników.
date_toNoKoniec zakresu dat (YYYY-MM-DD). Wymaga ustawienia date_field.
patternNoWzorzec wyszukiwania w składni RE2. Wielkość liter jest ignorowana. Obsługiwany podzbiór składni: alternatywa (a|b), klasy znaków ([a-z], \d, \p{L}, [[:alpha:]]), kwantyfikatory (*, +, ?, {n,m}; maksymalnie 4 zmienne zakresy), kotwice (^, $), grupy nieprzechwytujące. Lookaround ((?=...), (?<=...), (?!...)) oraz backreferencje (\1) nie są obsługiwane. Przykłady: 'zdrow|Minister Zdrowia|apteka|lekar', 'budżet.*państw', 'transport|drogow', '\p{L}+ o ochronie'
sort_byNoPole do sortowania: 'title', 'year', 'pos', 'promulgation_date', 'effective_date', 'type', 'status'.
date_fromNoPoczątek zakresu dat (YYYY-MM-DD). Wymaga ustawienia date_field.
sort_descNoSortuj malejąco (True) lub rosnąco (False, domyślnie).
date_fieldNoPole daty do filtrowania zakresem: 'promulgation_date' (data ogłoszenia) lub 'effective_date' (data wejścia w życie).
type_equalsNoFiltruj po typie dokumentu (dokładne dopasowanie). Wartości: 'Ustawa', 'Rozporządzenie', 'Obwieszczenie', 'Komunikat', 'Uchwała', 'Zarządzenie', 'Wyrok', 'Postanowienie', 'Oświadczenie rządowe'
year_equalsNoFiltruj po roku publikacji (dokładne dopasowanie). Np. 2024.
result_set_idYesIdentyfikator zestawu wyników z poprzedniego wyszukiwania (np. 'rs_1'). Zwracany przez search_legal_acts, browse_acts lub track_legal_changes w polu result_set_id.
status_equalsNoFiltruj po statusie aktu (dokładne dopasowanie). Wartości: 'akt obowiązujący', 'akt uchylony', 'akt jednorazowy', 'obowiązujący', 'uchylony', 'uznany za uchylony', 'nieobowiązujący'

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.9/5.0
Behavior5/5

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

No annotations are provided, so the description correctly carries the full behavioral burden. It discloses the 100-record input limit, hard-error behavior instead of partial results, creation of a new result_set_id, the ability to chain further filters, and the no_match_is_inconclusive signal. This is unusually transparent.

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: purpose first, then core behavioral constraints, then explicit usage rules, then examples. Every section has a clear job, and the examples are dense and illustrative rather than filler.

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 13-parameter filtering tool, the description covers the full workflow: where the result_set_id comes from, how results are stored as new sets, size limits, error semantics, and the important source_scope caveat. Since an output schema exists, not restating return values is appropriate.

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 description coverage is 100%, so the baseline is 3. The description adds value through composed examples showing result_set_id combined with type_equals, pattern, date ranges, sort_by/limit, and regex syntax, which helps an agent see realistic parameter combinations. It does not systematically redefine parameters, but the schema already covers them well.

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 a clear, specific operation: filtering and narrowing results from earlier legal-act searches. It names the upstream tools (search_legal_acts, browse_acts, track_legal_changes) and uses a grep/filter analogy, which makes its role distinct and immediately understandable.

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?

There is an explicit 'Kiedy użyć' section naming the exact preceding tools, plus two explicit 'Kiedy NIE używać' cases: one routing to search_legal_acts when new API results are needed, and one warning against using an empty result as proof that an act does not exist when source_scope is 'page'. This gives an agent clear decision rules.

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

get_act_detailsA

Pobierz szczegółowe informacje o akcie prawnym.

Zwraca metadane: tytuł, status, daty (ogłoszenia, wejścia w życie, uchylenia), słowa kluczowe, spis treści i dostępne formaty (HTML/PDF).

Ustaw load_content=True aby załadować treść — to wymagane przed użyciem read_act_content (czytanie sekcji) i search_in_act (wyszukiwanie w treści).

Kiedy użyć: Aby zobaczyć metadane aktu (status, daty, spis treści). Kiedy NIE używać: Aby czytać treść aktu → użyj read_act_content (po załadowaniu).

Przykłady:

  • get_act_details(eli="DU/2024/1692") - Metadane i spis treści

  • get_act_details(eli="DU/2024/1692", load_content=True) - Załaduj treść do analizy

  • get_act_details(eli="DU/2024/1716") - Sprawdź status, daty i referencje

  • get_act_details(eli="MP/2024/100") - Akt z Monitora Polskiego

  • get_act_details(eli="DU/2021/1500") - Sprawdź status i daty obowiązywania

ParametersJSON Schema
NameRequiredDescriptionDefault
eliYesIdentyfikator ELI aktu. Format: "{wydawca}/{rok}/{pozycja}". Wydawcy: DU (Dziennik Ustaw), MP (Monitor Polski). Przykłady: "DU/2024/1716", "MP/2023/500", "DU/2024/1".
load_contentNoZaładuj treść aktu do Document Store (pamięć). WYMAGANE przed użyciem: read_act_content, search_in_act. Lifecycle: załadowane → TTL 2h → wygasa → wymaga ponownego załadowania. Domyślnie False.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.6/5.0
Behavior4/5

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

No annotations are provided, so the description carries the full behavioral burden. It discloses the significant side effect: setting load_content=True loads the act's content into the Document Store, with a lifecycle (TTL 2h, expiration, reload required) that affects subsequent calls. It describes what the call returns and what it does not do (read content). For a read-oriented tool, this is strong disclosure, though it does not cover edge behaviors like error conditions or rate limits.

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 well-structured and front-loaded: purpose sentence, return-value list, prerequisite note, then usage guidance and examples. The five examples are somewhat repetitive but serve as practical invocation patterns. Nothing is wasted, though the load_content prerequisite is stated twice (once in prose, once in the guidance section), a minor redundancy.

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?

With an output schema present, return values need no further explanation; the description covers what remains: when to use it, when not to, the prerequisite side effect of loading content, and example invocations for both parameters. An agent has everything needed to select and correctly call this tool across its use cases.

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%, so the baseline is 3; the description adds value beyond the schema by providing five concrete usage examples with real ELI identifiers (DU/2024/1692, MP/2024/100) and by clarifying how load_content connects to downstream tools (read_act_content, search_in_act). This reinforces format understanding and invocation patterns beyond the raw parameter descriptions.

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 opens with a specific verb and resource ('Pobierz szczegółowe informacje o akcie prawnym') and enumerates exactly what is returned: metadata, title, status, dates, keywords, table of contents, and formats. It also explicitly distinguishes itself from read_act_content by clarifying that content loading is a separate prerequisite, so an agent can tell it apart from sibling tools without opening schemas.

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 contains explicit 'Kiedy użyć' and 'Kiedy NIE używać' sections, names the alternative tool (read_act_content) for the excluded case, and states the prerequisite relationship (load_content=True required before read_act_content and search_in_act). This is exactly the when/when-not/alternatives guidance that makes tool selection reliable.

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

get_system_metadataA

Pobierz metadane systemu aktów prawnych.

Zwraca dostępne słowa kluczowe, wydawców, typy dokumentów, statusy i instytucje, które można wykorzystać do filtrowania i wyszukiwania aktów prawnych.

Przykłady:

  • get_system_metadata(category="keywords") - Dostępne słowa kluczowe do wyszukiwania

  • get_system_metadata(category="types") - Typy dokumentów (Ustawa, Rozporządzenie itp.)

  • get_system_metadata(category="publishers") - Wydawcy (DU, MP)

  • get_system_metadata(category="statuses") - Statusy aktów (obowiązujący, uchylony itp.)

  • get_system_metadata(category="all") - Wszystkie kategorie metadanych

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaksymalna liczba wartości metadanych na stronie odpowiedzi (domyślnie 20, maks. 100).
offsetNoNieujemne przesunięcie strony metadanych.
categoryNoKategoria metadanych: 'keywords' (słowa kluczowe do wyszukiwania), 'publishers' (wydawcy: DU, MP), 'statuses' (statusy aktów), 'types' (typy dokumentów: Ustawa, Rozporządzenie itp.), 'institutions' (instytucje wydające), 'all' (wszystkie kategorie). Domyślnie 'all'.all

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A3.6/5.0
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses what data it returns and that results can be used for filtering, but does not mention pagination behavior, limits at the endpoint level beyond schema defaults, or any response structure beyond the existing output schema. Adequate but not rich for an unannotated tool.

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?

Compact paragraph with a clear return summary followed by five concrete examples. No filler or repetition of schema content; the examples earn their place.

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?

With a rich output schema already present and all 3 parameters fully documented, the description covers the key intent and usage patterns. The only minor gap is the lack of explicit mention of when metadata-prefetching is necessary versus unnecessary, but this is a low-complexity lookup tool, so the definition is effectively complete.

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%, so the baseline is 3. The description's examples genuinely enrich 'category' by mapping each value to real content (DU, MP for publishers; Ustawa, Rozporządzenie for types), going beyond the schema text. This lifts it to 4.

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

Purpose4/5

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

Description states a specific verb ('Pobierz') and resource ('metadane systemu aktów prawnych'), and enumerates exactly what it returns (keywords, publishers, document types, statuses, institutions). It is clearly distinct from document-oriented siblings like search_legal_acts or browse_acts, though it stops short of explicitly naming a sibling it is not.

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 examples with concrete category values (keywords, types, publishers, statuses, all) give an agent actionable when-to-use guidance for filtering and search. However, it never names an alternative tool or states when NOT to use it, leaving some inference required.

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

list_loaded_documentsA

Wyświetl dokumenty załadowane do pamięci (Document Store).

Dokumenty ładowane są przez get_act_details(eli=..., load_content=True). Każdy dokument ma TTL 2h — po tym czasie wymaga ponownego załadowania.

Zwraca jedną stronę listy wraz z metadanymi paginacji (page_info).

Kiedy użyć: Aby sprawdzić jakie akty są załadowane i dostępne do czytania/wyszukiwania. Kiedy NIE używać: Do wyszukiwania aktów → użyj search_legal_acts.

Przykłady:

  • list_loaded_documents() - Wyświetl pierwszą stronę załadowanych dokumentów

  • list_loaded_documents(limit=5) - Pierwsze pięć dokumentów

  • list_loaded_documents(limit=5, offset=5) - Kolejna strona

  • list_loaded_documents(limit=100) - Cała zawartość Document Store

  • list_loaded_documents(offset=0) - Jawne przesunięcie od początku

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaksymalna liczba dokumentów na stronie odpowiedzi (domyślnie 20, maks. 100).
offsetNoNieujemne przesunięcie strony dokumentów. Domyślnie 0.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.7/5.0
Behavior4/5

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

With no annotations provided, the description carries the full behavioral burden. It discloses that documents are loaded via get_act_details(eli=..., load_content=True), that they expire after 2h TTL, and that the tool returns a single page with pagination metadata (page_info). It doesn't detail the exact page_info fields, but an output schema exists to cover that.

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 and efficient: a brief lead sentence, essential context about loading and TTL, clear usage guidelines, and a compact example list. Every section earns its place and the description is 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?

Given the tool's low complexity (2 optional params, full schema coverage, existing output schema), the description is complete. It tells the agent what the tool does, when to use it, when not to use it, how documents get loaded, and what the response shape is (page_info). Nothing essential is missing.

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% for the two parameters, so the baseline is 3. The description adds value by giving concrete usage examples with limit/offset combinations (e.g., limit=5, offset=5 for next page) and clarifies the pagination behavior, which goes beyond the schema's defaults and constraints.

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 opens with a specific verb and resource: 'Wyświetl dokumenty załadowane do pamięci (Document Store)' — clearly stating the tool lists loaded documents. It also explicitly distinguishes itself from search operations by naming search_legal_acts as the alternative, preventing confusion.

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 has dedicated 'Kiedy użyć' (when to use) and 'Kiedy NIE używać' (when NOT to use) sections, and it names the alternative tool ('Do wyszukiwania aktów → użyj search_legal_acts'). It also adds useful context about TTL (2h) that informs when a document needs reloading.

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

list_result_setsA

Wyświetl aktywne zestawy wyników przechowywane w pamięci.

Każde wyszukiwanie (search_legal_acts, browse_acts, track_legal_changes) oraz filtrowanie (filter_results) tworzy zestaw wyników z unikalnym result_set_id. To narzędzie pokazuje aktywne zestawy (TTL: 1h) wraz z metadanymi paginacji.

Kiedy użyć: Aby sprawdzić jakie result_set_id są dostępne do filtrowania. Kiedy NIE używać: Do wyszukiwania nowych aktów → użyj search_legal_acts.

Przykłady:

  • list_result_sets() - Pierwsza strona aktywnych zestawów

  • list_result_sets(limit=5) - Pięć najnowszych wpisów strony

  • list_result_sets(limit=5, offset=5) - Kolejna strona

  • list_result_sets(limit=100) - Wszystkie aktywne zestawy

  • list_result_sets(offset=0) - Jawne przesunięcie od początku

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaksymalna liczba zestawów na stronie odpowiedzi (domyślnie 20, maks. 100).
offsetNoNieujemne przesunięcie strony zestawów. Domyślnie 0.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.3/5.0
Behavior4/5

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

With no annotations, the description carries the full burden. It discloses the TTL (1h), that only active sets are shown, and that pagination metadata is included. It implies a read-only operation. This is strong, though it doesn't mention auth or error behavior, which is acceptable for a simple list tool.

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 structured with a purpose statement, usage context, exclusions, and examples. It is slightly lengthy but well-organized and front-loaded. Every section adds value; no redundancy.

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?

The tool is simple with two optional params, and an output schema exists. The description explains the concept of result sets, TTL, and provides examples covering pagination. It doesn't cover edge cases like empty result sets, but that's minor. Overall complete for an agent to use correctly.

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%, so the schema already documents both parameters. The description adds examples of usage (e.g., limit=5, offset=5) that clarify practical limits, but it doesn't add meaning beyond what the schema provides. 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?

The description clearly states the verb ('list') and the resource ('active result sets stored in memory'). It distinguishes this tool from siblings by explaining that each search/filter creates a result set and that this tool lists them, and explicitly contrasts with search_legal_acts for new searches.

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?

Provides explicit 'Kiedy użyć' (when to use) and 'Kiedy NIE używać' (when not to use) sections, naming the alternative tool. The examples illustrate common calling patterns (pagination, limits), giving clear situational guidance.

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

read_act_contentA

Czytaj treść załadowanego aktu prawnego po sekcjach.

WYMAGANIE: Akt musi być wcześniej załadowany za pomocą get_act_details(eli=..., load_content=True).

Bez parametru section zwraca spis treści (listę sekcji). Z parametrem section zwraca treść wybranej sekcji.

Kiedy użyć: Aby czytać treść konkretnej sekcji załadowanego aktu. Kiedy NIE używać: Aby zobaczyć metadane → użyj get_act_details.

Przykłady:

  • read_act_content(eli="DU/2024/1692") - Spis treści (lista sekcji)

  • read_act_content(eli="DU/2024/1692", section="Art. 1") - Treść artykułu 1

  • read_act_content(eli="DU/2024/1692", section="Rozdział 1") - Treść rozdziału 1

  • read_act_content(eli="DU/2024/1692", section="Dział II") - Treść działu II

  • read_act_content(eli="MP/2024/100") - Spis treści aktu z MP

ParametersJSON Schema
NameRequiredDescriptionDefault
eliYesIdentyfikator ELI aktu. Format: "{wydawca}/{rok}/{pozycja}". Wydawcy: DU (Dziennik Ustaw), MP (Monitor Polski). Przykłady: "DU/2024/1716", "MP/2023/500", "DU/2024/1". Akt MUSI być wcześniej załadowany przez get_act_details(eli=..., load_content=True).
limitNoMaksymalna liczba elementów lub znaków na stronie. Domyślnie zależy od trybu odczytu.
offsetNoNieujemne przesunięcie początku strony. Domyślnie 0.
sectionNoIdentyfikator sekcji do odczytania. Można użyć surowego ID (np. 'art_1') lub formy czytelnej (np. 'Art. 1', 'Rozdział 1') - obsługiwane jest elastyczne dopasowanie. Aby poznać dostępne section_id, użyj get_act_details(load_content=true) i sprawdź tabelę treści. Jeśli None — zwraca spis treści z dostępnymi sekcjami.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.5/5.0
Behavior4/5

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

With no annotations, the description carries the behavioral burden well. It discloses the crucial precondition, the branching behavior when section is omitted or present, and flexible optional section matching (raw IDs or readable forms). It could go further by explicitly describing failure behavior when the act isn't loaded or a section isn't found, but it is still well above the minimal achievable.

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 organized first with a clear one-sentence purpose, then decisive blocks: WYMAGANIE, Kiedy użyć, and Kiedy NIE use. The examples are compact and practical, and no sentence feels wasted.

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 that there are no annotations but the schema is rich and an output schema is present, the description is effectively complete. It covers a precondition, differentiation from siblings, return behavior, optional section semantics, and multiple invocation examples, allowing an agent to select and call the tool without ambiguity.

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?

Input schema description/question_complete_coverage is 100%, and the schema already explains eli format, section forms, limit/offset behavior, and section meaning. The description adds concrete examples and contextual use, but the semantic load is already primarily carried by the schema, so 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?

The description states a specific action and resource: 'Czytaj treść załadowanego aktu prawnego po sekcjach'. It also defines the two output modes (spis treści without section, szkż section content with section), and clearly differentiates the tool from get_act_details for metadata.

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 provides an explicit precondition: the act must first be loaded via get_act_details(eli=..., load_content=True). It also includes explicit 'Kiedy użyć' and 'Kiedy NIE używać' guidance, naming get_act_details as the alternative for metadata retrieval.

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

search_in_actA

Wyszukaj termin w treści załadowanego aktu prawnego.

WYMAGANIE: Akt musi być wcześniej załadowany za pomocą get_act_details(eli=..., load_content=True).

Zwraca jedną stronę trafień z kontekstem, metadanymi paginacji i informacją o sekcji.

Przykłady:

  • search_in_act(eli="DU/2024/1692", query="straż") - Znajdź "straż" w akcie

  • search_in_act(eli="DU/2024/1692", query="obowiązek", context_chars=200) - Z krótszym kontekstem

  • search_in_act(eli="DU/2024/1692", query="art. 5", limit=10, offset=0) - Pierwsza strona trafień

  • search_in_act(eli="DU/2024/1692", query="kara", limit=5, offset=5) - Kolejna strona trafień

  • search_in_act(eli="DU/2024/1692", query="termin") - Domyślna strona (do 20 trafień)

ParametersJSON Schema
NameRequiredDescriptionDefault
eliYesIdentyfikator ELI aktu. Format: "{wydawca}/{rok}/{pozycja}". Wydawcy: DU (Dziennik Ustaw), MP (Monitor Polski). Przykłady: "DU/2024/1716", "MP/2023/500", "DU/2024/1". Akt MUSI być wcześniej załadowany przez get_act_details(eli=..., load_content=True).
limitNoMaksymalna liczba trafień na stronie. Domyślnie 20, maksimum 100.
queryYesTermin do wyszukania w treści aktu (np. 'podatek', 'obowiązek', 'art. 5'). Wielkość liter jest ignorowana.
offsetNoNieujemne przesunięcie początku strony. Domyślnie 0.
context_charsNoLiczba znaków kontekstu przed i po każdym trafieniu. Domyślnie 500. Maksymalnie 2000 — wartości większe są przycinane do 2000, a odpowiedź zawiera wtedy wskazówkę o przycięciu. Wywołanie nie kończy się błędem.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYesThe actual response data
hintsNoSuggested next steps
metadataNoAdditional metadata

TDQS

A4.1/5.0
Behavior4/5

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

With no annotations provided, the description carries the full disclosure burden. It discloses the return shape (one page of hits with context, pagination metadata, section info), the prerequisite load behavior, and the non-error truncation behavior for context_chars. It could additionally state what happens if the act was not previously loaded, but the prominent requirement line mitigates this gap.

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 front-loaded: purpose statement, then requirement, then return format, then examples. Each of the five examples demonstrates a distinct parameter combination (plain search, context_chars, limit/offset, offset continuation, defaults), so none is wasted, though the example block is slightly long.

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?

The output schema covers return values, so the description needn't explain them. The critical precondition, pagination behavior, context truncation edge case, and parameter defaults are all documented. What's missing is routing guidance across sibling tools (when to choose search_in_act over read_act_content or search_legal_acts), but the tool is callable correctly with the information provided.

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%, so the baseline is 3. The description adds marginal value via five examples showing concrete query values, context_chars, and limit/offset pagination combinations, but it does not explain parameters beyond what the schema already documents.

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 a specific verb and resource: 'Wyszukaj termin w treści załadowanego aktu prawnego' (search a term in the content of a loaded legal act). The qualifier 'załadowanego' (loaded) meaningfully distinguishes it from sibling search_legal_acts (searching act metadata/database) and read_act_content (reading content linearly), so an agent can tell them apart.

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 gives an explicit, critical precondition: the act must first be loaded via get_act_details(eli=..., load_content=True), stated with a WYMAGANIE (requirement) label and repeated in the eli parameter description and examples. It provides clear usage context and pagination patterns, but does not explicitly state when-not-to-use or name alternatives like search_legal_acts or read_act_content.

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

TDQS

A4.2/5.0
Disambiguation5/5

Every tool targets a distinct action in the legal-research workflow: browsing, searching, loading, reading, filtering, comparing, and computing dates. Potentially similar pairs such as search_legal_acts vs browse_acts vs track_legal_changes are explicitly differentiated in descriptions with separate usage cases.

Naming Consistency5/5

Tool names follow a consistent lowercase verb_noun pattern (list_loaded_documents, search_legal_acts, get_act_details, calculate_legal_date) with specific, descriptive verbs. There are no mixed casing or style conventions.

Tool Count5/5

13 tools is within the well-scoped range and each tool fills a clear role in the legal research surface, without excessive redundancy or an empty feeling. The count feels justified by the breadth of search, browsing, content loading, relationship analysis, filtering, and date utilities.

Completeness4/5

The server provides strong coverage for legal research: searching, browsing, loading, reading, comparing metadata, tracking changes, filtering results, and analyzing relationships. Minor gaps exist around retrieving raw HTML/PDF document formats and comparing full act texts directly, but core workflows are fully usable and have no dead ends.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables querying and analyzing Slovak legislation via natural language, including full-text search, provision retrieval, and EU law integration.
    99
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    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

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/numikel/law-scrapper-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server