domain-mcp
<p align="center">
<img src="docs/assets/logo-256.png" alt="domain-mcp" width="128" height="128">
</p>
# domain-mcp
[](https://github.com/danielgtmn/domain-mcp/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/)
[](https://github.com/danielgtmn/domain-mcp/pkgs/container/domain-mcp)
**MCP server** to check whether domain names are **available** or **already registered**.
Uses **RDAP** (structured registry JSON) first, with **WHOIS** fallback when a TLD has no RDAP endpoint. TLD coverage tracks the [IANA RDAP bootstrap](https://data.iana.org/rdap/dns.json) (~1,200 TLDs) plus community overrides (e.g. `.de`) and IANA WHOIS for the rest — not a hand-curated shortlist.
> **Disclaimer:** “Available” means no registration record was found. It is **not** a purchase guarantee. Premium, reserved, or policy-blocked names may still be unregistrable. Confirm at a registrar before buying.
## Features
- MCP tools for single and bulk domain checks
- RDAP-first lookups via [whoisit](https://github.com/meeb/whoisit)
- WHOIS fallback (IANA → registry, one referral hop)
- IDN / punycode support
- In-memory TTL cache
- **Remote hosted MCP** + local stdio
- Official Docker image on **GHCR** (versioned from GitHub Releases)
- Docs in-repo (`docs/`) synced to the [GitHub Wiki](https://github.com/danielgtmn/domain-mcp/wiki)
## Hosted endpoint
```text
https://domain.mcp.danielgtmn.com/mcp
```
| Check | URL |
|-------|-----|
| MCP | https://domain.mcp.danielgtmn.com/mcp |
| Health | https://domain.mcp.danielgtmn.com/health |
| Landing (browser) | https://domain.mcp.danielgtmn.com/ |
| Info (JSON) | https://domain.mcp.danielgtmn.com/info |
No install required for the hosted instance — only wire the URL (or a stdio bridge) into your client.
The previous host `https://domain-mcp.gietmanic.com/mcp` remains as an alias.
---
## Install remote MCP (hosted)
Use the same endpoint everywhere:
```text
https://domain.mcp.danielgtmn.com/mcp
```
> **Tip:** After editing config, fully restart the client (quit + reopen). Some hosts only load MCP servers at startup.
### Cursor
1. Open **Cursor Settings → MCP** (or edit the MCP config file).
2. Add:
**macOS / Linux** — `~/.cursor/mcp.json`
**Windows** — `%USERPROFILE%\.cursor\mcp.json`
```json
{
"mcpServers": {
"domain-mcp": {
"url": "https://domain.mcp.danielgtmn.com/mcp"
}
}
}
```
Project-scoped alternative: `.cursor/mcp.json` in the repo root (same JSON).
### Claude Desktop
Claude Desktop is primarily stdio-based. Bridge the remote URL with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) (requires Node.js 18+):
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
**Linux:** `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"domain-mcp": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://domain.mcp.danielgtmn.com/mcp"
]
}
}
}
```
Restart Claude Desktop. On first run, `npx` downloads `mcp-remote` automatically.
### Claude Code (CLI)
```bash
claude mcp add --transport http domain-mcp https://domain.mcp.danielgtmn.com/mcp
```
List / remove:
```bash
claude mcp list
claude mcp remove domain-mcp
```
### VS Code (GitHub Copilot Chat / MCP)
1. Command Palette → **MCP: Open User Configuration** (or workspace `.vscode/mcp.json`).
2. Add a server entry:
```json
{
"servers": {
"domain-mcp": {
"type": "http",
"url": "https://domain.mcp.danielgtmn.com/mcp"
}
}
}
```
If your VS Code build uses the older `mcpServers` shape, this equivalent also works in many setups:
```json
{
"mcp": {
"servers": {
"domain-mcp": {
"type": "http",
"url": "https://domain.mcp.danielgtmn.com/mcp"
}
}
}
}
```
### Windsurf (Codeium)
Edit **Windsurf → Settings → Cascade → MCP** or `~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"domain-mcp": {
"serverUrl": "https://domain.mcp.danielgtmn.com/mcp"
}
}
}
```
If `serverUrl` is ignored in your build, try the Cursor-style `url` key or the `mcp-remote` stdio bridge (same as Claude Desktop).
### Cline / Roo Code (VS Code extension)
In the extension MCP settings (often `cline_mcp_settings.json` / Roo MCP config):
```json
{
"mcpServers": {
"domain-mcp": {
"url": "https://domain.mcp.danielgtmn.com/mcp",
"disabled": false
}
}
}
```
If the extension only supports command-based servers, use the `mcp-remote` bridge:
```json
{
"mcpServers": {
"domain-mcp": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://domain.mcp.danielgtmn.com/mcp"],
"disabled": false
}
}
}
```
### Continue.dev
In `~/.continue/config.json` (or assistant config), under `mcpServers` / experimental MCP:
```json
{
"mcpServers": [
{
"name": "domain-mcp",
"type": "streamable-http",
"url": "https://domain.mcp.danielgtmn.com/mcp"
}
]
}
```
Exact schema can vary by Continue version — if HTTP type is unsupported, use:
```json
{
"name": "domain-mcp",
"command": "npx",
"args": ["-y", "mcp-remote", "https://domain.mcp.danielgtmn.com/mcp"]
}
```
### Zed
In `~/.config/zed/settings.json` (MCP support depends on Zed version):
```json
{
"context_servers": {
"domain-mcp": {
"command": {
"path": "npx",
"args": [
"-y",
"mcp-remote",
"https://domain.mcp.danielgtmn.com/mcp"
]
}
}
}
}
```
### JetBrains IDEs (AI Assistant / MCP plugins)
Prefer the **stdio bridge** (widest plugin support):
```json
{
"mcpServers": {
"domain-mcp": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://domain.mcp.danielgtmn.com/mcp"]
}
}
}
```
Paste into the plugin’s MCP server settings UI if it does not read a JSON file.
### Generic / any stdio-only client
If the client only runs local processes:
```bash
npx -y mcp-remote https://domain.mcp.danielgtmn.com/mcp
```
Map that command + args into the client’s MCP config the same way you would any other stdio server.
### Optional API key
If the host sets `DOMAIN_MCP_API_KEY`, send a bearer token.
**HTTP-native clients:**
```json
{
"mcpServers": {
"domain-mcp": {
"url": "https://domain.mcp.danielgtmn.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
**mcp-remote bridge:**
```json
{
"mcpServers": {
"domain-mcp": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://domain.mcp.danielgtmn.com/mcp",
"--header",
"Authorization: Bearer YOUR_API_KEY"
]
}
}
}
```
(Header flag support depends on `mcp-remote` version; if unsupported, use a client with native `headers`.)
### Quick matrix
| Client | Recommended setup |
|--------|-------------------|
| **Cursor** | `"url": "https://domain.mcp.danielgtmn.com/mcp"` |
| **Claude Desktop** | `npx mcp-remote` bridge |
| **Claude Code** | `claude mcp add --transport http …` |
| **VS Code / Copilot** | `"type": "http"` + `url` |
| **Windsurf** | `serverUrl` or `url` |
| **Cline / Roo** | `url` or `mcp-remote` |
| **Continue** | streamable-http / `mcp-remote` |
| **Zed / JetBrains** | `mcp-remote` bridge |
| **Other stdio-only** | `npx -y mcp-remote <url>` |
---
## Local install (stdio)
For offline use, development, or when you prefer a process on your machine.
### From source
```bash
git clone https://github.com/danielgtmn/domain-mcp.git
cd domain-mcp
uv sync
uv run domain-mcp
```
### Client config (local)
```json
{
"mcpServers": {
"domain-mcp": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/domain-mcp",
"run",
"domain-mcp"
]
}
}
}
```
Or the venv binary:
```json
{
"mcpServers": {
"domain-mcp": {
"command": "/absolute/path/to/domain-mcp/.venv/bin/domain-mcp"
}
}
}
```
### Docker (self-host)
**HTTP (remote-style URL on localhost):**
```bash
docker pull ghcr.io/danielgtmn/domain-mcp:latest
docker run --rm -p 8000:8000 ghcr.io/danielgtmn/domain-mcp:latest
# → http://localhost:8000/mcp
```
Then point any client at `http://localhost:8000/mcp` the same way as the hosted URL.
**stdio:**
```bash
docker run -i --rm -e MCP_TRANSPORT=stdio ghcr.io/danielgtmn/domain-mcp:latest
```
---
## MCP tools
| Tool | Purpose |
|------|---------|
| `check_domain` | Check one domain (status, registrar, expiry, NS) |
| `check_domains` | Bulk check (parallel, max 50 per call) |
| `domain_info` | Registration-oriented lookup |
| `list_supported_tlds` | TLDs with known RDAP endpoints |
| `clear_domain_cache` | Clear the in-memory TTL cache |
### Status values
| Status | Meaning |
|--------|---------|
| `available` | No RDAP/WHOIS record → likely free |
| `registered` | Registration found |
| `unknown` | Response could not be classified |
| `unsupported` | No RDAP and WHOIS fallback failed |
| `invalid` | Bad domain syntax |
| `error` | Network / rate-limit / server error |
## Supported TLDs
domain-mcp does **not** hardcode a small TLD list:
| Path | Coverage |
|------|----------|
| **RDAP** | ~1,200 TLDs from IANA + overrides (e.g. `.de`) |
| **WHOIS** | Fallback via IANA `whois:` referral for most other TLDs |
Full wiki table (1,200+ TLDs + RDAP URLs):
[docs/Supported-TLDs.md](docs/Supported-TLDs.md) · [Wiki · Supported-TLDs](https://github.com/danielgtmn/domain-mcp/wiki/Supported-TLDs)
Live list via tool `list_supported_tlds`. Regenerate docs:
```bash
uv run python scripts/generate_supported_tlds.py
```
## How it works
```
check_domain("foo.com")
→ normalize (IDN → punycode)
→ RDAP (IANA bootstrap + overrides)
├─ not found → available
├─ found → registered (+ metadata)
└─ no endpoint / error → WHOIS fallback
```
## Documentation
| | |
|--|--|
| In-repo | [`docs/`](docs/) |
| Wiki (published from `docs/`) | https://github.com/danielgtmn/domain-mcp/wiki |
| Page | Topic |
|------|--------|
| [Installation](docs/Installation.md) | Source, Docker, verify |
| [Configuration](docs/Configuration.md) | MCP client wiring |
| [Tools](docs/Tools.md) | API reference |
| [Supported TLDs](docs/Supported-TLDs.md) | Coverage model |
| [Architecture](docs/Architecture.md) | Internals |
| [Docker](docs/Docker.md) | Tags & release pipeline |
| [FAQ](docs/FAQ.md) | Caveats |
Wiki sync runs on every push to `main` that touches `docs/` (workflow `Publish Wiki`). Enable **Wikis** in repo settings and create an initial wiki page once so the wiki remote exists.
## Docker releases
Publishing a **GitHub Release** with tag `vX.Y.Z` builds a multi-arch image and pushes:
```text
ghcr.io/danielgtmn/domain-mcp:X.Y.Z
ghcr.io/danielgtmn/domain-mcp:vX.Y.Z
ghcr.io/danielgtmn/domain-mcp:X.Y
ghcr.io/danielgtmn/domain-mcp:latest # stable releases only
```
Workflow: [`.github/workflows/release-docker.yml`](.github/workflows/release-docker.yml)
## Development
```bash
uv sync --group dev
uv run pytest
uv run ruff check src tests
```
See [CONTRIBUTING.md](CONTRIBUTING.md).
## Project layout
```text
src/domain_mcp/
server.py # FastMCP tools (stdio + HTTP)
checker.py # RDAP + WHOIS orchestration
whois_fallback.py # Minimal WHOIS client
normalize.py # Validation / IDN
models.py # Result types
docs/ # → GitHub Wiki
.github/workflows/ # CI, wiki sync, release Docker
```
## Limitations
- WHOIS text formats vary; classification is best-effort outside RDAP.
- Registries rate-limit; keep bulk checks modest.
- Some ccTLDs expose little public data.
- Availability ≠ guaranteed registration at a given registrar.
## Security
See [SECURITY.md](SECURITY.md).
## License
[MIT](LICENSE) © Daniel Gietmann
TDQS
Scored across 5 tools
The tools are mostly distinct: check_domain and check_domains are clearly single vs bulk variants, while domain_info is differentiated by its stated purpose (metadata for known-registered domains). However, check_domain and domain_info perform essentially the same underlying check, which could cause mild misselection; the description mitigates this but the overlap is real.
The naming follows a consistent verb_noun pattern (check_domain, check_domains, domain_info, list_supported_tlds, clear_domain_cache). Minor deviation: domain_info uses 'info' rather than a clearer verb like 'get_domain_info', breaking the otherwise clean pattern, and clear_domain_cache uses an adjective+noun rather than object-first.
Five tools is within the ideal 3-15 range and appropriate for a domain-checking server. The count feels slightly trimmed—check_domain and domain_info are near-duplicates, suggesting one could be folded into the other—but the overall scope is reasonably well served.
The server covers checking, bulk checking, metadata lookup, TLD discovery, and cache management, covering the core domain-lookup workflows well. Minor gaps exist: there is no tool for WHOIS-style detailed records beyond raw payloads, and no capability to register or manage domains (if that were in scope). For pure lookups, the surface is complete.