Releases: malkreide/lindas-mcp
Release list
v0.2.1
[0.2.1] - 2026-08-02
Fixed
-
structlogcarried no upper bound, and the index already serves a major past
the floor. The declared range wasstructlog>=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 howswiss-energy-mcp0.3.3 became
uninstallable whenmcp2.0.0 removed the module it imported.Now
structlog>=24.1,<27. The bound is measured rather than guessed: this package
installs and imports againststructlog 26.1.0today, 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.0
[0.2.0] — 2026-08-02
This release exists so that a repair reaches the people running the server:
the published 0.1.0 cannot be installed any more. It declares mcp with
no upper bound, and mcp 2.0.0 removed mcp.server.fastmcp — so a fresh
pip install lindas-mcp resolves to 2.0.0 and the console script dies on
startup with ModuleNotFoundError. Measured against the real artefact in an
empty venv, cold and warm interpreter alike.
The repository has carried the fix since the 2.x migration was merged; it was
simply never released, and main kept the same version number as the broken
artefact — so nothing contradicted it.
Changed (breaking)
- Migrated to the
mcpPython SDK 2.x. The server API moved from
mcp.server.fastmcptomcp.server.mcpserverwith no compatibility shim,
and the dependency is nowmcp>=2.0.0,<3. The tool surface is unchanged —
what breaks is embedding this server's Python API and the dependency floor.
Anyone who must stay onmcp1.x should stay on 0.1.0, and pin an upper
bound themselves, because the published 0.1.0 has none.
Added
- Portfolio-standard repository structure:
CONTRIBUTING.md/.de,
SECURITY.md/.de,EXAMPLES.md,PUBLISHING.md,docs/network-egress.md,
docs/roadmap.md,Dockerfile,compose.yaml,.dockerignore,.gitignore,
claude_desktop_config.json,server.json(MCP Registry metadata), and
.github/workflows (ci,live,publish) +dependabot.yml. tool-definitions.lock.jsonwith a SEC-022 CI integrity check: a committed
hash snapshot of every tool name and its argument surface, verified by
server.tool_manifest()so a silent rug-pull fails the build.- Full
mcp-auditrun underaudits/2026-07-26T125407-Z-lindas-mcp/
(production-ready; 11 hardening findings). - Audit remediation — structured logging (OBS-003): stderr-bound JSON logs
viastructlog(logging_config.py), per-tool-call events (tool, duration,
outcome) and aLOG_LEVELenv var. stdout stays reserved for JSON-RPC. - Audit remediation — not-found heuristics (ARCH-003):
search_cubesand
resolve_municipalitynow returnmatch_type(exact/none) and an
actionablesuggestionon an empty result. - Audit remediation —
Contextinjection (SDK-003): tools accept aContext
and emit progress/debug events for long-running SPARQL calls.
Changed
- Egress hardening (SEC-021): added a code-layer
ALLOWED_HOSTSallow-list
andassert_host_allowed()inlindas/client.py; the HTTP client now uses
follow_redirects=Falseso an off-host redirect is surfaced as an error. - Binding default (SEC-016): the SSE / streamable-http transport now defaults
HOSTto127.0.0.1(loopback) instead of0.0.0.0; binding to all
interfaces is an explicit opt-in and prints a stderr warning. The container
image setsHOST=0.0.0.0deliberately. - Connection pooling (SDK-001): a single
httpxclient is now built once by
a FastMCPlifespanand shared across all tool calls (client_session()),
instead of a fresh client per call. Direct unit-test calls fall back to a
per-call client. - Tool annotations (ARCH-009): all tools now also set
idempotentHint: true
andopenWorldHint: true. - Schema-level input validation (SEC-018): tool arguments use
Annotated[..., Field(ge=/le=/min_length=/max_length=)]— out-of-range inputs
are rejected as aValidationErrorat the boundary instead of being clamped. - Error masking (OBS-002): unexpected exceptions are logged server-side and
surfaced to the LLM as a generic message;SparqlError/UpstreamError(which
carry only the public endpoint's own diagnostics) still propagate unchanged. - CORS (SDK-004): the HTTP transport serves a CORS-wrapped app exposing the
Mcp-Session-Idheader, with origins configurable viaALLOWED_ORIGINS
(comma-separated; default*) instead of a hardcoded wildcard. - Documented the single-file
server.pyrationale (ARCH-011) and the MCP
protocol-version policy (ARCH-012). - Aligned
pyproject.tomlto the portfolio floor (mcp>=1.28.1,structlog,
Python 3.13 classifier,IssuesURL).
v0.1.0 — Initial Release
[0.1.0] — 2026-07-21
Added
- Initial release. 7 read-only tools over the LINDAS SPARQL endpoint:
search_cubes,get_cube_structure,query_cube_observations,
list_publishers,resolve_municipality,run_sparql,api_status. - Three-layer, extraction-ready
lindas/package:client.py(raw SPARQL/HTTP),
queries.py(anchored templates),cube.py(vocabulary guardrail). - Two-phase cube access with automatic code-to-label resolution.
- Version deduplication in
search_cubes(latest_only, default on). - Dual transport via
LINDAS_MCP_TRANSPORT; retry 2s/4s/8s; 400 passthrough. - Bilingual documentation (EN/DE) and full probe report in
docs/probe-lindas.md.
Known findings
Discovered during the live probe on 2026-07-21.
- Broad SPARQL times out.
COUNT(*)over the whole store andDISTINCT ?g
over all named graphs ran 70–90 s into a timeout (HTTP 000). The same question
anchored on?x a cube:Cubeanswers in ~2 s. Every template is anchored;
run_sparqlwarns and caps runtime. - Observations hang off
cube:observationSet, never directly off the cube.
The naive?cube cube:observation ?obsreturns zero rows. Caught by a live
test, not by mocks. - Dimension values are codes, not labels (region
1805, level4). Code
lists resolve viash:in → rdf:rest*/rdf:first → schema:name. Cantons are
coded asld.admin.ch/canton/<n>even without an sh:in list; the bare number
is surfaced. - Cubes are versioned in the URI (
.../cube/2024-1) withschema:version
andschema:creativeWorkStatus. Search deduplicates to the newest published. - Licences are per cube and often Fedlex URIs (
fedlex.data.admin.ch/eli/cc/),
which doubles as a join to fedlex-mcp. - Publishers deduplicate cleanly on
dcterms:creator(a single URI), not on
schema:publisher(multilingual, splits one body into several rows).