Skip to content

v1.1.0

Choose a tag to compare

@malkreide malkreide released this 31 Jul 15:27
48654b6

[1.1.0] – 2026-07-30

Fixed

  • API-Basispfade und EPG-Endpunkt korrigiert (Basis: PR #46 von @aburossi).
    /video/v3 und /audio/v3 sind bei Apigee nicht mehr als Basepath
    registriert — sie antworten mit 302 auf developer.srgssr.ch, exakt wie
    ein frei erfundener Pfad. Weil 302 kein 4xx ist, greift raise_for_status()
    nicht: resp.json() lief auf HTML und der User sah nur «Unerwarteter
    Fehler». Neu /videometadata/v2 und /audiometadata/v2.

    Das EPG adressiert einen Tag über /{bu}/{tv|radio}/stations/{station}
    Unternehmenseinheit und Sendertyp sind Pfadsegmente, nicht Query-Parameter.
    Dafür kommt broadcast_type (tv/radio, Default tv) als neues Feld an
    srgssr_epg_get_programs. Die Antwort heisst programs (nicht
    programList) und trägt die Zeit in dateTimes.startTime, den Text in
    shortDescription/longDescription; die alten Feldnamen bleiben als
    Fallback erhalten.

    Die epg://{bu}/{channel_id}/{date}-Resource hing am selben alten Pfad und
    wird mitgezogen. URL-Bau und Programm-Extraktion liegen jetzt in
    _epg_station_url() / _extract_raw_programs() in tools/epg.py, die sich
    Tool und Resource teilen — die beiden Oberflächen können nicht mehr auf
    verschiedene Endpunkte auseinanderlaufen. Die Resource-URI hat kein Feld für
    den Sendertyp und ist damit TV-only; für Radio das Tool verwenden. Das steht
    jetzt auch in der Resource-Beschreibung.

    Sender-IDs folgen der Stations-Schreibweise (srf-1 statt srf1);
    Tool-Beispiele, Prompt-Default, READMEs, EXAMPLES.md und der Live-Test sind
    nachgezogen.

    Die EPG-Mocks in tests/test_unit.py matchen exakte URLs statt eines
    Präfix-Patterns — ein Mock, der jeden Pfad unter EPG_BASE akzeptiert, wäre
    über den nicht migrierten Resource-Aufruf grün durchgelaufen. Drei neue
    Tests decken die Station-Response-Form, den radio-Pfadsegment-Fall und den
    Endpunkt der Resource ab.

Removed

  • fastmcp als Abhängigkeit entfernt — nichts importierte es. Es war ein
    Rest aus der Zeit, in der dieser Server mcp transitiv darüber bezog; mit
    der direkten mcp-Deklaration wurde es überflüssig und blieb trotzdem
    stehen. Nichts schlug fehl, deshalb fiel es nicht auf.

    Gemessen in frischen venvs, nur Runtime-Abhängigkeiten: 84 → 44 Pakete,
    also 40 weniger, und keines kommt hinzu. Was mit verschwindet, ist der Punkt:
    redis und burner-redis, py-key-value-aio, pydocket,
    prometheus_client, keyring/SecretStorage/jeepney, Authlib,
    joserfc, websockets, cronsim, jsonschema-path. Ein Redis-Client, ein
    Keyring-Stack und eine OAuth-Bibliothek wurden in einen read-only Server über
    öffentliche Daten installiert, wegen einer ungenutzten Zeile.

    tests/test_dependencies.py hält das offen in beide Richtungen: der Test
    prüft Gleichheit von „deklariert" und „importiert", nicht bloss Abwesenheit.
    Ein legitimer Wiedereinzug von fastmcp verlangt also die Deklaration zurück,
    statt den Test zu löschen. Ein zweiter Test prüft den Scanner selbst gegen
    mcp, damit ein kaputter Import-Scan nicht jede Gleichheit trivial erfüllt.
    Beide Richtungen sind mutationsgetestet.

    Die Dependabot-Gruppen nennen fastmcp nicht mehr — das Pattern wäre mit der
    Abhängigkeit toter Code geworden.

    Geprüft: 162 passed / 14 deselected (156 plus die sechs neuen Tests),
    Coverage 96.7 % gegen das 80-%-Gate, ruff check src/ clean, und ein Install
    in einem frischen venv startet den Server mit allen 15 Tools. Kein
    yaml-Import im Projekt, obwohl PyYAML mit wegfällt.

Changed

  • Migration auf die mcp 2.x Server-API. Pin von >=1.28.1,<2 auf
    >=2.0.0,<3. Die Untergrenze ist hart: 2.0.0 hat mcp.server.fastmcp ohne
    Kompatibilitätsschicht entfernt, dieser Code läuft also gar nicht mehr auf
    1.x — ein >=1.x-Bereich würde einen Resolver eine Version wählen lassen,
    die beim Import scheitert. FastMCPMCPServer.

    Bestehende Clients sehen keinen Unterschied: der Legacy-initialize-Handshake
    deckelt weiterhin bei 2025-11-25 — nachgemessen, nicht aus einem
    Konstantennamen geschlossen; wer 2026-07-28 anfragt, bekommt 2025-11-25
    zurück. mcp 2.x bedient über denselben Server aber eine zweite, „moderne"
    Ära (Per-Request-Envelope; die erste Anfrage des Clients entscheidet), die
    2026-07-28 erreicht. Ein 2.x-Client verhandelt also die neuere Revision. Kein
    Bruch, aber auch kein Protokoll-No-op.

    PROTOCOL_VERSION bleibt bei 2025-06-18 — der Guard gegen
    SUPPORTED_PROTOCOL_VERSIONS (jetzt mcp.types.version statt
    mcp.shared.version) hält, der Wert ist also weiter gültig.

  • _build_mcp()_transport_kwargs(). MCPServer.settings trägt
    host/port/mount_path nicht mehr; sie sind run()-Argumente. Die alte
    Zuweisung ist kein stiller No-op, sondern wirft ValueError — ein Test hält
    beides fest, damit der Grund für die Indirektion nicht verloren geht.

    mount_path hat in 2.x keine Entsprechung. In 1.x schrieb es nur den
    angekündigten Message-Endpoint um, während die Route unpräfixiert blieb —
    korrekt allein dann, wenn eine äussere ASGI-App den Server unter diesem
    Präfix einhängt. 2.x nutzt einen Wert für beides, also bildet
    SRGSSR_MCP_MOUNT_PATH auf message_path ab, wodurch die Route mitwandert.
    Für streamable-http wurde die Einstellung bereits in 1.x ignoriert
    (run() gab sie nie an run_streamable_http_async weiter); sie bleibt
    ignoriert, denn ein Durchreichen wäre jetzt ein TypeError statt eines
    stillen No-ops.

    Geprüft: 156 passed / 14 deselected gegen die 1.x-Baseline von 152 — die
    Differenz sind genau die fünf neuen Tests minus dem einen ersetzten.
    ruff check src/, Coverage-Gate (96.7 % gegen 80 %) und ein Install in
    einem frischen venv sind grün.

Fixed

  • Declared mcp explicitly and capped it at <2. This server imports
    mcp.server.fastmcp, but never declared mcp — it arrived transitively via
    fastmcp. mcp 2.0.0, published 2026-07-28, removed that module, and the
    only reason installs still work today is an upper bound inside fastmcp-slim,
    a package this project never names. The dependency that is actually imported
    is now declared and bounded here rather than left to someone else's resolver.

  • User-Agent no longer reports a stale version. Three numbers had drifted
    apart: pyproject.toml said 1.0.3, __init__.__version__ said 0.1.0, and
    the hard-coded USER_AGENT in _http.py said 1.0.0. Every request to the
    SRG SSR APIs carried the stale value. __version__ now comes from the
    installed distribution metadata (importlib.metadata, generated from
    pyproject.toml) and the User-Agent is derived from it. Guarded by
    tests/test_version.py.