Skip to content

Releases: malkreide/termdat-mcp

v0.1.3

Choose a tag to compare

@malkreide malkreide released this 02 Aug 21:44
05e676b

[0.1.3] - 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; 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,<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.1.2

Choose a tag to compare

@malkreide malkreide released this 31 Jul 15:39
1d2afe9

[0.1.2] — 2026-07-30

Fixed

  • The User-Agent reports the actual package version again. The published
    0.1.1 sent termdat-mcp/0.1.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.

Documented

  • The public API exposes less than the website — a coverage limit of the
    source, not a scope setting on our side. Follow-up to #11: @dfch supplied the
    12 entry IDs the website lists for «Quellensteuer»; the API returns 7 at
    maximum recall (every language, all 11 fields, infix wildcard, all
    classifications and collections), and the two sets overlap in exactly one
    entry, 447912. Fetching the missing IDs directly via /v2/Entry returns
    HTTP 200 with an empty body — they are not served at all, so no query can
    reach them. The one exception, 1557, is served but carries status
    In Bearbeitung in a collection marked «(aufgehoben)», which suggests the
    search index covers validated entries while the website also shows drafts and
    repealed material.

    This corrects an earlier guess. The residual was described in #11 as probably
    a counting difference — the website listing designations where the API counts
    entries. The IDs disprove that: they are twelve distinct entries. Recorded
    here so the wrong explanation does not outlive the measurement.

    Documented in both READMEs, and in the search_terms docstring, where it
    matters most: a model told only that «a term may genuinely be absent» will
    conclude «absent from TERMDAT», which is false for entries the website shows.
    The docstring now distinguishes the two.

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.1.1

Choose a tag to compare

@malkreide malkreide released this 27 Jul 14:24
185e0dc

[0.1.1] — 2026-07-27

Fixed

  • MCP Registry publish was blocked by a missing ownership marker. The
    registry verifies ownership of a PyPI package by looking for an
    mcp-name: <server-name> marker in the published package README. The marker
    was added to README.md after 0.1.0 shipped, so it never reached PyPI — and
    PyPI releases are immutable, so it cannot be added to the 0.1.0 artifact
    retroactively. This release carries it into the package metadata.
  • Search was confined to the VARIA classification (#11, reported by
    @dfch). /v2/Search restricts an ID-less query to a "default set (=VARIA)",
    so every unfiltered search covered 1 of 23 subject areas — and reported the
    result as a normal empty answer. search_terms, translate_term and
    check_terms now send the full classification set unless the caller narrows
    it. «Quellensteuer» 0 → 3 entries, «Pensionskasse» 1 → 21.
  • fields could widen a search but never narrow it. Unsent Field.* flags
    keep their API-side default (Terminus, Name, Abbreviation,
    Phraseology = true), so fields="Terminus" was a no-op. All eleven flags
    are now sent explicitly.
  • Default field set widened to include Definition, Note and Source;
    translate_term and check_terms stay on the four designation fields, so a
    term merely mentioned in a definition is never reported as an equivalent.
  • Misleading scope caveat. The search_terms docstring presented an empty
    result as probable out-of-scope, which invited models to invent a designation
    instead of retrying. It now documents Lucene wildcards and asks for a retry;
    an empty SearchResult carries a hint field saying the same.

Security

  • SEC-016 (NeighborJack): the SSE transport now binds to 127.0.0.1 by
    default instead of 0.0.0.0. Binding to 0.0.0.0 is an explicit opt-in that
    logs a stderr warning when used outside a container. README/SECURITY updated.
  • SEC-021: code-layer egress allow-list (ALLOWED_HOSTS + assert_host_allowed),
    enforced before every request; docs/network-egress.md.
  • SEC-018: input bounds at the tool boundary — max_results 1–100, plus
    string/list length limits on search_term, term, terms, entry_ids, fields.
  • SEC-007: hardened non-root Dockerfile for SSE deployments.
  • SEC-005 / SCALE-002: accepted-risk ADRs for DNS pinning and stateful load
    balancing (docs/adr/0001, 0002).

Added

  • Typed configuration via pydantic-settings (settings.py); new env vars
    TERMDAT_MCP_LOG_LEVEL, TERMDAT_MCP_CORS_ORIGINS, TERMDAT_MCP_VOCAB_TTL.
  • Structured logging via structlog, pinned to stderr as JSON (logging_config.py).
  • FastMCP lifespan owning the shared HTTP client (cleanup on shutdown).
  • Explicit CORS for the SSE transport, exposing only Mcp-Session-Id.
  • CONTRIBUTING.md / CONTRIBUTING.de.md; scheduled/manual live-test workflow.
  • Expanded test suite (12 → 28 offline tests): per-tool coverage, error paths,
    egress allow-list, tool-schema input bounds.
  • MCP best-practice audit against the portfolio catalog (68 checks, 36
    applicable) under audits/: production-ready; the 19-item hardening
    backlog from that audit is addressed by the changes above.

Changed

  • api_status and the client no longer forward raw upstream exception strings to
    the model (OBS-002 error-detail masking); detail goes to the structured log.
  • check_terms runs its per-term lookups concurrently (asyncio.gather) and
    reports progress via ctx when available (ARCH-007 / SDK-003).
  • Tools grouped rationale + MCP-primitives note documented in the READMEs.

v0.1.0 — Initial Release

Choose a tag to compare

@malkreide malkreide released this 20 Jul 13:37
696921e

First public release of termdat-mcp — an MCP server for TERMDAT, the
terminology database of the Swiss Federal Administration. Seven read-only
tools expose officially validated designations of Swiss authorities,
departments and legal acts across DE / FR / IT / EN.

Install: uvx termdat-mcp

See CHANGELOG.md for details.