v4.2.0
Changelog - v4.2.0
All notable changes to the law-scrapper-mcp project for version v4.2.0 will be documented in this file.
The format is based on Keep a Changelog 1.1.0,
and this project adheres to Semantic Versioning 2.0.0.
[4.2.0] - 2026-09-02
Sweep of the deferred review findings from clusters 2–9 (GitHub issues #14, #16, #18–#21, #27,
#31, #32, #34, #38–#41, #45–#47, #49, #50, #52, #54, #55). No MCP tool changes its name,
parameter names or response shape; several fields and validations now behave as documented.
Added
LAW_MCP_API_MAX_SERVER_PAUSE(default60, at most600): the cap on how long a
server-sentRetry-Aftermay hold all outbound traffic, previously a fixed constant. The
clamp now lives in the rate limiter itself, so every caller ofpause_for()inherits it (#46,
#47).- Document downloads are budgeted while streaming: the client aborts a HTML or PDF body as
soon as it passesLAW_MCP_DOC_STORE_MAX_SIZE_BYTES(or earlier, fromContent-Length),
raising the same PolishContentTooLargeErrorthe conversion step already used. The whole
body is no longer materialised before the size check (#19). search_in_actbuilds only the requested page of match positions (DocumentStore.scan_page)
instead of every match in the document; memory is bounded bylimit,total_countstays
exact (#16).- CI builds the Docker image and smoke-tests both transports (
/healthover streamable-http, an
initializeround-trip over STDIO), so the image cannot silently stop building again. - CI runs the MCP conformance suite for real again, on
@modelcontextprotocol/conformance
0.2.0-alpha.11— the first line that accepts the SDK's protocol era2026-07-28— with the
baseline inconformance-baseline.yml(#14).
Changed
search_legal_actsvalidateslimitandoffsetlike every other listing tool: a malformed
or non-positivelimit, or a malformed or negativeoffset, is a tool error with a Polish
message instead of silently yielding page one.limitstill has no upper clamp; its
description now says why (#18, #19).track_legal_changessendslimitandoffsetupstream and readstotalCount, so a date
range wider than one API page is stored as apage-scoped set with a truthful
corpus_countinstead of being labelledcompleteafter silent upstream truncation (#54).DocumentStore.loadrefuses a document over the size limit instead of truncating it (the
branch was unreachable from production since 4.0.0, and its character slice against a byte
budget was wrong for Polish text) (#32, #21).LAW_MCP_LOG_LEVELaccepts exactlyDEBUG,INFO,WARNING,ERROR,CRITICAL
(case-insensitive);WARN-style aliases that used to work on STDIO and crash on
streamable-http are rejected at startup on both.LAW_MCP_SHUTDOWN_GRACEis an integer
(#31).LAW_MCP_AUTH_REQUIRED_SCOPESwithLAW_MCP_AUTH_MODE=beareris rejected at startup: a
shared secret has no scope semantics, so the setting could never restrict anything. Remove
the variable or switch tooauth(#38).LAW_MCP_API_RATE_PER_SECONDis bounded to0.1–100and rejects non-finite values;
LAW_MCP_API_RATE_BURSTto1–1000;LAW_MCP_API_MAX_ATTEMPTSto1–20(#47, #27)./healthis exempt from the per-client rate limiter only for loopback peers; a probe from
another host is metered like any other request (#39).- JWKS discovery no longer runs while holding the verifier's lock, so concurrent requests
during a slow identity-provider response are not serialised behind it (#39). build_http_app()derives the auth settings and token verifier from the livesettings
object, the same place it already reads host and rate-limit settings (#41).uvicornis a declared dependency rather than a transitive one (#31).- Removed two internal methods without production callers:
DocumentStore.search()and
MetadataService.get_metadata();ContentTooLargeErroris exported from the client
facade (#20, #31).
Fixed
- A bracketed IPv6 allowlist entry with a trailing slash (
http://[::1]:8080/) was classified as
remote after the[::1].evil.comtightening, so a value 4.1.0 accepted refused startup under
auth_mode=none; the host parser now drops any path suffix before classifying, for every form. - A cache entry for
acts/searchis shared bysearch_legal_acts,browse_actsand
track_legal_changeswhenever their parameters coincide, and the first writer's TTL used to
decide freshness for all three. Each read is now bounded by its own caller's TTL, so a call
documented as 300 s is never served a 600 s entry. - The Docker image did not build: the builder stage ran
uv sync --no-editable, which builds
the project wheel, withoutREADME.mdin the build context, and hatchling refuses to build
a package whose declared readme is missing. Broken since thereadmefield was added on
2026-08-10; surfaced by the Glama indexer. A test now pins that the builder stage copies
every file the package metadata names. effective_dateinsearch_legal_acts,browse_actsandtrack_legal_changeswas always
null: the code read adateEffectkey that neither list endpoint returns. It now reads
entryIntoForce, the field the API does return, so theeffective_datefilter and sort in
filter_resultsoperate on data (#52).- A negative
offsetpassed tosearch_legal_actsreachedapi.sejm.gov.plverbatim (#18). httpx's request log line carried the full search URL — keywords and title included — at
INFO, the path the F13 audit finding never covered. Thehttpxandhttpcoreloggers are now
held atWARNINGor above (#34).[::1].evil.cominLAW_MCP_ALLOWED_HOSTSwas classified as loopback because the bracket
parser ignored everything after]; trailing content other than a port or:*now disables
the loopback match (#38).backoff()raisedOverflowErrorfor a large attempt number and returned sub-basedelays
for attempts below 1; the exponent is now bounded and computed in floating point (#27).LAW_MCP_API_RATE_PER_SECOND=infwas accepted and silently disabled pacing (#47).- The pacing deadline and the rate limiter measured time on different clocks when a limiter
was injected, which made the deadline bound fail open; both now share the limiter's clock
(#45). StaticTokenVerifieraccepts no empty secret, so an emptyAuthorizationvalue can never
match one (#38).- A section with
end_pos == 0was treated as open-ended bysection_for_position;
overlapping sections are now rejected when a document is loaded (#20).