Skip to content
RedundantDNS
Menu

MCP server

RedundantDNS exposes every dashboard capability as an MCP tool at <public URL>/mcp (for example https://app.redundantdns.com/mcp). It uses the official MCP Go SDK over Streamable HTTP, stateless: each POST is served on its own, with JSON responses, so any instance answers any request and there is no session to keep (GET and DELETE answer 405).

Connecting from Claude

  1. In Claude (claude.ai or Claude Desktop): Settings → Connectors → Add custom connector, URL https://<your host>/mcp.
  2. Claude discovers the authorization server from the 401 challenge (WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource/mcp"), registers itself (dynamic client registration) and opens the consent page.
  3. Sign in, pick the organization and the permissions, approve. Claude receives an access token (1 hour, refreshed automatically).

Clients whose redirect hosts are all in RDNS_OAUTH_TRUSTED_DOMAINS (default claude.ai,claude.com) skip the consent screen after the first approval of the same scopes for the same organization. Any other client always asks.

With a personal access token

Clients that take a static header (Claude Code, scripts, CI) can use a personal access token created in Settings → API tokens:

claude mcp add --transport http redundantdns https://<your host>/mcp \
  --header "Authorization: Bearer rdns_..."

A dashboard session cookie never authorizes /mcp.

Authorization

ScopeAllows
zones:readread zones, records, status, delegation checks, alerts and the audit log
zones:writechange zones, records and attachments, run reconcile/verify/adopt, resolve alerts, test alert channels
connections:readlist provider connections
connections:writecreate and test provider connections

A tool call needs its scope and the user's role in the organization (the same rule as /v1: viewers read, editors change records, admins manage connections, attachments and zone deletion). The organization is the token's (a PAT is bound to one org; an OAuth grant to the org picked at consent). The org and token IP allowlists apply, with the client IP resolved behind trusted proxies (RDNS_TRUSTED_PROXIES).

MCP is a plan feature (Pro and above, see docs/api.md): on other plans every /mcp request answers 402 plan_limit_reached with the plan that unlocks it. Tool calls that hit a plan limit (a zone over the limit, a third provider) return error plan_limit_reached: ... with the details.

Errors come back as tool errors (isError: true) with the API's error code, for example error insufficientScope: the token lacks the connections:read scope or error apexNsManaged: the apex NS set is managed by the platform.

Tools

ToolScopeWhat it does
zone_listzones:readlist zones (attachments, NS plan, status; no records)
zone_getzones:readone zone with records, attachments, capabilities and status
zone_createzones:writecreate a canonical zone; parentDelegation (on by default when a parent zone of the org exists) writes its NS delegation into that parent
zone_deletezones:writedelete the canonical zone; confirmName must repeat the zone name (admins)
record_listzones:readrecord sets of a zone (managedBy: "delegation" marks a child zone's delegation, read-only)
record_upsertzones:writecreate or replace a record set (previousName/previousType rename one) and reconcile
record_deletezones:writedelete a record set and reconcile
provider_connection_listconnections:readprovider connections (credentials never returned)
provider_connection_createconnections:writetest and save a connection: BYO credentials, or mode: managed (admins; acceptManagedTerms: "<version>" accepts the Managed Provider Terms once for the org, otherwise the call fails with managed_terms_required)
provider_connection_testconnections:writere-test a stored connection
zone_attach_providerzones:writeattach a connection (creates or adopts the provider zone) (admins)
zone_detach_providerzones:writedetach; deleteRemote + confirmName deletes the provider zone (admins)
zone_reconcilezones:writepush the canonical zone to the providers now
zone_verifyzones:writecompare providers with the canonical zone now
zone_adopt_changeszones:writeimport one provider's records into the canonical zone
zone_delegation_checkzones:readcheck the delegation (parent NS vs NS plan) now
zone_statuszones:readsync state per attachment and the last delegation check
audit_listzones:readlatest audit events, optionally for one zone
audit_exportzones:readevery audit event of a period (from, to, optional zoneId), oldest first; admins, Business plan and above
alert_listzones:readalert history (drift, sync errors, delegation, providers down or degraded, zones not verified); filters zoneId, rule, state, limit
alert_resolvezones:writeresolve a firing alert by hand (editors); the channels are notified
alert_channel_testzones:writesend a test notification to an alert channel now (admins)
billing_getzones:readthe plan, billing status, limits with usage and the managed pass-through of the period (upgrades happen in the dashboard)

Tool names match ^[a-zA-Z0-9_-]{1,64}$ (Claude rejects the whole list otherwise); internal/mcp tests enforce it and that the catalog matches this table.

Audit

Every tool call is written to the org's audit log as mcp.tool.call (source mcp, target mcpTool/<name>) with a summary of the arguments, the resulting HTTP status and error code, the auth kind (pat or oauth) and the OAuth client id. Values under credential-like keys (credentials, secret, password, token, privateKey...) are never logged: only their field names. The action the tool performed (for example zone.create) is audited as usual, with source mcp.

Design notes

  • A tool does not re-implement the product: after its scope check it runs the matching /v1 request in process as the caller, so validation, role checks, capability intersection and audit are exactly the API's. Adding a /v1 capability means adding its tool in internal/mcp/tools.go.
  • Each POST builds a fresh MCP server bound to the caller (the tool schemas are cached), which is what makes the endpoint stateless.
  • DNS-rebinding protection of the SDK is off: /mcp only accepts bearer tokens, and the check would reject a reverse proxy on the same host.

Try it with curl

curl -s -X POST https://<host>/mcp \
  -H "Authorization: Bearer rdns_..." -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

curl -s -X POST https://<host>/mcp ... \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"zone_create","arguments":{"name":"example.com"}}}'