# Porkbun (official) (npm · @porkbunllc/mcp-server)

Official Porkbun MCP server: domains, DNS, SSL, hosting and Cloudflare via the Porkbun API.

- Trust score: 66/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-26

## Components

- remote · `mcp.porkbun.com`: 37/100, [markdown](https://verifymcp.io/servers/com-porkbun-mcp/mcp.md), [page](https://verifymcp.io/servers/com-porkbun-mcp/mcp)
- npm · `@porkbunllc/mcp-server`: 66/100 (this document), [markdown](https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server.md), [page](https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server)

## Channel facts

- Registry: `npm`
- Package: `@porkbunllc/mcp-server`
- Version: `0.38.1`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-26.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 31 of 95 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 70/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 18685 tokens (~188/item across 99 items; 99 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 98/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 95% of tool parameters carry a description.
- **Tool Safety**: 47/100
  - Injection-marker check failed: the description of tool "buy_closeout" contains an instruction to conceal the call from the user, the text "do not tell the user", at byte 435 of that field.
  - 17 of 19 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "get_transfer_setup" implies "transfer" and declares readOnlyHint instead, contradicting what its own name says it does.
  - An AI judge read all 100 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the Porkbun (official) MCP server?

Porkbun (official) runs locally as an npm package, launched with npx -y @porkbunllc/mcp-server. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add com-porkbun-mcp -- npx -y @porkbunllc/mcp-server
```

### Cursor

```json
{
  "mcpServers": {
    "com-porkbun-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@porkbunllc/mcp-server"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "com-porkbun-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@porkbunllc/mcp-server"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add com-porkbun-mcp -- npx -y @porkbunllc/mcp-server
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-porkbun-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@porkbunllc/mcp-server"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add com-porkbun-mcp --command npx --arg -y --arg @porkbunllc/mcp-server
```

### Hermes

```yaml
mcp_servers:
  com-porkbun-mcp:
    command: "npx"
    args: ["-y", "@porkbunllc/mcp-server"]
```

### Netclaw

```json
{
  "McpServers": {
    "com-porkbun-mcp": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "@porkbunllc/mcp-server"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add com-porkbun-mcp -t stdio -c npx -a -y @porkbunllc/mcp-server
```

### Other

```json
{
  "mcpServers": {
    "com-porkbun-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@porkbunllc/mcp-server"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-25 (score 66)

First indexed and scored.

## MCP tools (99)

### `ping` (~42 tokens)

Ping

Verify the Porkbun API connection and credentials. Returns the caller's public IP and whether the API key is valid. Use this as a first sanity check before making other calls.

### `check_domain` (~170 tokens)

Check Domain

Check whether a single domain is available for registration and what it costs. Returns availability (`avail: yes|no`), registration price, renewal price, transfer price, and (for premium domains) extended pricing details. Pricing is in USD. Use this BEFORE register_domain to confirm cost — Porkbun rejects registrations whose `cost_cents` doesn't match the current quote. **Checking more than one name? Use `check_domains` instead** — it takes up to 25 in a single call and draws on a separate, more generous budget (200 domains/minute against 10 checks/10s here), because Porkbun chunks checks per registry and a batch costs less than the same names one at a time.

Input parameters:

- `domain` (string, required): Fully qualified domain name to check, e.g. `example.com`

### `check_domains` (~351 tokens)

Check Domains

Check up to 25 domains for availability and price in ONE call. Prefer this over calling `check_domain` in a loop whenever you have more than one name: Porkbun chunks availability checks per registry, so a batch is materially less work than the same names individually, and it draws on a separate budget of 200 domains per 60 seconds (counted per domain, not per call) rather than the single check's 10 per 10 seconds.

\**Read all three result lists — they mean different things.** `domains` is keyed by domain name and each value has the same shape as `check_domain`'s response. `invalid` holds entries that are not checkable at all (not a domain, unsupported TLD); one bad entry does NOT fail the call, so the other names still come back answered. `unresolved` holds domains the registry did not answer for — these are **neither available nor taken**, and reporting them as unavailable would be wrong. Retry those.

Duplicates are removed before the budget is charged. More than 25 names returns BULK_CHECK_TOO_MANY and checks nothing, rather than silently truncating, so split longer lists yourself. Synchronous, no polling — 25 mixed TLDs answers in roughly three seconds. One exception to watch: a few registries accept only a few domains per command and .de accepts exactly one, so a batch heavy in those TLDs returns BULK_CHECK_TOO_SLOW without checking anything. Group .de names into their own small calls.

Input parameters:

- `domains` (array, required): Fully qualified domain names to check, e.g. [`example.com`, `example.net`]. Max 25; duplicates are ignored.

### `get_registration_requirements` (~169 tokens)

Get Registration Requirements

Get a TLD's registration requirements as JSON Schema. Returns whether the TLD is registerable via the API (`apiRegisterable`), the `register_domain` request body as a JSON Schema (with the fixed term, cost, agreeToTerms), WHOIS-privacy / validated-address / registrant-only flags, and — for TLDs with registry eligibility rules (e.g. .us nexus, .ca legal type) — a second schema (`registryRequirements`) listing those fields with allowed values and labels. Call this BEFORE register_domain to confirm a TLD can be registered and to build a valid payload, instead of discovering requirements from a failed registration.

Input parameters:

- `tld` (string, required): TLD without a leading dot, e.g. `com`, `us`, `ca`.

### `get_pricing` (~173 tokens)

Get Pricing

Get current Porkbun pricing: registration, renewal and transfer prices per TLD in USD. No authentication required. **Pass `tlds` whenever you know which TLDs matter** (e.g. `["com", "io"]`): without it the response lists every TLD Porkbun sells, around 900 of them. Case, a leading dot and IDN form are handled. A TLD Porkbun does not sell comes back in `unsupported` instead of being priced, and if none of them are sold the call fails with INVALID_TLD. Standard pricing only: premium names have their own per-domain price, reported by check_domain.

Input parameters:

- `tlds` (array): TLDs to price, e.g. ["com", "io", "dev"]. Omit for every TLD.

### `list_marketplace` (~398 tokens)

List Marketplace

Browse domains for sale on the Porkbun marketplace (aftermarket — domains owned by other users, not new registrations). Returns each listing's domain, TLD, SLD length, price (in USD), and listing date.

Filters (all optional, server-side, mirroring the porkbun.com/marketplace UI):
\- `query`: SLD substring match. Multi-word queries: prefix a word with `-` to exclude it (e.g. `"ai -test"` matches SLDs containing 'ai' but not 'test').
\- `tlds`: limit to a list of TLDs (without the leading dot).
\- `sld_length_min`, `sld_length_max`: SLD character length bounds.
\- `sort_name`: `domain` | `tld` | `price` | `sld_length`.
\- `sort_direction`: `asc` | `desc`.

When any filter is set, server returns up to 1000 matching listings. With no filters, supports raw pagination via `start` / `limit` (max 5000).

Input parameters:

- `limit` (integer): Page size (no-filter mode only). Default 1000, max 5000.
- `query` (string): SLD substring search. Use `-word` to exclude. Example: `'ai -test'`.
- `sld_length_max` (integer): Maximum SLD character length.
- `sld_length_min` (integer): Minimum SLD character length.
- `sort_direction` (string): Sort direction.
- `sort_name` (string): Sort field. Default: `sld_length` asc when query is set, else `create_date` desc.
- `start` (integer): Pagination offset (no-filter mode only). Default 0.
- `tlds` (array): Limit to these TLDs (no leading dot). Example: `['com', 'io', 'ai']`.

### `list_domains` (~398 tokens)

List Domains

List domains in the authenticated Porkbun account. Returns one page (up to 1000) with metadata: expire date, auto-renew, security lock, WHOIS privacy, API access opt-in, and notLocal flag.

Filters (all optional):
\- `domain`: exact match. Returns 0 or 1.
\- `name_contains`: substring search on domain name.
\- `tlds`: limit to these TLDs (no leading dot).
\- `expiring_within_days`: only domains expiring within N days. Useful for renewal automation.
\- `auto_renew`: 'yes' or 'no'.
\- `api_access`: 'yes' or 'no'. Filter to domains an API key can actually operate on — eliminates `API_ACCESS_DISABLED` errors downstream.
\- `sort_name`: 'domain' | 'tld' | 'create_date' | 'expire_date'. Default expire_date.
\- `sort_direction`: 'asc' | 'desc'. Default asc.

For a single domain by name, use `get_domain` instead — cleaner shape and 404-on-not-found semantics.

Input parameters:

- `api_access` (string): Filter to domains opted in to API access.
- `auto_renew` (string): Filter to domains with auto-renew on or off.
- `domain` (string): Exact domain match — returns 0 or 1 result.
- `expiring_within_days` (integer): Only domains expiring within this many days from now.
- `include_labels` (boolean): Include user-defined domain labels in the response.
- `name_contains` (string): Case-insensitive substring on the full domain name.
- `sort_direction` (string): Sort direction.
- `sort_name` (string): Sort field.
- `start` (integer): Pagination offset. Default 0.
- `tlds` (array): Limit to these TLDs (no leading dot). Example: ['com', 'io']

### `get_domain` (~109 tokens)

Get Domain

Get the metadata for a single domain in the authenticated account: status, TLD, create date, expire date, security lock, WHOIS privacy, auto-renew, API access opt-in, and (optionally) labels. Returns an error with code `DOMAIN_NOT_FOUND` if the domain isn't in the account.

Input parameters:

- `domain` (string, required): Fully qualified domain name in the account, e.g. `example.com`
- `include_labels` (boolean): Include user-defined domain labels in the response.

### `get_balance` (~167 tokens)

Get Balance

Get the available account credit balance for the authenticated Porkbun account. Returns the balance in cents (integer) and a human-readable display string (e.g. `$12.34`). Call it before offering to buy anything: a purchase is paid from this prepaid balance, not charged to a card, so this number decides whether a registration, renewal or transfer goes through as-is. If it is short and the account has a saved card, `top_up_account_credit` adds credit in one call (ask the user first, with the amount); `get_auto_topup` reports whether a card is on file. Without a card, or if you do not charge cards on a user's behalf, the user adds credit with **buy account credit** at https://porkbun.com/account/credit.

### `get_auto_topup` (~105 tokens)

Get Auto Topup

Read the account's auto top-up configuration: whether it is on, the balance threshold that triggers it, the amount added, whether a payment method is actually on file, and `effectiveAmount` — what `top_up_account_credit` would charge right now. If `paymentMethodOnFile` is false the settings are inert: nothing can be charged and auto top-up will never fire, and a card can only be saved on porkbun.com, never through the API.

### `configure_auto_topup` (~465 tokens)

Configure Auto Topup

Set the top-up amount, and/or turn auto top-up on or off.

\`amount_cents` (integer US cents) is what a top-up adds, and it stands on its own: `top_up_account_credit` charges this same figure on demand, so setting it without enabling automation is a normal thing to do. Capped at $500 (50000) with a $5 floor when set through the API; a larger figure has to be set by the account holder at https://porkbun.com/account/api and is honoured as-is.

\`enabled: true` (with `threshold_cents`) additionally makes it fire by itself: an order that drops the balance below `threshold_cents` charges the saved payment method for the amount and adds it — the way to stop an unattended workflow dead-ending on INSUFFICIENT_FUNDS. `enabled: false` stops it firing on a threshold but KEEPS the amount on file.

Confirm the numbers with the user before calling: this authorises charges to their card. No card is added or changed here, and none can be added over the API. Supports dry_run.

Input parameters:

- `amount` (integer): Deprecated alias of `amount_cents`, same unit (integer US cents). Send `amount_cents` instead.
- `amount_cents` (integer): What a top-up adds, 500-50000 when set via the API. Can be sent on its own. E.g. 10000 = $100. Integer US cents: 803 means $8.03, not $803.
- `dry_run` (boolean): If true, validate only — returns wouldSucceed and changes nothing.
- `enabled` (boolean): True to switch auto top-up on (threshold required, plus an amount either here or already on file), false to switch it off. Omit it to change only the amount.
- `threshold` (integer): Deprecated alias of `threshold_cents`, same unit (integer US cents). Send `threshold_cents` instead.
- `threshold_cents` (integer): Balance below which a top-up fires. Required when enabling. E.g. 2000 = $20. Integer US cents: 803 means $8.03, not $803.

### `top_up_account_credit` (~492 tokens)

Top Up Account Credit

\**Charges the user's saved payment method** (`amount_cents` is integer cents: 803 charges $8.03; tell the user the dollar figure first) and adds the money to their Porkbun account credit immediately. Use it when a purchase failed with INSUFFICIENT_FUNDS and the user wants to continue now — enabling auto top-up does not help in that moment, because it only fires on the next order.

\**Ask the user before calling, with the figure. This spends real money off a card, not credit they already bought.** Omit `amount_cents` and it charges what the account has configured (or $50 if it never has) — that is the right default, and `amount_source` in the response says which was used. Pass `amount_cents` only when the user wants a specific one-off figure, e.g. enough to cover a particular purchase; it does NOT change their saved setting, so prefer it over calling `configure_auto_topup` for a single charge.

Fails with `NO_PAYMENT_METHOD` when nothing is saved to charge (the user has to save a card or buy credit on porkbun.com; the API cannot add one), `CARD_DECLINED` when the card refuses, and `TOPUP_LIMIT_EXCEEDED` when the month's dollars or the frequency run out — the account's monthly spend limit caps top-up dollars as well as domain spend, an account with no limit set gets $100/month, and there are 5/day and 20/month count caps. `get_auto_topup` reports `monthlyCeiling`, `ceilingSource` and `toppedUpThisMonth`, so check there before promising a user a top-up will go through. Every successful charge emails the account holder. A sandbox key grants simulated credit and charges nothing. Supports dry_run, which previews and charges nothing.

Input parameters:

- `amount` (integer): Deprecated alias of `amount_cents`, same unit (integer US cents). Send `amount_cents` instead.
- `amount_cents` (integer): One-off amount to charge, 500-50000. Omit to charge the account's configured top-up amount. Does not change any saved setting. Integer US cents: 803 means $8.03, not $803.
- `dry_run` (boolean): If true, report what would be charged without charging it.

### `get_api_settings` (~81 tokens)

Get API Settings

Get the authenticated account's API spend control configuration: monthly spend limit, low-balance alert threshold, auto top-up settings, and current month's API spend total. All amounts are in cents. Useful for an agent to check budget headroom before initiating expensive operations — `register_domain` will be hard-blocked if it would push monthly spend over the configured limit.

### `get_nameservers` (~116 tokens)

Get Nameservers

Get the current nameservers configured for a domain in the authenticated account. Returns an array of nameserver hostnames, read live from the registry. Read-only complement to `update_nameservers`. **Treat the result as an unordered set** — registries return nameservers in whatever order they like, so this will often not match the order passed to `update_nameservers`. Order is meaningless in DNS; never report a reordering as a change.

Input parameters:

- `domain` (string, required): Fully qualified domain name, e.g. `example.com`

### `list_dns_records` (~99 tokens)

List DNS Records

List all DNS records for a domain in the authenticated account. Returns each record's id, type (A, AAAA, CNAME, MX, TXT, etc.), name (subdomain or empty for apex), content, ttl, and priority (where applicable). The `id` field is required when editing or deleting a specific record.

Input parameters:

- `domain` (string, required): Fully qualified domain name registered at Porkbun, e.g. `example.com`

### `search_closeouts` (~471 tokens)

Search Closeouts

Search expired-domain closeouts: names that did not sell at auction and are now offered at a fixed price that descends on a schedule. No bidding — the first buyer at the current price takes the name. Filter by keyword, TLD, exact name length, age range and price range; sort by domain, end time, price, revenue, visitors, inbound links or registration_date. **Every row carries `age` and `registrationDate`, and both are sortable** — sort_name=registrationDate with sort_direction=asc gives the OLDEST registrations first, which is the single most requested thing here and is not possible on the website. Note sort_direction=asc on revenue, visitors or inboundLinks lists the domains with no recorded figure first (they come back null); use desc on those. `price` is the closeout price ALONE. Do not quote a user a total from these results: the binding amount adds the renewal or transfer year and comes from get_closeout. Page with start/limit until start >= totalAvailable.

Input parameters:

- `age_max` (integer): Maximum domain age in years.
- `age_min` (integer): Minimum domain age in years.
- `limit` (integer): Rows per page, max 500. Default 100.
- `name_length` (integer): Exact SLD character count.
- `price_max` (integer): Deprecated alias of `price_max_cents`, same unit (integer US cents). Send `price_max_cents` instead.
- `price_max_cents` (integer): Maximum closeout price. Integer US cents: 803 means $8.03, not $803.
- `price_min` (integer): Deprecated alias of `price_min_cents`, same unit (integer US cents). Send `price_min_cents` instead.
- `price_min_cents` (integer): Minimum closeout price. Integer US cents: 803 means $8.03, not $803.
- `query` (string): Keyword match on the domain name.
- `sort_direction` (string): Sort direction.
- `sort_name` (string): Field to sort by.
- `start` (integer): Paging offset. Default 0.
- `tld` (string): Single TLD, with or without the leading dot. Omit to search all.

### `get_closeout` (~131 tokens)

Get Closeout

Get one closeout plus `totalPrice` — the binding amount, which is the closeout price plus the registration year that comes with it. Always call this before buy_closeout: totalPrice is what you must pass as `cost_cents`, and it cannot be derived from search results because a name already at Porkbun is renewed while anything else is transferred in, and those price differently. `available: false` means somebody already claimed it. Quote the user totalPrice, never the search `price`.

Input parameters:

- `domain` (string, required): Domain offered as a closeout, e.g. `example.com`

### `buy_closeout` (~341 tokens)

Buy Closeout

\**Spends account credit.** Buys a closeout at its current price and claims the name. Confirm the total with the user first. `cost_cents` must equal `totalPrice` from get_closeout exactly — any other value is refused, so you cannot accidentally charge a price the user did not agree to. Use dry_run with cost 0 to quote without charging. **The domain is reserved, not delivered.** The provider releases it over the following days, so do not tell the user it is in their account: poll list_domains or watch the domain.registered webhook. Every post-charge failure refunds automatically and reports refunded:true. Losing the race to another buyer (CLOSEOUT_UNAVAILABLE) is not worth retrying on the same name — closeouts are first-come at a fixed price. CLOSEOUT_NOT_ELIGIBLE means support has blocked this account from auctions and closeouts over past-due invoices or an auction terms violation — do not retry, tell the user to contact support. There is no account-age or order-history requirement: eligibility is the same as registering a domain, plus verified email and phone.

Input parameters:

- `cost` (integer): Deprecated alias of `cost_cents`, same unit (integer US cents). Send `cost_cents` instead.
- `cost_cents` (integer): Exact totalPrice from get_closeout. Use 0 only with dry_run. Required. Integer US cents: 803 means $8.03, not $803.
- `domain` (string, required): Domain to buy, e.g. `example.com`
- `dry_run` (boolean): Validate and price without charging or claiming.

### `get_transfer_setup` (~93 tokens)

Get Transfer Setup

Report where a held inbound transfer is and what it is waiting on: whether it is held at PENDINGDNS, whether its DNS zone exists, how many records it holds, what the domain currently delegates to, and the next step. Use this to resume a no-downtime transfer instead of tracking that state yourself.

Input parameters:

- `domain` (string, required): Domain with a pending inbound transfer, e.g. `example.com`

### `prepare_transfer` (~106 tokens)

Prepare Transfer

Create the Porkbun DNS zone for a domain whose inbound transfer is held, so records can be added before the domain moves. Step 2 of the no-downtime sequence (transfer_domain with hold_for_dns_setup, prepare_transfer, import_dns_records, start_transfer). Returns the Porkbun nameservers. The zone is created deliberately, not as a side effect of the first record write, which is why this call exists.

Input parameters:

- `domain` (string, required): Domain with a held inbound transfer.

### `start_transfer` (~130 tokens)

Start Transfer

Release a held inbound transfer to the registry. Final step of the no-downtime sequence, and the only thing that releases a hold — nothing does it on a timer, so a held transfer waits indefinitely until you call this. Refuses with TRANSFER_ZONE_EMPTY if the zone has no records, which is the outage the hold exists to prevent; only pass force when the domain genuinely needs no DNS at Porkbun.

Input parameters:

- `domain` (string, required): Domain with a held inbound transfer.
- `force` (boolean): Release even though the Porkbun zone is empty. Only for domains that need no DNS here.

### `cancel_transfer` (~141 tokens)

Cancel Transfer

\**Cancels a paid inbound transfer and refunds the order.** Confirm with the user first. The order is deliberate: mark cancelled locally, withdraw at the registry, verify the registry accepted the withdrawal, then refund. If the registry state cannot be confirmed it restores the transfer and returns TRANSFER_STATE_UNCONFIRMED rather than refunding something that may still be live — escalate to support in that case rather than retrying. The response reports withdrawnAtRegistry, registryResultCode, refunded and refundAmount.

Input parameters:

- `domain` (string, required): Domain whose pending inbound transfer should be cancelled.
- `dry_run` (boolean): Preview what would happen without cancelling or refunding.

### `update_transfer_auth_code` (~165 tokens)

Update Transfer Auth Code

Replace the authorization code on an inbound transfer that stalled because the code was wrong, and re-queue it — instead of cancelling, refunding and resubmitting. The new code is validated against the registry before being stored, so a bad one is rejected here rather than failing again days later. Only repairable transfers qualify; others return TRANSFER_NOT_REPAIRABLE. Note that a losing registrar's refusal also surfaces as a bad-auth-code status, so check for a denial notice before assuming the code is the problem.

Input parameters:

- `auth_code` (string, required): Replacement authorization (EPP) code from the losing registrar.
- `domain` (string, required): Domain with the stalled inbound transfer.
- `dry_run` (boolean): Validate the code without storing it or re-queueing.

### `scan_dns_records` (~255 tokens)

Scan DNS Records

Discover the DNS records a domain currently publishes by querying its live authoritative nameservers. Writes nothing. Use this before moving a domain to Porkbun: a registrar transfer carries only the delegation, never the zone contents, so the old registrar's records become permanently unreadable the moment it stops answering for the domain — and a domain whose records were never recreated goes dark right then (website down, mail bouncing). Scan first, review, then call import_dns_records, and only then change nameservers. The scan probes a wide list of well-known names (apex, common subdomains, MX, DKIM selectors, provider verification hosts) but cannot enumerate a zone — DNS has no listing operation — so treat it as thorough, not exhaustive, and tell the user that. If the losing registrar has its own API, reading the zone from there is authoritative: do that with the user's own credentials (they must never be sent to Porkbun) and pass the records to import_dns_records instead. Limited to 20 calls per hour per account, since each call is roughly 90 DNS lookups.

Input parameters:

- `domain` (string, required): Domain to inspect, e.g. `example.com`. Does not need to be registered at Porkbun yet.

### `import_dns_records` (~248 tokens)

Import DNS Records

Create many DNS records on a Porkbun domain in one call — the companion to scan_dns_records for keeping a transferred domain working. Pass `records` to import an exact list (preferred when you read the zone from the old registrar's API), or omit it entirely to import whatever a live scan of the domain discovers. Idempotent: a record that already exists is reported in `skipped`, not `failed`, so this is safe to re-run and safe as a converge step. Individual failures are listed in `failures` while everything else still imports. NS and SOA entries are ignored on purpose — they describe the delegation, not the zone. Important: imported records have no effect until the domain actually points at Porkbun's nameservers, so check get_nameservers afterwards and use update_nameservers when the user is ready to cut over — when the domain is delegated elsewhere the response also says so in `warnings`. Maximum 500 records per call.

Input parameters:

- `domain` (string, required): Porkbun domain to create the records on, e.g. `example.com`
- `records` (array): Exact records to create. Omit to import what a live scan of the domain finds.

### `list_dnssec_records` (~75 tokens)

List DNSSEC Records

List the DNSSEC DS records currently submitted to the registry for a domain. Returns key tag, algorithm, digest type, and digest. Use this to verify DNSSEC chain-of-trust setup. Empty array = DNSSEC not configured.

Input parameters:

- `domain` (string, required): Fully qualified domain name, e.g. `example.com`

### `list_url_forwards` (~110 tokens)

List URL Forwards

List all URL forwarding rules configured for a domain. Each entry includes its `id` (used by `delete_url_forward`), the source subdomain, the destination URL, the redirect `type` (permanent/temporary/masked), the exact `redirectType` code (301/302/307/masked — distinguishes 302 from 307), and whether the request path and wildcards are forwarded.

Input parameters:

- `domain` (string, required): Fully qualified domain name, e.g. `example.com`

### `list_glue_records` (~93 tokens)

List Glue Records

List glue records for a domain. Glue records associate a host (e.g. `ns1.example.com`) with one or more IP addresses at the registry, used when running your own nameservers on the same domain they serve. Returns the host, IPv4 addresses, and IPv6 addresses for each glue record.

Input parameters:

- `domain` (string, required): Domain to list glue records for, e.g. `example.com`

### `list_transfers` (~75 tokens)

List Transfers

List all in-progress and recent inbound domain transfers for the authenticated account. Returns each transfer's domain, status (`NEW`, `PENDINGAUTH`, `PENDINGSUBMIT`, `PENDINGTRANSFER`, `DONE`, `CANCELED`, etc.), and create date. Use this to monitor transfers initiated by `transfer_domain`.

### `get_transfer_status` (~83 tokens)

Get Transfer Status

Get the status of a specific inbound transfer for a domain. Useful for polling after `transfer_domain` to know when the transfer completes (typical window: 5-7 days). Returns the same status values as `list_transfers`, plus a human-readable description.

Input parameters:

- `domain` (string, required): Domain whose transfer status to check, e.g. `example.com`

### `get_ssl_bundle` (~90 tokens)

Get SSL Bundle

Retrieve the free Porkbun-issued SSL certificate bundle for a domain. Returns the certificate chain, private key, and public key (PEM-encoded strings). Porkbun automatically provisions Let's Encrypt certificates for all registered domains using Porkbun nameservers. Use this to install TLS on a server you control.

Input parameters:

- `domain` (string, required): Fully qualified domain name registered at Porkbun, e.g. `example.com`

### `get_contacts` (~67 tokens)

Get Contacts

Get the four contacts (registrant, admin, tech, billing) for a domain in the authenticated account, with their current field values (name, organization, address, phone, email). Read-only.

Input parameters:

- `domain` (string, required): Fully qualified domain name, e.g. `example.com`

### `register_domain` (~587 tokens)

Register Domain

\**Spends account credit.** Registers a new domain on the authenticated Porkbun account. The `cost_cents` parameter must exactly match the current registration price returned by `check_domain` (in cents) — Porkbun rejects mismatched quotes. Workflow: call `check_domain` first to get availability + price, confirm the spend with the user, then call this. The order is idempotency-safe: retries within 24 hours via the same Idempotency-Key return the original response without re-charging. Premium domains, .uk, and a handful of registry-specific TLDs cannot be registered via API and must be done on the website. The account's email and phone number must be verified. A single API registration cannot exceed $100 (`ORDER_TOO_LARGE`); above that the user has to register on the website.

\**Money comes from prepaid account credit.** The purchase itself charges the credit balance, never a card. If the balance is short, the call fails with `INSUFFICIENT_FUNDS` carrying `cost`, `balance`, `shortfall` and **`topUpAvailable`** (`dry_run: true` reports the same without charging). When `topUpAvailable` is true a card is saved: tell the user the shortfall and offer to top up — with their OK, call `top_up_account_credit` (`amount_cents` at least `topUpAmountToCover`, or omit it for their configured amount) and then retry this exact call. Only when it is false does the user have to add credit at https://porkbun.com/account/credit. Suggest `configure_auto_topup` if they want this to happen automatically next time. Money parameters are integer cents: state amounts to the user in dollars (`cost_cents: 1108` is $11.08). If you do not charge cards on a user's behalf, hand it back instead: tell them the shortfall and that they can add it with **buy account credit** at https://porkbun.com/account/credit, then retry once they have.

Input parameters:

- `cost` (integer): Deprecated alias of `cost_cents`, same unit (integer US cents). Send `cost_cents` instead.
- `cost_cents` (integer): Registration price. Must match the value returned by `check_domain` for this domain (multiplied by years if duration > 1). Required. Integer US cents: 803 means $8.03, not $803.
- `domain` (string, required): Fully qualified domain name to register, e.g. `example.com`
- `dry_run` (boolean): If true, validate everything (availability, price match, eligibility, funds, spend limit) and return a preview with `dryRun: true` and `wouldSucceed` WITHOUT registering or charging. Use to safely co…

### `renew_domain` (~502 tokens)

Renew Domain

\**Spends account credit.** Renews an existing domain in the authenticated account. The `cost_cents` parameter must exactly match the current renewal price returned by `check_domain` (in cents). The domain must be opted in to API access (per-domain or global toggle in account settings). Domains registered within the last 30 days, or already renewed within the last 30 days, cannot be renewed yet — the API returns `RENEWAL_TOO_SOON`. Premium domain renewals are not supported via API. Idempotency-safe: retries within 24 hours don't double-charge.

\**Money comes from prepaid account credit.** The purchase itself charges the credit balance, never a card. If the balance is short, the call fails with `INSUFFICIENT_FUNDS` carrying `cost`, `balance`, `shortfall` and **`topUpAvailable`** (`dry_run: true` reports the same without charging). When `topUpAvailable` is true a card is saved: tell the user the shortfall and offer to top up — with their OK, call `top_up_account_credit` (`amount_cents` at least `topUpAmountToCover`, or omit it for their configured amount) and then retry this exact call. Only when it is false does the user have to add credit at https://porkbun.com/account/credit. Suggest `configure_auto_topup` if they want this to happen automatically next time. Money parameters are integer cents: state amounts to the user in dollars (`cost_cents: 1108` is $11.08). If you do not charge cards on a user's behalf, hand it back instead: tell them the shortfall and that they can add it with **buy account credit** at https://porkbun.com/account/credit, then retry once they have.

Input parameters:

- `cost` (integer): Deprecated alias of `cost_cents`, same unit (integer US cents). Send `cost_cents` instead.
- `cost_cents` (integer): Renewal price. Must match the value returned by `check_domain`. Required. Integer US cents: 803 means $8.03, not $803.
- `domain` (string, required): Domain name to renew, e.g. `example.com`. Must already be in your account.
- `dry_run` (boolean): If true, validate and preview (`dryRun: true`, `wouldSucceed`) WITHOUT renewing or charging.

### `transfer_domain` (~624 tokens)

Transfer Domain

\**Spends account credit.** Initiates a transfer of an external domain into Porkbun. Requires the auth/EPP code from the losing registrar, and `cost_cents` must match the current transfer price from `check_domain`. Poll with `get_transfer_status`. Most transfers finish well inside the five-day worst case — two thirds within 24 hours — so do not promise the user a week. .uk and a few TLDs do not support inbound API transfers. Idempotency-safe.

\**Set `hold_for_dns_setup` unless the user has no DNS to preserve.** A transfer carries only the delegation, so a domain that moves before its records exist at Porkbun goes dark. Holding charges the transfer but parks it until you release it: hold → prepare_transfer → import_dns_records → start_transfer. Nothing releases a held transfer on a timer.

\**Money comes from prepaid account credit.** The purchase itself charges the credit balance, never a card. If the balance is short, the call fails with `INSUFFICIENT_FUNDS` carrying `cost`, `balance`, `shortfall` and **`topUpAvailable`** (`dry_run: true` reports the same without charging). When `topUpAvailable` is true a card is saved: tell the user the shortfall and offer to top up — with their OK, call `top_up_account_credit` (`amount_cents` at least `topUpAmountToCover`, or omit it for their configured amount) and then retry this exact call. Only when it is false does the user have to add credit at https://porkbun.com/account/credit. Suggest `configure_auto_topup` if they want this to happen automatically next time. Money parameters are integer cents: state amounts to the user in dollars (`cost_cents: 1108` is $11.08). If you do not charge cards on a user's behalf, hand it back instead: tell them the shortfall and that they can add it with **buy account credit** at https://porkbun.com/account/credit, then retry once they have.

Input parameters:

- `auth_code` (string, required): Authorization (EPP) code from the losing registrar.
- `cost` (integer): Deprecated alias of `cost_cents`, same unit (integer US cents). Send `cost_cents` instead.
- `cost_cents` (integer): Transfer price. Must match the value returned by `check_domain`. Required. Integer US cents: 803 means $8.03, not $803.
- `domain` (string, required): Domain to transfer in, e.g. `example.com`
- `dry_run` (boolean): If true, validate and preview (`dryRun: true`, `wouldSucceed`) WITHOUT initiating the transfer or charging.
- `hold_for_dns_setup` (boolean): Charge the transfer but hold it at PENDINGDNS instead of releasing it, so DNS can be set up before the domain moves. Not supported for .uk/Handshake (returns TRANSFER_HOLD_NOT_AVAILABLE and charges n…

### `update_auto_renew` (~96 tokens)

Update Auto Renew

Turn auto-renewal on or off for a domain in the authenticated account. When auto-renew is on, Porkbun automatically charges your account credit at expiration. When off, you must manually renew or the domain expires. Idempotent.

Input parameters:

- `domain` (string, required): Domain to update, e.g. `example.com`
- `status` (string, required): `on` enables auto-renew, `off` disables it.

### `update_nameservers` (~206 tokens)

Update Nameservers

Replace the nameservers for a domain in the authenticated account. **This is a full replacement, not an append** — the supplied list becomes the complete set of nameservers. Most TLDs require 2-13 entries. Setting custom nameservers disables Porkbun's free DNS hosting for the domain. Idempotent: applying the same NS list twice is a no-op. The list is applied as a set — the registry may return it in a different order, so do not expect `get_nameservers` to echo your ordering back, and do not treat a reordering as drift.

Input parameters:

- `domain` (string, required): Domain to update, e.g. `example.com`
- `dry_run` (boolean): If true, validate only — returns wouldSucceed without changing the nameservers.
- `nameservers` (array, required): Full list of nameservers (e.g. `['ns1.example.com', 'ns2.example.com']`). Minimum 2, maximum 13.

### `update_contacts` (~324 tokens)

Update Contacts

Edit a domain's contacts. Provide `contacts` keyed by role with ANY subset of registrant/admin/tech/billing (unspecified roles keep their current values), or a single `contact` applied to all four. Mirrors the website: pushes to the registry on thick TLDs, and a registrant change (name/organization/email) fires the same new-owner notice/verification email — no 60-day transfer lock. Supports `dry_run`. Note: a registrant name/organization change on a .au domain, or any registrant change on an address-validation TLD (.de/.nrw), is rejected with REGISTRANT_CHANGE_NOT_SUPPORTED — do those at porkbun.com; admin/tech/billing edits still work.

Input parameters:

- `address_validation_choice` (string): For a registrant change on an address-validated TLD (.de/.nrw/.uk/.us/.ca/.nyc/.au/.eu/.in/.nz families) after an ADDRESS_VALIDATION_REQUIRED response: 'accept_suggestion' saves the standardized sugg…
- `contact` (object): A single contact applied to all four roles. Use this OR `contacts`, not both.
- `contacts` (object): Per-role contacts; include only the roles you want to change.
- `domain` (string, required): Domain to edit, e.g. `example.com`
- `dry_run` (boolean): If true, validate only — returns wouldSucceed without applying the change.

### `list_hosting_plans` (~79 tokens)

List Hosting Plans

List the hosting plans provisionable via the API, with price (cents — pass as `acknowledged_cost_cents` to create_hosting), interval, trial length, and features. Use this to discover plans + costs before create_hosting rather than hardcoding them. Currently Secure Static Hosting; more products are added over time.

### `create_hosting` (~567 tokens)

Create Hosting

Provision hosting for a domain in the account — Secure Static Hosting OR Cloud for WordPress (a managed WordPress site), chosen by `sku`. The domain's FIRST provision starts a 15-day FREE trial that auto-renews at the plan price ($3/mo or $30/yr) when it ends; a re-provision after deprovision is charged to account credit (one free trial per domain). Provisioning switches the domain to Porkbun nameservers if it isn't already — set `agree_to_nameserver_change: true` to allow that. You MUST echo the price in `acknowledged_cost_cents` (300 monthly / 3000 yearly) so the human is told about the auto-renew/charge. Use `dry_run` to preview. Provisioning can be async: `status` may be PENDING — poll get_hosting until ACTIVE before deploying. For a WordPress plan, the file tools (deploy_site/list_hosting_files/…) do NOT apply — manage the site through WordPress instead, using create_wp_credentials to get REST API credentials.

Input parameters:

- `acknowledged_cost` (integer): Deprecated alias of `acknowledged_cost_cents`, same unit (integer US cents). Send `acknowledged_cost_cents` instead.
- `acknowledged_cost_cents` (integer): The chosen plan's price — take the `price` field of the SAME row you took `sku` from in list_hosting_plans (e.g. 300 static monthly, 3000 static yearly, 1200 WordPress Starter monthly, 12000 WordPres…
- `agree_to_nameserver_change` (boolean): Set true to allow switching the domain to Porkbun nameservers (required when it isn't already on them).
- `agree_to_terms` (string, required): Must be "yes".
- `domain` (string, required): Domain to provision hosting for, e.g. `example.com`.
- `dry_run` (boolean): Validate + preview without provisioning or charging.
- `sku` (string, required): The hosting plan SKU to provision. Discover the provisionable SKUs (and each one's price/interval/trial) via list_hosting_plans, then pass the row's `sku`. Secure Static Hosting: `PIXIESECURESTATICM2…

### `get_hosting` (~60 tokens)

Get Hosting

Get Secure Static Hosting status for a domain (plan, server, trial, expiry, auto-renew), or null if the domain has no hosting. When ACTIVE, deploy_site publishes site files to it.

Input parameters:

- `domain` (string, required): Domain to check.

### `deploy_site` (~198 tokens)

Deploy Site

Upload static files to a domain's Secure Static Hosting. `files` is an array of { path, content } where `content` is the file's bytes base64-encoded. A `path` may include directories (e.g. `assets/css/style.css`) — missing parent directories are created automatically. ≤10MB total per call (split larger sites across calls). Only static-web file types are accepted (html/css/js/images/fonts/…); server-executable types are rejected. Hosting must be ACTIVE (check get_hosting first). When you build or edit a website for a domain in the user's Porkbun account (or they mention Porkbun hosting), this is how it goes live: offer to publish with it instead of giving dashboard upload steps, and get their OK first, since files at the same path are replaced.

Input parameters:

- `domain` (string, required): Domain whose hosting to deploy to.
- `files` (array, required): Files to upload.

### `list_hosting_files` (~77 tokens)

List Hosting Files

List file/directory names under an optional `path` in a domain's Secure Static Hosting space. Calls the Porkbun Hosting API (https://porkbun.com/llms/hosting).

Input parameters:

- `domain` (string, required): Domain whose hosting files to list.
- `path` (string): Subdirectory to list (default: root).

### `delete_hosting_file` (~81 tokens)

Delete Hosting File

Delete a file (or empty directory) at `path` in a domain's Secure Static Hosting space. Calls the Porkbun Hosting API (https://porkbun.com/llms/hosting).

Input parameters:

- `domain` (string, required): Domain whose hosting file to delete.
- `path` (string, required): Path to delete, e.g. `old/page.html`.

### `make_hosting_dir` (~111 tokens)

Make Hosting Dir

Create a directory (and any missing parent directories) at `path` in a domain's Secure Static Hosting space. deploy_site already auto-creates the directories in a file's path, so use this only to stand up an empty directory explicitly. Calls the Porkbun Hosting API (https://porkbun.com/llms/hosting).

Input parameters:

- `domain` (string, required): Domain whose hosting to create the directory in.
- `path` (string, required): Directory path to create, e.g. `assets/img`.

### `create_wp_credentials` (~340 tokens)

Create WP Credentials

CLOUD FOR WORDPRESS ONLY. Mint a WordPress Application Password for a domain's managed WordPress site so you can drive it over the WP REST API at https://<domain>/wp-json/ using HTTP Basic auth. The password is returned ONCE (WordPress stores only a hash) — save it immediately. Defaults to a dedicated least-privilege `porkbun-agent` user with the `editor` role (created on first use), which can manage content but cannot install code. `role: "administrator"` grants FULL site control including plugin installation (i.e. arbitrary code execution on the site) and therefore requires `acknowledge_full_access: true` — only request it if the user explicitly asked for admin-level access. The site must be provisioned and ACTIVE (poll get_hosting) — this works on any Cloud for WordPress site in the account however it was provisioned, but free $0 preview sites are excluded. Revoke any time with delete_wp_credentials. Rate limit: 20 mints per account per hour.

Input parameters:

- `acknowledge_full_access` (boolean): Required when role=administrator: confirms the user understands the credential can run arbitrary code on the site.
- `domain` (string, required): Domain whose WordPress site to mint credentials for.
- `dry_run` (boolean): Validate without creating anything.
- `name` (string): Label shown in wp-admin (letters, digits, dashes), e.g. `Acme-Agent`.
- `role` (string): `editor` (default) = content only, recommended for agents. `administrator` = full control incl. plugin install; requires acknowledge_full_access.

### `list_wp_credentials` (~96 tokens)

List WP Credentials

CLOUD FOR WORDPRESS ONLY. List the WordPress application passwords on a domain's site (uuid, name, created, last used) so you can audit them or pick one to revoke. Metadata only — the passwords themselves can never be re-read.

Input parameters:

- `domain` (string, required): Domain whose WordPress site to inspect.
- `wp_user` (string): WordPress username (defaults to the dedicated `porkbun-agent` user).

### `delete_wp_credentials` (~128 tokens)

Delete WP Credentials

CLOUD FOR WORDPRESS ONLY. Revoke a WordPress application password by `uuid` (from list_wp_credentials), or every one for the user with `all: true`. Anything using that credential stops authenticating immediately.

Input parameters:

- `all` (boolean): Revoke every application password for the user.
- `domain` (string, required): Domain whose WordPress site to revoke on.
- `dry_run` (boolean): Validate without revoking.
- `uuid` (string): The application password uuid to revoke.
- `wp_user` (string): WordPress username (defaults to `porkbun-agent`).

### `delete_hosting` (~69 tokens)

Delete Hosting

Deprovision (cancel) Secure Static Hosting for a domain; teardown is scheduled by Porkbun. Note: the domain has already used its one free trial, so provisioning it again later will be charged (no second free trial).

Input parameters:

- `domain` (string, required): Domain to deprovision hosting for.

### `create_sandbox_key` (~154 tokens)

Create Sandbox Key

Instantly create a free SANDBOX API key — NO credentials or approval needed (works before you have any keys). Returns a `pk1_sb_` / `sk1_sb_` pair for a throwaway test account seeded with $1000 fake credit. Set the returned keys as PORKBUN_API_KEY / PORKBUN_SECRET_API_KEY (or pass them to any tool) and the whole API runs against an isolated sandbox: no real registry actions, DNS changes, certificates, or charges. Ideal for rehearsing register → DNS → renew end-to-end before using a live key. Optional `name` labels the key.

Input parameters:

- `name` (string): Optional label/title for the sandbox key.

### `sandbox_topup` (~170 tokens)

Sandbox Topup

SANDBOX ONLY. Grant fake account credit to the sandbox account so paid operations (register/renew/transfer) can keep being exercised after funds run out. Requires a sandbox API key (`pk1_sb_…`). Optional `amount_cents` (integer US cents) (default 100000 = $1000; capped 1,000,000). Returns the new balance. With a live key this endpoint is not available.

Input parameters:

- `amount` (integer): Deprecated alias of `amount_cents`, same unit (integer US cents). Send `amount_cents` instead.
- `amount_cents` (integer): Fake credit to add (default 100000 = $1000; max 1000000). Integer US cents: 803 means $8.03, not $803.

### `sandbox_reset` (~69 tokens)

Sandbox Reset

SANDBOX ONLY. Wipe the sandbox account's simulated state (domains, DNS, orders, credit) and re-grant $1000 fake credit — a clean slate between test runs. Requires a sandbox API key (`pk1_sb_…`). With a live key this endpoint is not available.

### `sandbox_trigger_webhook` (~115 tokens)

Sandbox Trigger Webhook

SANDBOX ONLY. Fire a sample signed webhook event to your registered endpoints so you can test your handler and HMAC signature verification for ANY event type on demand — including cron-driven events like `domain.expiring` that don't result from a single API call. Register an endpoint first with the webhook tools. Requires a sandbox API key (`pk1_sb_…`).

Input parameters:

- `domain` (string): Domain used in the sample payload (default example.com).
- `eventType` (string, required): The webhook event type to emit.

### `mock_call` (~164 tokens)

Mock Call

Get a schema-accurate EXAMPLE response for any API endpoint with NO credentials — nothing to set up. Mirrors the real path under /mock (e.g. path `domain/listAll` or `dns/create/example.com`). Touches no datastore and returns the exact shape the live API would. Set `error: true` to see the error-response shape instead. Use it to learn/verify response shapes before wiring up real or sandbox keys. Omit `path` (or pass empty) to list every mockable endpoint.

Input parameters:

- `error` (boolean): Return the error-response shape instead of success.
- `path` (string): The real endpoint path to mock, without leading slash — e.g. `domain/listAll`. Omit to list all mockable endpoints.

### `create_dns_record` (~491 tokens)

Create DNS Record

Create a DNS record on a domain in the authenticated account. Returns the new record's `id` so it can be referenced by `update_dns_record` and `delete_dns_record`. For the `name` field: omit or pass empty string for the apex/root, otherwise pass the subdomain prefix only (e.g. `www`, not `www.example.com`). For MX and SRV records, set `prio` (priority). Free, doesn't spend account credit. If a record with this exact name, type and content already exists the call fails with `DUPLICATE_RECORD` and returns that record's id in `existingId` — use it rather than retrying or creating a variant. If a CNAME would share a name with another record type (forbidden by RFC 1034) the call fails with `RECORD_CONFLICT` and lists the blocking rows in `conflictingRecords`; delete those or pick another name.

\**Check `warnings` in the response and pass it on.** Porkbun keeps a DNS zone for every domain in the account whether or not the domain is delegated to Porkbun's nameservers, so this write succeeds either way. If the domain points somewhere else, the response is still `SUCCESS` but carries a `warnings` string naming the nameservers that actually answer — the record exists and nothing resolves differently. Do not report plain success in that case: either make the change at the provider that runs those nameservers, or offer `update_nameservers` to point the domain at Porkbun.

Input parameters:

- `content` (string, required): Record value (e.g. an IP for A, a hostname for CNAME, the text body for TXT).
- `domain` (string, required): Domain to add the record to, e.g. `example.com`
- `dry_run` (boolean): If true, validate only — returns wouldSucceed without creating the record.
- `name` (string): Subdomain prefix (no domain). Empty string or omitted = apex. Examples: `www`, `mail`, `api.staging`.
- `prio` (integer): Priority — required for MX and SRV records, ignored otherwise.
- `ttl` (integer): Time-to-live in seconds. Minimum 60. Defaults to 600 if omitted.
- `type` (string, required): Record type. Common: A, AAAA, CNAME, MX, TXT.

### `preflight_domain` (~442 tokens)

Preflight Domain

Ask whether a change is about to break a domain, BEFORE making it. Read-only, free, and the right thing to call before changing nameservers, transferring a domain out, or enabling DNSSEC.

Every check comes from a real incident, so these are the failures where a zone looks fine and stops working anyway. Read `blockers` first — those will break something. `warnings` will not break outright but are usually what the user notices next. `safe` is true only when both are empty. Each check names the rule it comes from and carries a `next_action`, so you can explain the finding rather than just report it.

The two an agent should treat as hard stops:
\- `dnssec-active` — DS records are published at the registry, so new nameservers will serve answers that do not match them and validating resolvers will refuse the WHOLE zone. The domain goes dark rather than degrading. DNSSEC has to be removed and the DS TTL waited out BEFORE the nameservers change. Never move delegation past this one.
\- `spf-duplicate` / `spf-lookups` — RFC 7208 permerrors that make SPF fail for every sender, so the domain's mail is already being degraded whether or not the user has noticed.

Check `nameservers-ours` before you read anything else: it says whether this zone is what the world resolves at all. A domain delegated to another provider still accepts every DNS write here and none of them take effect, and the mixed case (some of the delegated nameservers are Porkbun's, some are not) means resolvers disagree query to query.

The one worth explaining because nobody expects it: `wildcard-shadowed`. Under RFC 4592 a wildcard answers only names that do NOT exist in the zone, so a name holding just an MX or TXT record stops inheriting the wildcard's address and stops resolving — with nothing in the zone looking wrong.

Input parameters:

- `domain` (string, required): Domain to check, e.g. `example.com`
- `intent` (string): What you are about to do; scopes the checks. Defaults to general, which runs everything applicable.

### `list_dns_restore_points` (~222 tokens)

List DNS Restore Points

List the saved versions of a domain's DNS zone, newest first. **DNS is the one layer with no undo built in, and this is it** — if a record was deleted or edited by mistake you do not need to know what it used to say, you can look.

Restore points are captured automatically before the first write to a zone in each hour (so a run of edits costs one point, not one per record), before any bulk import or zone wipe, and before any restore.

Read two fields carefully. `recordCount` is the size of the zone AS IT WAS at that moment, not now — a drop between consecutive points is where records were lost. `matchesLive` marks the point the zone currently sits on, which is where you are, not where you want to go. Up to 50 are returned.

Restore points only cover the zone as Porkbun held it; a domain on someone else's nameservers has nothing here.

Input parameters:

- `domain` (string, required): Domain whose zone history to list, e.g. `example.com`

### `diff_dns_restore_point` (~219 tokens)

Diff DNS Restore Point

Compare a DNS restore point with the live zone before changing anything. Always do this before `restore_dns_zone` — it is free and it is the difference between fixing a zone and finding out afterwards.

\`missing` is in the restore point but not live: these are the records a restore would ADD BACK. `extra` is live but not in the restore point: a restore LEAVES THESE ALONE unless you pass prune. `inSync` is true when both lists are empty.

Records are matched on name, type, content and priority rather than id, because an id means nothing across a delete and re-create. SOA and NS are excluded — the zone's own delegation is not what anyone means by their DNS records. For masked records (parking, ALIAS, HTTPS) the value shown is the one that was configured, not the internal host it resolves to.

Input parameters:

- `domain` (string, required): Domain, e.g. `example.com`
- `snapshot_id` (integer, required): Restore point id from `list_dns_restore_points`.

### `restore_dns_zone` (~369 tokens)

Restore DNS Zone

Put a domain's DNS zone back to a saved restore point. Call `diff_dns_restore_point` first and show the user what will change.

\**By default this only ADDS BACK what is missing.** It does not remove records added since — pass prune: true for that, and only after reading the `extra` list, because 'restore my records' usually means 'put back what I lost', not 'delete everything I have done since'.

\**The restore is itself reversible.** The zone's state from immediately before is saved and returned as `previousStateSavedAs`; restore that id to undo. Use dry_run: true to rehearse — it reports exactly what would change and changes nothing.

\**Read `failed` in the response.** Parking records and other masked types (ALIAS, HTTPS) are managed by another part of the platform and cannot be recreated this way; they appear in `failed` rather than being counted, so `restored` is always a true number. If a parking record is what is missing, the domain needs re-parking on the website instead. SOA and NS are never touched.

\**A restore only matters if this zone is the one being served.** If the domain is delegated to another provider, the response carries a `warnings` string saying so: the records went back, and nothing the user can see has changed.

Input parameters:

- `domain` (string, required): Domain to restore, e.g. `example.com`
- `dry_run` (boolean): If true, report what would change without changing anything.
- `prune` (boolean): Also DELETE live records that are not in the restore point. Default false. Destructive — confirm with the user first.
- `snapshot_id` (integer, required): Restore point id from `list_dns_restore_points`.

### `update_dns_record` (~325 tokens)

Update DNS Record

Update an existing DNS record by its numeric `record_id` (obtained from `list_dns_records`). All fields except `record_id` and `domain` are optional — pass only the ones you want to change. Idempotent: applying the same update twice is a no-op.

\**Check `warnings` in the response and pass it on.** Porkbun keeps a DNS zone for every domain in the account whether or not the domain is delegated to Porkbun's nameservers, so this write succeeds either way. If the domain points somewhere else, the response is still `SUCCESS` but carries a `warnings` string naming the nameservers that actually answer — the record exists and nothing resolves differently. Do not report plain success in that case: either make the change at the provider that runs those nameservers, or offer `update_nameservers` to point the domain at Porkbun.

Input parameters:

- `content` (string): New record value.
- `domain` (string, required): Domain the record belongs to, e.g. `example.com`
- `dry_run` (boolean): If true, validate only — confirms the record exists and is editable, returns wouldSucceed without changing it.
- `name` (string): New subdomain prefix (empty string = apex).
- `prio` (integer): New priority (MX/SRV only).
- `record_id` (string, required): Numeric record ID (as a string). Get this from `list_dns_records`.
- `ttl` (integer): New TTL in seconds.
- `type` (string): New record type (rarely changed).

### `delete_dns_record` (~230 tokens)

Delete DNS Record

Delete a single DNS record by its numeric `record_id` (obtained from `list_dns_records`). Idempotent: deleting an already-deleted record returns success. Free.

\**Check `warnings` in the response and pass it on.** Porkbun keeps a DNS zone for every domain in the account whether or not the domain is delegated to Porkbun's nameservers, so this write succeeds either way. If the domain points somewhere else, the response is still `SUCCESS` but carries a `warnings` string naming the nameservers that actually answer — the record exists and nothing resolves differently. Do not report plain success in that case: either make the change at the provider that runs those nameservers, or offer `update_nameservers` to point the domain at Porkbun.

Input parameters:

- `domain` (string, required): Domain the record belongs to, e.g. `example.com`
- `dry_run` (boolean): If true, validate only — confirms the record exists and is deletable, returns wouldSucceed without deleting it.
- `record_id` (string, required): Numeric record ID (as a string).

### `create_dnssec_record` (~514 tokens)

Create DNSSEC Record

Submit a DNSSEC DS record to the registry for a domain. Use when you sign DNS yourself (custom nameservers running BIND/Knot/PowerDNS/etc.) and need to publish the chain-of-trust at the parent zone. Required: keyTag, algorithm, digestType, digest. Optional key-data fields for registries that require full DNSKEY (rare). Algorithms and digest types are validated per registry, not globally: registries are retiring the values deprecated by RFC 9904/9905/9906 on their own schedules, so a value one registry has already dropped may still be accepted by another. A refused value returns DNSSEC_ALGORITHM_DEPRECATED naming a replacement, and retrying the same values will not help. Use alg 8 (RSA/SHA-256) or 13 (ECDSA/SHA-256) with digestType 2 (SHA-256) to be accepted everywhere. A SUCCESS response may carry a warnings array when a value still works but is being retired: the record WAS created. Deprecation never affects list_dnssec_records or delete_dnssec_record.

Input parameters:

- `alg` (string, required): Algorithm number, e.g. `13` for ECDSA P-256 SHA-256, `8` for RSA SHA-256. Algorithms 1, 3, 5, 6, 7 and 12 are deprecated (RFC 9904/9905/9906) and are already refused by some registries.
- `digest` (string, required): Hex-encoded DS digest value.
- `digestType` (string, required): Digest type, e.g. `2` for SHA-256, `4` for SHA-384. Digest types 1 (SHA-1) and 3 (GOST R 34.11-94) are deprecated and are being refused registry by registry; 2 is accepted everywhere.
- `domain` (string, required): Domain to add the DS record to.
- `keyDataAlgo` (string): DNSKEY algorithm (optional).
- `keyDataFlags` (string): DNSKEY flags (optional — typically 256 or 257).
- `keyDataProtocol` (string): DNSKEY protocol (optional — almost always 3).
- `keyDataPubKey` (string): Base64-encoded public key (optional).
- `keyTag` (string, required): DNSSEC key tag (16-bit identifier of the key).
- `maxSigLife` (string): Maximum signature lifetime in seconds (registry-specific, optional).

### `delete_dnssec_record` (~85 tokens)

Delete DNSSEC Record

Remove a DNSSEC DS record from the registry for a domain, identified by key tag. Use when retiring a key. Idempotent: deleting a non-existent key tag returns success.

Input parameters:

- `domain` (string, required): Domain to remove the DS record from.
- `keyTag` (string, required): Key tag of the DS record to remove (from `list_dnssec_records`).

### `create_url_forward` (~307 tokens)

Create URL Forward

Add a URL forwarding rule for a domain. Forwards a subdomain (or apex if `subdomain` is empty/omitted) to an arbitrary destination URL. Useful for redirects without setting up a web server. Free.

Input parameters:

- `domain` (string, required): Domain to add the forward to, e.g. `example.com`
- `includePath` (string, required): `yes` appends the request URI path to the forward target; `no` always sends to the bare destination.
- `location` (string, required): Destination URL to forward visitors to, e.g. `https://newsite.example.com`
- `redirect_type` (string): Optional exact redirect type; takes precedence over `type`. 301 = permanent, 302 or 307 = temporary (pass 307 here to get a 307), masked = URL masking. Omit to derive from `type` (temporary→302, perm…
- `subdomain` (string): Subdomain prefix to forward. Empty/omitted = the apex (root domain). Examples: `www`, `shop`.
- `type` (string, required): `permanent` = HTTP 301; `temporary` = HTTP 302 (default); `masked` = loads the destination in a frame (URL masking). For a precise code — including a 307 temporary redirect — use `redirect_type`.
- `wildcard` (string, required): `yes` also forwards all sub-subdomains; `no` forwards only the exact subdomain.

### `delete_url_forward` (~63 tokens)

Delete URL Forward

Delete a URL forwarding rule by its `id` (obtained from `list_url_forwards`). Idempotent.

Input parameters:

- `domain` (string, required): Domain the forward belongs to.
- `record_id` (string, required): Numeric forward record ID from `list_url_forwards`.

### `create_glue_record` (~152 tokens)

Create Glue Record

Create a glue record for a host on a domain. Used when running your own nameservers on the same domain they serve (e.g. `ns1.example.com` serving `example.com`). The `subdomain` is just the host part (e.g. `ns1`), not the full FQDN. Provide IPs as an array of IPv4 and/or IPv6 addresses. Idempotent.

Input parameters:

- `domain` (string, required): Parent domain, e.g. `example.com`
- `ips` (array, required): Array of IPv4 and/or IPv6 addresses to associate with the host.
- `subdomain` (string, required): Host portion only (no domain), e.g. `ns1`.

### `update_glue_record` (~98 tokens)

Update Glue Record

Update the IP addresses associated with an existing glue record. Replaces the full IP list — pass all IPs you want set, not just additions. Idempotent.

Input parameters:

- `domain` (string, required): Parent domain, e.g. `example.com`
- `ips` (array, required): Full replacement set of IPv4/IPv6 addresses for the host.
- `subdomain` (string, required): Host portion only, e.g. `ns1`.

### `delete_glue_record` (~67 tokens)

Delete Glue Record

Delete a glue record by host on a domain. Idempotent: deleting a non-existent glue record returns success.

Input parameters:

- `domain` (string, required): Parent domain, e.g. `example.com`
- `subdomain` (string, required): Host portion only, e.g. `ns1`.

### `list_doc_topics` (~96 tokens)

List Doc Topics

List the available Porkbun API documentation topics. Returns the docs index (Markdown) — every per-topic page (e.g. dns, domain, webhooks, ssl, pricing) with a one-line description and endpoint count, plus links to the full reference and the OpenAPI spec. Use this first to discover what docs exist, then read_doc to read one. Grounds an agent in Porkbun's own docs without leaving the conversation.

### `read_doc` (~138 tokens)

Read Doc

Read a Porkbun documentation page as Markdown. Pass a `topic` from list_doc_topics (e.g. `dns`, `domain`, `webhooks`, `ssl`, `pricing`, `account`, `marketplace`). Special values: `overview` (the llms.txt orientation), `full` (the entire flat reference — every endpoint), or `index` (the topic list). Returns the page's full Markdown, including endpoint signatures, parameters, and curl examples.

Input parameters:

- `topic` (string, required): Doc topic, e.g. `dns`, `webhooks`, `domain`; or `overview` / `full` / `index`.

### `search_docs` (~132 tokens)

Search Docs

Keyword-search the full Porkbun API reference and return the most relevant sections (endpoints/topics) as Markdown. Use for 'how do I…' questions — e.g. 'verify a webhook signature', 'register a domain with dry run', 'set a TXT record'. Returns the best-matching sections with their headings; follow up with read_doc for a full topic page. Searches Porkbun's own docs only.

Input parameters:

- `limit` (integer): Max sections to return (default 6).
- `query` (string, required): What you're looking for, e.g. `dry run register` or `webhook signature`.

### `get_webhook_event_types` (~85 tokens)

Get Webhook Event Types

List the event types you can subscribe a webhook endpoint to. Returns event-type strings like `domain.registered`, `domain.renewed`, `domain.transfer.completed`, `domain.expiring`, and `dns.record.created|updated|deleted`. Use these values (or `*` for all, or a prefix wildcard like `dns.*`) when calling create_webhook.

### `list_webhooks` (~106 tokens)

List Webhooks

List the webhook endpoints registered on the authenticated account. Each endpoint includes its id, URL, subscribed events, status (ACTIVE|DISABLED), consecutive failure count, last success/failure timestamps, last error, and signing secret. Porkbun POSTs a signed JSON payload to each endpoint when subscribed events occur; deliveries are signed with the endpoint's secret via HMAC-SHA256 over `{timestamp}.{rawBody}` and sent in the `X-Porkbun-Signature` header.

### `get_webhook` (~56 tokens)

Get Webhook

Fetch a single webhook endpoint by its numeric id, including its signing secret and delivery health (consecutive failures, last success/failure).

Input parameters:

- `id` (integer, required): The webhook endpoint id (from list_webhooks or create_webhook).

### `list_webhook_deliveries` (~160 tokens)

List Webhook Deliveries

List recent webhook delivery attempts (newest first), across all endpoints or filtered to one. Each row reports event type, event id, status (PENDING|PROCESSING|DELIVERED|FAILED), attempt count, HTTP status, and last error. Delivery history is retained ~30 days. Use this to audit what was sent and to find a delivery id to resend. The payload is omitted here — use get_webhook_delivery for the full signed payload.

Input parameters:

- `endpointId` (integer): Only deliveries for this endpoint.
- `limit` (integer): Page size, 1-200 (default 50).
- `start` (integer): Offset for pagination (default 0).
- `status` (string): Filter by delivery status.

### `get_webhook_delivery` (~57 tokens)

Get Webhook Delivery

Fetch a single webhook delivery by id, including the full JSON payload that was (or will be) sent and its delivery status. Get delivery ids from list_webhook_deliveries.

Input parameters:

- `id` (integer, required): The delivery id.

### `create_webhook` (~346 tokens)

Create Webhook

Register a webhook endpoint. Porkbun will POST a signed JSON payload to `url` whenever a subscribed event occurs. Returns the new endpoint including its `secret` — store it securely; it's used to verify the `X-Porkbun-Signature` header (HMAC-SHA256 over `{timestamp}.{rawBody}`). `url` must be a publicly reachable HTTPS endpoint: `https://` on port 443, no credentials embedded in the URL, and a hostname that resolves to a public internet address. Private, loopback (including tricks like `127.0.0.1.sslip.io`), link-local, CGNAT and reserved addresses are rejected with `INVALID_WEBHOOK_URL` — do not try to point this at localhost or an internal host. For local development use a public HTTPS tunnel (ngrok, Cloudflare Tunnel) or a sandbox key with sandbox_trigger_webhook. A hostname that does not resolve yet is accepted so you can register before the receiver is deployed, but the rules are re-checked before every delivery. Omit `events` (or pass `['*']`) to subscribe to all event types; you can also pass prefix wildcards like `dns.*`.

Input parameters:

- `events` (array): Event types to subscribe to, e.g. `['domain.registered','dns.*']`. Omit or use `['*']` for all events. Call get_webhook_event_types for the catalog.
- `url` (string, required): Publicly reachable HTTPS URL Porkbun will POST event payloads to. Port 443 only; the hostname must resolve to a public internet address (private/loopback/reserved targets are rejected).

### `update_webhook` (~165 tokens)

Update Webhook

Update a webhook endpoint. Only the supplied fields change. Set `status` to `DISABLED` to pause deliveries or `ACTIVE` to resume (resuming also clears the consecutive-failure counter). A replacement `url` must satisfy the same public-HTTPS rules as create_webhook. Idempotent. Calls the Porkbun Webhooks API (https://porkbun.com/llms/webhooks).

Input parameters:

- `events` (array): Replacement event subscription list (or `['*']` for all).
- `id` (integer, required): The webhook endpoint id.
- `status` (string): Enable or pause the endpoint.
- `url` (string): New HTTPS URL. Same rules as create_webhook: port 443, publicly resolvable hostname, no embedded credentials.

### `rotate_webhook_secret` (~60 tokens)

Rotate Webhook Secret

Generate a new signing secret for a webhook endpoint and return the endpoint with the new secret. Deliveries are signed with the new secret immediately, so update your verifier as part of the same operation.

Input parameters:

- `id` (integer, required): The webhook endpoint id.

### `test_webhook` (~75 tokens)

Test Webhook

Send a `webhook.test` event to an endpoint to confirm it's reachable and that signature verification works. The endpoint must be ACTIVE. Delivery happens asynchronously (usually within a minute); check the endpoint's last_success_date via get_webhook afterward.

Input parameters:

- `id` (integer, required): The webhook endpoint id to send a test event to.

### `resend_webhook` (~103 tokens)

Resend Webhook

Re-queue a past webhook delivery to its endpoint. Clones the delivery into a fresh attempt, reusing the ORIGINAL event id — so a consumer that dedupes on X-Porkbun-Webhook-Id treats the resend as the same event. The endpoint must still exist and be ACTIVE. Use after fixing a downstream bug to replay a delivery that previously FAILED.

Input parameters:

- `id` (integer, required): The delivery id to resend (from list_webhook_deliveries).

### `delete_webhook` (~53 tokens)

Delete Webhook

Delete a webhook endpoint by id. Deliveries stop immediately. Idempotent in effect: deleting a non-existent endpoint returns an error you can safely ignore.

Input parameters:

- `id` (integer, required): The webhook endpoint id to delete.

### `get_cloudflare_connection` (~138 tokens)

Get Cloudflare Connection

Check whether this Porkbun account is connected to a Cloudflare account, and which one. THIS IS THE POLL TARGET for the connect flow. Connecting is a HUMAN action: Cloudflare's consent screen must be completed in a browser and the authorization is bound to the Porkbun web session that starts it, so it cannot be driven over the API. When `connected` is false the response includes a `connectUrl` — show that URL to the user, ask them to authorize, then call this tool again every few seconds until `connected` is true. Everything else in the Cloudflare flow is fully automatable once this is true.

### `list_cloudflare_inventory` (~98 tokens)

List Cloudflare Inventory

List every domain in the account with its Cloudflare eligibility: `state` is eligible / warn / blocked / connected / inprogress, each with a human-readable reason (DNSSEC live, custom nameservers, uses Porkbun hosting, etc). Read this BEFORE queueing so you can tell the user what will move and what won't. Works even when the Cloudflare account isn't connected yet, so you can plan while the user authorizes.

### `connect_domains_to_cloudflare` (~245 tokens)

Connect Domains To Cloudflare

Queue domains to move into the customer's own Cloudflare account: Porkbun creates the zone there, copies over the DNS records it holds, and repoints the registry nameservers at Cloudflare. ASYNCHRONOUS — a successful call means QUEUED, never connected; the work runs in the background over the next few minutes, so poll get_cloudflare_queue afterwards. Results are PER DOMAIN: each name comes back under `queued`, `skipped` or `alreadyQueued` with its own reason. A non-empty `skipped` is a NORMAL outcome (DNSSEC live, custom nameservers, already connected), NOT a failed call — report the reasons rather than calling the whole thing an error. Eligibility is re-checked right before each domain is acted on, so a queued domain can still be skipped later. Requires the Cloudflare account to be connected first (see get_cloudflare_connection). Use dry_run to preview verdicts without queueing. Max 500 domains per call.

Input parameters:

- `domains` (array, required): Domain names to move into the customer's Cloudflare account.
- `dry_run` (boolean): Preview the per-domain verdicts without queueing anything.

### `get_cloudflare_queue` (~56 tokens)

Get Cloudflare Queue

List every Cloudflare move this account has requested, with per-domain status and message. Use this to poll progress after connect_domains_to_cloudflare. Queue rows are never deleted, so this is also the audit trail of past moves.

### `get_cloudflare_domain_status` (~70 tokens)

Get Cloudflare Domain Status

Get the Cloudflare move status for ONE domain, including the Cloudflare zone id once created and the nameservers Porkbun replaced (kept so the move can be undone). Returns NOT_QUEUED if the domain has never been queued.

Input parameters:

- `domain` (string, required): Domain to check.

### `retry_cloudflare_domain` (~95 tokens)

Retry Cloudflare Domain

Re-queue a domain whose Cloudflare move failed or was skipped. Fails if it is already connected, already in progress, or no longer in the account — the message says which. Fix the underlying reason first (e.g. disable DNSSEC) or the retry will be skipped again.

Input parameters:

- `domain` (string, required): Domain to re-queue.
- `dry_run` (boolean): Validate without re-queueing.

### `rollback_cloudflare_domain` (~132 tokens)

Rollback Cloudflare Domain

UNDO a completed Cloudflare move: point the domain's nameservers back at Porkbun, restoring the DNS Porkbun still holds. This changes authoritative DNS for a live domain, so confirm with the user first. The Cloudflare zone is deliberately LEFT IN PLACE — deleting a zone inside the customer's own Cloudflare account is their call. Fails if Porkbun never moved the domain, it is already back on Porkbun nameservers, or it is being worked on right now.

Input parameters:

- `domain` (string, required): Domain whose move should be undone.
- `dry_run` (boolean): Validate without changing nameservers.

### `disconnect_cloudflare` (~68 tokens)

Disconnect Cloudflare

Remove this account's stored Cloudflare connection. Domains already moved stay on Cloudflare and keep resolving — this only stops Porkbun making further changes on the customer's behalf. Reconnecting requires the human browser authorization again, so confirm before calling.

Input parameters:

- `dry_run` (boolean): Validate without disconnecting.

### `get_cloudflare_records` (~105 tokens)

Get Cloudflare Records

List a domain's LIVE DNS records as Cloudflare holds them, each with its orange-cloud (`proxied`) state and whether it is `proxiable` at all. Once a domain has moved to Cloudflare THIS is the authoritative record set — list_dns_records reads the Porkbun zone, which is no longer answering queries for it. Returns ZONE_NOT_READY if the zone does not exist yet.

Input parameters:

- `domain` (string, required): Domain whose Cloudflare zone to inspect.

### `set_cloudflare_proxy` (~273 tokens)

Set Cloudflare Proxy

Turn the Cloudflare proxy (orange cloud) on or off for a domain's DNS records. NOTE: connecting a domain deliberately imports every record DNS-only (grey cloud), because changing how traffic is served at the same time as changing who serves DNS makes failures hard to diagnose — so proxying is a separate, explicit step, best done AFTER confirming the site still works. Defaults to every proxiable record; pass `records` to target specific names ("@" = apex, bare labels like "www" are expanded). Only A/AAAA/CNAME can be proxied; anything else comes back under `skipped` with a reason rather than failing. Proxying hides the origin IP, so if MX points at a name you're proxying, mail to it breaks — that comes back in `warnings`. Reversible: call again with enabled=false. Use dry_run to preview.

Input parameters:

- `domain` (string, required): Domain whose Cloudflare records to change.
- `dry_run` (boolean): Preview which records would change, without changing them.
- `enabled` (boolean, required): true = proxy through Cloudflare (orange cloud); false = DNS-only (grey cloud).
- `records` (array): Optional: limit to these names. "@" = apex; bare labels are expanded (e.g. ["@","www"]).

### `preview_cloudflare_move` (~85 tokens)

Preview Cloudflare Move

Show exactly which DNS records would be copied into Cloudflare for a domain, and which would be dropped, WITHOUT queueing anything. Use this to tell a user what a move will do to their DNS before committing. Records are always created DNS-only (grey cloud); proxying is a separate step via set_cloudflare_proxy.

Input parameters:

- `domain` (string, required): Domain to preview.

### `get_cloudflare_zone` (~118 tokens)

Get Cloudflare Zone

Get what CLOUDFLARE currently says about a domain's zone (status, paused, its nameservers, activation date) rather than what Porkbun's queue row remembers. These drift: if someone repoints the nameservers elsewhere after the move, the Porkbun row still says done while Cloudflare has stopped serving the domain. The response includes the live public nameservers and a `nameserversDrifted` boolean so you don't have to diff them yourself.

Input parameters:

- `domain` (string, required): Domain whose Cloudflare zone to inspect.

### `get_cloudflare_zone_settings` (~133 tokens)

Get Cloudflare Zone Settings

Read the Cloudflare zone settings that matter after a move: ssl mode, always_use_https, automatic_https_rewrites, min_tls_version, development_mode, cache_level. Warns when SSL mode is `off` or `flexible`, which means Cloudflare fetches the origin over plain HTTP while visitors still see a padlock. If this returns CLOUDFLARE_REAUTHORIZE_REQUIRED the customer's Cloudflare connection predates this permission — show them the connectUrl and ask them to reconnect (one click; domains already moved are unaffected).

Input parameters:

- `domain` (string, required): Domain whose zone settings to read.

### `set_cloudflare_zone_settings` (~219 tokens)

Set Cloudflare Zone Settings

Change Cloudflare zone settings for a moved domain. Only an allowlist is editable (ssl, always_use_https, automatic_https_rewrites, min_tls_version, development_mode, cache_level) — WAF/firewall/security controls are deliberately not exposed. Prefer ssl `full` over `flexible`: `flexible` makes Cloudflare fetch the origin over plain HTTP, a downgrade the visitor cannot see. CLOUDFLARE_REAUTHORIZE_REQUIRED means the stored authorization lacks this permission — surface the connectUrl and ask the customer to reconnect. Supports dry_run.

Input parameters:

- `always_use_https` (string)
- `automatic_https_rewrites` (string)
- `cache_level` (string)
- `development_mode` (string): Temporarily bypass Cloudflare's cache.
- `domain` (string, required): Domain whose zone settings to change.
- `dry_run` (boolean)
- `min_tls_version` (string)
- `ssl` (string): TLS mode between Cloudflare and the origin. `full` is the safe default.

### `create_cloudflare_record` (~339 tokens)

Create Cloudflare Record

Create a DNS record in a domain's Cloudflare zone. IMPORTANT: for a domain that has been moved to Cloudflare this is the tool that changes what actually resolves — create_dns_record writes Porkbun's zone, which no longer answers for it. `name` accepts "@" for the apex or a bare label like "www". `ttl: 1` means Cloudflare-automatic. MX requires `priority`. `proxied` (orange cloud) applies to A/AAAA/CNAME only. Record types Cloudflare models as structured objects (SRV, CAA, …) need a `data` object instead of `content`. Supports dry_run.

Input parameters:

- `comment` (string): Note stored on the record.
- `content` (string): The value the record points at. Required unless `data` is given.
- `data` (object): Structured value for SRV/CAA/etc, shaped as Cloudflare documents it.
- `domain` (string, required): Domain whose Cloudflare zone to write to.
- `dry_run` (boolean)
- `name` (string): "@" for the apex, a bare label like "www", or a full hostname. Defaults to the apex.
- `priority` (integer): Required for MX; lower is preferred.
- `proxied` (boolean): Route through Cloudflare's proxy. A/AAAA/CNAME only.
- `ttl` (integer): 1 = automatic (default), otherwise 60-86400.
- `type` (string, required): A, AAAA, CNAME, TXT, MX, NS, PTR or SPF for a plain value; structured types need `data`.

### `edit_cloudflare_record` (~193 tokens)

Edit Cloudflare Record

Update a DNS record in a domain's Cloudflare zone. Partial: fields you omit keep their current value. Get `record_id` from get_cloudflare_records. The response includes both the new record and the previous one, so you can tell the user exactly what changed. Supports dry_run.

Input parameters:

- `comment` (string)
- `content` (string)
- `data` (object)
- `domain` (string, required): Domain whose Cloudflare zone to write to.
- `dry_run` (boolean)
- `name` (string): "@" for the apex, or a bare label.
- `priority` (integer)
- `proxied` (boolean): A/AAAA/CNAME only.
- `record_id` (string, required): Cloudflare record id from get_cloudflare_records.
- `ttl` (integer): 1 = automatic, otherwise 60-86400.
- `type` (string)

### `delete_cloudflare_record` (~126 tokens)

Delete Cloudflare Record

Delete a DNS record from a domain's Cloudflare zone. This changes live DNS for a domain Cloudflare is authoritative for, so confirm with the user first — deleting the wrong record can take a site or its mail offline. Get `record_id` from get_cloudflare_records. The record is read before deletion, so the response reports exactly what was removed. Supports dry_run.

Input parameters:

- `domain` (string, required): Domain whose Cloudflare zone to write to.
- `dry_run` (boolean)
- `record_id` (string, required): Cloudflare record id from get_cloudflare_records.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server#diagnostics

## Score history

- 2026-09-26: 66
- 2026-09-25: 66

## Common questions

### What is the Porkbun (official) MCP server?

Porkbun (official) is an MCP server listed in the public MCP registry as com.porkbun/mcp. Official Porkbun MCP server: domains, DNS, SSL, hosting and Cloudflare via the Porkbun API. This page covers its npm package (@porkbunllc/mcp-server).

### Is the Porkbun (official) MCP server safe to use?

Porkbun (official) scores 66 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 26 September 2026. It declares no install or post-install scripts. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Porkbun (official) MCP server expose?

Porkbun (official) exposes 99 tools: ping, check_domain, check_domains, get_registration_requirements, get_pricing, and 94 more. Their descriptions and schemas cost roughly 18,535 tokens of context every time the server is loaded.

### Is the Porkbun (official) MCP server still maintained?

Porkbun (official) is still listed as active in the MCP registry. We last reached this channel on 26 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Porkbun (official) MCP server under?

Porkbun (official) declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/@porkbunllc/mcp-server
- Socket report: https://socket.dev/npm/package/@porkbunllc/mcp-server
- Repository: https://github.com/oborseth/Porkbun-MCP
- Website: https://porkbun.com/mcp
- Changelog RSS feed: https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server.json
- HTML version of this page: https://verifymcp.io/servers/com-porkbun-mcp/porkbunllc-mcp-server
