namecheap-mcp
# namecheap-mcp
An [MCP](https://modelcontextprotocol.io) server that gives an AI agent safe, high-level control over
your domains and DNS across **three providers**: [Namecheap](https://www.namecheap.com),
[GoDaddy](https://developer.godaddy.com) (v3) and [AWS Route 53](https://aws.amazon.com/route53/).
One tool surface. A domain routes to whichever provider actually holds it; you can override with
`provider` on any tool. The name is historical — it started as Namecheap-only.
It is built for **Claude Code**, **Claude Desktop**, and any other MCP client that speaks stdio.
## The three providers do not agree
This is the part worth reading before you use it. The tools hide the wire formats, not the semantics.
| | Namecheap | GoDaddy v3 | Route 53 |
| --- | --- | --- | --- |
| DNS read / write | yes | yes | yes |
| Update one record | merge, then replace all | delete + create | UPSERT |
| **Atomic edit** | yes | **no** | yes |
| TTL range | 60–60000 | 600–86400 | 0–2147483647 |
| Record unit | one row | one row | a **set**: name+type, many values, one TTL |
| Availability | yes | yes (higher account threshold) | no |
| List domains | yes | **no endpoint** | hosted zones only |
| Register | yes | not implemented here | no |
| Renew / transfer / lock / privacy / email forwarding | yes | no | no |
| Extra record types | `MXE` `URL` `URL301` `FRAME` | `ALIAS` `SOA` | `ALIAS` `SOA` |
Two of those bite:
- **GoDaddy edits are not atomic.** v3 has no record-update endpoint. An edit is a `DELETE` then a
`POST`. If the `POST` fails, the record is **absent, not stale**. Every write that does this returns
`nonAtomicWarning` saying so — read it before assuming the zone is what you asked for.
- **Route 53's unit is a record set.** Three A records at `www` are three rows here and one set there.
Deleting one of three by emitting a `DELETE` would take all three; this server emits an `UPSERT`
carrying the survivors. The zone's own `SOA` and apex `NS` are never touched, because Route 53
creates both with the hosted zone and rejects deleting either.
A capability a provider lacks is **an answer, not an error**: the tool returns a sentence naming the
provider, why its API has no such thing, and where you can do it instead. Nothing is changed.
## Why this isn't a 1:1 mirror of any provider's API
Each provider's API is a flat catalogue with its own sharp edges. Handing those to an agent verbatim is
a footgun. This server exposes a **smaller set of task-shaped tools** designed for an LLM to call
correctly:
- **`domains.dns.setHosts` is REPLACE-ALL.** It overwrites *every* host record on a domain with exactly
the set you submit — omit your MX records and your mail silently breaks. A naive "add a TXT record"
tool that forwarded straight to `setHosts` would wipe the zone. So `modify_dns_records` here is a
**read-modify-write** engine: it reads the current zone, applies granular `add`/`update`/`delete`
operations in memory, and submits the full resulting set. Agents express intent, never hand-assemble
the whole zone. A separate, explicitly-named `replace_all_dns_records` exists for the rare deliberate
full rewrite, and it refuses to blank a non-empty zone unless you pass `allowEmptyZone: true`.
- **Money-spending actions are gated.** `register_domain`, `renew_domain`, `reactivate_domain` and
`transfer_domain` are annotated destructive and require `confirm: true`. Called without it, they
return a dry-run preview of what *would* be charged instead of spending.
- **Structured output + tool annotations.** Every tool returns both a human summary and a typed
`structuredContent` payload, and carries `readOnlyHint` / `destructiveHint` / `idempotentHint`
annotations so the client can reason about safety.
- **Errors become guidance.** Namecheap's cryptic error numbers are mapped to actionable hints — the
single most common failure ("Invalid request IP") is turned into a step-by-step whitelist fix.
- **Provenance on every answer.** Each result carries `provider`, `account` and `environment`. A caller
that cannot tell which provider answered cannot check the answer.
- **Types and TTLs are checked against the provider that will receive them.** The schema accepts the
union of all three, so you are never told "invalid enum" for a type that is valid where your domain
lives; the refusal names the provider and lists what it does have. A TTL outside the provider's range
is clamped **and the clamp is reported** — asking for 60 on GoDaddy gets you 600, and you are told.
Prior art: [johnsorrentino/mcp-namecheap](https://github.com/johnsorrentino/mcp-namecheap), which
covers 3 read/nameserver tools. This project is an independent, from-scratch implementation with a
broader, safety-first tool surface; credit to that project for charting the territory.
## Tools
| Tool | Kind | What it does |
| --- | --- | --- |
| `check_domain_availability` | read | Availability for up to 50 domains. Picks by capability, not ownership — an unregistered domain is owned by nobody. Namecheap answers a batch in one call; GoDaddy v3 takes one domain per request. Route 53 cannot answer. |
| `get_domain_pricing` | read | List-price lookup per TLD for register / renew / transfer / reactivate. |
| `list_domains` | read | Paged, filterable list of domains in the account. |
| `get_domain_info` | read | One coherent view of a domain: dates, registrar lock, privacy, DNS, nameservers. |
| `register_domain` | **paid · confirm** | Register a domain. Dry-runs unless `confirm: true`; guides on extended attributes. |
| `renew_domain` | **paid · confirm** | Renew a domain for N years. |
| `reactivate_domain` | **paid · confirm** | Reactivate a recently-expired domain (redemption). |
| `set_domain_lock` | write (idempotent) | Lock / unlock the registrar transfer lock. |
| `get_dns_records` | read | Read the zone at whichever provider holds the domain. |
| `modify_dns_records` | write | **Safe** add/update/delete of host records via read-modify-write. |
| `replace_all_dns_records` | **destructive · confirm** | Replace the entire zone with an explicit record set. |
| `get_nameservers` | read | Read the nameservers. On Route 53 this is the hosted zone's apex NS set. GoDaddy v3 cannot read them back, only replace them. |
| `set_nameservers` | write | Set custom nameservers. `mode=default` is Namecheap-only. Route 53 refuses: a zone's nameservers are set at the registrar. |
| `get_email_forwarding` | read | Read email-forwarding rules. |
| `set_email_forwarding` | write | Upsert (merge) or replace forwarding rules. |
| `transfer_domain` | **paid · confirm** | Start an inbound transfer; explains the EPP/auth-code requirement. |
| `get_transfer_status` | read | Poll a transfer, mapping the numeric StatusID to meaning + phase. |
| `get_account_balance` | read | Account balances and funds available for auto-renew. |
| `get_domain_privacy` | read | WhoisGuard / domain privacy subscription status. |
| `set_domain_privacy` | write | Enable (with a forwarding email) or disable WhoisGuard privacy. |
"paid" tools spend real money in production and require `confirm: true`; without it they return a
preview.
Every tool that names a domain also takes an optional **`provider`** (`namecheap` | `godaddy` |
`route53`) to override routing. You need it for a domain that does not exist yet, since routing works
by asking who holds it.
Six tools are Namecheap-only in practice — `renew_domain`, `reactivate_domain`, `set_domain_lock`,
`set_domain_privacy`, `get_email_forwarding` / `set_email_forwarding`, and `get_domain_info`. Called
against a domain at another provider they refuse and say why. A domain that resolves *nowhere* still
proceeds: that is the registration and transfer-in case.
## Install & configure
Requires **Node.js 20+**.
### Claude Code
```bash
claude mcp add namecheap \
--env NAMECHEAP_API_USER=your_user \
--env NAMECHEAP_API_KEY=your_key \
--env NAMECHEAP_CLIENT_IP=your_whitelisted_ip \
--env NAMECHEAP_SANDBOX=true \
-- npx -y namecheap-mcp
```
### Claude Desktop
Add to `claude_desktop_config.json` (macOS:
`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"namecheap": {
"command": "npx",
"args": ["-y", "namecheap-mcp"],
"env": {
"NAMECHEAP_API_USER": "your_user",
"NAMECHEAP_API_KEY": "your_key",
"NAMECHEAP_CLIENT_IP": "your_whitelisted_ip",
"NAMECHEAP_SANDBOX": "true"
}
}
}
}
```
Start in **sandbox** (`NAMECHEAP_SANDBOX=true`) and switch to production only once you've verified the
behaviour — production register/renew/transfer calls spend real money.
### From source
```bash
git clone https://github.com/fledgeling-co/namecheap-mcp.git
cd namecheap-mcp
npm install # runs the build via the prepare hook
npm test
node dist/index.js # expects the NAMECHEAP_* env vars to be set
```
## Configuration (environment variables)
| Variable | Required | Default | Notes |
| --- | --- | --- | --- |
| `NAMECHEAP_API_USER` | yes | — | API username (Profile → Tools → API Access). |
| `NAMECHEAP_API_KEY` | yes | — | API key from the same screen. Kept out of all logs. |
| `NAMECHEAP_USERNAME` | no | = `API_USER` | Account the commands act on; usually identical. |
| `NAMECHEAP_CLIENT_IP` | no | auto-detect | Public **IPv4** the requests come from. Auto-detected via `api.ipify.org` if unset — but the address still has to be whitelisted. |
| `NAMECHEAP_SANDBOX` | no | `false` | `true` → use the sandbox API (separate sandbox credentials). |
| `NAMECHEAP_TIMEOUT_MS` | no | `30000` | Per-request network timeout (clamped 1000–120000). |
| `GODADDY_PAT` | no | — | One GoDaddy account. A v3 **personal access token** from `developer.godaddy.com/personal-access-token`, sent as `Authorization: Bearer`. Not the v1 `sso-key` pair. |
| `GODADDY_ENVIRONMENT` | no | `godaddy` | One of `godaddy`, `ote-godaddy`, `test-godaddy`, `dev-godaddy`. |
| `GODADDY_ACCOUNTS` | no | — | Several accounts, as JSON keyed by label: `{"work":{"pat":"..."},"personal":{"pat":"...","environment":"ote-godaddy"}}`. Takes precedence over `GODADDY_PAT`. |
| `AWS_PROFILE` / `AWS_REGION` | no | CLI default | Route 53 goes through the `aws` CLI and uses whatever credentials it already resolves — profiles, SSO, assumed roles. No new secrets here. |
GoDaddy is off unless one of its variables is set; the startup banner says `godaddy=off`. Route 53 is
off if the `aws` binary is absent, and the banner says so rather than letting it look like a routing
miss.
A malformed `GODADDY_ACCOUNTS` is reported by **label**, never by contents — the contents are a token,
and the underlying `JSON.parse` error can quote the input verbatim.
The API key is only ever sent in the request body and is never written to logs or `stdout` (which is
the MCP protocol channel — all diagnostics go to `stderr`).
## Route 53
Reached by spawning the `aws` CLI, not the SDK: the CLI already resolves profiles, SSO sessions and
assumed roles, and reimplementing that to save a subprocess is a bad trade. Arguments go through
`execFile`, never a shell, and the change batch goes in on **stdin** — a record value is
caller-supplied, and neither a shell string nor a temp file is safe for one.
Reads follow pagination. A zone read short and written back deletes everything past the first page.
`scripts/verify-route53.mts` reads every hosted zone, diffs each against itself asserting **zero**
changes, and drops one value from a multi-value set asserting the result is one `UPSERT` carrying the
rest. Verified across 8 zones, 190 rows, 95 sets.
## GoDaddy
v3 only, `Authorization: Bearer {PAT}`. The `sso-key {KEY}:{SECRET}` scheme in GoDaddy's own DNS
announcement belongs to v1.
v3 has no list-domains endpoint, so routing **probes** `GET /domain-names/{domain}` per configured
account and caches the answer, including the miss. A 404 or 403 means "not this account" and the probe
moves on — GoDaddy's quickstart says a 403 can mean the account is not eligible for the API at all,
and does not say what eligibility is.
Registration is **not implemented**. v3 has `POST /registrations`, but using it means modelling
contacts, consent records and agreement acceptance, and every test run spends real money. The
capability is not claimed rather than half-built.
`scripts/verify-godaddy.mts <domain> [--write]` exercises a real account. Read-only without `--write`.
## Namecheap API prerequisites
Before anything works you must, at Namecheap → Profile → Tools → **API Access**:
1. **Enable API access** on your account.
2. **Whitelist the IP** the requests originate from (the machine running this server).
Namecheap also **gates production API access** behind eligibility — per their API FAQ your account
must have at least one of:
- **20+ domains** in your account, **or**
- **$50+** in account balance, **or**
- **$50+** spent in the last 2 years.
The **sandbox** (`https://www.sandbox.namecheap.com`, separate credentials) has none of these
requirements — always develop against it first.
## Rate limits
Namecheap's documented ceilings are **50 requests/minute, 700/hour, 8000/day** per key. This server
runs a small client-side limiter (~45/min) that *delays* rather than fails bursty tool calls to keep
you under the per-minute wall. Note that `get_domain_pricing` issues one API call per TLD.
## Error handling
Namecheap returns numeric error codes; this server maps the common ones to actionable guidance,
including:
- **`1011150` — Invalid request IP** → the exact whitelist steps (this is the #1 first-run failure).
- **`1011102` / "API key is invalid"** → check the key *and* that the IP is whitelisted / API access
is enabled.
- **Order/charge failures (`2528166`, insufficient funds)** → check `get_account_balance`.
- **`2020166` — renewal not permitted** → the domain is likely expired; use `reactivate_domain`.
- Extended-attribute, phone-format and DNS-provider errors → what to fix.
Unknown codes are surfaced verbatim so nothing is hidden.
## Development
```bash
npm run build # tsc -> dist/
npm test # vitest
npm run typecheck # tsc -p tsconfig.test.json (covers src, test and scripts)
npm run typecheck:build # tsc --noEmit (src only, matching the build)
```
153 tests, none needing the network. The pure logic — XML envelope parsing, the DNS read-modify-write
engine, the rrset expand/regroup/diff, error-code mapping, transfer-status mapping, config handling —
runs against fixtures. Providers are driven through injected `fetch` and an injected `aws` runner. The
tool layer is driven through the registered handlers against fake providers.
Two scripts need real credentials and sit outside the gate:
```bash
npx tsx scripts/verify-route53.mts # every zone, read-only
npx tsx scripts/verify-route53.mts example.com --write # adds and removes one TXT record
npx tsx scripts/verify-godaddy.mts example.com # read-only
npx tsx scripts/verify-godaddy.mts example.com --write # adds and removes one TXT record
```
## Security notes
- Binds nothing — it's a stdio server, not a network listener.
- The Namecheap API key travels only in POST bodies and is never logged. The GoDaddy PAT travels only
in an `Authorization` header, and config errors name the account label, never its contents.
- Route 53 arguments are passed as an argv array, never a shell string; the change batch goes on stdin.
- Money-spending and zone-destroying operations require explicit `confirm` / `allowEmptyZone` flags.
- Validates and normalises every value read back from the API before acting on it.
## Licence
MIT — see [LICENSE](./LICENSE).
Credit to [johnsorrentino/mcp-namecheap](https://github.com/johnsorrentino/mcp-namecheap) for the
original Namecheap MCP server that inspired this one.
TDQS
Scored across 20 tools
Most tools target clearly distinct actions, but check_domain_availability and get_domain_pricing both deal with pricing/availability and could be confused without reading descriptions carefully. All other tools have clear boundaries, including the DNS trio (get, modify, replace_all) where descriptions emphasize the safe vs dangerous distinction.
All tool names follow a consistent verb_noun pattern in snake_case (e.g., get_domain_info, set_nameservers, renew_domain). No mixing of styles or vague verbs, making the set predictable and scannable.
At 20 tools, the count is on the heavy side but each tool serves a distinct domain-management function (lifecycle, DNS, nameservers, email forwarding, privacy, billing, transfers). The breadth justifies the count, though it slightly exceeds the typical well-scoped range.
The toolset covers the full domain lifecycle: availability, pricing, registration, renewal, reactivation, transfer, DNS management, nameservers, email forwarding, privacy, and billing. Minor gaps exist (e.g., no direct tool to update domain contact info or toggle auto-renew), but these are edge cases agents can work around.