Registrar Domains.

Domain MCP server

Registrar Domains is a domain registrar your AI agent can use through the Model Context Protocol. One endpoint, https://registrar-domains.com/mcp, gives 32 tools: search names, read prices, start a registration, then manage DNS records, nameservers, the transfer lock and code, and renewals. Every order answers a payment link for you. The one exception is automatic renewal: an agent can ask to turn it on for a domain (set_auto_renew), and once you confirm it by email, we charge your saved card before expiry. An unlock, a transfer code and a nameserver change also wait for your confirmation by email.

Connect a client

Sign-in (OAuth 2.1) is the simplest: the app opens a page where you sign in with your email and press Allow. With a key from your dashboard, send it as a bearer token instead. Search and prices work with no account at all.

Claude

Claude.ai

Add a custom connector in the app's settings with this URL, then sign in when the app asks:

https://registrar-domains.com/mcp/oauth

Claude Code

claude mcp add --transport http registrar-domains https://registrar-domains.com/mcp/oauth

Or with a key:

claude mcp add --transport http registrar-domains https://registrar-domains.com/mcp --header "Authorization: Bearer apex_…"

Claude Desktop

In claude_desktop_config.json, through the mcp-remote bridge, which opens the sign-in page:

{
  "mcpServers": {
    "registrar-domains": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://registrar-domains.com/mcp/oauth"
      ]
    }
  }
}

ChatGPT

Add a connector in the app's settings with this URL, then sign in when the app asks:

https://registrar-domains.com/mcp/oauth

Cursor

In .cursor/mcp.json:

{
  "mcpServers": {
    "registrar-domains": {
      "url": "https://registrar-domains.com/mcp/oauth"
    }
  }
}

VS Code and others

In .vscode/mcp.json:

{
  "servers": {
    "registrar-domains": {
      "type": "http",
      "url": "https://registrar-domains.com/mcp/oauth"
    }
  }
}

Other clients

Any client with Streamable HTTP works: give it the URL https://registrar-domains.com/mcp and the header Authorization: Bearer <key>, or the URL https://registrar-domains.com/mcp/oauth if it can sign in. Many read this shape:

{
  "mcpServers": {
    "registrar-domains": {
      "type": "http",
      "url": "https://registrar-domains.com/mcp",
      "headers": {
        "Authorization": "Bearer apex_…"
      }
    }
  }
}

Tools and arguments

The first three are open to anyone. The others act on your account and need a key or a signed-in connection with the right scope. Every answer is JSON.

Every failed tool call has isError: true. A refusal, for a business rule or for access, also carries structuredContent { "error", "requires"?, "scope"?, … }: error says what went wrong in plain words, and hints such as save_card_url say what to do. An argument that does not fit the schema answers a text that starts with MCP error -32602 and names the field. On /mcp, a missing or bad key is a refusal in the tool answer (HTTP 200), because the open tools work without a key. On /mcp/oauth, the sign-in provider answers HTTP 401 to a bad token, before any tool runs. The other errors outside tool answers are in Endpoint details.

check_domain open

Is this exact domain available? Answers available, taken, or unknown. Taken also covers a name we already sold.

  • domain string — Full domain, for example tiny.fr

suggest_domains open

Try one idea across the launch TLDs and rank by availability.

  • idea string — A word, a short phrase, or a full domain such as quietharbor.fr
  • tlds string[], optional — TLDs to try. Defaults to the launch list.

get_price open

First-year and renewal price in USD before VAT for one TLD, next to our registrar's costs (cost, renewCost). Our price is our registrar's cost plus 30 %, and never less than what covers the payment fees, the VAT and US$1.00 of margin.

  • tld string — TLD without the dot, for example fr

register_domain account

Start a registration. Needs an API key or a signed-in connection. Answers an order with a payment link for the human; the domain is registered once payment clears. A retried call answers the same open order. .fr needs a registrant living or established in the EU, Iceland, Liechtenstein, Norway or Switzerland; .eu needs one in the EU, Iceland, Liechtenstein or Norway. Other registrants are refused before payment (see /terms#eligibility).

  • domain string
  • years integer, optional
  • registrant object — Legal registrant of the domain
  • addons object, optional — Options for the domain, priced per year: privacy, premiumDns, spamExperts, easyDmarc. Only the ones this deployment offers are kept.

get_order account

Follow an order. The state is one of: awaiting_payment, expired, paid, registering, registered, partly_failed, failed, refunded. The final states: registered, partly_failed, failed, refunded, expired. partly_failed: some names were registered and the others were not; each line's status, and whether its refund is done or pending, is in lines[]. A line with heldUntil set waits for a registrar top-up on our side; after that time, it fails and a refund is started.

  • orderId string — Order id returned by register_domain

list_domains account

The domains of your account, soonest expiry first, with state, expiry, days left and auto-renew.

No arguments.

list_orders account

The orders of your account, newest first, from every key and connection, with the amount paid and the invoice link.

  • limit integer, optional — How many orders, newest first. 1 to 200, 50 by default.

get_domain account

One domain: expiry, auto-renew, nameservers, transfer lock, DNSSEC, what the registry allows, and whether it is managed here.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

renew_domain account

Start a renewal for N years. Answers an order with a payment link for the human; the domain is renewed once payment clears. A registrant whose country cannot hold the .fr or .eu name is refused with { error, field }.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • years integer, optional — Years to add, 1 to 10. The domain must stay at most 10 years ahead of today, so a domain with years left takes fewer. 1 by default.
  • addons object, optional — The options to keep with the renewal: privacy, premiumDns, spamExperts, easyDmarc. Absent: the domain keeps its current options. {}: drops them all.

get_basket account

The account's basket, priced live: domains, years, options, line totals and the subtotal.

No arguments.

set_basket account

Replace the basket with lines of { domain, years, addons }. Nothing is charged. It does not cancel an open order.

  • lines object[]

checkout_basket account

One order and one payment link for the whole basket, every domain to the same registrant. A failed line is refunded alone. Payment clears the saved basket only when its domain names match the paid order; a basket changed after checkout stays. A retried call, or any key or connection of the account, gets the same open order for the same lines and registrant. A new order closes the older open ones for the same names. .fr needs a registrant living or established in the EU, Iceland, Liechtenstein, Norway or Switzerland; .eu needs one in the EU, Iceland, Liechtenstein or Norway. Other registrants are refused before payment (see /terms#eligibility).

  • lines object[], optional
  • registrant object — Legal registrant of every domain in the basket

list_dns_records account

The DNS records of a domain, each with a stable id, and whether our nameservers serve them.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

add_dns_record account

Add a DNS record: A, AAAA, CNAME, MX, TXT, CAA or SRV. TTL 600 to 86400 seconds.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • name string, optional — Host name inside the domain: "" or "@" for the domain itself, "www" for www.<domain>
  • type "A" | "AAAA" | "CNAME" | "MX" | "TXT" | "CAA" | "SRV" | "NS"
  • value string — Value: an IP for A/AAAA, a host for CNAME/MX, text for TXT (without quotes), for example v=spf1 include:_spf.example.com ~all
  • ttl integer, optional — Seconds, 600 to 86400
  • prio integer, optional — Priority, for MX and SRV only

update_dns_record account

Replace one DNS record by its id.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • id string — The record's id from list_dns_records, for example rec_3f2a9c0d1e4b5a67
  • name string, optional — Host name inside the domain: "" or "@" for the domain itself, "www" for www.<domain>
  • type "A" | "AAAA" | "CNAME" | "MX" | "TXT" | "CAA" | "SRV" | "NS"
  • value string — Value: an IP for A/AAAA, a host for CNAME/MX, text for TXT (without quotes), for example v=spf1 include:_spf.example.com ~all
  • ttl integer, optional — Seconds, 600 to 86400
  • prio integer, optional — Priority, for MX and SRV only

delete_dns_record account

Remove one DNS record by its id.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • id string — The record's id from list_dns_records, for example rec_3f2a9c0d1e4b5a67

set_nameservers account

Ask to point the domain to other nameservers (two to thirteen), or back to ours with an empty list. Nothing changes until the owner confirms it from an email. Hosts public DNS does not find come back as warnings.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • nameservers string[] — Two to thirteen distinct host names, for example ["ns1.cloudflare.com", "ns2.cloudflare.com"]. An empty list puts the domain back on our nameservers.

use_cloudflare account

Ask to move the domain's DNS to Cloudflare: exactly the two nameservers Cloudflare assigned. Refused while DNSSEC is on. Nothing changes until the owner confirms it from an email.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • nameservers string[] — Exactly the two nameservers Cloudflare assigned to the domain, as its dashboard shows them, for example ["ada.ns.cloudflare.com", "bob.ns.cloudflare.com"].

get_cloudflare_status account

Where the move to Cloudflare stands: pending, waiting, live (visible in public DNS), failed with the reason, or replaced, and what public DNS answers now.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

export_zone_file account

The domain's DNS records as a BIND zone file, to import in Cloudflare (DNS → Records → Import and Export) before a move.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

turn_off_dnssec account

Ask to turn DNSSEC off before a move to Cloudflare. Nothing changes until the owner confirms it from an email; public DNS then drops the DS record within a day.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

connect_cloudflare account

Link the owner's Cloudflare account with an API token (Zone: Read, Zone: Edit, DNS: Edit), so the one-click move can create the zone and copy the records. The token is stored encrypted and never shown again.

  • token string — A Cloudflare API token with Zone: Read, Zone: Edit and DNS: Edit, as Cloudflare shows it once after you create it. It is stored encrypted and never shown again.
  • account_id string, optional — The Cloudflare account id, when the token sees several accounts (the refusal lists them).
  • remember boolean, optional — true keeps Cloudflare linked for other domains. false (the default) deletes the stored token once the move is done (active, failed or cancelled).

get_cloudflare_connection account

Whether a Cloudflare account is linked: its id and name, when, and whether the link stays after a move. Never the token.

No arguments.

disconnect_cloudflare account

Delete the stored Cloudflare token. Delete the token in Cloudflare too.

No arguments.

prepare_cloudflare_move account

Create the domain's zone on the linked Cloudflare account (Free plan) and copy every DNS record exactly, then read it back. Shows the diff. Nameservers do not change.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • token string, optional — Optional: link Cloudflare in the same call. Without it, the linked account is used.
  • account_id string, optional — The Cloudflare account id, when the token sees several accounts (the refusal lists them).
  • remember boolean, optional — true keeps Cloudflare linked for other domains; false deletes the stored token once the move is done (active, failed or cancelled). Absent: the stored link's choice, else false.

switch_cloudflare_move account

Point a prepared move's nameservers to Cloudflare, after every check again. Nothing changes until the owner confirms it from an email.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • moveId string — The move's id, from prepare_cloudflare_move or get_cloudflare_move.

get_cloudflare_move account

Where the one-click move stands: preparing, prepared (with the diff), switching, switched, active, failed or cancelled, and what public DNS shows.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

cancel_cloudflare_move account

Cancel a prepared move. Deletes the zone only when apex created it and it is still pending.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

set_transfer_lock account

Lock the domain against transfers, at once, where the registry allows it. An unlock waits for the owner to confirm it from an email.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • locked boolean — true locks the domain against transfers; false unlocks it, which a transfer away needs

set_auto_renew account

Turn auto-renew off at once, or ask to turn it on. On: we charge the saved card 7 days before expiry, then renew. Turning it on waits for the owner to confirm it from an email, and needs a card the owner saved with Stripe through Manage payment method in the dashboard (save_card_url). A registrant whose country cannot hold the .fr or .eu name is refused with { error }.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • enabled boolean — true: we charge the saved card 7 days before expiry, then renew for one year. false: renewal waits for you (renew_domain).

get_auth_code account

Ask for the transfer code, to move the domain to another registrar. The owner confirms it from an email and sees the code on that page. The agent never gets the code.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr

get_domain_activity account

Who changed what on a domain, newest first: DNS, nameservers, lock, auto-renew, transfer code, renewals.

  • domain string — A domain of your account, as list_domains names it, for example ada.fr
  • limit integer, optional — How many events, newest first. 1 to 200, 50 by default.

The same tools have REST routes, described in /openapi.json. Prices per extension: domain extensions.

Example prompts

What needs you

Scopes

A connected app asks for scopes, and the consent page shows each one with the line below. Read is always granted. An app without the scope a tool needs gets { "error", "requires": "scope", "scope" }: disconnect it under Connect agent, then connect it again and allow that scope. An app connected before the split (scope domains) can only read until you connect it again. An API key has no scopes: it can do everything an app with all three scopes can do.

domains:read

Search and price domains, read your domains, orders, DNS records and activity, and start registrations and renewals that you pay through a link.

domains:write

Change DNS records (which can redirect your web and email traffic), nameservers and transfer locking (locking only), and turn auto-renew on or off. Turning auto-renew on lets us charge your saved card off-session at renewal. Nameserver changes and auto-renew on wait for your email confirmation.

domains:transfer

Unlock domains and ask for transfer codes. Each request waits for your email confirmation, and only you see the code.

No scope

Open to anyone, with no key and no connection.

Confirmation by email

Four actions let someone take a domain away from you. When an agent asks for one, we email the account owner a link, and nothing changes until the owner confirms it.

The link works once, for 30 minutes. It opens /account/confirm, which asks the owner to sign in to the same account. The page shows the request and who asked for it, with two buttons: Confirm and Refuse. Confirm carries out the change, with the rules checked again. Refuse cancels it. The domain's activity log shows the request (confirm.requested), a refusal (confirm.refused), and the change itself once it is carried out, under the name of who asked.

The tool answers at once:

{
  "status": "confirmation_sent",
  "domain": "quietharbor.fr",
  "action": "nameservers",
  "expiresAt": "2026-10-21T10:30:00.000Z",
  "message": "We emailed the account owner a link. Nothing changes until they confirm it. The link works once, for 30 minutes."
}

Endpoint details

Errors outside tool answers, from the transport or the sign-in provider:

A raw call, to see the shape:

curl -s https://registrar-domains.com/mcp \
  -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_price","arguments":{"tld":"fr"}}}'

Changes

, prices in US dollars

, server version 0.6.0

More: all docs, /llms-full.txt for assistants.