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).