An agent skill for DNS and email debugging
Mail problems are a good fit for a skill because the diagnosis is almost entirely about order. The records are not complicated individually. What goes wrong is that people read them in the wrong sequence, reach a conclusion at step four that step one would have ruled out, and change the wrong thing.
This is a skill that encodes the order. The full file is below, then the reasoning behind each decision in it.
What it depends on
Every step here needs a real DNS answer. A skill cannot provide that, and skills running through the API have no network access at all, so the live checks come from an MCP server:
claude mcp add --transport http toolhq https://toolhq.io/api/mcp
That gives mx_lookup, dns_lookup, dns_propagation, reverse_dns and ip_reputation, which is everything the skill calls. The split is covered in when to use a skill and when you need an MCP server.
The skill
Save as .claude/skills/email-deliverability/SKILL.md:
---
name: email-deliverability
description: Diagnose why mail from a domain is bouncing, being rejected,
or landing in spam. Use when a user reports bounced mail, messages going
to spam, a DMARC failure, mail rejected by Gmail or Microsoft, or asks
about SPF, DKIM, DMARC, MTA-STS or an IP blocklist.
---
# Email deliverability triage
Look every record up. Never state a record's contents from memory, and
never infer a record from the domain's other records.
Work in this order. Each step can end the investigation, so do not read
ahead.
## 1. Is there a mail route at all
`mx_lookup` on the domain.
No MX records means no inbound mail, and nothing below matters. Report
that and stop.
If MX records point at a host that does not resolve, that is the fault.
Check with `dns_lookup`.
## 2. SPF
`dns_lookup` for the TXT records on the domain. The SPF record is the
one starting `v=spf1`.
Three faults, in order of how often they are the cause:
- **More than one SPF record.** This is a hard failure, not a warning.
Receivers are required to treat it as permerror. Two records usually
means someone added a provider without merging.
- **Over ten DNS lookups.** Each `include:`, `a`, `mx`, `ptr` and
`exists` costs a lookup, and `include:` is recursive, so a provider's
own record can spend five on its own. Over ten is permerror. Count it,
do not estimate it.
- **Ends in `+all`.** This authorises the entire internet. Treat as
critical.
A record ending `~all` (softfail) rather than `-all` (fail) is not a
bug. It is a deliberate, common choice and not the cause of a rejection.
## 3. DKIM
DKIM lives at `<selector>._domainkey.<domain>` and you cannot enumerate
selectors from DNS. So establish which selectors the sender actually
uses before concluding anything.
If a message is available, read the `s=` tag in its DKIM-Signature
header. Otherwise ask, or try the provider's documented defaults.
A selector that does not resolve and a selector that was never
configured look identical from outside. Say which you have established
and which you are assuming.
## 4. DMARC
Read this last. It depends on SPF and DKIM, and reading it first leads
to recommending a policy change when the real fault is below it.
`dns_lookup` for TXT on `_dmarc.<domain>`.
- `p=none` is monitoring only and does not cause rejection. If mail is
being rejected and the policy is `p=none`, DMARC is not your cause.
- `p=reject` with a broken SPF and DKIM pair is the classic bounce.
- Alignment is what catches people: SPF must align on the envelope
domain and DKIM on the signing domain. A provider that sends with its
own envelope domain passes SPF and still fails DMARC alignment. Check
the domains match, not just that the checks pass.
## 5. Transport and reputation
Only if the records above are correct.
- MTA-STS: fetch the policy at
`https://mta-sts.<domain>/.well-known/mta-sts.txt` and confirm the
`_mta-sts` TXT record exists. A policy in `enforce` mode with an MX
host missing from it will break mail.
- `ip_reputation` on the sending IP, for blocklist and proxy status.
- `reverse_dns` on the sending IP. Missing or mismatched PTR is a
common cause of filtering by large receivers.
## Report back
1. The cause, in one sentence.
2. The evidence: the record as it actually reads, and the count if a
limit was exceeded.
3. The fix: the exact record to publish.
4. Anything you assumed rather than verified, named explicitly.
Why the order is the whole point
Each step above is cheap. The value is that they are numbered.
The commonest wasted afternoon in mail debugging goes like this. Someone reports bounces, you read the DMARC record, see p=reject, and recommend relaxing it. The policy was not the fault. The domain had two SPF records, which is a permanent failure regardless of policy, and relaxing DMARC hides the symptom while leaving the domain unauthenticated.
An assistant makes that mistake readily, because DMARC is the record that sounds most like it governs rejection. Numbering the steps fixes it, and nothing else does.
The three traps worth writing down
Most of a good skill is traps. These are the ones that recur.
Two SPF records is a hard failure. People expect records to merge or for the first to win. Neither happens. Receivers must return permerror, so the domain is treated as having no SPF at all. It is also invisible unless you look at every TXT record rather than the first one that matches.
The ten lookup limit is recursive. A record that looks modest can blow the limit through nested includes. include:_spf.google.com alone costs several. This is why the skill says count, not estimate, and why you can check it directly in the email score tool.
DMARC alignment is not DMARC pass. SPF can pass on a provider's envelope domain while DMARC fails because that domain is not yours. The checks are green and the mail still bounces. Instructing an assistant to compare the domains, not just the results, is the difference between a correct answer and a confusing one.
Verifying the skill's answers
A skill that reasons well about wrong data is still wrong, so check the assistant's findings against something exact. Each step has a matching tool on the site:
- Overall posture and the lookup count: email score
- SPF syntax, before you publish: SPF generator
- DMARC policy and tags: DMARC generator
- A real message's headers and what the receiver concluded: email header analyzer
- Blocklist status: blacklist check
- Transport policy: MTA-STS checker
Running the same question both ways for the first few cases is how you find out whether the skill is actually helping. How to test an agent skill covers that properly.
Extending it
Two sibling files are worth adding once the main file settles, so the detail loads only when relevant:
email-deliverability/
SKILL.md
spf.md mechanisms, the lookup budget, how to merge providers
dmarc.md tags, alignment modes, reading aggregate reports
Keep the decision making in SKILL.md and let the syntax reference live next door. Bundled files cost nothing until something reads them.
For a worked example of an assistant running checks like these against a live domain, see debugging DNS, SSL and mail with an AI agent.