Skip to content

Releases: malkreide/bag-epl-mcp

v1.0.3

Choose a tag to compare

@malkreide malkreide released this 02 Aug 21:04
9d62f28

[1.0.3] - 2026-08-02

Behoben

  • structlog hatte keine Obergrenze, und der Index fuehrt bereits einen Major
    oberhalb der Untergrenze.
    Deklariert war structlog>=25.5.0; auf PyPI liegt
    26.1.0. Das Artefakt aendert sich nicht — die Antwort des Resolvers auf
    die naechste frische Installation schon, und genau so wurde
    swiss-energy-mcp 0.3.3 uninstallierbar, als mcp 2.0.0 das Modul entfernt
    hat, das es importierte.

    Neu structlog>=25.5.0,<27. Die Grenze ist gemessen, nicht geraten: dieses Paket installiert
    und importiert heute gegen structlog 26.1.0, die Obergrenze laesst also zu,
    was nachweislich funktioniert, und stoppt nur den naechsten, unbekannten
    Major.

Ein Abhaengigkeitsbereich erreicht die Nutzenden nur ueber ein neues
Release, daher der Versions-Bump. Am Code aendert sich nichts.

v1.0.2

Choose a tag to compare

@malkreide malkreide released this 31 Jul 20:01
761390e

[1.0.2] - 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 den HTTP-Client
    bag-epl-mcp/<version> (+github.com/malkreide/bag-epl-mcp).

    Die Version stammt aus importlib.metadata und kann nicht getrennt vom
    Paket driften.

Fixed

  • Streamable-HTTP wies unter jedem echten Hostnamen mit 421 ab (SEC-005).
    _build_http_app() rief mcp.streamable_http_app() ohne host auf. 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 das Argument selbst auf 127.0.0.1 defaultet, traf das jedes
    Cloud-Deployment mit MCP_HOST=0.0.0.0. Vor der Migration ging host an den
    FastMCP-Konstruktor, wo dieselbe Logik den echten Bind sah und den Schutz
    korrekt ausliess.

    Besonders tückisch hier: /healthz ist bewusst von der Transport-Prüfung
    ausgenommen (SCALE-004) und antwortete weiter mit 200. Ein Load-Balancer sah
    also einen gesunden Server, der keine einzige MCP-Anfrage bedienen konnte.

    Der Bind reist jetzt in die App, und eine echte Allow-List wird aus dem neuen
    MCP_ALLOWED_HOSTS gebaut. 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. https://claude.ai (CORS-Default) wird in die
    Origin-Liste übernommen, sonst weist der Transport genau den Browser-Client ab,
    für den die CORS-Konfiguration existiert.

    13 neue Tests, darunter der tragende Fall „richtiger Hostname, falscher Port"
    und einer, der die /healthz-Asymmetrie festhält — sie ist gewollt, war aber
    auch der Grund, warum der Fehler verdeckt blieb. Mutationsgetestet: nimmt man
    den host-Kwarg wieder weg, reproduziert der Test das 421.

    Geprüft mit den wörtlichen CI-Kommandos: 70 passed / 8 deselected,
    ruff check src/ tests/ clean, Versions-Sync OK.

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.

v0.2.0 — Production hardening (audit remediation)

Choose a tag to compare

@malkreide malkreide released this 01 Jun 16:30
af2bb44

v0.2.0 — Production hardening (audit remediation)

This release closes the security/production-readiness audit: from
11 pass / 26 partial / 7 fail at baseline to 39 pass / 5 partial / 0 fail
across the 44 applicable checks — all failed checks resolved, 0 blocking findings.

Highlights

Cloud transport, for real

  • Env-driven transport selection (MCP_TRANSPORT, MCP_HOST, MCP_PORT); the
    advertised Streamable-HTTP transport is now actually implemented (default stays
    stdio). CORS allow-list (MCP_CORS_ORIGINS) exposing Mcp-Session-Id.
  • Multi-stage Dockerfile (non-root UID 10001, HEALTHCHECK), render.yaml
    blueprint, /healthz probe, $PORT fallback.

Security (SSRF / egress / transport)

  • Egress allow-list guard (ALLOWED_HOSTS + _assert_safe_url): HTTPS-only,
    host allow-list, resolved-IP blocklist before any outbound request.
  • DNS-pinned HTTP transport (_PinnedNetworkBackend): host resolved exactly
    once, IP validated and the TCP connection pinned to it, TLS SNI/cert still
    verified against the hostname — eliminates the resolve/connect TOCTOU (SEC-005).
  • Pydantic strict=True input models; errors surface as sanitized MCP isError
    results (no raw exception text to the model). docs/SECURITY.md threat model.

MCP / SDK

  • Typed structured tool output (SDK-002): all 6 tools declare Pydantic output
    schemas and return a hybrid CallToolResult — curated Markdown/JSON in
    content plus validated structuredContent. Machine-readable, no UX loss.
  • ctx: Context injection in every tool; tool annotations
    (readOnlyHint/openWorldHint); protocolVersion constant + policy.

Observability

  • Structured JSON logging (structlog) to stderr (stdout reserved for the
    stdio JSON-RPC stream); per-call bound context (tool, correlation_id).
  • OpenTelemetry on by default (OBS-006): MCP_OTEL_ENABLED defaults on;
    silent no-op without the [otel] extra. TracerProvider + OTLP + Starlette/httpx
    auto-instrumentation. MCP_OTEL_ENABLED=0 to disable.

Ops / supply chain

  • Pooled lifespan-managed httpx.AsyncClient; unit/live test split;
    scripts/snapshot_tool_hashes.py records SHA-256 of tool definitions (SEC-022);
    deploy/haproxy.cfg sticky-session reference for horizontal scaling.

Remaining (non-blocking, by design)

SEC-009 (no-auth, accepted-risk), SCALE-002/003 (multi-instance, deferred),
SEC-014/015 (enterprise gateway, accepted-risk). See
docs/audit/2026-06-01-reaudit/.

Full changelog: v0.1.0...v0.2.0

v1.0.0 – Initial Release

Choose a tag to compare

@malkreide malkreide released this 31 Mar 04:44
44c4571
Add GitHub Actions workflow for publishing to PyPI