DoDomain MCP server: Claude, ChatGPT, Cursor
DoDomain remote MCP server: one URL, OAuth 2.1 sign-in, every dashboard action as a tool. Setup for Claude, ChatGPT, Cursor, Codex and other MCP clients.
DoDomain ships a remote MCP (Model Context Protocol) server, so AI assistants and agents (Claude, coding agents, anything that speaks MCP) can check a domain's DNS provider, mint a connect session, hand your user the hosted connect link, and verify DNS, using the same REST API and permissions your integration already has.
Pick your tool in the box at the top of this page: Claude and ChatGPT open DoDomain's own listing, Cursor and VS Code add the server in one click, and coding agents get one prompt to paste. The manual setup for each client follows.
One URL, nothing else to configure
The server lives at:
https://app.dodomain.io/api/mcpHow sign-in works
It speaks Streamable HTTP (stateless) and authenticates with OAuth 2.1 (PKCE plus dynamic client registration), publishing the standard discovery documents (RFC 9728 protected-resource and RFC 8414 authorization-server metadata). MCP clients find the authorization server, register themselves, and start the sign-in flow automatically. You add the URL, sign in with your DoDomain account, and approve the requested scopes on the consent page. No API keys, no manual client setup.
Claude Code
claude mcp add --transport http dodomain https://app.dodomain.io/api/mcpStart a new session, run /mcp, pick dodomain and choose Authenticate.
Codex
codex mcp add dodomain --url https://app.dodomain.io/api/mcp
codex mcp login dodomainCursor
Add the server to ~/.cursor/mcp.json (every project) or .cursor/mcp.json (one project), merging into mcpServers if the file exists:
{
"mcpServers": {
"dodomain": {
"url": "https://app.dodomain.io/api/mcp"
}
}
}OpenCode
Add the server to opencode.json in the project root (or ~/.config/opencode/opencode.json), then sign in:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"dodomain": {
"type": "remote",
"url": "https://app.dodomain.io/api/mcp",
"enabled": true
}
}
}opencode mcp auth dodomainGitHub Copilot (VS Code)
Add the server to .vscode/mcp.json, then start it from MCP: List Servers and sign in when VS Code asks:
{
"servers": {
"dodomain": {
"type": "http",
"url": "https://app.dodomain.io/api/mcp"
}
}
}Claude.ai
DoDomain is listed in Claude's connector directory: open the listing, click Connect, then approve the DoDomain sign-in. By hand, as a custom connector: in Claude's web or desktop app, open Settings → Connectors → Add custom connector and paste the URL above as the remote MCP server URL. Claude walks you through the DoDomain sign-in and consent screen, then the tools appear in your conversations.
Connect from any MCP client
Any client that supports remote MCP servers over Streamable HTTP with OAuth works the same way: give it the URL below, sign in to DoDomain when the browser opens, and approve the scopes. The URL is the only configuration. Claude Code, Codex, Cursor, OpenCode, GitHub Copilot and Claude.ai have their own sections above; the other common clients follow.
https://app.dodomain.io/api/mcpChatGPT
One click: Open DoDomain in ChatGPT opens DoDomain's published ChatGPT app; click Connect, then sign in. Manual: Settings → Apps & Connectors → Create (developer mode), name it DoDomain, paste the URL, authentication OAuth. (For the Codex CLI, see Codex above.)
Google Antigravity
Install the plugin from the repository (or from the Antigravity Marketplace once listed):
agy plugin install https://github.com/DevinoSolutions/mcp-servers/antigravity-plugins/dodomainOr add the server by hand in ~/.gemini/config/mcp_config.json (Antigravity requires the serverUrl key):
{ "mcpServers": { "dodomain": { "serverUrl": "https://app.dodomain.io/api/mcp" } } }Gemini
- Gemini app: Settings → Connected apps → Custom apps → Add a custom app, paste the URL, then mention
@DoDomainin a chat. (Custom apps are currently limited to personal Google accounts in the US.) - Gemini CLI:
gemini extensions install https://github.com/DevinoSolutions/mcp-serversthen/mcp auth dodomain, or add{ "mcpServers": { "dodomain": { "httpUrl": "https://app.dodomain.io/api/mcp" } } }to~/.gemini/settings.json.
Kiro
One click: Add to Kiro. Or in .kiro/settings/mcp.json:
{ "mcpServers": { "dodomain": { "url": "https://app.dodomain.io/api/mcp" } } }Perplexity, Mistral Le Chat, Grok
- Perplexity (Pro, Max, Enterprise): Settings → Connectors → Custom connector, paste the URL, authentication OAuth.
- Mistral Le Chat (all plans): Intelligence → Connectors → Add Connector → Custom MCP Connector, paste the URL.
- Grok: grok.com → Connectors → New connector → Custom, paste the URL.
Other clients
- Replit: Add to Replit, or Integrations → Add MCP server.
- Devin, Windsurf, Zed, JetBrains Junie, Cline, MCP Inspector, your own agent built on an MCP SDK: add a remote (HTTP) MCP server with the URL above; each prompts for the OAuth sign-in on first use. Cline can also install it from the Cline marketplace entry using its
llms-install.md.
Tokens are scoped to what you approve on the consent screen; a tool you did not grant is never shown to the assistant. Revoke access at any time: ask the assistant to run revoke_connected_app (it asks for a confirmation first), or use the Connected apps page; see Revoking access.
The tools
Everything you can do in the DoDomain dashboard, your assistant can do by calling a tool: check a domain, create apps and connect sessions, manage API keys and webhook endpoints, read sessions and deliveries, invite teammates, upgrade or cancel your plan, open a support request, and delete things when you say so. A new team can start from chat: call create_app with a name and the permissions you choose for its key, then pass its returned id as appId to create_connect_session.
Anything that needs your hands, such as typing a card number, finishing a Cloudflare sign-in or signing in with Google, comes back as a one-time link the assistant gives you (see Handoff links). Everything else runs through the tool.
The server uses Code Mode. Its tool list has two tools, search_tools and execute_typescript, not one tool per row below. Your assistant calls search_tools to find the tools below, then runs a short TypeScript program through execute_typescript that calls them. Each call goes through the same scope and team checks as a direct call, and a refused call comes back as an error inside the program, such as SCOPE_MISSING or TEAM_REQUIRED. Tools that delete or cancel something return CONFIRMATION_REQUIRED first and act only when called again with the one-time token (see Confirmations).
Where a decision is yours, the tool takes it as a required argument and the assistant asks you instead of choosing: the plan and billing interval, which permissions a new key gets, whether a rotated key overlaps the old one, a teammate's role, and the exact app name or account email you type to confirm a deletion. A call that leaves one out is refused with VALIDATION_ERROR, and next.args shows the argument as <ask the user: ...>.
| Tool | What it does | Scope | Access |
|---|---|---|---|
accept_team_invitation | Accept a team invitation as the signed-in person, using the token from the invitation link. | teams:write | Writes |
add_webhook_endpoint | Register an HTTPS URL that receives this app's signed webhooks (session.* and connection.* events). | webhooks:write | Writes |
billing_status | The team's plan, subscription state (status, billing interval, trial), this month's verified-connection usage against the plan cap, and the app count against the app limit. | billing:read | Read-only |
check_domain | Pre-flight a domain before connecting it. | domains:read | Read-only |
convert_guest_account | Turn a guest account into a permanent one. | account:write | Writes |
create_api_key | Mint an additional secret key for an app with a label and the permission groups it may use. | keys:write | Writes |
create_app | Create an app in the team (admins and owners; limited by the plan's app cap, QUOTA_EXCEEDED points at upgrade_plan). | apps:write | Writes |
create_connect_session | Start a domain-connect session so an end user can point their domain at the integrator's product. | sessions:write | Writes |
create_shared_setup_link | Mint (or rotate) the shared setup link for a session: a page the end user can forward to whoever edits their DNS. | sessions:write | Writes |
create_support_ticket | Send a support request to the DoDomain team on the signed-in person's behalf. | support:write | Writes |
create_team | Create a new team owned by the signed-in person and make it their active team. | teams:write | Writes |
decline_team_ownership | Turn down an ownership invitation sent to the signed-in person. | teams:write | Writes |
delete_account | Permanently delete the signed-in person's DoDomain account and every team they solely own. | account:delete | Confirms first |
delete_app | Permanently delete an app and everything under it (owners only). | apps:delete | Confirms first |
delete_team | Delete a team once it is empty: no apps, no subscription, and you are its only member (owners only). | teams:delete | Confirms first |
delete_webhook_endpoint | Stop delivering to an endpoint. | webhooks:delete | Confirms first |
detect_connect_session | Run DNS-provider detection for a session and persist the detected tier (what the hosted flow does when it opens). | sessions:write | Writes |
disconnect | Disconnect something. | connections:write | Confirms first |
disconnect_sign_in_method | Remove one way to sign in (accountId from list_sign_in_methods). | account:write | Confirms first |
export_data | Export data. | account:write | Read-only |
get_connect_links | Click-ready links for the connect flows that finish in a browser. | connections:write | Writes |
get_connect_session | Fetch the current state of one domain-connect session by its sessionToken (the token returned by create_connect_session). | sessions:read | Read-only |
get_connection | Read one connection by id (from list_connections or a connection.* webhook): domain, recordFqdns, status (active or broken), verifiedAt, lastCheckedAt, brokenAt and disconnectedAt. | connections:read | Read-only |
get_notification_inbox_session | The session details the in-app notification bell needs for this person (application id, subscriber id and hash). | account:read | Read-only |
get_profile | The signed-in person's profile: name, email, display currency, the tips-and-prompts preference, whether the account is a guest, and every team with their role. | account:read | Read-only |
get_referral | The person's Refer and Earn state: their referral link, click, sign-up and sale counts, and upcoming versus paid earnings. | account:read | Read-only |
get_webhook_delivery | One webhook delivery: its status, how many attempts were made and the last error. | webhooks:read | Read-only |
invite_member | Invite someone to the team by email (admins and owners only). | teams:write | Writes |
leave_team | Leave the team. | teams:write | Confirms first |
list_api_keys | The app's API keys: the primary secret key and any additional ones, each with its label, the permission groups it holds, when it was created and (for the primary key) when the previous key stops working. | keys:read | Read-only |
list_apps | List the apps in one team. | apps:read | Read-only |
list_connected_apps | The AI apps the person has connected to their DoDomain account, with the permission groups each holds and when it was last used. | grants:read | Read-only |
list_connections | List verified domain connections and their live DNS health, in one team. | connections:read | Read-only |
list_granted_scopes | What this very connection may do: its permission groups, the exact scopes, and the teams it reaches. | grants:read | Read-only |
list_invites | Pending team invitations with the invited email, role, state (pending or expired) and expiry. | teams:read | Read-only |
list_members | Everyone on the team with their role (owner, admin, member) and when they joined. | teams:read | Read-only |
list_plans | The paid plans with monthly and annual prices, caps and highlights, and which one is current. | billing:read | Read-only |
list_sessions | The team's connect sessions, newest first (50 per page; pass nextCursor back as cursor). | sessions:read | Read-only |
list_sign_in_methods | The ways into the signed-in person's own account (Google, Apple, email and password), each with its accountId and whether disconnect_sign_in_method may remove it. | account:read | Read-only |
list_support_tickets | The support requests this person has sent, newest first, last 50: ticketId, type, subject, a preview and when. | support:read | Read-only |
list_teams | List the teams this connection can use: each team's id and name. | teams:read | Read-only |
list_vercel_connections | The team's Vercel connections: which app each belongs to, the Vercel account, and the linked projects. | connections:read | Read-only |
list_webhook_deliveries | The team's webhook delivery log, newest first (50 per page; pass nextCursor back as cursor). | webhooks:read | Read-only |
list_webhook_endpoints | The webhook endpoints of an app: id, URL, creation time and pausedAt (set while the endpoint is auto-paused after repeated failures). | webhooks:read | Read-only |
manage_subscription | Manage the team's subscription. | billing:write | Confirms first |
redrive_webhook_delivery | Send a failed or skipped delivery again with its original id. | webhooks:write | Writes |
remove_member | Remove a teammate from the team. | teams:write | Confirms first |
rename_app | Rename an app. | apps:write | Writes |
request_scopes | Ask the user for more access. | grants:read | Writes |
resume_webhook_endpoint | Resume an auto-paused endpoint. | webhooks:write | Writes |
reverify_connection | Queue an on-demand DNS health recheck of ONE existing connection (find its id via list_connections). | connections:write | Writes |
revoke_api_key | Revoke one additional key of an app by its id (from list_api_keys). | keys:write | Confirms first |
revoke_connected_app | Disconnect an AI app from the person's account by its id (from list_connected_apps). | grants:write | Confirms first |
revoke_invite | Cancel a pending invitation by its id (from list_invites). | teams:write | Writes |
rotate_api_key | Replace the app's primary secret key. | keys:write | Writes |
rotate_webhook_secret | Mint a new signing secret for an endpoint. | webhooks:rotate | Writes |
run_dns_tool | DoDomain's keyless DNS tools. | domains:read | Read-only |
send_test_webhook_event | Send a session.completed-shaped test event (payload marked test:true) to one endpoint. | webhooks:write | Writes |
switch_active_team | Point the person's dashboard at one of their teams. | teams:write | Writes |
update_app_allowed_origins | Replace the app's allowed origins with exactly this list (send the whole list; origins are normalised and de-duplicated). | apps:write | Writes |
update_app_branding | Change the end-user-facing branding of the connect flow: logoUrl (https), brandColor (#RRGGBB), and on Pro/Scale the headline, subheadline, success button label and redirect, font preset and footer-help toggle. | apps:write | Writes |
update_app_tls_issuer | Set (or clear with an empty string) the CAA issuer domain of the certificate authority this app's customers' certificates come from, e.g. | apps:write | Writes |
update_member_role | Change a teammate's role (owners only). | teams:write | Writes |
update_notification_prefs | Change notification preferences: the tips-and-prompts switch (promptsEnabled). | account:write | Writes |
update_profile | Change the profile name and/or display currency. | account:write | Writes |
update_webhook_endpoint | Point an existing endpoint at a new HTTPS URL. | webhooks:write | Writes |
upgrade_plan | Start a purchase or a plan change. | billing:write | Writes |
verify_connect_session | Trigger a live DNS check of a session's expected records against authoritative nameservers. | sessions:write | Writes |
Teams
If you belong to more than one DoDomain team, the consent screen asks which ones the connection can use: the teams you tick (your current team is ticked to start with), or All my teams, which also covers teams you join later. The connection can act on a team only while you are still a member of it; leave a team and the connection loses it on the next request. If you rejoin a team the connection was granted, it can use that team again; to stop that, disconnect the client (revoke_connected_app, or the Connected apps page) or connect it again without that team. To change the teams later, connect the client again and choose again: the new choice replaces the old one for the whole connection, including the access tokens the client already holds.
When a connection covers more than one team, the agent says which team each call is for. list_teams returns the teams it can use, and the tools that read or write team data (check_domain, list_apps, create_app, list_connections, create_connect_session, reverify_connection) take an optional teamId. With more than one team, a call without teamId is refused with TEAM_REQUIRED (except check_domain, whose answer does not depend on the team), and a call naming a team the connection cannot use is refused with TEAM_NOT_FOUND; both list the usable teams. With a single team, teamId can be left out.
Over REST, the same choice is the DoDomain-Team header (a team id from GET /api/v1/teams); see the API reference. A secret key (dd_sk_) belongs to exactly one team, so it needs no header, and a header naming any other team is refused with 404 TEAM_NOT_FOUND.
Errors and recovery
Every refusal, over MCP and over REST, uses one envelope: { "code", "message", "next", "docs" }. next names the tool to call to recover, with its arguments; message is one sentence and never contains a link.
| Code | Meaning | next |
|---|---|---|
SCOPE_MISSING | The connection does not hold the permission this tool needs. | request_scopes for the missing tier |
CONFIRMATION_REQUIRED | A destructive tool needs a one-time confirmation. | the same tool, with a confirmToken |
QUOTA_EXCEEDED | The plan limit is reached. | upgrade_plan |
VALIDATION_ERROR | An argument is missing or wrong. | the argument to supply, when known |
NOT_FOUND | The thing does not exist or is not yours. | |
RATE_LIMITED | Too many requests for the plan. Back off and retry. | |
CONFLICT | The call conflicts with the current state. | |
TOOL_FAILED | An unexpected failure. Retry once, then contact support. | |
UNAUTHORIZED, CONSENT_REVOKED | The credential is missing, expired or revoked. Connect again. | |
TEAM_REQUIRED, TEAM_NOT_FOUND | Name a usable team with teamId. |
Permissions
The consent screen groups what a connection can do into nine permission tiers, all ticked by default: read your data, create and edit, delete, billing and subscription, profile and account deletion, members and invites, connected services, support requests, and API keys. Untick a tier and the connection cannot call its tools; search_tools does not even list them. Read your data stays on, because every other tier needs it. A tool the connection cannot call returns SCOPE_MISSING, and request_scopes gives a one-time link to widen the grant.
Write tools also follow your role. Creating apps, creating sessions and disconnecting or re-verifying connections need the Admin role or higher on the teams the connection covers. If you are a member, the consent screen says so and approves the rest of what you ticked: the connection is read-only, without those tools. Ask a team admin to connect the client when it needs them.
An app's dd_sk_ secret key chooses from five of those tiers when it is created or rotated: read, create and edit, delete, connected services and API keys. Billing, account, team and support tools act on a person, so a secret key never holds them.
Confirmations
Tools that delete, cancel or remove things return CONFIRMATION_REQUIRED the first time, with a confirmToken in next.args. State the action to the user, then call the same tool again with that token. A token works once, for the same arguments, for ten minutes. We keep backups: contact support to restore anything removed by mistake.
Handoff links
Some steps cannot happen in chat: entering a card, finishing a Cloudflare, Domain Connect or Vercel sign-in, downloading an export, converting a guest account or widening a grant. The tool for that step (upgrade_plan, manage_subscription for the billing portal, get_connect_links, request_scopes, export_data, convert_guest_account) returns a https://app.dodomain.io/go/<id> link. Give it to the person as it is. The link expires after 15 minutes, and a link tied to a person works once and only for that person.
Secret keys over MCP
An app's dd_sk_ secret key is accepted as the Bearer token. It acts as its one app, within the tiers chosen when the key was created, and cannot call any tool that acts on a person.
Billing from chat
billing_status shows the plan, state and usage, and list_plans shows what you can buy. upgrade_plan returns a checkout link with the plan and interval you chose, and the payment happens there. manage_subscription opens the payment-method and invoice portal, or cancels or refunds the latest payment, and always asks for a confirmation before cancelling or refunding. A plan bought through the App Store or Google Play is managed in that store, so the tool says so and returns the store's management link instead of cancelling it. Deleting your account cancels a web subscription for you. A store subscription you cancel yourself in the store, and the delete result and the deletion email say so.
Revoking access
Every connected client is listed by list_connected_apps and on the Connected apps page. Revoking one (revoke_connected_app, which asks for a confirmation) takes effect immediately: both its access and refresh tokens stop working on the next request, because every MCP request checks that your consent still exists.
Rate limits and pricing
MCP access is included on every plan: there is no separate SKU. Requests ride the same per-minute plan meters as the REST API (Free 60, Pro 300, Scale 1,200 requests/min per team), and the monthly connection quota is metered identically to REST: creating a session costs nothing, and one unit is charged when a domain is first verified. create_connect_session is refused with quota_exceeded exactly when the REST endpoint would refuse it: on every plan once the team's monthly cap is reached, and on Free also once the Free teams owned by any of this team's owners together reach it, or once its unfinished sessions reach what the month has left plus 10 (see Pricing).
Security
MCP callers authenticate with OAuth 2.1 (PKCE on every flow, access tokens that expire after 30 days by default, a revoked client refused on its next request, and refresh only for clients granted it at consent) or with an app's dd_sk_ secret key, which acts only as that app. Every tool call is recorded with its tool, outcome and a hash of its arguments, never the arguments themselves. Grants are per-user, per-client, scope-limited, and revocable instantly with revoke_connected_app.