cloudflare-dns-mcp
README.md
# cloudflare-dns-mcp
An [MCP](https://modelcontextprotocol.io) server that lets an LLM check, claim, release, and
list subdomains under a root domain, backed by the Cloudflare DNS API. Runs over stdio
(local, default) or streamable HTTP (remote, behind a bearer token).
## Tools
- **check_subdomain_availability** — check whether `<subdomain>.<root_domain>` already has a DNS record.
- **request_domain** — claim `<subdomain>.<root_domain>` by creating an A record pointing at an IPv4 address. Fails if the subdomain is already taken.
- **release_subdomain** — delete the DNS record for `<subdomain>.<root_domain>`, freeing it up again.
- **list_subdomains** — list every record under `<root_domain>` currently registered.
## Setup
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
Copy `.env.example` to `.env` and fill in your Cloudflare credentials:
```bash
cp .env.example .env
```
| Variable | Required | Description |
| --- | --- | --- |
| `CLOUDFLARE_API_TOKEN_DNS` | yes | Cloudflare API token with `Zone:DNS:Edit` permission for the target zone. |
| `CLOUDFLARE_ZONE_ID` | yes | Zone ID of the domain in Cloudflare. |
| `CLOUDFLARE_ROOT_DOMAIN` | yes | Root domain that subdomains are created under, e.g. `example.com`. |
| `CLOUDFLARE_API_BASE_URL` | no | Overrides the Cloudflare API base URL. Defaults to `https://api.cloudflare.com/client/v4`. |
## Running
By default the server runs over stdio, meant to be launched by an MCP client rather than
run standalone:
```bash
python main.py
```
To run it as a standalone remote server over streamable HTTP instead, set `MCP_TRANSPORT=http`
plus a shared-secret `MCP_HTTP_TOKEN` (see `.env.example`):
```bash
MCP_TRANSPORT=http MCP_HTTP_HOST=0.0.0.0 MCP_HTTP_PORT=8000 MCP_HTTP_TOKEN=a-long-random-secret python main.py
```
`MCP_HTTP_TOKEN` is required in HTTP mode — the server refuses to start without it, since it
can create and delete DNS records and must not be exposed unauthenticated. Every request needs
`Authorization: Bearer <token>`; requests without it (or with the wrong token) get a 401. Put a
reverse proxy with TLS in front of it if it's reachable from the public internet.
### Claude Code
```bash
claude mcp add cloudflare-dns /path/to/.venv/bin/python /path/to/main.py \
--env CLOUDFLARE_API_TOKEN_DNS=your-token \
--env CLOUDFLARE_ZONE_ID=your-zone-id \
--env CLOUDFLARE_ROOT_DOMAIN=example.com
```
Or add it by hand to `.mcp.json` (project scope) or `~/.claude.json` (user scope):
```json
{
"mcpServers": {
"cloudflare-dns": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/main.py"],
"env": {
"CLOUDFLARE_API_TOKEN_DNS": "your-token",
"CLOUDFLARE_ZONE_ID": "your-zone-id",
"CLOUDFLARE_ROOT_DOMAIN": "example.com"
}
}
}
}
```
If the server is running remotely over HTTP instead:
```bash
claude mcp add --transport http cloudflare-dns https://your-host/mcp \
--header "Authorization: Bearer your-mcp-http-token"
```
or in `.mcp.json`:
```json
{
"mcpServers": {
"cloudflare-dns": {
"type": "http",
"url": "https://your-host/mcp",
"headers": {
"Authorization": "Bearer your-mcp-http-token"
}
}
}
}
```
### opencode
Add to `opencode.json` (project) or `~/.config/opencode/opencode.json` (global):
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"cloudflare-dns": {
"type": "local",
"command": ["/path/to/.venv/bin/python", "/path/to/main.py"],
"enabled": true,
"environment": {
"CLOUDFLARE_API_TOKEN_DNS": "your-token",
"CLOUDFLARE_ZONE_ID": "your-zone-id",
"CLOUDFLARE_ROOT_DOMAIN": "example.com"
}
}
}
}
```
If the server is running remotely over HTTP instead, use `type: "remote"`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"cloudflare-dns": {
"type": "remote",
"url": "https://your-host/mcp",
"enabled": true,
"headers": {
"Authorization": "Bearer your-mcp-http-token"
}
}
}
}
```
### Other MCP clients
Generic stdio client config:
```json
{
"mcpServers": {
"cloudflare-dns": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/main.py"],
"env": {
"CLOUDFLARE_API_TOKEN_DNS": "...",
"CLOUDFLARE_ZONE_ID": "...",
"CLOUDFLARE_ROOT_DOMAIN": "example.com"
}
}
}
}
```
Generic streamable HTTP client config:
```json
{
"mcpServers": {
"cloudflare-dns": {
"type": "http",
"url": "https://your-host/mcp",
"headers": {
"Authorization": "Bearer your-mcp-http-token"
}
}
}
}
```
## Security
Never commit your `.env` file — it contains a Cloudflare API token that can edit DNS records
for your zone. `.gitignore` already excludes it; only `.env.example` (with placeholder values)
is tracked.
If you run the HTTP transport, `MCP_HTTP_TOKEN` is the only thing standing between the
internet and DNS write access to your zone — treat it like the Cloudflare token itself
(long/random, not committed, rotated if it ever leaks), and put it behind TLS.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues