Skip to main content
Glama
piersdd

cloudflare-dns-mcp-server

by piersdd
README.md
# cloudflare-dns-mcp-server

A best-of-breed MCP server for Cloudflare DNS management. Designed for extreme token efficiency, full CRUD coverage, and easy deployment as both a local MCP server and a Cloudflare Worker.

## Features

- **10 DNS tools** covering zones, records, bulk operations, and BIND export
- **Token-efficient by default** — concise output strips 60-80% of Cloudflare API bloat
- **Summary mode** — get record counts + type distribution without fetching individual records
- **Random sampling** — audit large zones without loading everything
- **Safety gates** — deletes require explicit `confirm: true`
- **Dual transport** — stdio for Claude Desktop, Streamable HTTP for remote access
- **Worker-ready** — deploy to Cloudflare Workers with `npm run deploy`

## Quick Start

### 1. Create a Cloudflare API Token

Go to [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens) and create a token with:

- **Zone → Zone → Read**
- **Zone → DNS → Edit**
- Scope: All zones (or specific zones you want to manage)

### 2. Install

```bash
git clone https://github.com/piersdd/cloudflare-platform-mcp.git
cd cloudflare-platform-mcp
npm install
npm run build
```

### 3. Configure Claude Desktop

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "cloudflare-dns": {
      "command": "node",
      "args": ["/path/to/cloudflare-platform-mcp/dist/index.js"],
      "env": {
        "CLOUDFLARE_API_TOKEN": "your-api-token-here"
      }
    }
  }
}
```

Restart Claude Desktop. The server validates your token on startup.

## Tools

### Read Tools

| Tool | Description |
|------|-------------|
| `cf_dns_list_zones` | List zones with optional name/status/account filters |
| `cf_dns_get_zone` | Get zone by ID or domain name |
| `cf_dns_list_records` | List records with filters, pagination, summary mode, random sampling |
| `cf_dns_get_record` | Get a single record by ID |
| `cf_dns_export_records` | Export zone as BIND zonefile |

### Write Tools

| Tool | Description |
|------|-------------|
| `cf_dns_create_record` | Create any DNS record type (A, AAAA, CNAME, MX, TXT, SRV, CAA, etc.) |
| `cf_dns_update_record` | Partial update (PATCH) — only changed fields |
| `cf_dns_delete_record` | Delete with mandatory `confirm: true` safety gate |
| `cf_dns_bulk_create` | Create up to 100 records in one call |
| `cf_dns_bulk_update` | Update up to 100 records in one call |

## Token Efficiency

Every tool defaults to concise output. Here's what that means:

**Concise (default)** — only the fields an LLM needs:
```json
{ "id": "abc123", "type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true, "ttl": "auto" }
```

**Full Cloudflare response** (with `include_details=true`) — includes zone_id, zone_name, meta, settings, proxiable, locked, etc. ~5x more tokens.

### Efficiency Modes for `cf_dns_list_records`

| Mode | Tokens | Use Case |
|------|--------|----------|
| `summary_only=true` | ~50 | "How many records? What types?" |
| `random_sample=true` | ~200 | "Show me a few records to check" |
| Filtered + concise | ~500 | "Show me all CNAME records" |
| Full details | ~2500 | "I need all metadata for debugging" |

## Example Prompts

```
"List all my Cloudflare zones"
"Show me the DNS records for example.com"
"How many DNS records does example.com have, broken down by type?"
"Create an A record for app.example.com pointing to 203.0.113.10 with proxy enabled"
"Set up email DNS for example.com with Google Workspace MX records"
"Switch the A record for api.example.com from 10.0.0.1 to 10.0.0.2"
"Delete the old staging CNAME record"
"Export all DNS records for example.com as a BIND file"
"Audit example.com for unproxied A records that might expose our origin IP"
```

## HTTP Transport (Remote Access)

Run as an HTTP server instead of stdio:

```bash
TRANSPORT=http CLOUDFLARE_API_TOKEN=your-token node dist/index.js
```

The server starts on port 8787 (configurable via `PORT` env var). An API key is auto-generated and printed to stderr on first run. Set `CLOUDFLARE_DNS_MCP_API_KEY` to persist it.

**Endpoints:**
- `POST /mcp` — MCP Streamable HTTP (requires `X-API-Key` header)
- `GET /health` — Health check

### Remote MCP Client Config

```json
{
  "mcpServers": {
    "cloudflare-dns": {
      "url": "http://localhost:8787/mcp",
      "headers": {
        "X-API-Key": "your-mcp-api-key"
      }
    }
  }
}
```

## Deploy as a Cloudflare Worker

The same codebase deploys to Cloudflare Workers for always-on remote access:

```bash
# Set secrets
npx wrangler secret put CLOUDFLARE_API_TOKEN
npx wrangler secret put CLOUDFLARE_DNS_MCP_API_KEY

# Deploy
npm run deploy
```

Your MCP server is now at `https://cloudflare-dns-mcp.your-subdomain.workers.dev/mcp`.

### Public Exposure with Custom Domain

For production use behind a custom domain:

1. Add a custom domain in the Cloudflare dashboard for your Worker
2. TLS is automatic via Cloudflare's edge certificates
3. Optionally put it behind Cloudflare Access for SSO authentication

### Bearer Token Authentication

When exposing the server through a tunnel or reverse proxy, set `MCP_API_TOKEN` to require a bearer token on every request:

```bash
export MCP_API_TOKEN=your-secret-token-here
```

All HTTP requests must then include the header:

```
Authorization: Bearer your-secret-token-here
```

Requests without a valid token receive `401 Unauthorized`. If the env var is **unset or empty**, bearer auth is disabled and the server falls back to the existing `X-API-Key` mechanism (suitable for localhost-only access).

### Via Cloudflare Tunnel

For exposing a local instance:

```bash
cloudflared tunnel --url http://localhost:8787
```

## Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `CLOUDFLARE_API_TOKEN` | Yes | — | Cloudflare API token with Zone:Read + DNS:Edit |
| `TRANSPORT` | No | `stdio` | `stdio` or `http` |
| `PORT` | No | `8787` | HTTP server port (http transport only) |
| `MCP_API_TOKEN` | No | (none) | Bearer token for HTTP auth — if set, requires `Authorization: Bearer <token>` |
| `CLOUDFLARE_DNS_MCP_API_KEY` | No | auto-generated | API key for HTTP transport authentication (X-API-Key header) |

## Development

```bash
npm run dev          # stdio mode with auto-reload
npm run dev:http     # HTTP mode with auto-reload
npm run build        # Compile TypeScript
npm run typecheck    # Type-check without emitting
npm run deploy       # Build + deploy to Workers
```

## License

MIT