Namecheap MCP Connector connects your AI assistant (such as Claude) to your Namecheap account. Through it, the assistant can check domain availability and prices, hand you a link to buy a domain on Namecheap's own site, manage domain contacts and nameservers, and read or edit DNS records on your behalf — you just ask in plain language, and the assistant calls the right tools.
The assistant never charges your account. Buying a domain always goes through domain_purchase_link: you open the link, then approve and pay on namecheap.com yourself.
You need a Namecheap account. Namecheap MCP Connector is available at https://connector-mcp.namecheap.com/mcp.
https://connector-mcp.namecheap.com/mcp. Anthropic's Claude is the client we have verified this to work with.https://connector-mcp.namecheap.com/mcp. Other clients may work, but we haven't verified them yet.When you connect, you'll be asked to sign in to Namecheap and grant the assistant access to your account. Which tools the assistant can use depends on the access you approve — if a tool is not available because access wasn't granted, see Access you grant, then reconnect and approve the access it needs.
The agreements and policies that apply to Namecheap MCP Connector and to anything you buy through it:
Every tool completes as part of the call, so there is no background-operation tool to poll. To buy a domain, domain_purchase_link returns a link the customer opens and pays on themselves, choosing their own payment method, and domains_list is what confirms the outcome afterwards.
| Tool | What it does | Access |
|---|---|---|
contacts_save
|
Save contact details and get a contact ID | Write |
contacts_get
|
Read a saved contact by its ID | Read |
contacts_list
|
List all saved contacts to find and reuse one | Read |
domains_list
|
List your domains, or look up one domain | Read |
domains_check_availability
|
Check if domains are available to register, and their price | Read |
domain_purchase_link
|
Get a link for the customer to buy a domain on namecheap.com's payment page — nothing is charged by the call | Write |
domain_set_contacts
|
Assign contacts to a domain you own | Sensitive write |
domain_set_nameservers
|
Switch a domain to basic or custom nameservers | Sensitive write |
dns_records_get
|
Read DNS records for a domain | Read |
dns_records_save
|
Add DNS records or update their TTL | Write |
dns_records_delete
|
Delete DNS records | Sensitive write |
Access: Read only retrieves information. Write changes data in a way you can easily change back. Sensitive write has an effect you cannot simply take back (spends money, cannot be undone, immediately changes how your domain works); your assistant should ask you to confirm every call.
The Namecheap sign-in page can show a consent screen with a choice between read access and read-and-write access; read access is always included and write access is a separate choice. Where it does, the access you consent to decides which tools the assistant can use:
| You allow | The assistant can use |
|---|---|
| Read | The tools marked Read above: contacts_get, contacts_list, domains_list, domains_check_availability, and dns_records_get. |
| Read and write | Every tool in the table above. |
-32602 with the message Tool <name> not found. The answer never says why.Wherever a contact is required (domain_set_contacts), each role accepts either:
firstName, lastName, email, …). It is saved to your Namecheap account automatically; you do not have to call contacts_save first.{ "contactId": "…" } pointing at a contact you saved earlier with contacts_save, or one you found with contacts_list.A contactId is an opaque string (1–32 characters). Do not parse it or assume a format — just pass it back where a contact is accepted. You may also reuse a contactId from a contacts_list result, so look up the contacts already saved on your account before entering new details.
Several tools are designed to be used together: the output of one becomes the input of the next.
domains_check_availability
— check the name(s) you want. Only proceed when result is available. Each available name includes the USD price to register it (standard and premium alike), or priceUnavailableReason when it can't be determined. Note the price covers price.pricedYears years, which is the TLD's shortest allowed term and is not always 1.domain_purchase_link
— once the customer agrees, get a consentUrl and hand it to them. This call charges nothing. The customer opens the link, approves, and pays on namecheap.com with a payment method they pick there. The consent page has no field for term, WHOIS privacy or contacts, so none of those can be set this way.domains_list
— after the customer says they've completed checkout, call it for the domain to confirm it is registered. Don't tell the customer the domain is theirs before that.
Don't hand out a second link for a purchase that may already have gone through. If the customer may already have paid for the domain — they followed an earlier link, or bought it some other way — call domains_list for the domain first, and offer a new link only if it is genuinely not registered. Otherwise the customer risks being charged twice.
domains_check_availability --> domain_purchase_link --> (domains_list to confirm)
(available? + price) (consentUrl the customer after the customer says
opens and pays on they've completed checkout
namecheap.com; no charge
from this call)
contacts_list
— look up the contacts already saved on the account and pick one to reuse (optional — inline details work too).domain_set_contacts
— assign contacts to the domain, inline or by { contactId }, reusing a contactId from contacts_list where one fits. Completes immediately. verificationStatus is always null for Namecheap. Some TLD registries require extended attributes — pass them via tldAttributes.domains_list
— find the domain and see its current nameservers ({ provider, hosts }; populated when fetching a single domain).domain_set_nameservers
— switch it to Namecheap's default nameservers with provider: "basic" (no hosts), or point it at your own with provider: "custom" and a list of 2–12 hosts. It returns the resulting { provider, hosts }. Re-applying the state a domain is already in is not an error: nothing is written, and the answer carries the note "Already set; nothing changed.".domains_list
— find the domain you want to manage (or pass its name directly if you know it).dns_records_get
— read the current records for the domain.dns_records_save
or dns_records_delete — add, update, or remove records. Records returned by dns_records_get have the same shape the save and delete tools accept (delete just omits ttl), so the assistant can read, adjust, and write back. Matching is case-insensitive except for TXT records, which are case-sensitive. MX records are saved only while the domain's Mail Settings are Custom MX; otherwise the tool refuses them (or, with force, lists them in failed) and says to switch Mail Settings to Custom MX in the Namecheap dashboard.domains_list
— page through all your domains with sorting, or fetch a single domain by name. See the tool below for which fields Namecheap reports.contacts_list
— page through all the contacts saved on your account to find and reuse an existing one (by its contact ID) instead of creating a duplicate.contacts_get
— look up the details behind any contact ID you see on a domain or in a contacts_list result.Every tool returns its result as structured JSON, and every tool completes immediately — Namecheap has no long-running background operations.
Contacts are the people or organizations attached to a domain registration (registrant, admin, tech, billing). A contact is referenced everywhere by its contact ID — an opaque string.
contacts_save — Save ContactAccess: Write.
Saves contact details and returns the generated contact ID. Validation of some fields (such as stateProvince and postalCode) depends on the selected country.
Idempotent. Saving details identical to a contact you saved before reuses that existing contact ID instead of creating a duplicate.
If the call fails with infrastructure.serviceUnavailable, the contact may still have been saved: look for it with contacts_list before saving again (see Retrying writes).
| Parameter | Required | Type & constraints |
|---|---|---|
firstName
|
Yes | String, 1–64 chars. May include hyphens and apostrophes. |
lastName
|
Yes | String, 1–64 chars. May include hyphens and apostrophes. |
email
|
Yes | Valid email address, max 254 chars. |
address1
|
Yes | Address line 1. String, 1–128 chars. |
city
|
Yes | String, 1–64 chars. |
country
|
Yes | Two-letter country code (ISO 3166-1 alpha-2), e.g. US. |
phone
|
Yes | International format +CountryCode.Number, e.g. +1.2025551234. Max 32 chars. |
organization
|
No | Organization/company name. 1–128 chars. |
address2
|
No | Address line 2. 1–128 chars. |
stateProvince
|
No | State/province name, 1–64 chars. May be required depending on the country. |
postalCode
|
No | 1–16 chars. May be required depending on the country. |
phoneExt
|
No | Phone extension, 1–16 chars. |
fax
|
No | Fax number, same +CountryCode.Number format, max 32 chars. |
taxNumber
|
No | Tax number, 1–32 chars. |
Returns
{ "contactId": "..." }
contactId (opaque, 1–32 chars) is what you pass to domain_set_contacts and contacts_get; contacts_list returns the ids of everything you have saved.
When Namecheap's address validation proposes a corrected address, the result may additionally include an addressSuggestion:
{
"contactId": "...",
"addressSuggestion": {
"status": "corrected",
"suggestedAddress": {
"address1": "...", "address2": "...", "city": "...",
"stateProvince": "...", "country": "...", "postalCode": "..."
}
}
}
Each field of suggestedAddress is present only when Namecheap proposes a value for it. Treat it as a suggestion to show the user, not an automatic change — the contact is still saved with the details you submitted.
contacts_get — Get ContactAccess: Read.
Reads the details of a saved contact by its contact ID. Contact IDs come from a contacts_list result or from saving a contact.
| Parameter | Required | Type & constraints |
|---|---|---|
contactId
|
Yes | Opaque contact ID, 1–32 chars. |
A contactId that is not numeric, or that belongs to another account, returns business.notFound ("Contact not found in this account.").
Returns — { contact } with:
| Field | Type |
|---|---|
firstName, lastName, email, address1, city, country, phone, postalCode |
String |
organization, address2, stateProvince, phoneExt, fax, faxExt, taxNumber |
String or null |
contacts_list — List ContactsAccess: Read.
Lists all contacts saved under your account, so you can find and reuse an existing contact (by its contact ID) instead of creating a duplicate. The list is paginated and sortable, consistent with domains_list.
| Parameter | Required | Type & constraints |
|---|---|---|
take
|
No | Items per page, 1–100. Default 10. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | Up to 8 sort keys: name, email, organization; prefix with - for descending (e.g. -name). Without orderBy, contacts come back in the order your account stores them. |
Returns — { items, total } where total is the number of contacts saved on the account (not the page size), and each item carries enough to tell contacts apart without a follow-up call:
| Field | Type |
|---|---|
contactId
|
String (opaque). Pass to contacts_get or domain_set_contacts. |
name
|
String — the contact's name. |
email
|
String or null when the contact has no email on record. |
organization
|
String or null when the contact has no organization on record. |
{
"items": [
{ "contactId": "424242", "name": "Ada Lovelace", "email": "ada@example.com", "organization": "Analytical Engines" }
],
"total": 1
}
Domain name inputs (domain/domainName) accept Unicode (IDN) or ASCII (A-label) — either way, the tool normalizes the name to punycode automatically before use. domains_check_availability additionally requires a TLD Namecheap supports for registration: a domain whose TLD isn't supported is reported as not available rather than checked. The other domain tools (domains_list, domain_purchase_link, domain_set_contacts, domain_set_nameservers) and the DNS tools normalize the name only and never reject on TLD support.
domains_list — List DomainsAccess: Read.
Retrieves a paginated list of your domains. Pass domain to fetch a single domain by name instead (pagination and ordering are then ignored, and the result includes a note saying so if they were supplied). A domain that is not in your account, whether unregistered or registered by someone else, is not an error: the result is { "items": [], "total": 0, "note": "This domain is not in this account." }, the same for both cases.
| Parameter | Required | Type & constraints |
|---|---|---|
domain
|
No | Fully qualified domain name to fetch a single domain. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
take
|
No | Items per page, 1–100. Default 10. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | One sort key: name, unicodeName, registrationDate, or expirationDate; prefix with - for descending (e.g. -expirationDate). unicodeName sorts the same way as name. Any other value, or a second key, is rejected with application.request.badRequest. |
Returns — { items, total } where each item describes a domain:
| Field | Meaning |
|---|---|
name / unicodeName |
Domain name. Namecheap reports unicodeName as a mirror of name (no distinct Unicode form). |
isPremium
|
Whether the domain is a premium name. |
autoRenew
|
Whether auto-renew is enabled — see the note below on when this is reported. |
registrationDate / expirationDate |
Registration and expiration timestamps. |
lifecycleStatus
|
creating, registered, grace1, or redemption. |
verificationStatus
|
Not reported by Namecheap — always null. |
eppStatuses
|
Not reported by Namecheap — empty. |
suspensions
|
Not reported by Namecheap — empty. |
privacyProtection
|
{ level: "public" | "high", contactForm: boolean }. |
nameservers
|
{ provider: "basic" | "custom", hosts: [...] }. See the note below. |
contacts
|
Not reported by Namecheap — comes back empty. Use contacts_list to find your saved contacts, or contacts_get if you already hold a contact ID. |
contactDetails
|
Present only when fetching a single domain via domain: the full contact details assigned to the domain, per role (registrant, admin, tech, billing). See the note below. |
Namecheap-specific reporting:
contacts, eppStatuses, suspensions, and verificationStatus are not reported — they come back empty or null.unicodeName always mirrors name.nameservers is populated only when fetching a single domain via domain. In the multi-item list it reports the basic default with no hosts.contactDetails is populated only when fetching a single domain via domain. It carries the actual registrant/admin/tech/billing contact details assigned to that domain, so you can read them without holding a contact ID. The id-based contacts field still comes back empty even in the single-domain case, and contactDetails is not included in the multi-item list.autoRenew is a real true/false only in the multi-item list. Fetching a single domain via domain returns autoRenew: null, because Namecheap's single-domain lookup carries no auto-renew field — reported as null rather than a misleading false.domains_check_availability — Check Domain AvailabilityAccess: Read.
Checks whether one or more domain names are available to register. Names are checked in groups of up to 5, a few groups at a time. A domain whose TLD isn't supported for registration is not sent to the availability check at all — it's returned immediately as tldNotSupported.
| Parameter | Required | Type & constraints |
|---|---|---|
domains
|
Yes | 1–20 fully qualified domain names. Each accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
Returns — { results }, one entry per requested name:
| Field | Meaning |
|---|---|
domain
|
The checked name. |
result
|
available, taken, invalidDomainName, tldNotSupported, unexpectedError, or checkTimedOut. |
premiumPricing
|
For premium names: list of { operation, price, currency } where operation is register, transfer, renew, or restore. Empty for regular names. |
price
|
For available names (standard and premium): the USD price to register the domain for the shortest term the TLD allows — { amount, currency: "USD", pricedYears?, pricePerYear?, icannFee?, isPremium }. amount is the payable total for that whole term; pricedYears states how many years it covers. No pre-discount or "was" price is reported. icannFee is the ICANN fee (USD) already included in amount, returned separately so the breakdown can be explained; it appears only when the TLD carries a fee. |
pricePerYear
|
Inside price: amount divided by pricedYears, so a yearly figure is always available for comparison. When pricedYears is 1 it is the real one-year price; above that it is a per-year average of the term, not a term you could buy. Omitted for premium names, whose quote carries no term. |
minRegisterPeriodInYears / maxRegisterPeriodInYears |
For available names: the shortest and longest registration period that TLD actually permits, as two plain numbers. They tell the customer which terms the TLD can be registered for; the purchase link itself does not take a term. Both omitted when the allowed period could not be determined. |
priceUnavailableReason
|
Present instead of price when the price could not be determined for an available name. The check itself still succeeds. |
Only available names are priced; taken/invalid results carry neither price nor priceUnavailableReason.
checkTimedOut
means the check for that name did not finish in time, so nothing is known about it — it is not taken and not available. It carries no price. Names are checked in groups, and only the names of a group that timed out come back this way; the other results are valid. Check just the checkTimedOut names again later. The call fails with an error only when every group timed out, or when a group fails for any other reason.
Most TLDs allow one year, but some do not. .ai, for example, has a two-year minimum. For those, price.amount is the total for the minimum term — not a one-year price you could act on — and price.pricedYears says so:
{
"domain": "example.ai",
"result": "available",
"premiumPricing": [],
"price": { "amount": 79.98, "currency": "USD", "pricedYears": 2, "pricePerYear": 39.99, "isPremium": false },
"minRegisterPeriodInYears": 2,
"maxRegisterPeriodInYears": 10
}
pricePerYear is present here — 79.98 divided by the two years it covers gives 39.99. This is the total divided by the term, not a price you could pay for a single year (a one-year .ai registration cannot be bought). Always show amount together with pricedYears ("$79.98 for 2 years"), never amount alone. For an ordinary TLD, pricedYears is 1 and pricePerYear equals amount.
Namecheap-specific reporting
minRegisterPeriodInYears/maxRegisterPeriodInYears come from Namecheap's own TLD catalogue. Where that catalogue understates a minimum, the gateway corrects it from what the pricing service will actually sell: the first affected request may report the catalogue's value, and every request after it reports the corrected one.pricePerYear is never reported for a premium name — a premium quote carries no term, so a per-year figure would be invented.checkTimedOut only after both attempts failed.domain_purchase_link — Get Domain Purchase LinkAccess: Write.
Hands the customer a link to buy a domain on Namecheap's own consent and payment page. This is the only way to buy a domain through Namecheap MCP Connector. This call charges nothing and registers nothing — it only creates the purchase link.
The customer approves and pays on namecheap.com, choosing their own payment method there. Never hand out a new link when the domain may already have been bought — check domains_list first, or the customer can be charged twice. See Buy a domain through the purchase link.
Takes domain and an optional autoRenew. The consent page has no field for term length, WHOIS privacy, or contacts, so none of those can be set through this tool.
| Parameter | Required | Type & constraints |
|---|---|---|
domain
|
Yes | String. Fully-qualified domain name the customer agreed to buy, e.g. "example.com". Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
autoRenew
|
No | Boolean. Whether the domain and its WHOIS privacy should renew automatically, charging the customer again each term. Defaults to on when omitted. The consent page shows this to the customer but does not let them change it there — they can change it afterwards in their account. |
Returns — the customer needs to open the link:
{
"domain": "example.com",
"status": "consent_required",
"consentUrl": "https://www.namecheap.com/apps/consent/1ec9218b-7766-49c8-b25f-a5cff1840746",
"expiresInSeconds": 1200,
"autoRenew": true,
"note": "To buy example.com, open this link and complete the checkout on Namecheap's site: https://www.namecheap.com/apps/consent/1ec9218b-7766-49c8-b25f-a5cff1840746\n\nAfter paying, follow the link back to finish the purchase. Nothing has been charged yet. The domain and its WHOIS privacy will renew automatically, charging the customer again each term. The consent page shows this but does not let the customer change it there — they can change it afterwards in their Namecheap account."
}
Returns — the domain cannot be bought right now:
{
"domain": "example.com",
"status": "unavailable",
"note": "example.com is not available to purchase right now. Check availability again before retrying."
}
status can be:
| Status | Meaning |
|---|---|
consent_required
|
The link was created. consentUrl is what the customer must open; expiresInSeconds is how long it stays valid. Nothing charged. |
unavailable
|
The domain is not available to purchase. This is a normal result, not an error. |
Namecheap-specific reporting: the customer approves and pays entirely on namecheap.com, and there is no follow-up call for you to make. However, the purchase is only completed once the customer follows the link back after paying — that return is what commits it. If the customer never returns, the purchase can be reverted and any amount charged is refunded automatically. So do not tell the customer the domain is theirs on the strength of this call: confirm with domains_list first. The link is single-use — reopening it after a completed purchase may report a failure even though the domain was bought, so domains_list is the authority on the outcome.
domain_set_contacts — Set Domain ContactsAccess: Sensitive write — changing the owner can lock transfers for 60 days.
Changes the contacts assigned to a domain you own. Completes immediately. You may also reuse a contactId from a contacts_list result, so look up the contacts already saved on the account and reuse one before entering new details.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
registrant
|
Yes | Inline contact details or { contactId }. |
admin
|
No | Inline contact details, { contactId }, or null. |
tech
|
No | Inline contact details, { contactId }, or null. |
billing
|
No | Inline contact details, { contactId }, or null. |
tldAttributes
|
No | Extended attribute values keyed by name, for TLDs that require them. |
Returns
{ "verificationStatus": null }
Namecheap does not report an RAA verification state, so verificationStatus is always null.
domain_set_nameservers — Set Domain NameserversAccess: Sensitive write — moves all of the domain's traffic.
Changes a domain's registrar-level nameservers. Completes immediately. The change is reflected by domains_list (single-domain fetch) afterwards.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
provider
|
Yes |
basic (Namecheap's default nameservers) or custom (your own hosts). |
hosts
|
Conditional | Required when provider is custom: 2–12 nameserver hostnames (each a valid FQDN, 4–255 chars). Must be omitted when provider is basic. |
Returns
{ "provider": "custom", "hosts": ["ns1.example.com", "ns2.example.com"] }
Re-applying the state a domain is already in (e.g. setting basic when it is already basic) is not an error. The domain's current nameservers are read first; when they already match the request (the same provider, and for custom the same hosts in any order and letter case), nothing is written and the answer is the current state with a note:
{ "provider": "custom", "hosts": ["ns1.example.com", "ns2.example.com"], "note": "Already set; nothing changed." }
If the current nameservers cannot be read in time, or the domain uses another Namecheap DNS service (such as PremiumDNS or FreeDNS), the change is sent as requested. When Namecheap reports that it did not apply the change, the call returns business.invalidArgument ("The nameservers were not updated.").
The domainName these tools take accepts Unicode (IDN) or ASCII (A-label) and is normalized to punycode automatically; TLD support is not enforced here.
dns_records_get — Get DNS RecordsAccess: Read.
Retrieves a paginated list of DNS resource records for a domain.
A domain that uses other nameservers returns business.invalidArgument ("The domain does not use Namecheap DNS. Point its nameservers to Namecheap DNS before managing records here."); point the nameservers to Namecheap DNS first. A domain that is not in the account returns business.notFound ("This domain is not in this account.").
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to fetch. |
take
|
No | Items per page, 1–500. Default 100. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | Up to 8 sort keys: type, -type, name, -name. |
Returns — { items, total }, plus omittedRecords when the zone contains records this tool cannot return (see below). Each item is a record as described in Record shapes, plus an optional group field indicating where the record comes from (custom — created by you, product — managed by a product, personalNs — personal nameservers).
Namecheap-specific: a record that cannot be represented in the typed contract is omitted from items. Two kinds are omitted: record types outside the contract — the URL, FRAME and URL301 redirect records Namecheap stores in the zone (a parked domain normally has one) — and malformed CAA/TLSA/HTTPS/SVCB values. total still counts every record in the zone, including the omitted ones, so total can be larger than the number of records you can page through. Every omitted record is listed in omittedRecords by type and name, so nothing is dropped without being reported; the field is absent when nothing was omitted. Manage omitted records in the Namecheap dashboard.
{
"items": [
{ "type": "CNAME", "name": "www", "cname": "parkingpage.namecheap.com.", "ttl": 1800 }
],
"total": 2,
"omittedRecords": [
{ "type": "URL", "name": "@" }
]
}
dns_records_save — Save DNS RecordsAccess: Write.
Adds custom DNS records or updates the TTL of existing ones. Records are matched case-insensitively, except TXT records (case-sensitive).
If the call fails with infrastructure.serviceUnavailable, or reports a record as not saved because a connected service could not complete the request, the records may still have been saved: read them with dns_records_get before saving again (see Retrying writes).
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to update. |
records
|
Yes | 1–500 records — see Record shapes. Each may include an optional ttl. |
force
|
No | Boolean. Skips the conflict-resolution check and forces the zone update. |
Returns — { "saved": <number> }, plus failed with force when some records were not saved (see below). Unless force is set, a successful response means every record was accepted and confirmed present in the zone; if any record fails, the whole call returns a business.conflict error instead. Its detail states how many records were saved and how many failed and names them as <type>:<name> (up to ten per list, then "and N more"), and validationErrors gives the reason for each failed record. A record named as saved was written and need not be sent again; send only the failed ones, after reading the zone with dns_records_get.
Namecheap-specific — Mail Settings and MX records: Namecheap keeps custom MX records only while the domain's Mail Settings (Advanced DNS) are set to Custom MX. In any other mode it removes the MX records from every DNS write and still reports success, so before writing the tool reads the domain's Mail Settings. When they are set to No Email Service, Email Forwarding, Private Email or Gmail and the call contains at least one MX record:
force, the call is refused before anything is written, with business.invalidArgument. The detail names the Mail Settings mode, says Namecheap does not keep custom MX records in it, and says to switch Mail Settings to Custom MX in the Namecheap dashboard (Advanced DNS) or to save the other records without the MX records. validationErrors holds one entry per submitted MX record (field MX:<name>) with the same reason. No record is saved — the other records in the call are not written either — so send them again without the MX records, or with force.force: true, the other records are written and each MX record is listed in failed as { "record": <the record as you sent it>, "reason": "<Mail Settings reason>" }. saved counts only the records written and confirmed, so saved plus the length of failed equals the number of records submitted. A call whose records are all MX answers { "saved": 0, "failed": [...] } and writes nothing.To save MX records, switch the domain's Mail Settings to Custom MX in the Namecheap dashboard (Advanced DNS) yourself; the tool never changes Mail Settings. The switch changes how the domain's mail is delivered: the Email Forwarding, Private Email or Gmail setup stops applying and mail follows only the MX records in the zone, so have the complete set of MX records ready before switching. Under Custom MX, under any other mode that keeps MX records, or when the mode cannot be determined, MX records are written and confirmed like any other record. The Mail Settings check adds no request beyond the zone read the tool already makes before writing, and no result field: the result is still { "saved": <number> } plus failed with force.
Namecheap-specific — SRV records: an SRV record is identified by its service and protocol (for example _sip._tcp) together with its priority, weight, port and target; its name is not part of the identity, because Namecheap ignores the host of an SRV record and reports it as <service>.<protocol>. Saving an SRV record with the same identity again therefore updates it (for example its TTL) instead of adding a duplicate, the re-read confirms it, and dns_records_delete finds it by the same identity whichever name you send.
Namecheap-specific — re-read after writing: after writing, the zone is re-read to confirm the records actually persisted. Namecheap can accept a record and then drop it — for example an MX record when the domain's Mail Settings changed between the Mail Settings check above and the write, or a record that conflicts with another DNS feature on the domain. A record accepted but missing from that re-read is reported as a conflict naming the record, not counted as saved. force: true opts out of that protection: the call then does not fail on a dropped record. saved counts only the records the re-read confirmed, and the others are listed in failed, each as { "record": <the record as you sent it>, "reason": "<why it did not persist>" }. failed is omitted when every record persisted. Use force only when you intend to bypass conflict handling. If the re-read itself cannot be completed, the call fails — with force set or not — with infrastructure.serviceUnavailable: the write may have been applied, so re-read the zone with dns_records_get before retrying, rather than repeating the write. Any record already known to have failed is still listed in validationErrors. SRV records are the one exception: if Namecheap cannot return SRV records during the re-read, they are reported as saved without being confirmed.
dns_records_delete — Delete DNS RecordsAccess: Sensitive write — deletions cannot be undone.
Deletes custom DNS records. Deletions cannot be undone. A submitted record is deleted only when it matches a stored record on every field; the rules are listed under the table.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to delete. |
records
|
Yes | 1–500 records to delete — same shapes as save, but without ttl. A record that is not in the zone is reported in notFound, not as an error. |
Returns — { "deleted": <number>, "notFound": [...] }. deleted is the number of records that existed and were removed. notFound is present only when some submitted records were not in the zone; it lists them as submitted, in order, each once. Those records were not deleted and that is not an error — there is nothing to retry:
{
"deleted": 1,
"notFound": [
{ "type": "TXT", "name": "_old", "value": "v=old" }
]
}
Every submitted record that exists is deleted, even when others are missing. If Namecheap refuses to delete one of the matching records, the call fails with business.conflict and its per-record reasons; records deleted before that stay deleted and are not counted in a deleted number. The detail states how many of the matched records were deleted and how many failed and names them as <type>:<name> (up to ten per list, then "and N more"), and validationErrors gives the reason for each failed record. Submitted records that were not found are not part of that count.
How records are matched — a submitted record matches a stored one on its type, name and value, compared case-insensitively; an SRV record is matched on its service, protocol, priority, weight, port and target instead, and its name is ignored. For the value, host values (cname, aliasName, nameserver, pointer, the MX exchange, the SRV target) ignore one trailing dot and the CAA value ignores wrapping quotes; the TXT value is compared exactly, character for character. A trailing dot on name is not ignored. The MX preference is not compared: an MX record is matched by name and exchange alone, so when a zone holds several MX records for the same exchange, only one of them is matched and deleted, and it is not necessarily the one with the preference you sent. Call dns_records_get to check which MX records remain.
Every record has:
type — one of the 13 supported types below.name — the record name excluding the domain: use @ for the domain itself (apex) and * for a wildcard.ttl (save only, optional) — cache time in seconds, 60–3600.Type-specific fields:
| Type | Fields |
|---|---|
A
|
address — IPv4 address. |
AAAA
|
address — IPv6 address. |
CNAME
|
cname — canonical domain name (max 253 chars). |
ALIAS
|
aliasName — canonical domain name; CNAME-like behavior for the apex, where CNAME isn't allowed. |
NS
|
nameserver — nameserver name. |
PTR
|
pointer — domain name for the given IP address. |
TXT
|
value — text value (matched case-sensitively). |
MX
|
exchange — mail server; preference — priority (0–65535, lower preferred). |
CAA
|
flag — 0 or 128 (critical bit); tag — issue, issuewild, or iodef; value — CA identifier with optional parameters. |
SRV
|
service (e.g. _sip); protocol (e.g. _tcp); priority and weight (0–65535); port (1–65535); target — server domain name. |
TLSA
|
usage, selector, matching (each 0–255); port — * or _<1–65535>; protocol (e.g. _tcp); associationData — certificate hash or data. |
HTTPS
|
svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port (* or _<1–65535>), scheme (must be _https when port is set), svcParams. |
SVCB
|
svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port, scheme (e.g. _tcp), svcParams. |
CAA/TLSA/HTTPS/SVCB values is normalized into the fields above where possible. Values that don't fit the typed shape are readable in the zone but are omitted from dns_records_get's items and listed in its omittedRecords (see that tool). Standard records (A, AAAA, CNAME, MX, TXT, NS, etc.) are unaffected.CNAME, ALIAS, NS, PTR, the MX exchange, the SRV target) come back with a trailing dot, and a CAA value comes back quoted. You do not need to reproduce that when matching a record — dns_records_save and dns_records_delete accept either the value you originally submitted or the value dns_records_get reads back.When a call fails, the tool returns an error with a code and a human-readable detail explaining what went wrong — for example invalid input (a malformed domain name or contact ID), a domain or contact that doesn't exist, or a conflict with the current state. If a tool is not available because the assistant wasn't granted access to it, reconnect Namecheap MCP Connector and approve the access it asks for.
A failed call always has the same shape: isError is true, the text content reads <code>: <detail>, and structuredContent.error holds code, detail and, when a rejected input has several problems, a validationErrors list. The shape is stable; read detail for the cause. Arguments that do not match a tool's parameters (a missing required field, a wrong type, a value out of range) are rejected before the tool does anything with the code application.request.badRequest: detail names each problem, and validationErrors holds one entry per problem with the argument's path in field (for example items.0.domain) and the reason in message. A call beyond the rate limit returns application.request.tooManyRequests, and a call that the token's scopes do not allow returns application.forbidden. A tool that the access you granted does not cover is not listed, and a call to it is answered as an unknown tool instead (see Access you grant).
Every other failure carries one of five codes that name the situation, so you can decide what to do next. The detail text is written by the service, is fixed for each situation and never carries internal identifiers or text from a connected system:
| Code | Meaning | Fixed detail texts |
|---|---|---|
business.notFound
|
The domain or contact is not in this account. | "This domain is not in this account."; "Contact not found in this account."; "The requested item was not found in this account." |
business.invalidArgument
|
The request was understood but the input is not acceptable. | "The domain does not use Namecheap DNS. Point its nameservers to Namecheap DNS before managing records here."; "This operation is not supported for the domain."; "Too many records for one request."; "The page size is outside the range the domains list accepts."; "The request has a missing or invalid value."; or a message naming the problem |
business.conflict
|
The request conflicts with the current state. | "The request conflicts with the current state of the item."; or a message naming the conflicting record; or, for a dns_records_save or dns_records_delete call that applied only some records, the count of records applied and failed with their names |
infrastructure.serviceUnavailable
|
Namecheap could not complete the call. Retry later. After a failed write, see Retrying writes. | "A connected service could not complete the request."; "The service could not be reached." |
A rejection of a single value may carry one validationErrors entry shaped { "field": "<parameter name>", "rule": "<kind of rejection>" }, for example { "field": "DOMAINNAME", "rule": "NOT_AVAILABLE_OR_NOT_FOUND" }; the texts above are fixed and name no value you sent.
A failure the service cannot attribute to a situation above, such as an unexpected internal fault, returns the code infrastructure.unhandled with the fixed detail "The request could not be completed because of an internal error." Retry such a call later; repeating it at once rarely helps.
A write that fails with infrastructure.serviceUnavailable may still have been applied. Before calling it again:
contacts_save — look for the contact with contacts_list first.dns_records_save — read the records with dns_records_get first.domain_set_contacts — call it again; it assigns the same contacts again.domain_set_nameservers — call it again. A repeat that finds the nameservers already set answers with the note "Already set; nothing changed."; domains_list shows the current nameservers, so read it when an answer leaves you unsure.dns_records_delete — call it again; a record the first call already removed comes back in notFound, or in unconfirmed when the zone could not be read fully.Tools that only read can be called again at any time. A call refused with
application.request.tooManyRequests was not run: wait a short while before calling it again.
Need help? We're always here for you.