# DoDomain MCP server: Claude, ChatGPT, Cursor (https://dodomain.io/docs/connecting-ai-assistants)

DoDomain remote MCP server: one URL, OAuth 2.1 sign-in, eight scoped tools. 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.

## One URL, nothing else to configure [#one-url-nothing-else-to-configure]

The server lives at:

```
https://app.dodomain.io/api/mcp
```

### Fastest: Add to Claude [#fastest-add-to-claude]

Using Claude on the web, desktop or mobile? [Add DoDomain to Claude](https://claude.ai/directory/a7fc39a1-9ea8-48f0-9d66-2ac84f2508d1) opens DoDomain's listing in Claude's connector directory: click Connect, then approve the DoDomain sign-in.

### Let your coding agent set itself up [#let-your-coding-agent-set-itself-up]

Paste this sentence into Claude Code, Codex, Cursor, OpenCode or GitHub Copilot. The agent fetches DoDomain's setup instructions and adds this server, installs the SDK for your stack and verifies the result:

```text
Fetch and execute the appropriate instructions to set me up for DoDomain from https://dodomain.io/agent-setup/prompt.md
```

The same commands, one page per agent, are at [dodomain.io/agent-setup](https://dodomain.io/agent-setup). The manual setup for each client follows.

### How sign-in works [#how-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-code]

```bash
claude mcp add --transport http dodomain https://app.dodomain.io/api/mcp
```

Start a new session, run `/mcp`, pick `dodomain` and choose Authenticate.

### Codex [#codex]

```bash
codex mcp add dodomain --url https://app.dodomain.io/api/mcp
codex mcp login dodomain
```

### Cursor [#cursor]

Add the server to `~/.cursor/mcp.json` (every project) or `.cursor/mcp.json` (one project), merging into `mcpServers` if the file exists:

```json
{
  "mcpServers": {
    "dodomain": {
      "url": "https://app.dodomain.io/api/mcp"
    }
  }
}
```

### OpenCode [#opencode]

Add the server to `opencode.json` in the project root (or `~/.config/opencode/opencode.json`), then sign in:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "dodomain": {
      "type": "remote",
      "url": "https://app.dodomain.io/api/mcp",
      "enabled": true
    }
  }
}
```

```bash
opencode mcp auth dodomain
```

### GitHub Copilot (VS Code) [#github-copilot-vs-code]

Add the server to `.vscode/mcp.json`, then start it from `MCP: List Servers` and sign in when VS Code asks:

```json
{
  "servers": {
    "dodomain": {
      "type": "http",
      "url": "https://app.dodomain.io/api/mcp"
    }
  }
}
```

### Claude.ai [#claudeai]

One click: [Add DoDomain to Claude](https://claude.ai/directory/a7fc39a1-9ea8-48f0-9d66-2ac84f2508d1) opens DoDomain's listing in Claude's connector directory. 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 [#connect-from-any-mcp-client]

<div style={{ display: "flex", flexWrap: "wrap", gap: "8px" }}>
  <a
    href="https://claude.ai/directory/a7fc39a1-9ea8-48f0-9d66-2ac84f2508d1"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-claude.svg" alt="Add to Claude" height="32" />
  </a>
  <a
    href="https://chatgpt.com/plugins/plugin_asdk_app_6a7ae2ab834c8191a93649ac23775d1f"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-chatgpt.svg" alt="Add to ChatGPT" height="32" />
  </a>
  <a
    href="https://cursor.com/install-mcp?name=dodomain&amp;config=eyJ1cmwiOiJodHRwczovL2FwcC5kb2RvbWFpbi5pby9hcGkvbWNwIn0%3D"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-cursor.svg" alt="Add to Cursor" height="32" />
  </a>
  <a
    href="https://insiders.vscode.dev/redirect/mcp/install?name=dodomain&amp;config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fapp.dodomain.io%2Fapi%2Fmcp%22%7D"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-vscode.svg" alt="Add to VS Code" height="32" />
  </a>
  <a
    href="https://kiro.dev/launch/mcp/add?name=dodomain&amp;config=%7B%22url%22%3A%22https%3A%2F%2Fapp.dodomain.io%2Fapi%2Fmcp%22%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-kiro.svg" alt="Add to Kiro" height="32" />
  </a>
  <a
    href="https://replit.com/integrations?mcp=eyJkaXNwbGF5TmFtZSI6ImRvRG9tYWluIiwiYmFzZVVybCI6Imh0dHBzOi8vYXBwLmRvZG9tYWluLmlvL2FwaS9tY3AifQ%3D%3D"
    target="_blank"
    rel="noopener"
  >
    <img src="/docs/badges/add-to-replit.svg" alt="Add to Replit" height="32" />
  </a>
  <a href="#google-antigravity">
    <img src="/docs/badges/add-to-antigravity.svg" alt="Add to Antigravity" height="32" />
  </a>
  <a href="#gemini">
    <img src="/docs/badges/add-to-gemini.svg" alt="Add to Gemini" height="32" />
  </a>
  <a href="#codex">
    <img src="/docs/badges/add-to-codex.svg" alt="Add to Codex" height="32" />
  </a>
  <a href="#perplexity-mistral-le-chat-grok">
    <img src="/docs/badges/add-to-perplexity.svg" alt="Add to Perplexity" height="32" />
  </a>
  <a href="#perplexity-mistral-le-chat-grok">
    <img src="/docs/badges/add-to-mistral.svg" alt="Add to Mistral" height="32" />
  </a>
  <a href="#perplexity-mistral-le-chat-grok">
    <img src="/docs/badges/add-to-grok.svg" alt="Add to Grok" height="32" />
  </a>
</div>

Buttons for Claude and ChatGPT open the directory listing; the others install directly or jump to the instructions.

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/mcp
```

### ChatGPT [#chatgpt]

One click: [Open DoDomain in ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6a7ae2ab834c8191a93649ac23775d1f) 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](#codex) above.)

### Cursor and VS Code, one click [#cursor-and-vs-code-one-click]

[Add to Cursor](https://cursor.com/install-mcp?name=dodomain&config=eyJ1cmwiOiJodHRwczovL2FwcC5kb2RvbWFpbi5pby9hcGkvbWNwIn0%3D) · [Add to VS Code](https://insiders.vscode.dev/redirect/mcp/install?name=dodomain&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fapp.dodomain.io%2Fapi%2Fmcp%22%7D). The hand-written config files are in the [Cursor](#cursor) and [GitHub Copilot (VS Code)](#github-copilot-vs-code) sections above; VS Code's _MCP: Add Server_ → _HTTP_ command writes the same `.vscode/mcp.json`.

### Google Antigravity [#google-antigravity]

Install the plugin from the repository (or from the Antigravity Marketplace once listed):

```bash
agy plugin install https://github.com/DevinoSolutions/mcp-servers/antigravity-plugins/dodomain
```

Or add the server by hand in `~/.gemini/config/mcp_config.json` (Antigravity requires the `serverUrl` key):

```json
{ "mcpServers": { "dodomain": { "serverUrl": "https://app.dodomain.io/api/mcp" } } }
```

### Gemini [#gemini]

- **Gemini app:** Settings → Connected apps → _Custom apps_ → _Add a custom app_, paste the URL, then mention `@DoDomain` in a chat. (Custom apps are currently limited to personal Google accounts in the US.)
- **Gemini CLI:** `gemini extensions install https://github.com/DevinoSolutions/mcp-servers` then `/mcp auth dodomain`, or add `{ "mcpServers": { "dodomain": { "httpUrl": "https://app.dodomain.io/api/mcp" } } }` to `~/.gemini/settings.json`.

### Kiro [#kiro]

One click: [Add to Kiro](https://kiro.dev/launch/mcp/add?name=dodomain&config=%7B%22url%22%3A%22https%3A%2F%2Fapp.dodomain.io%2Fapi%2Fmcp%22%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D). Or in `.kiro/settings/mcp.json`:

```json
{ "mcpServers": { "dodomain": { "url": "https://app.dodomain.io/api/mcp" } } }
```

### Perplexity, Mistral Le Chat, Grok [#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 [#other-clients]

- **Replit:** [Add to Replit](https://replit.com/integrations?mcp=eyJkaXNwbGF5TmFtZSI6ImRvRG9tYWluIiwiYmFzZVVybCI6Imh0dHBzOi8vYXBwLmRvZG9tYWluLmlvL2FwaS9tY3AifQ%3D%3D), 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](https://github.com/DevinoSolutions/mcp-servers/tree/main/dodomain) 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 from the dashboard under [Connected apps](https://app.dodomain.io/dashboard/connected-apps); see [Revoking access](#revoking-access).

## The eight tools [#the-eight-tools]

Every tool calls the same `/api/v1` REST surface documented in the [API reference](https://dodomain.io/docs/api), on the teams you chose when you approved the connection (see [Teams](#teams)). None are destructive: nothing an agent can call deletes apps, connections, or DNS records.

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`.

| Tool                     | What it does                                                                                                                                                                                                                                                                                                                                                                                         | Scope               | Access    |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | --------- |
| `check_domain`           | Pre-flight a domain before connecting it: which DNS provider manages it, the registrable zone, the connect tier its provider supports (Cloudflare one-click, Domain Connect, or guided manual), whether one-click is actually available there (a Domain Connect provider may serve none of our templates; one-click needs a Pro or Scale session), nameservers, and a provider-specific setup guide. | `domains:read`      | Read-only |
| `list_teams`             | List the teams this connection can use: id and name. The source of the `teamId` the other tools take when the connection covers more than one team.                                                                                                                                                                                                                                                  | `teams:read`        | Read-only |
| `list_apps`              | List the apps in your team: id, name, public (widget) key, sandbox flag. Never returns secret keys. The source of the `appId` other tools take.                                                                                                                                                                                                                                                      | `apps:read`         | Read-only |
| `list_connections`       | List verified domain connections and their live DNS health (active vs broken), filterable by app or domain.                                                                                                                                                                                                                                                                                          | `connections:read`  | Read-only |
| `get_connect_session`    | Fetch one connect session's state by its token: domain, requested DNS records, status, detected provider, expiry. Poll it to see whether the end user finished.                                                                                                                                                                                                                                      | `sessions:read`     | Read-only |
| `create_connect_session` | Start a connect session for a domain. Returns a `connectUrl` the agent hands your end user to finish DNS setup in the browser, plus the composed fqdn each record will be verified at. Refused with `quota_exceeded` at the monthly cap; on Free the cap is shared across the owners' Free teams and unfinished sessions count against it (see [Rate limits and pricing](#rate-limits-and-pricing)). | `sessions:write`    | Writes    |
| `verify_connect_session` | Trigger a live DNS check of a session's expected records against authoritative nameservers. When every record matches, the connection is finalized and webhooks fire.                                                                                                                                                                                                                                | `sessions:write`    | Writes    |
| `reverify_connection`    | Queue an on-demand DNS health recheck of one existing connection. The result arrives asynchronously as a `connection.verified` / `connection.failed` webhook and on the dashboard.                                                                                                                                                                                                                   | `connections:write` | Writes    |

## Scopes and consent [#scopes-and-consent]

Access is scoped. When a client connects, the consent page lists exactly what it asked for, and the token it receives carries only the scopes you approve. What `search_tools` returns is filtered to the granted scopes: a client granted only read scopes never even sees `create_connect_session`.

These are the seven scopes, worded as the consent screen words them:

| Scope               | Grants                                                           |
| ------------------- | ---------------------------------------------------------------- |
| `domains:read`      | Check which DNS provider manages a domain and how it can connect |
| `apps:read`         | List the apps in your team                                       |
| `connections:read`  | List verified domain connections                                 |
| `connections:write` | Request re-verification of an existing connection                |
| `sessions:read`     | Read the status of domain-connect sessions                       |
| `sessions:write`    | Create domain-connect sessions and trigger DNS verification      |
| `teams:read`        | See which of your teams this connection can use                  |

## Teams [#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 (dashboard, **Connected apps**) 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`, `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](https://dodomain.io/docs/api). 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`.

## Revoking access [#revoking-access]

Every connected client is listed in the dashboard under [Connected apps](https://app.dodomain.io/dashboard/connected-apps). Revoking one 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 [#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](https://dodomain.io/docs/pricing)).

## Security [#security]

MCP callers authenticate exclusively with OAuth 2.1: PKCE on every flow, access tokens that expire after 30 days by default (a revoked client is still refused on its next request), and refresh only for clients granted it at consent. Your `dd_sk_` secret keys are never exposed to agents: the MCP endpoint does not accept them. Grants are per-user, per-client, scope-limited, and revocable instantly from the dashboard.
