# Reported issues for hetzner

Pod holds 18 of 19 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [hetzner](/mcp/hetzner).

## Most discussed

### Harden get-action-exclusion policy test against concatenated/indirected GET paths

Surfaced by codex review during #53 (PR #67).

PR #67 widened the check-(a) regex in `src/tests/get-action-exclusion.test.ts` to also match `storageBoxRequest(` (previously `hetznerRequest(` only). codex noted the underlying **regex-based source-text scan** still has ordinary false-negative paths — both of these forbidden calls pass with zero violations:

```ts
storageBoxRequest('GET', '/actions/' + actionId);      // string concatenation

const path = `/actions/${actionId}`;
storageBoxRequest('

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/69) · 2026-07-18 · closed · 1 comment

### Fleet: lockstep drift — shared-helper divergence, lockfile patch lag, drift-check script

The fleet deliberately copies shared code instead of extracting a package; these are the places where the copies have drifted or should be re-synced in one pass.

## Findings

### axios patch-version drift across fleet lockfiles (1.18.0 vs 1.17.0), all behind latest 1.18.1

`hetzner-mcp-server/package-lock.json:1127` — **low** refactor

**Evidence:** hetzner-mcp-server/package-lock.json L1126-1127: "node_modules/axios": { "version": "1.17.0" }  |  transkribus-mcp-server/package-lock.json L1126-1

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/59) · 2026-07-03 · closed · 1 comment

### Small cleanups: hetzner_list_server_actions missing sort/status filters; rrsetPath helper in zones.ts

## Findings

### hetzner_list_server_actions is the only 1 of 11 list-actions tools missing sort/status filters

`hetzner-mcp-server/src/tools/servers.ts:272` — **low** refactor

**Evidence:** inputSchema: z.object({ id: IdSchema.describe('Server ID'), ...PaginationParams }) — no SortParam/ActionStatusFilterParam, while the other ten list_*_actions tools (volumes.ts:167-171, floating-ips.ts:166-170, zones.ts:214-218, etc.) all spread '...SortParam, ...ActionStatusFilterParam, ...PaginationParams

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/55) · 2026-07-03 · closed · 1 comment

### Port the hardened Retry-After parser from lexware #54 (HTTP-date → NaN → immediate retry; unbounded sleep)

Same fleet bug-class lexware fixed in lazyants/lexware-mcp-server#54 — the fix (`parseRetryAfterMs` + `retry-after.test.ts`) is vendor-neutral and can be copied nearly verbatim. Sibling issue exists for transkribus.

## Findings

### 429 Retry-After parsed with parseInt only — HTTP-date value yields NaN delay (immediate retry hammering); huge delta-seconds sleeps unbounded

`hetzner-mcp-server/src/services/hetzner.ts:72` — **low** bug

**Evidence:** "const retryAfter = error.response.headers['re

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/50) · 2026-07-03 · closed · 1 comment

### Scrub config.params/url in the axios sanitizer (response.data value-redaction: decided against)

Follow-up from #44 (PR #46). Originally filed as an open fleet-wide design question. **Triaged 2026-08-20 against `fdf1336`: the question is now answered — transkribus v3.0.0 (PR #37, trk #26) shipped the reference design, and part of it has already landed here.** Scope narrowed to what is actually left.

## Decided — no work

**1. Fail-closed sanitizer + combined-regex value redaction across `response.data` / headers / statusText / message / stack: NOT taken for hetzner.**

Transkribus needs it

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/47) · 2026-06-22 · open · 1 comment

### Defense-in-depth: drop leaky { cause: err } on axios rethrows

`src/services/hetzner.ts` `request()` rethrows with `new Error(msg, { cause: err })`, where `err` is the `AxiosError` whose `config.headers.Authorization` holds the bearer token. The fleet documents this as a secret-leak risk (`util.inspect`/`console.error(err)` walking the cause chain can surface the token).

**Not currently exploitable / not introduced by #42:** pre-existing on `main`; the only consumers (`handleToolRequest`, `toolError`) read `err.message` ONLY and never walk the cause. #42 m

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/44) · 2026-06-20 · closed · 1 comment

### CI audit gate is red: fast-uri/hono override pins have rotted, axios lockfile stale (6 vulns, 2 high)

The CI gate `npm audit --audit-level=moderate --omit=dev` in `.github/workflows/test.yml` **fails on the current checkout**: exit 1, **6 vulnerabilities (3 high, 2 moderate, 1 low)**.

Nothing in this repo changed — the advisory database did. Two override pins were correct when written and have since **rotted in place** as later GHSAs extended their ranges.

## Measured — re-measured 2026-08-20 against `fdf1336`

The table below **replaces** the original 2026-07-26 measurement. Three lines chang

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/71) · 2026-07-26 · closed · 0 comments

### list-actions sort/status filters: single comma-separated string vs repeatable query keys

Surfaced by codex review during the #55a work (PR #64).

The shared `SortParam` and `ActionStatusFilterParam` in `src/schemas/common.ts` model `sort` and `status` as single optional strings (`status` is documented as a "comma-separated list of running, success, error"). These are spread into all 11 `hetzner_list_*_actions` tools.

Hetzner's action-list endpoints (`GET /<resource>/{id}/actions`) may expect **repeatable query keys** (`?status=running&status=success`, `?sort=id:asc&sort=command:des

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/68) · 2026-07-18 · open · 0 comments

## Most recent

### pathSeg('..') does not stop traversal where the URL template supplies literal slashes

Latent, found during transkribus-mcp-server #32 (PR #37).

`pathSeg` (= `encodeURIComponent`) does **not** stop `..` path traversal when a URL template supplies its own literal slashes, because `encodeURIComponent` never escapes `.` — `pathSeg('..') === '..'`. So for a template like `/x/${pathSeg(seg)}/y`, `seg='..'` still normalizes up a directory after the server decodes.

hetzner's own `src/schemas/common.ts` `pathSeg` + its `common.test.ts` lock have **no** `''`/`'.'`/`'..'` cases. Transkrib

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/62) · 2026-07-17 · closed · 0 comments

### 429 backoff mis-parses Retry-After HTTP-date and ratelimit-reset to NaN → immediate retry

The 429 retry interceptor computes its delay with `parseInt(...) * 1000`, which yields `NaN` for the RFC 7231 HTTP-date form of `Retry-After` (only delta-seconds parse). `setTimeout(NaN)` fires immediately, collapsing the backoff into a tight retry loop against an already rate-limited API. Same class as lexware #54 (fixed there with `parseRetryAfterMs`, shipped in lexware 4.0.0).

- `src/services/hetzner.ts:72` — `delay = parseInt(retryAfter, 10) * 1000` (the `Retry-After` path).
- `src/services

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/61) · 2026-07-17 · closed · 0 comments

### Fleet: fleet-root docs drift (CLAUDE.md versions/counts, stale REGISTRY-STEPS.md + publishing guide, hygiene SKILL.md Zod4 claim)

These files live at the local fleet root (not inside any of the three server repos); tracked here per the Fleet: convention.

**Re-verified 2026-08-20 against `fdf1336`.** Two of the five findings moved; the evidence below replaces the 2026-07-03 measurement.

## 1. Fleet CLAUDE.md npm version table — CONFIRMED, but the cited numbers were superseded and it has rotted again

The original evidence (`3.2.0/2.3.0/2.1.1`) is stale — the table was since updated and now reads `4.2.0 / 2.3.1 / 3.0.0`. M

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/60) · 2026-07-03 · open · 0 comments

### Feature ideas from audit: wait_for_action, metrics `step`, list-filter parity, vswitch route param, pricing filter

Curated from the audit's gap analysis against the live Hetzner API docs. Ordered by value.

## Proposals

- **Add a hetzner_wait_for_action convenience tool (poll per-resource action list until terminal status)** _(medium)_ — Nearly every mutating tool (create_server, resize, rebuild, attach_volume, change_type, import_zonefile...) returns an async `action` object, and Hetzner deprecated the per-action-id GET endpoints in April 2026 — so an LLM client today must hand-loop hetzner_list_*_actions 

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/56) · 2026-07-03 · open · 0 comments

### Fleet: packaging hygiene — compiled tests ship in all three npm tarballs; add pack guards

Applies to all three fleet repos. **Re-measured 2026-08-20 against `fdf1336` via `npm pack --dry-run --json`.**

## Finding

**The compiled test suite ships to every npm consumer.** Of the **211 files** in the hetzner tarball, **102 are `dist/tests/**`** — 34 `.test.js`, plus a `.d.ts` and `.js.map` for each. Nearly half the published package is tests.

Root cause is one line: `tsconfig.json:17` `"include": ["src/**/*"]` with `"outDir": "dist"` compiles `src/tests/**` into `dist/tests/**`, and `

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/57) · 2026-07-03 · open · 0 comments

### Dead code: src/types/ (~550 lines, one real import) and dead pagination constants

## Findings

### src/types/ is ~550 lines of dead code — only ZONE_RRSET_TYPES is ever imported

`hetzner-mcp-server/src/types/common.ts:1` — **medium** refactor

**Evidence:** Only import from the whole tree: 'src/tools/zones.ts:15:import { ZONE_RRSET_TYPES } from ../types/zones.js'. No handler uses hetznerRequest<T>/storageBoxRequest<T> generics, so 15 files of interfaces (Server, HetznerAction, ZoneRRSet...) type-check nothing and silently drift; services/hetzner.ts even re-declares its own '

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/54) · 2026-07-03 · closed · 0 comments

### Registrar-list duplication leaves 29 Storage Box tools outside the Zod4 required[] guard (+ other scan blind spots)

The zod4-schema-enumeration test is the fleet's primary defense against the Zod 4 optin required[]-drop, and it silently omits the entire Storage Box domain because registrar lists are hand-copied in 11 places.

## Findings

### Zod4 required[]/describe regression guard omits all 29 Storage Box tools

`hetzner-mcp-server/src/tests/zod4-schema-enumeration.test.ts:54` — **medium** test-gap

**Evidence:** The header claims "This test enumerates EVERY tool registered against a freshly-built MCP serv

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/53) · 2026-07-03 · closed · 0 comments

### hetzner_create_server exposes `automount` but not the `volumes` param it depends on

## Findings

### hetzner_create_server exposes `automount` but not the `volumes` param it depends on

`hetzner-mcp-server/src/tools/servers.ts:64` — **low** bug

**Evidence:** Schema has `automount: z.boolean().optional().describe('Auto-mount volumes after attach')` but no `volumes` field. The live spec's POST /servers body includes `volumes` (volume IDs to attach at creation); `automount` only applies to those volumes. As exposed, `automount` promises behavior the tool can never trigger — a dea

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/52) · 2026-07-03 · closed · 0 comments

### Error sanitizer keeps config.data — request-body secrets survive on the cause chain

Distinct from #47 (which is about `response.data`): this is the **request** body. Certificate `private_key`, Storage Box passwords and TSIG keys travel through request bodies and survive `scrubConfig`.

## Findings

### Axios error sanitizer keeps config.data — request-body secrets (certificate private_key, Storage Box passwords, TSIG keys) survive on the cause chain

`hetzner-mcp-server/src/services/hetzner.ts:144` — **low** security

**Evidence:** scrubConfig only scrubs headers/auth: "scrubAu

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/51) · 2026-07-03 · closed · 0 comments

### hetzner_create_primary_ip: replace removed `datacenter` param with `location` (+ optional `assignee_id`)

API drift verified against the live Hetzner OpenAPI spec (2026-07-03): the create schema now has `additionalProperties: false` and no `datacenter` property, so the tool cannot place an unassigned Primary IP at all. Related to the deprecation wave tracked in #43 (reference-data tools), but this one breaks a create tool.

## Findings

### hetzner_create_primary_ip still sends removed `datacenter` param and lacks its `location` replacement

`hetzner-mcp-server/src/tools/primary-ips.ts:49` — **mediu

[Read the thread](https://github.com/lazyants/hetzner-mcp-server/issues/49) · 2026-07-03 · closed · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/lazyants/hetzner-mcp-server/issues).
