Skip to content

Releases: malkreide/swiss-electricity-mcp

v0.2.5

Choose a tag to compare

@malkreide malkreide released this 02 Aug 21:32
846ce03

[0.2.5] - 2026-08-02

Fixed

  • structlog carried no upper bound, and the index already serves a major past
    the floor.
    The declared range was structlog>=24.1.0; PyPI has been serving
    26.1.0. The artefact does not change — the resolver's answer to the next
    fresh install does, and that is exactly how swiss-energy-mcp 0.3.3 became
    uninstallable when mcp 2.0.0 removed the module it imported.

    Now structlog>=24.1.0,<27. The bound is measured rather than guessed: this package
    installs and imports against structlog 26.1.0 today, so the cap admits what
    demonstrably works and stops only the next, unknown major.

A dependency range only reaches users through a new release, hence the
version bump. No code changed.

v0.2.4

Choose a tag to compare

@malkreide malkreide released this 31 Jul 15:37
ebd8435

[0.2.4] - 2026-07-30

Fixed

  • The User-Agent reports the actual package version again. The published
    0.2.3 sent swiss-electricity-mcp/0.2.0 to every upstream — the version string was
    hardcoded and had been left behind by earlier bumps. The version now comes
    from the package metadata, so it can no longer drift from the package.

  • HTTP-Modus 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, galt das auch
    für den SWISS_ELECTRICITY_HOST=0.0.0.0-Bind, den dieses Modul für Container
    dokumentiert. Vor der Migration ging host an den FastMCP-Konstruktor, wo
    dieselbe Logik den echten Bind sah und den Schutz korrekt ausliess.

    Der Bind reist jetzt in die App, und eine echte Allow-List wird aus dem neuen
    SWISS_ELECTRICITY_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.

  • SWISS_ELECTRICITY_CORS_ORIGINS funktionierte nie in der dokumentierten
    Form.
    Vorbestehender Fehler, den das zweite Listen-Feld sichtbar gemacht
    hat: pydantic-settings JSON-dekodiert komplex typisierte Felder aus der
    Umgebung, bevor ein mode="before"-Validator läuft. Eine kommagetrennte
    Liste löste damit SettingsError aus, und _split_csv war für Env-Eingaben
    unerreichbar — toter Code. Beide Felder tragen jetzt NoDecode.

    14 neue Tests, darunter der tragende Fall „richtiger Hostname, falscher Port":
    nur er unterscheidet eine portgenaue Allow-List von einer, die alles
    durchlässt. Mutationsgetestet in beide Richtungen — NoDecode entfernen bricht
    die CSV-Tests, den host-Kwarg entfernen reproduziert das 421.

    Geprüft mit dem wörtlichen CI-Kommando (pytest -m "not live" -q):
    58 passed, 3 deselected; ruff check src/ tests/ clean.

v0.2.0 — Audit remediation (production-ready)

Choose a tag to compare

@malkreide malkreide released this 04 Jun 03:30
c159273

Audit-remediation release. Closes all critical/high findings from the
mcp-audit-skill audit. The
re-audit reports production-ready: 36 pass · 0 fail · 2 partial · 6 todo
(catalog hash 091f446b, run-id 2026-06-03T191138-Z-swiss-electricity-mcp).

🔒 Security

  • SEC-016 — HTTP host defaults to 127.0.0.1; 0.0.0.0 only explicitly (containers).
  • SEC-007 — Multi-stage Dockerfile, non-root user (UID 10001), HEALTHCHECK.
  • SEC-018 — SPARQL-injection closed: category whitelist + canton escaping + string bounds.
  • SEC-004/005/021 — HTTPS-only, host-allow-listed egress gate (assert_url_allowed); see docs/network-egress.md.
  • SEC-022 — Tool definitions pinned in tool-definitions.lock.json with a drift test.
  • SEC-019/013/008 — Security posture documented (docs/security-posture.md): lethal-trifecta, secrets, supply chain.

🔭 Observability

  • OBS-003/004 — Structured JSON logging via structlog to stderr.
  • OBS-006 — Opt-in OpenTelemetry tracing (per-tool spans + httpx auto-instrumentation).
  • OBS-002 — Upstream errors logged server-side, masked in client responses.
  • OBS-001 — Execution- and protocol-error-path tests.

🧩 SDK & architecture

  • SDK-001 — Shared HTTP clients owned by a FastMCP lifespan.
  • SDK-003ctx: Context on SPARQL tools; progress reporting in compare.
  • SDK-004 — CORS middleware exposes/allows Mcp-Session-Id.
  • ARCH-004 — Configuration centralised in a Pydantic-Settings object; clients via lifespan context.
  • ARCH-009 — All 12 tools declare explicit MCP annotations (readOnlyHint/openWorldHint).
  • ARCH-007tariff_compare_municipalities fetches concurrently (asyncio.gather).
  • ARCH-003 — Search tools return match_type + actionable suggestion on zero hits.

⚙️ CI / infrastructure

  • ARCH-011/OPS-001 — CI (test.yml: ruff + pytest on 3.11–3.13) and publish.yml (PyPI OIDC).
  • ARCH-005 — Gitleaks secret scan on every push/PR.
  • ARCH-012 — Dependabot; mcp[cli] pinned >=1.2.0,<2.0.0; MCP protocol-version policy in README.
  • OPS-002/003 — ASCII architecture diagram; Phase-1 declaration + docs/roadmap.md.

Full changelog: CHANGELOG.md · compare v0.1.0...v0.2.0