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.
Files changed (79) hide show
  1. i14y_mcp-0.2.0/.dockerignore +19 -0
  2. i14y_mcp-0.2.0/.github/dependabot.yml +19 -0
  3. i14y_mcp-0.2.0/.github/workflows/ci.yml +33 -0
  4. i14y_mcp-0.2.0/.github/workflows/live.yml +25 -0
  5. i14y_mcp-0.2.0/.github/workflows/publish.yml +84 -0
  6. i14y_mcp-0.2.0/.gitignore +12 -0
  7. i14y_mcp-0.2.0/CHANGELOG.md +121 -0
  8. i14y_mcp-0.2.0/CONTRIBUTING.de.md +42 -0
  9. i14y_mcp-0.2.0/CONTRIBUTING.md +40 -0
  10. i14y_mcp-0.2.0/Dockerfile +44 -0
  11. i14y_mcp-0.2.0/LICENSE +21 -0
  12. i14y_mcp-0.2.0/PKG-INFO +319 -0
  13. i14y_mcp-0.2.0/PUBLISHING.md +157 -0
  14. i14y_mcp-0.2.0/README.de.md +271 -0
  15. i14y_mcp-0.2.0/README.md +266 -0
  16. i14y_mcp-0.2.0/SECURITY.de.md +73 -0
  17. i14y_mcp-0.2.0/SECURITY.md +68 -0
  18. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/applicability.json +342 -0
  19. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/audit-meta.json +13 -0
  20. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/audit-report.md +1031 -0
  21. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-003-not-found-heuristics.md +44 -0
  22. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-007-capability-aggregation.md +43 -0
  23. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-008-three-primitives.md +43 -0
  24. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/ARCH-012-protocol-version-pinning.md +43 -0
  25. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-001-protocol-vs-execution-errors.md +44 -0
  26. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-002-mask-error-details.md +43 -0
  27. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OBS-003-structured-logging.md +44 -0
  28. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/OPS-003-phase-architecture.md +44 -0
  29. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SCALE-004-container-healthcheck.md +44 -0
  30. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SCALE-006-resource-limits-fd.md +43 -0
  31. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-001-fastmcp-lifespan.md +47 -0
  32. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-003-context-injection.md +45 -0
  33. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SDK-004-cors-session-id-exposure.md +45 -0
  34. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-014-tool-allow-listing.md +44 -0
  35. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-015-tool-poisoning-detection.md +44 -0
  36. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-016-binding-prevention.md +45 -0
  37. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-018-input-validation-boundaries.md +46 -0
  38. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-021-egress-allow-list.md +45 -0
  39. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/findings/SEC-022-tool-hash-namespace.md +44 -0
  40. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/profile.json +24 -0
  41. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/arch-ops.json +162 -0
  42. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/scale-obs.json +119 -0
  43. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/sdk-ch.json +79 -0
  44. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/raw/sec.json +176 -0
  45. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/summary.json +229 -0
  46. i14y_mcp-0.2.0/audits/2026-07-23T151450-Z-i14y-mcp/verification-results.json +624 -0
  47. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/applicability.json +342 -0
  48. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/audit-meta.json +13 -0
  49. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/audit-report.md +331 -0
  50. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/ARCH-011-single-file-server.md +46 -0
  51. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/ARCH-012-protocol-version-pinning.md +45 -0
  52. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/OBS-002-mask-error-details.md +44 -0
  53. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/SDK-004-cors-allow-origins.md +45 -0
  54. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/findings/SEC-022-tool-namespace-prefix.md +44 -0
  55. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/profile.json +24 -0
  56. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/arch-ops.json +163 -0
  57. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/scale-obs.json +114 -0
  58. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/sdk-ch.json +54 -0
  59. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/raw/sec.json +171 -0
  60. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/summary.json +128 -0
  61. i14y_mcp-0.2.0/audits/2026-07-24T091742-Z-i14y-mcp/verification-results.json +595 -0
  62. i14y_mcp-0.2.0/audits/tracker.csv +2 -0
  63. i14y_mcp-0.2.0/claude_desktop_config.json +8 -0
  64. i14y_mcp-0.2.0/compose.yaml +24 -0
  65. i14y_mcp-0.2.0/docs/network-egress.md +46 -0
  66. i14y_mcp-0.2.0/docs/probe-i14y.md +137 -0
  67. i14y_mcp-0.2.0/docs/roadmap.md +41 -0
  68. i14y_mcp-0.2.0/pyproject.toml +62 -0
  69. i14y_mcp-0.2.0/server.json +28 -0
  70. i14y_mcp-0.2.0/src/i14y_mcp/__init__.py +7 -0
  71. i14y_mcp-0.2.0/src/i14y_mcp/__main__.py +6 -0
  72. i14y_mcp-0.2.0/src/i14y_mcp/client.py +196 -0
  73. i14y_mcp-0.2.0/src/i14y_mcp/logging_config.py +46 -0
  74. i14y_mcp-0.2.0/src/i14y_mcp/mappers.py +269 -0
  75. i14y_mcp-0.2.0/src/i14y_mcp/models.py +314 -0
  76. i14y_mcp-0.2.0/src/i14y_mcp/server.py +733 -0
  77. i14y_mcp-0.2.0/tests/test_client.py +138 -0
  78. i14y_mcp-0.2.0/tests/test_tools.py +255 -0
  79. i14y_mcp-0.2.0/tool-definitions.lock.json +139 -0
@@ -0,0 +1,19 @@
1
+ .git/
2
+ .github/
3
+ .venv/
4
+ venv/
5
+ __pycache__/
6
+ *.pyc
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .coverage
10
+ htmlcov/
11
+ dist/
12
+ build/
13
+ *.egg-info/
14
+ tests/
15
+ docs/
16
+ audits/
17
+ *.md
18
+ !README.md
19
+ compose.yaml
@@ -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,12 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .coverage
6
+ htmlcov/
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .venv/
11
+ venv/
12
+ .env
@@ -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.