Changelog
[0.3.0] - 2026-08-16
Added
Dockerfile(multi-stage, non-root, stdio entrypoint) and.dockerignore, so registries that build and introspect the server in a container no longer have to guess a build.- Multi-arch container images (amd64/arm64) at
ghcr.io/ni-c/hetzner-dns-mcp, published from CI with an SBOM and max-mode build provenance and scanned with Trivy on every push and pull request.server.jsonlists the image as an OCI package, so the MCP registry offers it alongside the npm package. HETZNER_READ_ONLY=trueregisters only the seven read-only tools. The write tools are not registered at all rather than rejected when called, so there is no code path from a write request to the API.- Documentation site at hetzner-dns-mcp.ni-c.de with a guide, the full tool reference and the security model.
Changed
- A missing
HETZNER_API_TOKENno longer exits at startup. The server completes the MCP handshake and lists its tools without credentials; the token is required when a tool actually calls the API, which then fails with the same setup instructions as before. Base URL validation still exits, since a bad base URL can leak the token. - Breaking: the
confirmboolean is gone. Destructive tools now take an optionalconfirmTokeninstead — see below. Callers that passedconfirm: truewill be refused and handed a token to call again with. engines.noderaised to>=22; Node 20 is end-of-life and is no longer in the CI matrix. The container has been on Node 24 all along.- Published source maps embed their sources, since only
dist/is shipped and the maps previously pointed at asrc/that is not in the tarball.
Security
Destructive tools require a server-issued confirmation token. Every irreversible tool refuses its first call and returns a random, single-use token with a five-minute lifetime; a second call must repeat the identical arguments and pass it. For
set_records,remove_records,import_zonefileandchange_primary_nameserversthe token is bound to a SHA-256 fingerprint of the payload, so a confirmation for one record list cannot write a different one.The previous
confirmboolean was a value the model set itself, while the refusal messages pasted the current RRSet contents back as raw API JSON. Together that was a self-approving loop: an instruction hidden in a TXT record value or a zone-file comment arrived verbatim in the very message asking for confirmation. A token cannot be produced that way, because it only ever exists in a previous result from this server.Removing protection now counts as destructive.
change_zone_protectionandchange_rrset_protectionhad no guard at all, so unprotecting a zone and deleting it was two uninterrupted calls; disabling protection is now gated exactly like the deletion it enables, while enabling it stays immediate.Confirmation messages no longer quote anything read back from the API. They report record counts and TTLs only.
API responses are wrapped in an
<untrusted-data>envelope, keys matchingtsig_key,token,secret,passwordorcredentialare redacted, single values are truncated at 4 000 characters and whole results at 200 000.Upstream error bodies are truncated at 2 000 characters and HTML error pages — a reverse proxy or WAF in front of the API — are dropped entirely instead of being pasted into the model's context.
HETZNER_API_TOKENandHETZNER_API_BASE_URLare deleted from the environment once read, so a later crash report or diagnostic dump cannot expose them. An unparseable base URL is no longer echoed back, since it can contain auser:token@part.mcp-publisheris pinned to a release and verified against its SHA-256 before it runs. It was fetched from/releases/latestunverified, in a job holdingid-token: write.The runtime image no longer ships npm, npx or corepack; they are never invoked there, but their vendored dependencies kept appearing in scans.
CI additionally runs CodeQL and a Trivy scan of the image for both architectures.
[0.2.3] - 2026-08-13
Changed
- zod updated to v4 (the MCP SDK supports
^3.25 || ^4.0);z.record()now uses the explicit two-argument form. - Dev dependencies updated: TypeScript 6 (
@types/nodeis listed explicitly in the tsconfigtypesfield, as TS 6 no longer auto-includes@typespackages),@eslint/js10 (matching ESLint 10).
[0.2.2] - 2026-08-11
Added
- Listed in the official MCP Registry as
io.github.ni-c/hetzner-dns-mcp; the release workflow publishes registry updates automatically via GitHub OIDC (server.json,mcpNamefield).
Changed
- Dev dependencies updated: vitest 4 (+ matching
@vitest/coverage-v8), ESLint 10,@types/node26; GitHub Actions pins bumped to current major versions. Coverage thresholds rebased to vitest 4's stricter AST-based measurement.
[0.2.1] - 2026-08-11
Added
- Release workflow: pushing a
vX.Y.Ztag runs the tests, publishes to npm via Trusted Publishing (OIDC, with provenance) and creates a GitHub release with the notes from this changelog. - CI: test matrix extended to Node 24, coverage report (thresholds enforced, uploaded as artifact), weekly
npm auditjob, Dependabot for npm packages and pinned GitHub Actions.
[0.2.0] - 2026-08-11
Changed
- Renamed the package from
mcp-hetzner-dnstohetzner-dns-mcp. set_records,remove_recordsandchange_primary_nameserversnow requireconfirm=true(like the other destructive tools) and report the current state when refusing;change_primary_nameserversis annotated as destructive.
Security
HETZNER_API_BASE_URLis validated: https only (http allowed for localhost), URLs with embedded credentials are rejected, and a warning is printed for non-default hosts, since the API token is sent there.zoneand RRSetnameparameters are restricted to a safe character set and./..are rejected, preventing URL path traversal out of the intended API endpoints.- API requests no longer follow redirects and time out after 30 seconds.
- Fatal errors log only the error message instead of the full stack trace.
- CI: workflow token restricted to
contents: read, actions pinned to commit SHAs.
[0.1.0] - 2026-08-06
Added
- Initial release targeting the DNS endpoints of the Hetzner Cloud API (
api.hetzner.cloud/v1, Bearer token authentication). The legacy DNS API (dns.hetzner.com) is not supported. - Zone tools:
list_zones,get_zone,create_zone,update_zone,delete_zone(guarded by aconfirmparameter),export_zonefile,import_zonefile(guarded by aconfirmparameter),change_zone_ttl,change_zone_protection,change_primary_nameservers. - RRSet tools:
list_rrsets,get_rrset,create_rrset,update_rrset,delete_rrset(guarded by aconfirmparameter),set_records,add_records,remove_records,change_rrset_ttl,change_rrset_protection. - Action tools:
list_zone_actions,get_zone_action. - Configuration via
HETZNER_API_TOKEN, optionalHETZNER_API_BASE_URL.