Built for AI agents

Ask your network.
Approve what changes.

TelltaleDNS treats AI assistants as first-class clients: every node speaks the Model Context Protocol, every API answer is written to be read by a model, and every change an agent wants waits as a plan you can see, approve, or throw away.

An agent asks questions through MCP; changes become plans, a person approves, and everything is audited AI agentClaude, scripts, … MCP any node/mcp · REST · scopes answersread-only wants a change plan + previewbefore · after · impact you approveor reject, in the UI applied on every node · audited always on scoped tokens rate limits privacy level kill switch

Connectivity

Ways to connect

Pick whatever your agent speaks. They all go through the same API, so the same permissions, limits, and audit apply everywhere.

MCP over HTTP

Every node serves the Model Context Protocol at /mcp (Streamable HTTP). Point an MCP client at any node: in a cluster, each one is an entry point to the whole system.

MCP over stdio

For desktop assistants that start their tools as a subprocess: telltale mcp --stdio relays to a node with your agent token.

Sign in with SSO

Instead of pasting a token, an MCP client can sign you in through your OIDC provider (MCP's OAuth 2.1 flow). You choose which scopes the agent gets; it never goes above your own role.

REST and OpenAPI

The whole API is described in OpenAPI, written for models: units in field names (latencyMs), examples, and errors that say how to fix the request. Every change can be tried with ?dryRun=true.

The shell

telltale ctl gives scripts and terminal agents the API as commands: status, the query log, top lists, explain, quick rules, pause, cache, and approving plans.

Events out

A live query stream (server-sent events), alerts to webhooks, ntfy, Gotify, or Slack-compatible channels, and query events to files, syslog, webhooks, or OpenTelemetry, for agents that react instead of ask.

Two minutes

Connect an assistant

  1. Make an agent token

    In Settings → API tokens → for an AI agent, or through the API from a signed-in session (the session cookie and X-CSRF-Token: a token can't create tokens). The default is read-only and leaves out the query log; add scopes only for what the agent should do.

    POST /api/v1/tokens
    {"name": "assistant", "kind": "agent",
     "scopes": ["analytics:read", "config:read", "querylog:read"]}
  2. Add TelltaleDNS to your MCP client

    Over HTTP, use http://<node>:8053/mcp with Authorization: Bearer tt_…. For clients that start a subprocess:

    {"mcpServers": {"telltale": {
      "command": "telltale",
      "args": ["mcp", "--stdio", "--url", "http://192.168.1.2:8053"],
      "env": {"TELLTALE_TOKEN": "tt_…"}}}}
  3. Ask

    "Why is the TV slow tonight?" "What did the kids' tablets get blocked on this week?" The assistant picks the tools; you see every change before it happens.

How interactions work

Example conversations

Illustrative transcripts. The tool names and the shape of the answers are the real ones; the numbers are made up.

"Why is the living-room TV slow tonight?"

Why is the living-room TV slow tonight?

get_client_profile client="living-room-tv", window="-6h"

p95 latency 840 ms (usually about 30) · 92% of its uncached questions go to quad9-doh

upstream_health

quad9-doh: circuit breaker opening and closing since 19:40 · cloudflare-dot: healthy, p95 18 ms

plan_update_upstreams kind="upstream_group", name="default", strategy="fastest", reason="quad9 degraded"

Plan pl_7f3c: upstream group default, strategy failover → fastest · affects about 41,000 questions a day · waiting for approval

Quad9's DoH endpoint has been flapping since 19:40, and the TV sends almost everything there. I've proposed switching the default group to the fastest healthy upstream. It's waiting for you under Agent changes.

"What did the kids' tablets get blocked on this week?"

What did the kids' tablets get blocked on this week, and when?

vqlog from -7d | where group = kids and status = blocked | top 5 domain

tiktokv.com 1,912 · doubleclick.net 806 · roblox-ads.example 377 · app-measurement.com 301 · unityads.unity3d.com 244

vqlog from -7d | where group = kids and name under tiktokv.com | bucket 1d | stats count

Mon 12 · Tue 9 · Wed 301 · Thu 655 · Fri 588 · Sat 214 · Sun 133

Mostly TikTok and ad networks. The TikTok attempts jumped on Wednesday, the day the new tablet joined. Everything was blocked, as your schedule says. Want me to look at that tablet's other new domains?

"Block whatever the new camera keeps calling home to."

The new camera in the garage keeps phoning home. Block that.

new_domains client="192.168.1.57"

cam-telemetry.vendor.example (DGA score 0.12) · p2p-relay.vendor.example (0.18) · firmware.vendor.example (0.10)

explain_decision name="p2p-relay.vendor.example", client="192.168.1.57"

outcome: allowed · no list matches · forwarded to cloudflare-dot

plan_block_domain domain="cam-telemetry.vendor.example", devices=["garage-camera"], reason="camera telemetry"

Plan pl_91aa: blocks it for the garage camera only · 2,880 questions in the last day would have been blocked · waiting for approval

I planned a block on the telemetry domain for the camera only. I left the firmware server alone, so it still gets updates. Approve it under Agent changes, and I'll check tomorrow that the camera still works.

The catalog

What an agent can do

Ask (read-only)

healthhealthy, degraded, or severe, and every reason with its node
shadow_lists, overblocking_suspectswhat a list would block before it does; blocked names someone seems to want
upstream_checkswhether upstreams filter, fail DNSSEC, or answer differently from a second opinion
probe_statuswhether every listener answers its own probe, and when certificates expire
list_annotationswhat changed and when, on every node: configuration versions and who made them, restarts, list refreshes, rollouts, pauses, and notes (get_overview includes the latest three)
cache_advicewhether each node's cache would gain from being bigger, or could be smaller
slo_statusthe service-level objectives: error budget left and how fast it's going
get_overviewqueries, blocked %, cache hits, clients, latency for a window
top_itemstop domains, blocked names, NXDOMAIN, clients
search_queriesthe query log, filtered and paged
explain_decisionwhy a name was blocked or routed, for a device
simulate_changewhat a change would have done to the logged queries: newly blocked, newly allowed, rerouted, by name and device
get_client_profilea device: identity, groups, recent and slow queries
latency_breakdownpercentiles by stage, upstream, client, or type
upstream_healthhealth, circuit breakers, latency per upstream
list_effectivenesslist hits, unique names, overlap
find_anomaliesrate spikes, beaconing, NXDOMAIN storms, DGA-like names, from every node, optionally only the unacknowledged ones
new_domainsfirst-seen domains per device
vqlogany count, top list, percentile, or time series (below)
cluster_statusmembers, roles, sync, versions, health
rollout_statusthe change baking on the canary nodes, the guard's readings, the pin, and the versions kept
get_configa configuration section, without secrets

Change (as plans)

plan_block_domain, plan_allow_domaina quick rule, for everyone, devices, or groups, optionally for a while
plan_rename_client, plan_assign_clientname devices and put them in groups
plan_set_local_name, plan_remove_local_nameanswer local names with your own records
plan_forward_domainsend a domain to other DNS servers
plan_add_list, plan_update_groupfilter lists and groups
plan_set_scheduleweekly schedules: a bedtime, extra lists, blocked services
plan_update_upstreamsupstream servers and strategies
plan_set_ratelimitthe per-client rate limit: queries per window, exempt addresses, how clients are grouped
plan_pin_version, plan_unpinpin the cluster to a kept version, or unpin it (only where a person approves each plan)
apply_plan, discard_plan, list_plansmake, drop, or review planned changes

Every plan tool takes simulate ("24h", up to "7d"): the plan then shows what the change would have done to those queries. Plans only simulate when asked, unless an operator turns on simulating by default in Settings; no agent tool can turn that on.

Act at once (low risk, audited)

pause_blocking (1 to 60 minutes), resume_blocking, flush_cache, acknowledge_anomalies (marks findings as seen on every node, or takes it back), start_maintenance (up to 2 hours: a node steps out of the load balancer, keeps answering, and its alerts pause) and end_maintenance, and annotate (a note on every time chart, to say why you changed something). And two checks that change nothing: check_upstream and check_list try a server or a list from the node before a plan adds it.

Resources and prompts

Resources an assistant can attach: telltale://cluster/status, telltale://config (no secrets), telltale://reports/daily. Ready-made prompts: investigate_device, weekly_network_report, tune_blocklists, upstream_health_review.

The catalog is committed in the repository (mcp-tools.json) and checked in CI with the official MCP SDK, so what you read here is what the server offers.

One tool for analytics

vqlog: questions as one line

Instead of dozens of narrow tools, agents (and you) can ask the query log anything in a small pipe language: filter, group, top lists, percentiles, and time series. It's parsed into a fixed plan, so it can't run code or SQL, and every answer says what it cost to compute. Counts over windows longer than the query log keeps come from the long-term rollups, and the answer says which it used.

from -24h | where status = blocked and group = kids | top 10 name
from -7d  | where client = 192.168.1.20 | bucket 1h | stats count, p95(latency)
where name under roku.com | by client | stats count, distinct(name)
from -1h  | where latency > 200 | by upstream | stats count, p50(latency), p99(latency)
from -1d  | where status = blocked and (client = 192.168.1.20 or name under roku.com) | top 10 name
from -180d | where status = blocked | bucket 1d | stats count

Agents can check the cost first (estimateOnly). Over-broad questions are refused with a hint on how to narrow them, and long scans stop and say so instead of slowing DNS down. Full syntax: Analytics in one query.

Safe by design

Guardrails that are always on

Least privilege

Each token has scopes: analytics:read, querylog:read, config:read, one config:write:… per area, ops:pause, ops:cache, ops:anomalies, ops:maintenance, ops:annotate, ops:drill (failover drills, never granted by default). Simulating a change also needs querylog:read. A token can also be limited to one group of devices.

Preview before change

Every change is a plan with before and after, how many recent questions it would affect, and warnings. A plan made against an older configuration goes stale instead of overwriting newer work.

A person in the loop

Turn on [agents] require_approval, and plans wait in the UI's Agent changes inbox. Agents can't approve anything, their own plans included.

Full audit

Changes are recorded as agent:<token> (owner: <you>), with the MCP client and the reason the agent gave. Every write needs a reason.

Limits and a kill switch

Per-token rate limits, results capped at 200 rows or 32 KiB, the same privacy level as the UI, and [agents] enabled = false to turn every agent off on every node at once.

DNS comes first

Agents use the same API as people, off the query path. Searches run at background priority, so a busy agent never slows down an answer.

Go further