Skip to content

Tools ​

22 tools, in three groups. Tools marked read-only are the ones that survive HETZNER_READ_ONLY=true; tools marked 👤 ask a person before they act, through MCP elicitation — a dialog the model cannot answer on its behalf. Where the client cannot show one they fall back to a two-call confirm_token, and ELICITATION=false takes that fallback deliberately. See Asking a person.

Every tool declares an outputSchema and answers with structuredContent beside the text block, so a client can use a result without parsing prose. All of them carry untrusted: true and source: "hetzner-cloud-api" as fields of that object — there is no exception list, because record values, comments, labels and zone files are written by whoever controls the zone. The <untrusted-data> fence stays in the text block, where it is the readable presentation of that marker.

Every tool declares all four MCP annotations — readOnlyHint, destructiveHint, idempotentHint, openWorldHint. openWorldHint is false throughout: this server talks to the one Hetzner Cloud API it is configured for.

All 22 are registered unless you say otherwise. HETZNER_ALLOW_TOOLS and HETZNER_DENY_TOOLS narrow the list to the ones you want, and essential selects a curated eight — see choosing the tools that load.

Shared argument types:

ArgumentType
zoneZone ID or name, [A-Za-z0-9._-]+, e.g. example.com
nameRRSet name relative to the zone, [A-Za-z0-9@*._-]+, e.g. www or @ for the apex
typeA, AAAA, CAA, CNAME, DS, HINFO, HTTPS, MX, NS, PTR, RP, SOA, SRV, SVCB, TLSA, TXT
recordsArray of { value, comment? }, at least one; value is zone-file syntax
ttlInteger seconds, 60 – 2147483647
labelsObject of string key/value pairs
page, per_pagePagination; per_page 1 – 100, default 25

Zones ​

list_zones read-only ​

essential

List the zones of the project with status, mode, default TTL, assigned nameservers and record counts.

Arguments: name? (exact match), mode? (primary | secondary), label_selector? (e.g. env=prod), page?, per_page?

get_zone read-only ​

essential

Full details of a single zone.

Arguments: zone

create_zone 👤 asks a person when it carries content ​

Create a zone. primary for zones managed at Hetzner, secondary with primary_nameservers to transfer from external primaries. A primary zone can be initialized from a zone file in the same call.

Arguments: name, mode, ttl?, labels?, primary_nameservers?, zonefile?, confirm_token?

Creating an empty zone is additive and asks nobody. Creating one with primary_nameservers or a zonefile asks first: those two arguments carry the whole content of the zone, and if the name is already delegated to Hetzner's nameservers, that content is what the internet is served. They are the same payloads change_primary_nameservers and import_zonefile are guarded for, so denying either of those tools without this guard would not have taken the capability away.

WARNING

A tsig_key inside primary_nameservers becomes part of the conversation context and the client transcript. Treat it as disclosed and rotate if in doubt.

update_zone ​

Replace the labels of a zone. The given set replaces all existing labels; other properties have their own change_zone_* tools.

Arguments: zone, labels

delete_zone 👤 asks a person ​

Permanently delete a zone and every record in it. The refusal reports how many records the zone holds.

Arguments: zone, confirm_token?

export_zonefile read-only ​

essential

Export the zone as a BIND zone file. Worth calling before any bulk change — it is the only backup you get.

Arguments: zone

import_zonefile 👤 asks a person ​

Import a BIND zone file into an existing primary zone, replacing its current records. The approval is bound to a hash of the zone file, so a confirmation for one import cannot execute a different one. Returns an action to poll.

Arguments: zone, zonefile, confirm_token?

change_zone_ttl ​

Change the zone's default TTL, which applies to RRSets without an explicit one.

Arguments: zone, ttl

change_zone_protection 👤 asks a person before it disables ​

Enable or disable delete protection. Enabling is immediate; disabling removes the last safeguard in front of delete_zone and is gated like a deletion.

Arguments: zone, delete (boolean), confirm_token?

change_primary_nameservers 👤 asks a person ​

Replace the primary nameservers of a secondary zone. The entire zone content is taken from the new primaries on the next transfer. The token is bound to the nameserver list.

Arguments: zone, primary_nameservers, confirm_token?

Each entry: { address, port?, tsig_key?, tsig_algorithm? } with tsig_algorithm one of hmac-md5, hmac-sha1, hmac-sha256.

RRSets (record sets) ​

An RRSet is all records of one name and type — for example every A record for www. Hetzner's API works in RRSets, not individual records, which is why replacing one value means passing the whole set.

list_rrsets read-only ​

essential

List the RRSets of a zone with records, TTLs and protection status.

Arguments: zone, name?, type? (array, e.g. ["A", "AAAA"]), label_selector?, page?, per_page?

get_rrset read-only ​

essential

A single RRSet by name and type.

Arguments: zone, name, type

create_rrset 👤 asks a person for authority records ​

essential

Create a new RRSet. Fails if one with the same name and type exists — use set_records or add_records then. Gated only when the record decides who answers for a name — see Asking a person.

Arguments: zone, name, type, records, ttl?, labels?, confirm_token?

The confirmation binds ttl and labels as well as the record list: this call writes all three, so a token issued for one TTL cannot redeem another.

update_rrset ​

Replace the labels of an RRSet. Records and TTL have their own tools.

Arguments: zone, name, type, labels

delete_rrset 👤 asks a person ​

essential

Permanently delete an RRSet with all its records.

Arguments: zone, name, type, confirm_token?

set_records 👤 asks a person ​

essential

Replace all records of an RRSet. Anything not listed is removed. The token is bound to a hash of the record list.

Arguments: zone, name, type, records, confirm_token?

add_records 👤 asks a person for authority records ​

Append records to an RRSet, keeping the existing ones. Creates the RRSet if it does not exist — so denying create_rrset does not remove the ability to create one. Gated only when the record decides who answers for a name — see Asking a person.

Arguments: zone, name, type, records, ttl?, confirm_token?

The confirmation binds ttl as well as the record list: this call writes both, and a week-long TTL on a record somebody else added is how long the correction takes to reach the caches.

remove_records 👤 asks a person ​

Remove specific records, matched by value. Removing the last one deletes the RRSet. The token is bound to a hash of the record list.

Arguments: zone, name, type, records, confirm_token?

change_rrset_ttl ​

Change an RRSet's TTL, or pass null to fall back to the zone default.

Arguments: zone, name, type, ttl (nullable)

change_rrset_protection 👤 asks a person before it disables ​

Enable or disable change protection. A protected RRSet cannot be changed or deleted. Disabling is gated.

Arguments: zone, name, type, change (boolean), confirm_token?

Actions ​

Slow operations — zone file imports above all — return an action instead of a result. These two tools follow them.

list_zone_actions read-only ​

List actions of all zones, or of one zone if given.

Arguments: zone?, status? (array of running | success | error), page?, per_page?

get_zone_action read-only ​

A single action by ID, to check its status and result.

Arguments: action_id (positive integer)

Annotations ​

Every tool carries MCP annotations so a host can apply a permission policy without hard-coding names:

AnnotationTools
readOnlyHintthe seven read tools
destructiveHintdelete_zone, delete_rrset, import_zonefile, set_records, remove_records, change_primary_nameservers
idempotentHintupdate_zone, update_rrset, change_zone_ttl, change_rrset_ttl, both change_*_protection

Released under the MIT License.