i14y-mcp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- i14y_mcp-0.2.0/.dockerignore +19 -0
- i14y_mcp-0.2.0/.github/dependabot.yml +19 -0
- i14y_mcp-0.2.0/.github/workflows/ci.yml +33 -0
- i14y_mcp-0.2.0/.github/workflows/live.yml +25 -0
- i14y_mcp-0.2.0/.github/workflows/publish.yml +84 -0
- i14y_mcp-0.2.0/.gitignore +12 -0
- i14y_mcp-0.2.0/CHANGELOG.md +121 -0
- i14y_mcp-0.2.0/CONTRIBUTING.de.md +42 -0
- i14y_mcp-0.2.0/CONTRIBUTING.md +40 -0
- i14y_mcp-0.2.0/Dockerfile +44 -0
- i14y_mcp-0.2.0/LICENSE +21 -0
- i14y_mcp-0.2.0/PKG-INFO +319 -0
- i14y_mcp-0.2.0/PUBLISHING.md +157 -0
- i14y_mcp-0.2.0/README.de.md +271 -0
- i14y_mcp-0.2.0/README.md +266 -0
- i14y_mcp-0.2.0/SECURITY.de.md +73 -0
- i14y_mcp-0.2.0/SECURITY.md +68 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/applicability.json +342 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/audit-meta.json +13 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/audit-report.md +1031 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-003-not-found-heuristics.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-007-capability-aggregation.md +43 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-008-three-primitives.md +43 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-012-protocol-version-pinning.md +43 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-001-protocol-vs-execution-errors.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-002-mask-error-details.md +43 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-003-structured-logging.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OPS-003-phase-architecture.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SCALE-004-container-healthcheck.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SCALE-006-resource-limits-fd.md +43 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-001-fastmcp-lifespan.md +47 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-003-context-injection.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-004-cors-session-id-exposure.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-014-tool-allow-listing.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-015-tool-poisoning-detection.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-016-binding-prevention.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-018-input-validation-boundaries.md +46 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-021-egress-allow-list.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-022-tool-hash-namespace.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/profile.json +24 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/arch-ops.json +162 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/scale-obs.json +119 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/sdk-ch.json +79 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/sec.json +176 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/summary.json +229 -0
- i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/verification-results.json +624 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/applicability.json +342 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/audit-meta.json +13 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/audit-report.md +331 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/ARCH-011-single-file-server.md +46 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/ARCH-012-protocol-version-pinning.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/OBS-002-mask-error-details.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/SDK-004-cors-allow-origins.md +45 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/SEC-022-tool-namespace-prefix.md +44 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/profile.json +24 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/arch-ops.json +163 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/scale-obs.json +114 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/sdk-ch.json +54 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/sec.json +171 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/summary.json +128 -0
- i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/verification-results.json +595 -0
- i14y_mcp-0.2.0/audits/tracker.csv +2 -0
- i14y_mcp-0.2.0/claude_desktop_config.json +8 -0
- i14y_mcp-0.2.0/compose.yaml +24 -0
- i14y_mcp-0.2.0/docs/network-egress.md +46 -0
- i14y_mcp-0.2.0/docs/probe-i14y.md +137 -0
- i14y_mcp-0.2.0/docs/roadmap.md +41 -0
- i14y_mcp-0.2.0/pyproject.toml +62 -0
- i14y_mcp-0.2.0/server.json +28 -0
- i14y_mcp-0.2.0/src/i14y_mcp/__init__.py +7 -0
- i14y_mcp-0.2.0/src/i14y_mcp/__main__.py +6 -0
- i14y_mcp-0.2.0/src/i14y_mcp/client.py +196 -0
- i14y_mcp-0.2.0/src/i14y_mcp/logging_config.py +46 -0
- i14y_mcp-0.2.0/src/i14y_mcp/mappers.py +269 -0
- i14y_mcp-0.2.0/src/i14y_mcp/models.py +314 -0
- i14y_mcp-0.2.0/src/i14y_mcp/server.py +733 -0
- i14y_mcp-0.2.0/tests/test_client.py +138 -0
- i14y_mcp-0.2.0/tests/test_tools.py +255 -0
- i14y_mcp-0.2.0/tool-definitions.lock.json +139 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Python dependencies (incl. the mcp SDK — keep protocol support current)
|
|
4
|
+
- package-ecosystem: "pip"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "monthly"
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
labels:
|
|
10
|
+
- "dependencies"
|
|
11
|
+
|
|
12
|
+
# GitHub Actions
|
|
13
|
+
- package-ecosystem: "github-actions"
|
|
14
|
+
directory: "/"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "monthly"
|
|
17
|
+
labels:
|
|
18
|
+
- "dependencies"
|
|
19
|
+
- "ci"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v7
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
pip install -e ".[dev]"
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: |
|
|
30
|
+
PYTHONPATH=src pytest tests/ -m "not live"
|
|
31
|
+
|
|
32
|
+
- name: Lint
|
|
33
|
+
run: ruff check src/ tests/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Live API tests
|
|
2
|
+
|
|
3
|
+
# OPS-001: the live suite hits the real I14Y API, so it is excluded from PR CI
|
|
4
|
+
# and run on a schedule (and on demand) instead.
|
|
5
|
+
on:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: "17 5 * * 1" # Mondays, 05:17 UTC
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
live:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v7
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: pip install -e ".[dev]"
|
|
23
|
+
|
|
24
|
+
- name: Run live tests
|
|
25
|
+
run: PYTHONPATH=src pytest tests/ -m live -v
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v7
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v7
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.11"
|
|
19
|
+
|
|
20
|
+
- name: Install build tools
|
|
21
|
+
run: pip install build
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: python -m build
|
|
25
|
+
|
|
26
|
+
- name: Check distribution metadata
|
|
27
|
+
run: |
|
|
28
|
+
pip install twine
|
|
29
|
+
twine check dist/*
|
|
30
|
+
|
|
31
|
+
- name: Upload build artifact
|
|
32
|
+
uses: actions/upload-artifact@v7
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
name: Publish to PyPI
|
|
39
|
+
needs: build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/project/i14y-mcp/
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- name: Download build artifact
|
|
49
|
+
uses: actions/download-artifact@v8
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
with:
|
|
57
|
+
skip-existing: true
|
|
58
|
+
|
|
59
|
+
publish-mcp:
|
|
60
|
+
name: Publish to MCP Registry
|
|
61
|
+
needs: publish
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
permissions:
|
|
64
|
+
id-token: write # required for MCP Registry Trusted Publishing (OIDC)
|
|
65
|
+
contents: read
|
|
66
|
+
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v7
|
|
69
|
+
|
|
70
|
+
- name: Sync server.json version to the release tag
|
|
71
|
+
run: |
|
|
72
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
73
|
+
jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' \
|
|
74
|
+
server.json > server.tmp && mv server.tmp server.json
|
|
75
|
+
|
|
76
|
+
- name: Install mcp-publisher
|
|
77
|
+
run: |
|
|
78
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
79
|
+
|
|
80
|
+
- name: Authenticate to MCP Registry (GitHub OIDC)
|
|
81
|
+
run: ./mcp-publisher login github-oidc
|
|
82
|
+
|
|
83
|
+
- name: Publish server to MCP Registry
|
|
84
|
+
run: ./mcp-publisher publish
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-07-24
|
|
10
|
+
|
|
11
|
+
First production-ready release. Aligns the repository with the Swiss Public Data
|
|
12
|
+
MCP portfolio, runs a full MCP best-practice audit, and remediates all findings.
|
|
13
|
+
|
|
14
|
+
**Audit verification:** production-ready ✅ — run-id
|
|
15
|
+
`2026-07-24T091742-Z-i14y-mcp`, catalog hash `091f446b2796…`, results 36 pass ·
|
|
16
|
+
0 fail · 5 non-blocking partials · 3 todo. Details under
|
|
17
|
+
[`audits/`](audits/2026-07-24T091742-Z-i14y-mcp/audit-report.md).
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Portfolio-standard repository scaffolding to align with the other Swiss Public
|
|
21
|
+
Data MCP servers: `Dockerfile`, `compose.yaml`, `claude_desktop_config.json`,
|
|
22
|
+
`.dockerignore`, `.gitignore`, and a `server.json` manifest for the MCP Registry.
|
|
23
|
+
- GitHub Actions workflows: `ci.yml` (matrix 3.10–3.13), `live.yml` (scheduled
|
|
24
|
+
live API suite), `publish.yml` (PyPI + MCP Registry via OIDC Trusted Publishing),
|
|
25
|
+
plus `.github/dependabot.yml`.
|
|
26
|
+
- Contributor and security documentation: `CONTRIBUTING.md` / `CONTRIBUTING.de.md`,
|
|
27
|
+
`SECURITY.md` / `SECURITY.de.md`, and `PUBLISHING.md`.
|
|
28
|
+
- MCP best-practice audit results under `audits/` (44 checks, 19 findings).
|
|
29
|
+
- `docs/roadmap.md` documenting the Read-only-First phase architecture (OPS-003).
|
|
30
|
+
- `HEALTHCHECK` in the Docker image so orchestrators can detect an unhealthy
|
|
31
|
+
container (SCALE-004).
|
|
32
|
+
- Structured JSON logging on stderr via `structlog`, with per-request severity
|
|
33
|
+
levels (OBS-003).
|
|
34
|
+
- `Context` injection across all tools for client-visible progress/logging, with
|
|
35
|
+
per-endpoint progress in `api_status` (SDK-003).
|
|
36
|
+
- `search_catalog` now returns a `match_type` and an actionable `hint` on an empty
|
|
37
|
+
result instead of a bare empty list (ARCH-003).
|
|
38
|
+
- `docs/network-egress.md` documenting the code- and network-layer egress
|
|
39
|
+
controls (SEC-021).
|
|
40
|
+
- `tool-definitions.lock.json`, a committed hash snapshot of the tool set and
|
|
41
|
+
each tool's argument surface (names + required), verified by a test as a
|
|
42
|
+
rug-pull guard that is stable across SDK patch upgrades (SEC-022).
|
|
43
|
+
- README sections on MCP primitives (tools-only rationale, ARCH-008) and the MCP
|
|
44
|
+
protocol version / update policy (ARCH-012).
|
|
45
|
+
- Container FD `ulimits` and a memory reservation in `compose.yaml` (SCALE-006).
|
|
46
|
+
- All tool annotations now also set `idempotentHint: true` and
|
|
47
|
+
`openWorldHint: true` (every tool is a side-effect-free GET against an external
|
|
48
|
+
API), alongside the existing `readOnlyHint`/`destructiveHint` (ARCH-009).
|
|
49
|
+
- Second audit run under `audits/` confirming production-readiness (36 pass, 5
|
|
50
|
+
residual non-blocking partials, 0 fail).
|
|
51
|
+
- Tests for the shared client, error masking, CORS session-header exposure, empty
|
|
52
|
+
search hints, boundary input rejection, egress control and tool-lock integrity.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- **HTTP transports now default to `HOST=127.0.0.1` (loopback)** instead of
|
|
56
|
+
`0.0.0.0`; binding to all interfaces is an explicit opt-in and warns on stderr
|
|
57
|
+
outside a container (SEC-016). The Docker image sets `HOST=0.0.0.0` on purpose;
|
|
58
|
+
remote/PaaS deployments must set it explicitly.
|
|
59
|
+
- A single pooled `httpx.AsyncClient` is now created once in a FastMCP lifespan and
|
|
60
|
+
reused across tool calls instead of being rebuilt per call (SDK-001).
|
|
61
|
+
- Tool arguments now carry strict schema constraints (`ge`/`le`, `min_length`,
|
|
62
|
+
whitelist `pattern` on IDs), so malformed input is rejected at the boundary
|
|
63
|
+
instead of silently clamped (SEC-018).
|
|
64
|
+
- The anchor demo query is now answered in two tool calls; `get_dataset` is
|
|
65
|
+
documented as the aggregated detail tool (ARCH-007).
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
- SSE / streamable-http now sets CORS to expose the `Mcp-Session-Id` header, so
|
|
69
|
+
browser MCP clients keep their session (SDK-004).
|
|
70
|
+
- Upstream failures surface actionable execution errors (pointing at
|
|
71
|
+
`api_status` / `search_catalog`), covered by execution- and protocol-error
|
|
72
|
+
tests (OBS-001).
|
|
73
|
+
|
|
74
|
+
### Security
|
|
75
|
+
- Upstream 4xx error bodies are no longer embedded verbatim in client-facing
|
|
76
|
+
errors; a categorised message (HTTP status + path) is surfaced instead (OBS-002).
|
|
77
|
+
- Code-layer egress allow-list (`ALLOWED_HOSTS` frozenset) with
|
|
78
|
+
`follow_redirects=False`, refusing any off-host redirect (SEC-021).
|
|
79
|
+
|
|
80
|
+
## [0.1.0] — 2026-07-21
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
- Initial release. 13 read-only tools over the I14Y interoperability platform:
|
|
84
|
+
`search_catalog`, `list_datasets`, `get_dataset`, `get_dataset_distributions`,
|
|
85
|
+
`list_data_services`, `get_data_service`, `list_public_services`,
|
|
86
|
+
`list_concepts`, `get_concept`, `search_codelist_entries`, `list_publishers`,
|
|
87
|
+
`list_catalogs`, `api_status`.
|
|
88
|
+
- Dual transport: stdio and streamable-http/SSE via `I14Y_MCP_TRANSPORT`.
|
|
89
|
+
- Retry with exponential backoff (2s/4s/8s); 4xx other than 429 fail fast.
|
|
90
|
+
- Pydantic v2 response envelope carrying `source` and `provenance`.
|
|
91
|
+
- Bilingual documentation (EN/DE) and full probe report in `docs/probe-i14y.md`.
|
|
92
|
+
|
|
93
|
+
### Known findings
|
|
94
|
+
Discovered during the live probe on 2026-07-21. Recorded here so the next
|
|
95
|
+
server in the portfolio does not have to rediscover them.
|
|
96
|
+
|
|
97
|
+
- **The search parameter is `query`, not `q`.** Unknown query parameters are
|
|
98
|
+
silently discarded and the endpoint returns the entire index — a 15 MB
|
|
99
|
+
response with HTTP 200. The API never says no; it says everything.
|
|
100
|
+
- **`page` and `pageSize` are ignored on `/api/search`.** The complete result
|
|
101
|
+
set is always returned. Capping must happen client-side.
|
|
102
|
+
- **The `types` filter on search only works for `Dataset`.** `Concept`,
|
|
103
|
+
`DataService`, `PublicService` and `MappingTable` are accepted without error
|
|
104
|
+
but yield zero results, although those entities exist.
|
|
105
|
+
- **The search index covers about 51 % of the register** (1013 of ~2003
|
|
106
|
+
datasets). Search is the entry point, `list_datasets` the completeness
|
|
107
|
+
guarantee.
|
|
108
|
+
- **Multilingual nesting is inconsistent.** Themes wrap their language object
|
|
109
|
+
under `name`, keywords under `label`, titles sit directly as
|
|
110
|
+
`{de, fr, it, en}`. A naive extractor leaks a dict into a string field.
|
|
111
|
+
Caught by a live test, not by unit tests.
|
|
112
|
+
- **`endpointUrls` may contain entries without a URI**, carrying only a label
|
|
113
|
+
such as «OpenAPI Spezifikation». These are surfaced, not dropped.
|
|
114
|
+
- **`/api/concepts/{id}/codelist-entries` returns 405.** Only the `/search`
|
|
115
|
+
subpath exists, and it requires `language` (HTTP 400 otherwise).
|
|
116
|
+
- **Read endpoints need no authentication**, despite the OpenAPI document
|
|
117
|
+
declaring a Bearer scheme. Write endpoints do.
|
|
118
|
+
|
|
119
|
+
[Unreleased]: https://github.com/malkreide/i14y-mcp/compare/v0.2.0...HEAD
|
|
120
|
+
[0.2.0]: https://github.com/malkreide/i14y-mcp/compare/v0.1.0...v0.2.0
|
|
121
|
+
[0.1.0]: https://github.com/malkreide/i14y-mcp/releases/tag/v0.1.0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Mitwirken
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English Version](CONTRIBUTING.md)
|
|
4
|
+
|
|
5
|
+
Danke für dein Interesse an `i14y-mcp`. Dies ist ein Read-only-MCP-Server über
|
|
6
|
+
die öffentliche I14Y-API; Beiträge sollen das so belassen.
|
|
7
|
+
|
|
8
|
+
## Grundregeln
|
|
9
|
+
|
|
10
|
+
- **Read-only.** Jedes Tool bleibt mit `readOnlyHint: true` und
|
|
11
|
+
`destructiveHint: false` annotiert. Keine Schreib-, Sende- oder
|
|
12
|
+
Dateisystem-Fähigkeit. Schreib-Endpunkte existieren in der Quell-API, werden
|
|
13
|
+
hier aber bewusst nicht exponiert.
|
|
14
|
+
- **Nur ein Egress-Host.** Anfragen gehen ausschliesslich an die fixe Basis-URL
|
|
15
|
+
`https://api.i14y.admin.ch/api`, erzwungen durch die `ALLOWED_HOSTS`-Allow-List
|
|
16
|
+
in `src/i14y_mcp/client.py` (siehe [`docs/network-egress.md`](docs/network-egress.md));
|
|
17
|
+
keine nutzergesteuerten URLs, daher keine SSRF-Angriffsfläche.
|
|
18
|
+
- **Keine Secrets.** Die Lese-Endpunkte sind unauthentifiziert; keine
|
|
19
|
+
Credential-Verarbeitung hinzufügen.
|
|
20
|
+
|
|
21
|
+
## Entwicklung
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m venv .venv && . .venv/bin/activate
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
|
|
27
|
+
PYTHONPATH=src pytest tests/ -m "not live" # offline, respx-gemockt
|
|
28
|
+
PYTHONPATH=src pytest tests/ -m live # gegen die echte API
|
|
29
|
+
ruff check src tests
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Pull Requests
|
|
33
|
+
|
|
34
|
+
- Tests für nutzersichtbare Änderungen ergänzen; `ruff check` und die
|
|
35
|
+
Offline-Suite grün halten.
|
|
36
|
+
- Einen `CHANGELOG.md`-Eintrag unter `[Unreleased]` hinzufügen.
|
|
37
|
+
- Bei Doku-Änderungen sowohl `README.md` als auch `README.de.md` aktualisieren.
|
|
38
|
+
- Für Release/Publishing siehe [`PUBLISHING.md`](PUBLISHING.md).
|
|
39
|
+
|
|
40
|
+
## Sicherheitsprobleme melden
|
|
41
|
+
|
|
42
|
+
Siehe [`SECURITY.md`](SECURITY.md) — bitte privat melden, keine öffentlichen Issues.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
[🇩🇪 Deutsche Version](CONTRIBUTING.de.md)
|
|
4
|
+
|
|
5
|
+
Thanks for your interest in `i14y-mcp`. This is a read-only MCP server over the
|
|
6
|
+
public I14Y API; contributions should keep it that way.
|
|
7
|
+
|
|
8
|
+
## Ground rules
|
|
9
|
+
|
|
10
|
+
- **Read-only.** Every tool stays annotated `readOnlyHint: true`,
|
|
11
|
+
`destructiveHint: false`. No write, send, or filesystem capability. Write
|
|
12
|
+
endpoints exist in the upstream API but are deliberately not exposed.
|
|
13
|
+
- **One egress host.** Requests go only to the fixed base URL
|
|
14
|
+
`https://api.i14y.admin.ch/api`, enforced by the `ALLOWED_HOSTS` allow-list in
|
|
15
|
+
`src/i14y_mcp/client.py` (see [`docs/network-egress.md`](docs/network-egress.md));
|
|
16
|
+
no user-supplied URLs, so there is no SSRF surface.
|
|
17
|
+
- **No secrets.** The read endpoints are unauthenticated; do not add credential
|
|
18
|
+
handling.
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m venv .venv && . .venv/bin/activate
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
|
|
26
|
+
PYTHONPATH=src pytest tests/ -m "not live" # offline, respx-mocked
|
|
27
|
+
PYTHONPATH=src pytest tests/ -m live # hits the real API
|
|
28
|
+
ruff check src tests
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Pull requests
|
|
32
|
+
|
|
33
|
+
- Add tests for user-facing changes; keep `ruff check` and the offline suite green.
|
|
34
|
+
- Add a `CHANGELOG.md` entry under `[Unreleased]`.
|
|
35
|
+
- Update both `README.md` and `README.de.md` for any documentation change.
|
|
36
|
+
- For release/publishing, see [`PUBLISHING.md`](PUBLISHING.md).
|
|
37
|
+
|
|
38
|
+
## Reporting security issues
|
|
39
|
+
|
|
40
|
+
See [`SECURITY.md`](SECURITY.md) — please use private reporting, not public issues.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
# Multi-stage build: install deps with pip into a venv, then ship a slim runtime.
|
|
3
|
+
FROM python:3.14-slim AS builder
|
|
4
|
+
|
|
5
|
+
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
6
|
+
PIP_NO_CACHE_DIR=1
|
|
7
|
+
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
# LICENSE must be copied — pyproject.toml declares `license = { file = "LICENSE" }`,
|
|
10
|
+
# so the wheel build fails without it.
|
|
11
|
+
COPY pyproject.toml README.md LICENSE ./
|
|
12
|
+
COPY src/ ./src/
|
|
13
|
+
|
|
14
|
+
RUN python -m venv /app/.venv \
|
|
15
|
+
&& /app/.venv/bin/pip install --no-cache-dir .
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
FROM python:3.14-slim AS runtime
|
|
20
|
+
|
|
21
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
22
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
23
|
+
PATH="/app/.venv/bin:$PATH" \
|
|
24
|
+
I14Y_MCP_TRANSPORT=sse \
|
|
25
|
+
HOST=0.0.0.0 \
|
|
26
|
+
PORT=8000
|
|
27
|
+
|
|
28
|
+
RUN groupadd --system mcp \
|
|
29
|
+
&& useradd --system --gid mcp --home-dir /app --shell /usr/sbin/nologin mcp
|
|
30
|
+
|
|
31
|
+
WORKDIR /app
|
|
32
|
+
COPY --from=builder --chown=mcp:mcp /app/.venv /app/.venv
|
|
33
|
+
|
|
34
|
+
USER mcp
|
|
35
|
+
EXPOSE 8000
|
|
36
|
+
|
|
37
|
+
# SCALE-004: let orchestrators/load balancers detect an unhealthy container.
|
|
38
|
+
# The SSE runtime opens PORT; a successful TCP connect means the server is up.
|
|
39
|
+
# Uses stdlib only (no curl in the slim image).
|
|
40
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
41
|
+
CMD python -c "import os,socket; socket.create_connection(('127.0.0.1', int(os.getenv('PORT','8000'))), 3).close()" || exit 1
|
|
42
|
+
|
|
43
|
+
# Read-only, no-auth public-data server — no secrets required at runtime.
|
|
44
|
+
CMD ["python", "-m", "i14y_mcp.server"]
|
i14y_mcp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 malkreide
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|