Skip to content

v0.3.4

Choose a tag to compare

@malkreide malkreide released this 31 Jul 20:06
d62c5cc

[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.metadata und 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 mit mcp.streamable_http_app() ohne host.
    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 Default 127.0.0.1 ist, traf das das dokumentierte
    uvicorn … --factory --host 0.0.0.0-Deployment.

    Der Kern der Sache: uvicorn ruft eine --factory ohne Argumente auf. Das
    --host-Flag erreicht die App also nie — es konfiguriert nur den Listener.
    Die Factory liest den Bind jetzt aus denselben Settings wie main()
    (MCP_HOST/MCP_PORT bzw. 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 den host-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 in tests/test_server.py bestehen vor und sind
    netzabhängig — mit gestashten Änderungen fallen sie identisch.

Fixed

  • Capped mcp at <2. mcp 2.0.0, published 2026-07-28, removed
    mcp.server.fastmcp — the module this server imports. With the previous
    unbounded >=1.28.1 every fresh resolve picked 2.0.0 and failed at import
    with ModuleNotFoundError, in CI and for anyone running pip install alike.
    Verified in both directions: 2.0.0 fails, <2 resolves 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.transcripts with two tools:
    • parlament_search_transcripts — searches the Transcript entity and returns
      short, citable excerpts (snippet, ~320 chars) with an AB citation, a
      stable source_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 at max_chars and paginated via offset/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) with respx fixtures built from
    real, shortened API responses, plus @pytest.mark.live end-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 verbatim Text
    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_transcripts tool (it searched Text
    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.json updated accordingly (7 tools); clients
    should re-approve the tool set.

Known findings (transcript API, live-probed 2026-07-19)

  • The Transcript entity carries no page/column field, so AB <year> N <page>
    cannot be constructed — a stable substitute reference + SubjectId URL is used.
  • Language is the edition, not the speech language (LanguageOfText is the
    latter). Structured coverage starts 1999-12-06; 1891–1999 is scans only.
  • $count over a Text substringof is ~40 s; retrieving top-N is ~3 s. An
    exact prefilter (IdSession/VoteBusinessNumber) cuts full-text reads to ~1 s.