Releases: lazyants/hetzner-mcp-server
Release list
v2.5.0
Added
- The publish workflow fails before
npm publishwhen the GitHub Release tag
andpackage.jsonversion disagree.check-versions.mjsproved that
package.jsonandserver.jsonagreed with each other — it does not read
package-lock.jsonin this repo — but nothing tied that version to the tag
the release was cut from — so tagging
v1.9.0on a commit reading2.0.0would have published 2.0.0 to npm and the
MCP Registry while the GitHub Release — the artifact humans read — claimed
otherwise, silently and on the irreversible side of the publish. Ported from
lexware-mcp-server 5.2.0 (lexware #103).
Changed
- The publish job runs on Node 24 (Active LTS) instead of Node 20. npm's Trusted
Publishing prerequisite is two-part — npm 11.5.1+ and Node 22.14+ — and
the job satisfied only the npm half, leaving the irreversiblenpm publish
step one npm patch away from breaking if that floor starts being enforced. The
CI test matrix stays on Node 20 + 22: it tracksengines.node, which is
unchanged. Ported from lexware-mcp-server 5.2.0 (lexware #102). - Raised the
@modelcontextprotocol/sdkfloor to^1.30.0, which declares
@hono/node-server: ^1.19.9 || ^2.0.5where 1.29.0 declared only^1.19.9.
A correctness alignment, not a security fix: the resolved tree already carried
a patched@hono/node-server, and the production audit gate was green before
and after. What it buys is that a resolver cannot fall back to an SDK whose
range predates the widening. Ported from lexware-mcp-server 5.2.0 (lexware
#81).
Security
-
Refreshed the
overridesfloors. Three had drifted inside live advisory
ranges and two packages that reach the production audit gate were not pinned
at all — verified against the GitHub advisory API on 2026-08-20:Package Was Now Advisory range fast-uri^3.1.2^3.1.5GHSA-7p8r-x3mc-p8w7, >=3.0.0 <3.1.5brace-expansion^5.0.6^5.0.9GHSA-rgw5-rvv9-x895, >=4.0.0 <5.0.9hono^4.12.25^4.12.34GHSA-8j4g-w8fx-2239 et al., <4.12.34ip-addressunpinned ^10.3.1GHSA-mwp4-54f8-5fhr, <=10.3.0body-parserunpinned ^2.3.0GHSA-v422-hmwv-36x6, >=2.0.0 <2.3.0package-lock.jsonis unchanged: every resolved version was already above the
new floors, so this closes a latent gap rather than a live one, and the audit
gate was green before and after. The unpinned entries are the dangerous ones —
anoverridesentry is the only thing that pulls a sticky lockfile forward on
install, so an unpinned transitive dep is the one that drifts into a range
while pinned ones re-resolve themselves. -
The override guard test now covers every pin. It asserted only
qs,hono
andform-data, leavingfast-uriandbrace-expansiondeclared but
unchecked — a pin nothing checks is indistinguishable from no pin once it
rots. The six hand-rolled assertions are replaced by aPINStable plus a
completeness assertion that fails when theoverridesblock and the table
disagree in either direction, so a pin cannot be added without a guard. Both
directions were vacuity-probed rather than assumed.
v2.4.0
Added
hetzner_create_servernow accepts avolumesarray, so volumes can be attached
at creation time instead of requiring a separate attach call — theautomount
flag already referenced volumes but there was no way to supply them (#64).hetzner_list_server_actionsgainssortandstatusfilters, bringing it in
line with the other tenlist_<resource>_actionstools (#64).hetzner_create_primary_ipgains an optionalassignee_id, so a Primary IP can
be created and assigned in one call (#63).PathSegmentSchemainschemas/common.ts: a second guard layer alongside
pathSeg()that rejects.,.., empty and embedded-slash values before a URL
is built.encodeURIComponentnever escapes., sopathSeg('..')was a no-op.
Applied toIdOrNameSchemaand the nine path-interpolated zone RRSetname
fields (#66).
Changed
hetzner_create_primary_ip: thedatacenterparameter is replaced by
location. Hetzner removeddatacenterfromPOST /primary_ips
(additionalProperties: false), so calls passing it were already being rejected
upstream.locationmatches the convention used by servers, volumes and load
balancers (#63).assignee_typeis now pinned explicitly wheneverassignee_idis supplied
without it. Hetzner's default flips fromservertounassignedon 2026-08-01,
which would have silently stopped create-and-assign calls from assigning (#63).index.tsand the eightentry-*.tsbinaries iterate a sharedSPLITS/
ALL_REGISTRARSmap from the newsrc/splits.tsinstead of hand-listing
register*Toolscalls, so the entry-point partition can no longer drift between
runtime wiring and the tests asserting tool counts (#67).- Removed the unused
src/types/tree (15 files);ZONE_RRSET_TYPESmoved to its
sole consumer, andMAX_PER_PAGEis now the single source of theper_page
ceiling inPaginationParams(#66). - Extracted an
rrsetPath()helper so the zone/name/type path is built and
encoded in exactly one place (#66).
Fixed
- 429 backoff was defeated by malformed headers.
Retry-Afterand
ratelimit-resetwere parsed with a bareparseInt, which returnsNaNfor an
HTTP-dateRetry-After;Math.max(0, NaN)isNaNandsetTimeout(NaN)fires
immediately, so a rate-limited request retried in a tight loop. Ported lexware's
parseRetryAfterMs(delta-seconds plus strict IMF-fixdate parsing, clamped to
thesetTimeoutceiling) and guarded theratelimit-resetfallback with a strict
/^\d+$/parse so a malformed value falls back to exponential backoff (#61, #65).
Security
- Request-body secrets are now scrubbed from chained error causes.
scrubConfigleftconfig.data— the serialized request body — intact when an
AxiosErrorwas chained as{ cause: err }, so a certificateprivate_key, a
Storage Boxpassword, or a DNS TSIG key could reach a logger walking the cause
viautil.inspect(err, { depth: null })orAxiosError.toJSON()(#51, #65). - Regenerated
package-lock.jsonto clear thenpm auditgate, which had gone red
as the advisory database moved: 6 production vulnerabilities, 3 high
(fast-uri,axios,ip-addresshigh;hono,@hono/node-servermoderate;
body-parserlow). No manifest change was needed — every package resolved past
its advisory range within the ranges already declared (#71, #73).
Removed
-
dist/types/**is no longer published (45 files). Thesrc/types/tree was
dead code — 14 of its 15 modules exported only TypeScriptinterface
declarations, so their compiled.jsfiles were empty stubs and no runtime value
can break. The single runtime export,ZONE_RRSET_TYPES, moved into
src/tools/zones.tsand is no longer exported from a published path.This package declares no
exportsmap, so deep imports such as
@lazyants/hetzner-mcp-server/dist/types/servers.jswere resolvable, and type
imports of those interfaces will stop resolving. No compatibility shim is
provided: the package's product is its nine stdiobinentries, and no library
API is documented. If you were importing these types, copy the interfaces you
need — they were never part of a supported surface.
Internal
- Test-coverage gaps closed by the
splits.tsrefactor: the Zod-4
required[]/describeenumeration test was building its "full server" from 15 of
16 registrars (156 of 185 tools, omitting Storage Boxes) and now covers all 185;
the annotation test swept 3 registrars and now sweeps every registered tool for
boolean-hint presence and the read-only/destructive exclusivity invariant. The
get_action-exclusion GET scan was widened to also catchstorageBoxRequest
calls (#53, #67).
v2.3.1
Security
- Redact the bearer token (
HETZNER_API_TOKEN/HETZNER_STORAGE_API_TOKEN) from rethrown axios error cause chains (defense-in-depth, #44). API errors are wrapped asnew Error(msg, { cause: err }); the chainedAxiosErrorpreviously retained the token inconfig.headers.Authorizationand Node's rawrequest._headerblock, so a logger walking the cause viautil.inspect(err, { depth: null })orAxiosError.toJSON()could surface it. A centralwrapHetznerErrornow sanitizes the error in place — scrubbingAuthorization/proxy-authorization/cookieheaders (case-insensitively) on bothconfigrefs, and droppingconfig.auth/proxy.auth, therequest/response.requestobjects, and any objectcause. The singlerequest()chokepoint routes both the Cloud (api.hetzner.cloud) and Storage Box (api.hetzner.com) call paths through it, so both token families are covered. Thrown messages are unchanged. Locked with a regression test asserting no token survivesutil.inspect(depth:null)ortoJSON(), through bothhetznerRequestandstorageBoxRequest.
Fleet lockstep with lexware v3.2.1.
Full Changelog: v2.3.0...v2.3.1
v2.3.0
Minor release.
Added — Storage Boxes domain (29 tools, 156 → 185)
New hetzner-mcp-storage-boxes entry point covering Storage Box CRUD + folders + actions (protection/type/password/access-settings, snapshot plan enable/disable + rollback), snapshots CRUD, subaccounts CRUD + actions, and Storage Box types. Storage Boxes call the separate https://api.hetzner.com/v1 host via a second cached client that reuses the same retry, 429 backoff, and error normalization. Token resolves as HETZNER_STORAGE_API_TOKEN || HETZNER_API_TOKEN (an empty storage token falls back to the Cloud token), so a single existing token keeps working. pathSeg URL-encoder promoted to schemas/common.ts and unit-tested.
Deprecated — datacenters
hetzner_list_datacenters / hetzner_get_datacenter now flag Hetzner's removal of /datacenters (after 2026-10-01, HTTP 410) and point to hetzner_list_server_types (locations[].available/recommended) and hetzner_list_locations. Tools remain functional until the cutover.
See CHANGELOG.md for details.
v2.2.1
Security patch — clears two HIGH npm-audit advisories that were failing the npm audit --audit-level=moderate --omit=dev CI gate:
- form-data (GHSA-hmw2-7cc7-3qxx) — CRLF injection via unescaped multipart field/file names; reaches the prod tree via
axios. Pinned^4.0.6(lockfile resolves 4.0.6). - hono (
<= 4.12.24) —serve-staticpath traversal et al.; reaches prod via@modelcontextprotocol/sdk→@hono/node-server. Override^4.12.25.
Dependency-only change — no runtime or API behaviour changes. See CHANGELOG.md for full details.
v2.2.0
Wave-2 minor release.
Added
- 9 new Cloud API tools (147 → 156):
hetzner_attach_server_to_network,hetzner_detach_server_from_network,hetzner_add_server_to_placement_group,hetzner_remove_server_from_placement_group,hetzner_reset_server_password,hetzner_enable_lb_public_interface,hetzner_disable_lb_public_interface,hetzner_change_lb_dns_ptr, andhetzner_get_pricing. - Read-only API-reference Resource
reference://hetzner/api(text/markdown) on the main binary and all 7 split entry points.
Fixed
hetzner_attach_lb_to_networknow forwards the optionalip_rangefield.
v2.1.1
Patch release.
- Security (#35): pin a
qsoverride (^6.15.2) to clear the transitive npm-audit DoS advisory (GHSA-q8mj-m7cp-5q26) and reinstate a cleannpm auditgate. No runtime behavior change. - Deps (#34): grouped minor+patch Dependabot bump (lockfile-only refresh of 5 transitive packages). No behavior changes.
Published to npm with provenance + submitted to the MCP Registry via publish-registry.yml.
v2.1.0
Added
- DNS Zones module (
src/tools/zones.ts) + new entry binary
hetzner-mcp-dns(src/entry-dns.ts) covering 22 tools across
the Hetzner DNS Zones API (zones CRUD, RRSets CRUD,
primary-nameserver changes, protection, TTL changes, zonefile
import/export). Wraps the GA Nov 2025 surface on the existing
api.hetzner.cloudbaseURL + token (PR #24). change_*_protectionacross 7 resources: servers, load
balancers, volumes, networks, floating IPs, primary IPs, images.
Adds the data-loss guard rails the API has supported for years
but were never wrapped (PR #21).- Per-resource
list_*_actionsfor 8 resources: load balancers,
volumes, floating IPs, primary IPs, networks, firewalls,
certificates, images (PR #23). Replaces the global/actions
endpoint Hetzner deprecated in January 2025. - Server actions:
request_console,enable_backup,
disable_backup,change_alias_ips,change_dns_ptr(servers);
change_ip_range(networks). Plusmust_be_unassigned
precondition notes ondelete_floating_ip/delete_primary_ip
descriptions (PR #25). - Per-tool axios-mock test coverage across all 14 tool modules
(PRs #26, #28, #29, #30). Every tool now has at least one test
asserting path/method/body/query shape against a mocked
hetznerRequest(). - README tool counts updated to reflect the 147-tool surface
(PR #27).
Changed
- Migrated to Zod 4 (
zod ^4.4.3). Brings hetzner into line
with@lazyants/lexware-mcp-serverand
@lazyants/transkribus-mcp-server(both already on Zod 4 since
their 2.0.1). Allz.record(z.unknown())call sites updated to
the Zod-4 two-argument formz.record(z.string(), z.unknown()).
Runtime-verified.describe()propagation andtools/list
required[]correctness on the full schema surface (PR #22). - Bumped grouped minor+patch deps (PR #18): see the Dependabot PR
for the exact diff. No behavior changes.
Fixed
formatResponseno longer setsstructuredContentfor array
payloads. The MCP SDK rejects arrays instructuredContent;
previously this would crash on any tool returning a top-level
array (e.g. paginatedlist_*shortcuts). Now array payloads
go through the JSON-stringifiedcontent[]path only (PR #31).
Note on tool count
Total registered tools grew from 104 to 147 (+43): +22 DNS Zones
(PR #24), +8 list_*_actions (PR #23), +6 server/network actions
(PR #25), +7 change_*_protection (PR #21). Smoke test asserts
147 across full server + per-entry splits (28/21/25/20/17/14/22).
v2.0.1 — Clear transitive CVE backlog
Catch-up release matching lexware-mcp-server v2.0.1 and transkribus-mcp-server v2.0.1.
What's new
This is a security-hygiene release. No code changes; no tool additions.
- npm overrides block added to clear transitive CVE backlog:
- `fast-uri ^3.1.2` — clears GHSA-q3j6-qgpj-74h6 (path traversal, HIGH) + GHSA-v39h-62p7-jpjc (host confusion, HIGH).
- `brace-expansion ^5.0.6` — clears GHSA-jxxr-4gwj-5jf2 (large-numeric-range DoS, moderate).
- `hono ^4.12.21` — clears GHSA-qp7p-654g-cw7p (JSX CSS injection), GHSA-hm8q-7f3q-5f36 (JWT NumericDate), GHSA-p77w-8qqv-26rm (Vary cache leak).
- Hygiene baseline alignments since v2.0.0: ESLint 10 + cause-chaining, .nvmrc pinning, SECURITY.md disclaimer wording, version-check script cleanup.
All overrides target dev-transitive deps — no runtime impact on the published package surface.
Verification
```
npm view @lazyants/hetzner-mcp-server version # → 2.0.1
```
v2.0.0 — FSL-1.1-MIT relicense + hygiene baseline
First major release. Two themes: (1) license switch to FSL-1.1-MIT, and
(2) project-hygiene baseline shared across the Lazy Ants MCP server
fleet (npm provenance, Trusted Publishing, ESLint 9, Dependabot,
multi-Node CI, version-sync guard).
Versions ≤ 1.1.1 remain under the original MIT license.
Changed
- License → FSL-1.1-MIT. Auto-reverts to MIT after two
years. The license switch is what makes this a semver-major bump;
the rest would be minor/patch on their own. - Dependencies:
typescript→^6.0.0,vitest→^4.1.0,
actions/checkout→v6,actions/setup-node→v6,
axios→^1.16.0(CVE coverage),@modelcontextprotocol/sdk→
^1.29.0.zodstays on^3.25.0— the MCP SDK has known
.describe()propagation bugs with zod 4. - Releasing: GitHub Releases auto-publish to npm with provenance
(--provenance --access public) before pushing to the MCP Registry.
Authentication via npm Trusted Publishing — noNPM_TOKENsecret
in the repo.mcp-publisheris pinned by version + SHA-256 and
smoke-tested before the irreversiblenpm publish. Cutover/recovery
guard skipsnpm publishcleanly if the version is already on npm. - CI: test workflow runs on Node 20 and 22 with npm cache, lint,
version-sync check, andnpm audit --audit-level=moderate --omit=dev.
Added
- ESLint 9 flat config (
eslint.config.mjs) with
tseslint.configs.recommended+globals.node. Newnpm run lint
script, gated in CI. - Dependabot for weekly npm + GitHub Actions updates, grouped
minor+patch, ignoring@types/nodemajor bumps. scripts/check-versions.mjsenforces
package.json#version==server.json#/packages[0].version
(hard fail) and warns on registry-version regressions. Implements
full semver §10/§11 (build metadata stripped, prerelease ordering).SECURITY.mdvulnerability disclosure path.CHANGELOG.md(Keep a Changelog format).engines.node >= 20inpackage.json.- Hetzner API field additions from the upstream changelog:
hetzner_rebuild_serveraccepts optionaluser_data(2026-01-16).- LB HTTP service schemas accept optional
timeout_idle(2026-04-30). hetzner_create_primary_ipno longer requiresassignee_type
(2026-04-27).
Verification
- npm:
npm view @lazyants/hetzner-mcp-server@2.0.0will show a
provenance attestation block once the publish workflow completes. - MCP Registry: listing at
https://registry.modelcontextprotocol.io/ updates to 2.0.0.
Full Changelog: v1.1.1...v2.0.0