v0.3.4
[0.3.4] - 2026-07-31
Hinzugefuegt
-
Der Server nennt jetzt seinen Namen. Bisher ging gegenueber jedem
Upstream der httpx-Default hinaus: der Betreiber der Datenquelle sah
eine Bibliothek, nicht uns, und hatte keinen Weg, uns bei Fehlverhalten
zu erreichen. Neu traegt jeden der 2 HTTP-Clients
parlament-mcp/<version> (+github.com/malkreide/parlament-mcp).Die Version stammt aus
importlib.metadataund kann nicht getrennt vom
Paket driften.
Fixed
-
Die HTTP-Factory wies unter jedem echten Hostnamen mit 421 ab (SEC-005).
create_http_app()baute die App mitmcp.streamable_http_app()ohnehost.
Unter mcp 2.x ist das kein neutraler Default: das SDK leitet daraus seine
Host-Allow-List ab und aktiviert bei loopback-artigem Wert automatisch
127.0.0.1:*. Da der Default127.0.0.1ist, traf das das dokumentierte
uvicorn … --factory --host 0.0.0.0-Deployment.Der Kern der Sache: uvicorn ruft eine
--factoryohne Argumente auf. Das
--host-Flag erreicht die App also nie — es konfiguriert nur den Listener.
Die Factory liest den Bind jetzt aus denselben Settings wiemain()
(MCP_HOST/MCP_PORTbzw.PORT). Der Docstring nennt beides ausdrücklich,
weil die Env-Vars neben den uvicorn-Flags wie Redundanz aussehen und keine
sind.Der Server hat zwei HTTP-Pfade; nur die Factory war betroffen.
main()ruft
mcp.run(transport=…, host=settings.host, …), dort sieht das SDK den echten
Bind.Eine echte Allow-List entsteht aus dem neuen
MCP_ALLOWED_HOSTS; ohne diese
Variable bleibt der Schutz auf einem Nicht-Loopback-Bind bewusst aus und der
Aufrufer warnt — eine geratene Liste wäre genau der 421-Fall.Unabhängig von
MCP_BEARER_TOKENS. Die Token-Prüfung sagt, wer fragt,
die Host-Prüfung, unter welchem Namen der Server angesprochen wird. Ein
Rebinding-Angriff läuft in einem Browser, der das Token bereits hält; ein Test
hält fest, dass ein gültiges Bearer-Token einen fremden Host nicht rettet.14 neue Tests, darunter der tragende Fall „richtiger Hostname, falscher Port".
Mutationsgetestet: nimmt man denhost-Kwarg wieder weg, fallen genau die
zwei Tests, die ihn betreffen.Geprüft mit den wörtlichen CI-Kommandos: die 14 neuen Tests grün,
ruff check src/ tests/clean, Versions-Sync OK. Die beiden
test_live_*-Fehler intests/test_server.pybestehen vor und sind
netzabhängig — mit gestashten Änderungen fallen sie identisch.
Fixed
- Capped
mcpat<2.mcp2.0.0, published 2026-07-28, removed
mcp.server.fastmcp— the module this server imports. With the previous
unbounded>=1.28.1every fresh resolve picked 2.0.0 and failed at import
withModuleNotFoundError, in CI and for anyone runningpip installalike.
Verified in both directions: 2.0.0 fails,<2resolves to 1.29.0 and imports
cleanly. Migrating to the 2.x API (mcp.server.mcpserver) stays a separate,
deliberate piece of work.
Added
- Amtliches Bulletin – verbatim debate transcripts. New separated module
parlament_mcp.transcriptswith two tools:parlament_search_transcripts— searches theTranscriptentity and returns
short, citable excerpts (snippet, ~320 chars) with an AB citation, a
stablesource_url(SubjectId), speaker/council/date/language, and hard
caps (default 10, max 30 hits). Filters:keyword(full text),speaker_name,
session_id,council,business_number,date_from/date_to.parlament_get_transcript— fetches the verbatim full text of a single
speech by ID, capped atmax_charsand paginated viaoffset/next_offset.- Mandatory Pydantic-v2 fields on every result:
citation,source_url,
speaker,council,date,language,is_excerpt,total_length_chars.
- Transcript tests (
tests/test_transcripts.py) withrespxfixtures built from
real, shortened API responses, plus@pytest.mark.liveend-to-end checks. - README (EN/DE): second Anchor Demo Query (verbatim transcripts), transcript
path in the architecture diagram, Art. 5 URG note (official proceedings are
copyright-exempt → verbatim quotation allowed), a Testing section, and expanded
Known Limitations.
Changed
- Transcript language handling. Filtering
Language eq 'DE'now serves purely
to deduplicate the three byte-identical editions (DE/FR/IT); the verbatimText
is always the original wording and the real spoken language is surfaced as
language(de/fr/it). French/Italian speeches are no longer hidden by the
edition filter — verified live. - Transcript reads use a dedicated 45 s timeout and retry with exponential backoff
(metadata tools are unchanged at 20 s, no retry).
BREAKING
- Removed the speculative
parlament_get_transcriptstool (it searchedText
without an indexed prefilter — timing out on broad queries — returned vote-result
and procedural rows as "transcripts", and carried no citation). It is replaced by
the two tools above.tool-hashes.jsonupdated accordingly (7 tools); clients
should re-approve the tool set.
Known findings (transcript API, live-probed 2026-07-19)
- The
Transcriptentity carries no page/column field, soAB <year> N <page>
cannot be constructed — a stable substitute reference +SubjectIdURL is used. Languageis the edition, not the speech language (LanguageOfTextis the
latter). Structured coverage starts 1999-12-06; 1891–1999 is scans only.$countover aTextsubstringofis ~40 s; retrieving top-N is ~3 s. An
exact prefilter (IdSession/VoteBusinessNumber) cuts full-text reads to ~1 s.