nextdns-mcp
This MCP server lets AI assistants manage NextDNS through grouped CRUD tools.
Manage NextDNS profiles: list, create, get, update/rename, delete.
Read and update per-profile settings for categories including general, privacy, security, parental, performance, logs, and blockpage.
Manage allowlists, denylists, privacy blocklists, native tracking, security TLDs, parental categories, and parental services.
List, add, and delete DNS rewrite entries.
Get recent or raw query logs, clear logs, and download retained logs as CSV.
Query analytics metrics such as status, domains, devices, protocols, query types, IP versions, DNSSEC, encryption, reasons, IPs, and destinations, with time-series support.
Generate PNG line charts from analytics time-series data.
Perform DNS-over-HTTPS lookups using a NextDNS profile.
Provides tools for managing NextDNS profiles, settings, logs, analytics, security, privacy, parental controls, and content lists via the NextDNS API.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nextdns-mcpget my recent DNS query logs"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
NextDNS MCP Server
A Model Context Protocol (MCP) server for the NextDNS API, built with FastMCP and a small set of grouped CRUD tools.
Overview
This project provides an MCP server that exposes NextDNS API operations as tools that can be used by AI assistants and other MCP clients. The server registers a small set of domain-grouped CRUD tools directly; it does not generate tools from an OpenAPI spec.
Related MCP server: Cloudflare Control
Features
Domain-grouped CRUD tools exposing the full NextDNS API surface through ~8 high-level tools
Profile Management: Full CRUD operations - create, read, update, and delete profiles
Profile Access Control: Fine-grained read/write restrictions per profile, with read-only mode support
DNS-over-HTTPS Testing: Perform DoH lookups to test DNS resolution through profiles
Settings Configuration: Comprehensive grouped settings management including logs, block page, and performance
Logs: Query log retrieval, download, and clearing
Analytics: Comprehensive DNS query analytics and statistics, including time-series and plotting
Content Lists: Manage denylist, allowlist, privacy blocklists, native tracking, security TLDs, and parental control
Security: Complete security settings and TLD blocking configuration
Privacy: Privacy settings, blocklists, and native tracking protection management
Parental Control: Settings management with safe search and YouTube restrictions
Reference OpenAPI spec: The bundled nextdns-openapi.yaml documents the NextDNS API surface for validation tooling
Docker Support: Containerized deployment with proper OCI labels
Safety Mechanisms: Write operation protections and validation
Documentation
Complete documentation can be found in docs/index.md.
Quick Start
Prerequisites
Python 3.12+
uv (for development)
Docker (for containerized deployment)
NextDNS API key (get one here)
Configuration
Copy the
.env.examplefile to.env:cp .env.example .envEdit
.envand set your NextDNS API key:NEXTDNS_API_KEY=your_api_key_here NEXTDNS_DEFAULT_PROFILE=your_profile_id # Optional # Optional: Profile access control (see docs/configuration.md). # There is no separate test-profile variable for writes: list a dedicated # sandbox profile in NEXTDNS_WRITABLE_PROFILES, or block writes entirely # with NEXTDNS_READ_ONLY. # NEXTDNS_READABLE_PROFILES=profile1,profile2 # NEXTDNS_WRITABLE_PROFILES=test_profile # NEXTDNS_READ_ONLY=false
Running with Docker
Build the Docker image:
docker build -t nextdns-mcp:latest .Run the container with environment variables:
Option A: Direct environment variables (simple)
docker run -i --rm \ -e NEXTDNS_API_KEY=your_api_key_here \ -e NEXTDNS_DEFAULT_PROFILE=your_profile_id \ nextdns-mcp:latestOption B: Docker secrets (recommended for production)
# Create secret echo "your_api_key_here" | docker secret create nextdns_api_key - # Run with Docker Swarm docker service create \ --name nextdns-mcp \ --secret nextdns_api_key \ -e NEXTDNS_API_KEY_FILE=/run/secrets/nextdns_api_key \ nextdns-mcp:latestOr for non-swarm (using mounted file):
# Create a secret file echo "your_api_key_here" > /tmp/api_key.txt chmod 600 /tmp/api_key.txt # Run with mounted secret docker run -i --rm \ -v /tmp/api_key.txt:/run/secrets/nextdns_api_key:ro \ -e NEXTDNS_API_KEY_FILE=/run/secrets/nextdns_api_key \ nextdns-mcp:latestOption C: Environment file (development)
docker run -i --rm \ --env-file .env \ nextdns-mcp:latestNote: the commands above use stdio (standard input/output), the default transport. For the opt-in streamable-HTTP transport, see HTTP Transport below.
Alpine variant
An Alpine Linux image is also available. To build it locally, use
Dockerfile.alpine:docker build -f Dockerfile.alpine -t nextdns-mcp:alpine .The published Alpine tags use the
-alpinesuffix (e.g.nextdns-mcp:alpine,nextdns-mcp:2.0-alpine). Thepython:3.14-slimimage remains the recommended default.
Running Locally (Development)
Install dependencies:
uv syncRun the server:
uv run python -m nextdns_mcp.server
HTTP Transport (network access)
By default the server uses stdio, which is what MCP clients (Claude Desktop, CLI tools) expect. You can switch to a streamable-HTTP transport for network-based clients, but the HTTP endpoint has no built-in authentication.
Variable | Default | Description |
|
| Set to |
|
| Interface to bind. Loopback-only by default |
|
| Port to listen on |
Because there is no authentication, the default bind is loopback-only (127.0.0.1), so the HTTP endpoint is only reachable from the same host. That keeps it off the network, but on its own it does not stop a browser on that host from being tricked into reaching it (DNS rebinding) — the header validation below is what addresses that.
FastMCP's Host/Origin header validation (FASTMCP_HTTP_HOST_ORIGIN_PROTECTION, which mitigates DNS rebinding against that unauthenticated endpoint) is not enabled by default for application runs, because on a loopback bind it also enforces a Host allowlist and would reject the name your proxy or clients present. Behind a TLS-terminating proxy, enabling it takes three steps: make the server trust that proxy's forwarded headers (the FORWARDED_ALLOW_IPS environment variable, which defaults to 127.0.0.1 and so already covers a same-host proxy — set it to the proxy's address otherwise, and never to *), then set the public host in FASTMCP_HTTP_ALLOWED_HOSTS='["mcp.example.com"]' (a JSON array, not a bare hostname), and only then set FASTMCP_HTTP_HOST_ORIGIN_PROTECTION=auto. Skipping the allowlist answers 421 Misdirected Request; skipping the trusted forwarded headers makes the server compare an http origin against the browser's https Origin and answer 403. See configuration.
In HTTP mode the server also serves GET /health, a readiness check that probes the NextDNS API with the configured key: it returns 200 only when the credentials work, and 503 with a failure class (auth or unreachable) when they do not. See the FAQ.
Binding to a non-loopback interface (explicit opt-in)
To make the endpoint reachable from other hosts you must explicitly set MCP_HOST to a non-loopback address (e.g. 0.0.0.0 or a specific IP). The server logs a prominent SECURITY warning on startup when this happens, because it exposes every tool — and the full reachability of your NextDNS API key — to anyone who can reach the port.
A non-loopback bind is only production-suitable when it is fronted by a reverse proxy (or gateway) that provides authentication and/or TLS, such that unauthenticated remote callers cannot reach the /mcp endpoint. Recommended setup:
Terminate TLS at the reverse proxy.
Require authentication (e.g. a shared secret, OAuth2/OIDC, or IP allow-listing) before forwarding to the MCP server.
Bind the MCP server itself to a private interface or
127.0.0.1behind the proxy where possible.
# Loopback-only (default; see the Host/Origin validation note above):
MCP_TRANSPORT=http uv run python -m nextdns_mcp.server
# Non-loopback bind — MUST be placed behind an authenticating reverse proxy:
MCP_TRANSPORT=http MCP_HOST=0.0.0.0 uv run python -m nextdns_mcp.serverArchitecture
This server uses a modern, declarative approach:
OpenAPI Specification (nextdns-openapi.yaml): Complete NextDNS API documentation
FastMCP Foundation: Server built from grouped CRUD tools registered in
src/nextdns_mcp/tools/; the OpenAPI spec is a reference for validation only (issues #141/#146)HTTP Client: Authenticated
httpx.AsyncClientwith profile-level access control for NextDNS API callsMCP Protocol: Tools, resources, and prompts exposed via Model Context Protocol
Key Components
src/nextdns_mcp/nextdns-openapi.yaml: OpenAPI 3.0 specification for NextDNS API (reference/validation only); a Spectral lint workflow (openapi-lint.yml) enforces explicit per-operation security declarations via spectral.yaml, runnable locally withnpx @stoplight/spectral lint -r spectral.yaml src/nextdns_mcp/nextdns-openapi.yamlsrc/nextdns_mcp/server.py: FastMCP server implementationDockerfile: Container definition with OCI labels for container metadataAGENT.md: Development guidelines and safety rules
Docker Tags
This project publishes official Docker images with a standardized tagging policy. The default image is based on python:3.14-slim; an Alpine Linux variant is also available and tagged with an -alpine suffix.
Primary (python:3.14-slim) tags
:latest: Floating tag that tracks the most recent successful build from themainbranch. This tag is rebuilt on changes tomainand via scheduled rebuilds.:<major>: Floating tag for the most recent build in a given major series (e.g.,:2). This tag is updated whenever a new image for that major line is published and may include unreleased changes if the corresponding build comes from a branch head.:<major>.<minor>: Floating tag for the most recent build in a given minor series (e.g.,:2.0). Like:<major>, it is updated when new images are built for that series and may include unreleased changes.:<major>.<minor>.<patch>: Tags for specific application releases (e.g.,:2.0.3). These are intended to be immutable once published via the release workflow.
Alpine tags
:alpine: Floating tag for the most recent Alpine build frommain.:<major>-alpine: Floating tag for the most recent Alpine build in a major series (e.g.,:2-alpine).:<major>.<minor>-alpine: Floating tag for the most recent Alpine build in a minor series (e.g.,:2.0-alpine).:<major>.<minor>.<patch>-alpine: Specific Alpine release tag (e.g.,:2.0.3-alpine).
All floating tags (:latest, :<major>, :<major>.<minor> and their -alpine counterparts) are rebuilt regularly to include the latest OS security updates and any application changes present in the source commit used for that build. Consumers who require strict version pinning should use the full :<major>.<minor>.<patch> or :<major>.<minor>.<patch>-alpine tags.
License
This project is released under the MIT License.
Contributing
See
AGENT.mdfor guidelines and architectureNote that NextDNS does not provide an OpenAPI specification. This is based on their documentation and may not reflect the current state of the API.
Available Tools
8 toolsdohLookupDohlookupC
Perform a DNS-over-HTTPS lookup using a NextDNS profile.
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | The domain name to look up (e.g., "adwords.google.com") | |
| profile_id | No | NextDNS profile ID. If not provided, uses NEXTDNS_DEFAULT_PROFILE. | |
| record_type | No | DNS record type to query (default "A"). | A |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. 'Lookup' implies a non-mutating read, which is useful, but nothing is said about authentication requirements, rate limits, whether the profile must pre-exist, or how the default-profile fallback behaves at runtime.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no waste. It is efficient, though the brevity edges toward under-specification rather than optimal conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, so return-value explanation is not required, and the three parameters are fully covered by the schema. However, with no annotations and no usage or behavioral context, the definition is only minimally sufficient for an agent to invoke this confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents domain, profile_id (including the NEXTDNS_DEFAULT_PROFILE fallback), and record_type. The description adds no parameter meaning beyond what the schema provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (lookup) and resource (DNS-over-HTTPS via a NextDNS profile), which an agent can distinguish from the manage*/analytics siblings. It stops short of saying anything that would differentiate it from a hypothetical non-DoH DNS lookup, but the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no indication of when to use this tool versus the sibling tools (queryAnalytics, manageLogs) or when a DoH lookup is the right approach. There are no prerequisites or exclusion conditions stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manageListsManagelistsA
Manage allow/deny/block lists for a NextDNS profile.
List types:
- allowlist / denylist: Always-allow or always-block specific domains.
- privacy_blocklists: Subscribed blocklists (e.g., nextdns-recommended).
- privacy_natives: Native tracking blockers (e.g., apple, facebook).
- security_tlds: Dangerous top-level domains to block (e.g., zip).
- parental_categories: Content categories (e.g., gambling, porn).
- parental_services: Specific apps/services (e.g., tiktok, youtube).
Operations:
- get: Return the current list.
- add: Append one entry (pass entry as {"id": "value"} or as a plain id string).
The entry must carry a string id; an entry without one is rejected locally with
invalid_argument rather than sent upstream.
- remove: Delete one entry by entry_id.
- update: Toggle an existing entry by entry_id (pass entry={"active": True|False}).
Only supported for allowlist, denylist, parental_categories, and
parental_services.
- replace: Replace the entire list with entries (list of dicts). Each entry must be
an object with a string id; a malformed entry is rejected locally with
invalid_argument naming its index, rather than sent upstream.
Examples:
- get: manageLists(list_type="denylist", operation="get", profile_id="abc123")
- add: manageLists(list_type="denylist", operation="add", profile_id="abc123", entry={"id": "example.com"})
- remove: manageLists(list_type="denylist", operation="remove", profile_id="abc123", entry_id="example.com")
- replace: manageLists(list_type="privacy_blocklists", operation="replace", profile_id="abc123", entries=[{"id": "nextdns-recommended"}])
- add parental category: manageLists(list_type="parental_categories", operation="add", profile_id="abc123", entry={"id": "gambling"})
- update parental service: manageLists(list_type="parental_services", operation="update", profile_id="abc123", entry_id="tiktok", entry={"active": False})
| Name | Required | Description | Default |
|---|---|---|---|
| entry | No | ||
| entries | No | ||
| entry_id | No | ||
| list_type | Yes | ||
| operation | Yes | ||
| profile_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses local validation behavior (entries lacking a string 'id' rejected with invalid_argument, named by index for replace), the destructive nature of 'replace' overwriting the entire list, and the list-type restriction on 'update'. It omits permission/auth requirements and any rate-limit or mutation-side-effect notes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose, then cleanly sectioned into list types, operations, and examples, so it scans well. The six examples are largely a restatement of the operations section and add length, though each is individually compact and useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, multi-operation tool with 0% schema coverage and no annotations, the description supplies everything needed to call it correctly: valid list types, per-operation argument shapes, validation failure modes, and an output schema covers return values. No meaningful gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: 'entry' is documented as either {"id": "value"} or a plain id string, 'entries' as a list of dicts, 'entry_id' as the remove/update key, plus the accepted values for list_type and operation. The worked examples pin down exact call shapes for each operation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a specific verb and resource ('Manage allow/deny/block lists for a NextDNS profile') and then enumerates the exact list types and operations, which cleanly separates it from siblings like manageSettings, manageProfiles, and manageRewrites. An agent can identify what this tool covers without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each operation is defined with its intended use ('get' returns, 'add' appends, 'remove' deletes, 'replace' overwrites the entire list), and a constraint is given for 'update' (only allowlist/denylist/parental_categories/parental_services). There is no explicit comparison against alternative tools for overlapping tasks, which keeps it short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manageLogsManagelogsA
Manage query logs for a NextDNS profile.
Operations:
- get: Return recent query log entries (use limit to cap results).
Set raw=true to bypass deduplication/noise filtering.
- clear: Delete all stored logs for the profile.
- download: Download retained logs as CSV. from_time and to_time are
ignored by the NextDNS download endpoint. The CSV is streamed to a
temporary file; only the file path, size, row count, and a small
capped preview are returned (the full text is never inlined).
A hard total-size cap bounds how much is streamed to disk (the
NEXTDNS_DOWNLOAD_MAX_BYTES environment variable, default 1 GiB);
a download that would exceed it is aborted mid-stream and the
partial file is removed. On success the temp
file is not cleaned up: the CSV stays in the OS temp directory
(mode 0600) and the returned path is the only record of it, so
delete it when it is no longer needed. Failed downloads do remove
the temp file and its parent directory.
Time values can be Unix timestamps or relative strings like -1d or -7d.
They are only used by get.
limit is clamped server-side to the range 1-1000 entries for get
(the range accepted by the NextDNS API): values above 1000 are reduced to
1000 and non-positive values to 1. The clamp is silent, so ask for at
most 1000.
Examples:
- get recent: manageLogs(operation="get", profile_id="abc123", limit=10)
- get raw logs: manageLogs(operation="get", profile_id="abc123", raw=true)
- download: manageLogs(operation="download", profile_id="abc123", from_time="-1d")
- clear: manageLogs(operation="clear", profile_id="abc123")
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | ||
| user | No | ||
| limit | No | ||
| device | No | ||
| to_time | No | ||
| from_time | No | ||
| operation | Yes | ||
| profile_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does so thoroughly: clear is destructive (deletes all stored logs), download is bounded by NEXTDNS_DOWNLOAD_MAX_BYTES, aborts mid-stream and deletes the partial file on overflow, and on success leaves a mode-0600 temp file that the caller must delete. It also discloses that only a path/size/row-count/preview is returned and that limit clamping is silent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and a bulleted operation list, then examples, so it is easy to scan. It is verbose in places (the temp-file lifecycle is dwelt on at length), but each sentence conveys actionable information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter, unannotated, multi-mode tool this is well covered: operation modes, clamping, time semantics, destructive behavior, and side effects of download are all spelled out, and an output schema exists so return values need no further explanation. Only the user/device filters remain unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
At 0% schema description coverage the description must compensate, and it does for most parameters: it explains the three operation values, the 1-1000 silent clamp on limit, raw=true semantics, accepted time formats (Unix or '-1d'), and that time values apply only to get. The user and device parameters are never mentioned, leaving two of eight undocumented in both schema and description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first line names a concrete verb+resource (manage query logs for a NextDNS profile) and breaks the tool into three explicitly enumerated operations (get/clear/download), so an agent knows exactly what it does. It does not, however, distinguish itself from siblings like queryAnalytics or plotAnalytics, which also deal with log/analytics data, so the boundary is left implicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives per-operation usage: 'use limit to cap results', 'set raw=true to bypass deduplication/noise filtering', notes that from_time/to_time are ignored by the download endpoint and only used by get, and provides four concrete call examples. It lacks guidance on when to prefer this tool over the analytics siblings, so it stops short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manageProfilesManageprofilesA
Manage NextDNS profiles.
NextDNS profiles are named configurations that contain DNS settings, blocklists,
analytics, and logs. Most other tools require a profile_id from this tool.
Operations:
- list: Return profiles the API key can access (supports cursor for pagination).
- create: Create a new profile (requires name).
- get: Retrieve a single profile (requires profile_id).
- update: Rename a profile (requires profile_id and name).
- delete: Remove a profile (requires profile_id).
Examples:
- list: manageProfiles(operation="list")
- list (page): manageProfiles(operation="list", cursor="j2k3zl3b4v")
- create: manageProfiles(operation="create", name="Home Network")
- get: manageProfiles(operation="get", profile_id="abc123")
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| cursor | No | ||
| operation | Yes | ||
| profile_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It does disclose useful traits beyond the schema, such as cursor-based pagination for list and that delete removes a profile, but it omits permission requirements, irreversibility of delete, and rate-limit behavior, leaving meaningful gaps for a mutation-capable tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The structure is excellent: a one-line purpose, an operations list with per-op requirements, and concrete call examples. It is slightly verbose since the examples partly restate the operations list, but every block adds routing value for a multi-operation tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and the description covers all operations, their parameters, pagination, and upstream dependency. It is nearly complete; only safety/permission context for destructive operations is missing, which is a modest gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it largely does: it maps each operation to its required parameters (create→name, get/delete→profile_id, update→profile_id+name) and explains cursor's pagination role. All four parameters are given meaning, though name/cursor formats remain unspecified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource (NextDNS profiles) and enumerates the five distinct operations it performs (list/create/get/update/delete). It also distinguishes itself from siblings by noting that most other tools require a profile_id obtained from this tool, so an agent can place it in the workflow without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly routes usage: most other tools depend on a profile_id sourced here, and each operation lists its required inputs. There is no explicit when-not-to-use guidance or exclusion of sibling tools, so it falls short of a full 5 despite the strong context provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manageRewritesManagerewritesA
Manage DNS rewrite entries for a NextDNS profile.
Rewrites let you return a custom answer for a hostname. Typical uses:
Point an internal hostname to a private IP.
Block a domain by rewriting it to
0.0.0.0.
Operations:
- list: Show existing rewrites.
- add: Create a rewrite (requires name and content).
- delete: Remove a rewrite (requires entry_id from list).
Examples:
- list: manageRewrites(operation="list", profile_id="abc123")
- add: manageRewrites(operation="add", profile_id="abc123", name="router.home", content="192.168.1.1")
- delete: manageRewrites(operation="delete", profile_id="abc123", entry_id="<id-from-list>")
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| content | No | ||
| entry_id | No | ||
| operation | Yes | ||
| profile_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry behavioral burden. It discloses that add mutates by creating an entry and delete removes one, and that entry_id must be sourced from list (an ordering dependency), which is useful. However it says nothing about authorization needs, idempotency, whether deletes are reversible, or side effects on DNS resolution beyond the illustrative examples.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded one-line purpose, then a short usage rationale, then an operation list, then examples. Each block earns its place and nothing is redundant; examples directly reinforce the required-parameter rules.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the description covers all three operations with required inputs. The omission of any note on profile_id semantics or failure modes keeps it from being fully complete for a mutation-capable multi-operation tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: it maps name/content to add and entry_id to delete, and the operation enum values are each explained with an example. The only gap is profile_id, which is required but never described beyond appearing in examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Manage DNS rewrite entries for a NextDNS profile') with clear scope, and the sibling set (manageSettings, manageProfiles, manageLists) makes it obvious this handles rewrites specifically. The enumeration of list/add/delete operations further pins down what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit per-operation guidance, including which parameters each operation requires (add needs name/content, delete needs entry_id from list) and concrete call examples. It stops short of when-not-to-use guidance or steering to a sibling for adjacent tasks, so it lands at 4 rather than 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manageSettingsManagesettingsA
Manage a settings category for a NextDNS profile.
Settings categories:
- general: Core profile settings (e.g., name, web3 blocking).
- privacy: Privacy features such as disguised-tracker blocking and affiliate links.
- security: Threat intelligence, Google Safe Browsing, typosquatting protection.
- parental: Parental control enablement (safe search, YouTube restricted mode).
- performance: ECS, cache boost, and other performance options.
- logs: Query-logging enablement and retention.
- blockpage: Whether to show a custom block page for blocked queries.
Operations:
- get: Retrieve current settings for the category.
- update: Apply new settings (requires settings payload).
The settings argument can be a Python dict or a JSON string. For update,
first call get to see the current schema, then send only the fields you want
to change.
Examples:
- get: manageSettings(operation="get", category="privacy", profile_id="abc123")
- update: manageSettings(operation="update", category="privacy", profile_id="abc123", settings={"disguisedTrackers": True})
| Name | Required | Description | Default |
|---|---|---|---|
| category | Yes | ||
| settings | No | ||
| operation | Yes | ||
| profile_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that update requires a settings payload, that settings can be a dict or JSON string, and that you should retrieve first to see the schema. It doesn't describe permissions, reversibility, or rate limits, but for a settings-management tool, the get-before-update guidance and payload format are substantial behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear purpose statement, a bulleted category list, a bulleted operation list, and concrete examples. Every sentence earns its place; the formatting makes it easy to scan for the relevant category or operation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (4 parameters, 2 enums, no annotations, but an output schema exists), the description is complete enough. It covers all categories, both operations, the settings payload format, and the recommended workflow. The output schema handles return values, so the description doesn't need to explain them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It does: it enumerates all category values with meanings, lists the two operations with their effects, explains the settings argument format (dict or JSON string), and provides examples showing exact parameter usage. This fully compensates for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource: managing a settings category within a NextDNS profile. It enumerates all categories and operations, so the agent understands the exact scope. It doesn't explicitly distinguish from siblings like manageProfiles or manageLogs, though the category list implicitly covers some overlap.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear when-to-use guidance: 'first call get to see the current schema, then send only the fields you want to change.' It explains the get-then-update workflow, which is a strong usage guideline. It doesn't state when not to use this tool or name alternatives, but the workflow guidance is explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plotAnalyticsPlotanalyticsA
Generate a PNG line chart for a NextDNS analytics time-series metric.
Use this to visualize query trends over time. The profile should have recent query history; otherwise the tool returns an error explaining that no data is available.
Supported metrics: status, devices, protocols, queryTypes,
ipVersions, dnssec, encryption, reasons, ips.
Time values can be Unix timestamps or relative strings like -1d.
The ;series endpoint takes no limit parameter: limit is part of
this tool's published surface, is rejected above 500 rather than clamped,
and is never forwarded, so the requested range is the only thing that bounds
the response. Three server-side limits are enforced before the request is
built, and each is rejected with an invalid_argument error naming the
limit rather than silently adjusted:
intervalmust be between 60 and 86400 seconds.limitmust not exceed 500.The range must be interpretable (a Unix timestamp, an ISO 8601 timestamp,
now, or a relative offset such as-7d; a relative unit letter is read case-sensitively and a timestamp with no timezone is read as UTC), must end after it starts, and spans at most 2000 intervals (at most 2000 points per series), so a wide range needs a proportionally largerinterval.
Examples:
- plotAnalytics(metric="status", profile_id="abc123", from_time="-1d")
- plotAnalytics(metric="devices", profile_id="abc123", from_time="-7d", interval=86400)
- a year of data: plotAnalytics(metric="status", profile_id="abc123", from_time="-1y", interval=43200)
Returns: An MCP ImageContent PNG chart, or an error dict if data is unavailable.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| metric | Yes | ||
| to_time | No | now | |
| interval | No | ||
| partials | No | none | |
| timezone | No | GMT | |
| alignment | No | end | |
| from_time | No | -1d | |
| profile_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses the failure mode (error when no data), the return type (ImageContent PNG or error dict), and the exact server-side enforcement behavior — limits rejected ('invalid_argument' naming the limit) rather than silently clamped, plus the 60–86400s interval and 2000-interval span caps. This is behavior an agent genuinely needs and could not infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and metric list are front-loaded, and the bulleted limits plus concrete examples make the dense content scannable. The middle paragraph about the ';series' endpoint and the limit being 'never forwarded' is somewhat convoluted and could be tightened, but the overall structure earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the description names the return form (PNG chart or error dict), and with no annotations it supplies the safety/behavioral context itself. Only the unexplained secondary parameters keep it from being fully complete for a 9-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 9 parameters, so the description must compensate, and it does for several: metric values, from_time/to_time timestamp and relative-offset formats, the interval range, and the limit ceiling. However, partials, alignment, and the timezone parameter are never explained, leaving a meaningful share of the surface undocumented in either place.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a specific verb and resource: 'Generate a PNG line chart for a NextDNS analytics time-series metric,' immediately separating it from a data-returning sibling like queryAnalytics. The follow-up 'visualize query trends over time' pins the distinct job down without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use it ('visualize query trends over time') and gives a real precondition (the profile needs recent history or it errors). It stops short of naming an alternative tool or an explicit when-not-to-use condition, so it is clear context rather than full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryAnalyticsQueryanalyticsA
Query NextDNS analytics metrics.
Metrics:
- status: Query resolution status (default, blocked, allowed, relayed).
- domains: Top domains; supports the status and root filters.
- devices: Queries per device.
- protocols: DNS transport protocol (DoH, DoT, Do53 UDP/TCP, DoQ).
- queryTypes: DNS record types requested (A, AAAA, CNAME, etc.).
- ipVersions: IPv4 vs IPv6 queries.
- dnssec: DNSSEC validation results.
- encryption: Encrypted vs unencrypted queries.
- reasons: Why queries were blocked or allowed.
- ips: Top source IPs.
- destinations: Top destinations; requires destination_type such as
countries or gafam.
Set series=true to fetch time-series data instead of aggregate totals.
Time values can be Unix timestamps or relative strings like -1d.
Note: series=true is not supported when metric="domains".
limit is clamped server-side to the range 1-500 (the range accepted by
the NextDNS API): values above 500 are reduced to 500 and non-positive
values to 1. The clamp is silent, so ask for at most 500.
Optional filters:
- cursor: Pagination cursor from a previous response.
- device: Filter analytics to a single device id.
- status: For the domains metric, filter by resolution status.
- root: For the domains metric, group results by root domain (boolean).
Examples:
- totals: queryAnalytics(metric="status", profile_id="abc123", from_time="-1d")
- time series: queryAnalytics(metric="status", profile_id="abc123", from_time="-1d", series=true)
- destinations: queryAnalytics(metric="destinations", profile_id="abc123", from_time="-1d", destination_type="countries")
| Name | Required | Description | Default |
|---|---|---|---|
| root | No | ||
| limit | No | ||
| cursor | No | ||
| device | No | ||
| metric | Yes | ||
| series | No | ||
| status | No | ||
| to_time | No | ||
| interval | No | ||
| partials | No | ||
| timezone | No | ||
| alignment | No | ||
| from_time | No | ||
| profile_id | Yes | ||
| destination_type | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and adds substantial detail: silent server-side limit clamping to 1-500, the series/domains incompatibility, cursor-based pagination, destination_type requirement, and time input formats. It still does not state auth requirements, error behavior, or explicitly that the operation is read-only, so it is not fully transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and organized into metrics, notes, filters, and examples. It is fairly long, but the length is justified by the 15-parameter surface and the need to document metric semantics. Slight tightening is possible, but there is little wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the high parameter count, 0% schema coverage, no annotations, and an available output schema, the description is mostly complete. It covers the core metrics, filters, and examples needed for basic calls, but the four undocumented optional parameters and the lack of sibling-tool routing leave gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains metric values, series, limit clamping, cursor, device, status, root, destination_type, and from_time/to_time formats. However, interval, partials, timezone, and alignment are never described, leaving four parameters undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb and resource: 'Query NextDNS analytics metrics.' It then enumerates the metrics it can return, making the tool's scope concrete. It does not explicitly distinguish itself from sibling tools such as plotAnalytics, so it falls short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: aggregate totals by default, series=true for time-series, destinations requiring destination_type, and examples for common calls. It also notes that series=true is unsupported for metric='domains'. It does not explain when to choose this tool over plotAnalytics or manageLogs, so no sibling routing is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v3.2.0- Changed
manageProfiles1 field changed- added
Input schema / properties / cursorAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null +}
2 tool updates
v3.1.5- Added
dohLookup - Added
manageSettings
3 tool updates
v3.1.3- Removed
dohLookup - Added
manageLists - Added
manageRewrites
3 tool updates
v3.1.2- Removed
manageLists - Removed
manageRewrites - Removed
manageSettings
82 tool updates
v3.0.1- Removed
addPrivacyBlocklist - Removed
addPrivacyNative - Removed
addSecurityTLD - Removed
addToAllowlist - Removed
addToDenylist - Removed
addToParentalControlCategories - Removed
addToParentalControlServices - Removed
clearLogs - Removed
createProfile - Removed
deleteProfile - Changed
dohLookup2 fields changed- changed
Input schema / properties / profile_id / descriptionPrevious value: -"NextDNS profile ID (6-character alphanumeric). If not provided, uses NEXTDNS_DEFAULT_PROFILE"New value: +"NextDNS profile ID. If not provided, uses NEXTDNS_DEFAULT_PROFILE." - changed
Input schema / properties / record_type / descriptionPrevious value: -"DNS record type to query. Common types:\n- A: IPv4 address (default)\n- AAAA: IPv6 address\n- CNAME: Canonical name\n- MX: Mail exchange\n- TXT: Text records\n- NS: Name servers\n- SOA: Start of authority\n- PTR: Pointer record"New value: +"DNS record type to query (default \"A\")."
- Removed
getAllowlist - Removed
getAnalyticsDestinations - Removed
getAnalyticsDestinationsSeries - Removed
getAnalyticsDevices - Removed
getAnalyticsDevicesSeries - Removed
getAnalyticsDNSSEC - Removed
getAnalyticsDNSSECSeries - Removed
getAnalyticsDomains - Removed
getAnalyticsEncryption - Removed
getAnalyticsEncryptionSeries - Removed
getAnalyticsIPs - Removed
getAnalyticsIPsSeries - Removed
getAnalyticsIPVersions - Removed
getAnalyticsIPVersionsSeries - Removed
getAnalyticsProtocols - Removed
getAnalyticsProtocolsSeries - Removed
getAnalyticsQueryTypes - Removed
getAnalyticsQueryTypesSeries - Removed
getAnalyticsReasons - Removed
getAnalyticsReasonsSeries - Removed
getAnalyticsStatus - Removed
getAnalyticsStatusSeries - Removed
getBlockPageSettings - Removed
getDenylist - Removed
getLogs - Removed
getLogsSettings - Removed
getParentalControlCategories - Removed
getParentalControlServices - Removed
getParentalControlSettings - Removed
getPerformanceSettings - Removed
getPrivacyBlocklists - Removed
getPrivacyNatives - Removed
getPrivacySettings - Removed
getProfile - Removed
getSecuritySettings - Removed
getSecurityTLDs - Removed
getSettings - Removed
listProfiles - Added
manageLists - Added
manageLogs - Added
manageProfiles - Added
manageRewrites - Added
manageSettings - Added
plotAnalytics - Added
queryAnalytics - Removed
removeFromAllowlist - Removed
removeFromDenylist - Removed
removeFromParentalControlCategories - Removed
removeFromParentalControlServices - Removed
removePrivacyBlocklist - Removed
removePrivacyNative - Removed
removeSecurityTLD - Removed
replaceAllowlist - Removed
replaceDenylist - Removed
replaceParentalControlCategories - Removed
replaceParentalControlServices - Removed
replacePrivacyBlocklists - Removed
replacePrivacyNatives - Removed
replaceSecurityTLDs - Removed
updateAllowlistEntry - Removed
updateBlockPageSettings - Removed
updateDenylistEntry - Removed
updateLogsSettings - Removed
updateParentalControlCategoryEntry - Removed
updateParentalControlServiceEntry - Removed
updateParentalControlSettings - Removed
updatePerformanceSettings - Removed
updatePrivacySettings - Removed
updateProfile - Removed
updateSecuritySettings - Removed
updateSettings
TDQS
Scored across 8 tools
Most tools target clearly distinct resources: profiles, settings, lists, rewrites, logs, and DNS lookup. The main ambiguity is between queryAnalytics and plotAnalytics, which both cover analytics metrics but return data versus a chart, so an agent could occasionally misselect.
The dominant pattern is camelCase verb_noun (manageSettings, manageProfiles, manageLists, manageRewrites, manageLogs, queryAnalytics, plotAnalytics). dohLookup is the one mild deviation because it starts with an acronym/noun rather than a verb, but the overall convention is still predictable.
Eight tools is well-scoped for a NextDNS management server. Each tool groups a coherent operation family, and none appear redundant or gratuitous.
The surface covers profile CRUD, per-category settings get/update, allow/deny/blocklist management, rewrites, DoH lookup, log retrieval/clear/download, and analytics querying/plotting. This provides full lifecycle coverage for the domain, with no obvious dead ends for typical NextDNS administration.
Maintenance
Related MCP Connectors
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Manage hosts, redirects, SSL, and traffic analytics from Claude and other AI assistants.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables AI assistants to interact with Fastly's CDN API through the Model Context Protocol, allowing secure management of CDN services, caching, security settings, and performance monitoring without exposing API keys.218 npm7MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Cloudflare infrastructure including DNS records, cache purging, SSL settings, Workers, and analytics through the Cloudflare API. Eliminates dashboard context-switching by allowing natural language control of domain management and infrastructure operations.-
- FlicenseNot gradedqualityAmaintenanceReal-time DNS security analysis for AI assistants via MCP. Enables DNSSEC chain validation, email authentication posture, and registration intelligence directly from chat sessions.1-
- AlicenseNot gradedqualityDmaintenanceEnables natural language management of Cloudflare services including Workers, KV, R2, D1, Queues, and more via the Model Context Protocol.2,408 npmApache 2.0