toolhq.io

skills / network and email

email-deliverability

Why mail bounces or lands in spam, read in the order that stops you changing the wrong record.

SKILL.md

Save as .claude/skills/email-deliverability/SKILL.md in a repository, or under ~/.claude/skills/email-deliverability/SKILL.md to have it everywhere. The copy button takes the whole file, frontmatter included.

---
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.

## Requires

The toolhq MCP server, for `mx_lookup`, `dns_lookup`, `reverse_dns` and
`ip_reputation`:

    claude mcp add --transport http toolhq https://toolhq.io/api/mcp

If those checks are unavailable, say so and stop. Do not describe what a
record probably contains.

## 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, so the domain is handled as
  having no SPF at all. Two records usually means someone added a provider
  without merging. You must read every TXT record to see this, not just the
  first that matches.
- **Over ten DNS lookups.** Each `include:`, `a`, `mx`, `ptr` and `exists`
  costs a lookup, and `include:` is recursive, so one provider's own record can
  spend five by itself. Over ten is permerror. Count it, do not estimate it.
- **Ends in `+all`.** This authorises the entire internet to send as the
  domain. Treat as critical.

A record ending `~all` (softfail) rather than `-all` (fail) is not a bug. It is
a deliberate and common choice, and it is not the cause of a rejection.

## 3. DKIM

DKIM lives at `<selector>._domainkey.<domain>` and selectors cannot be
enumerated from DNS. 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. State which you established and which you assumed.

## 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 the 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. Compare the domains, not
  just whether the checks passed.

## 5. Transport and reputation

Only once 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 that omits a live MX
  host will break mail.
- `ip_reputation` on the sending address, for blocklist and proxy status.
- `reverse_dns` on the sending address. A 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.

what it needs

This one looks things up rather than describing them, so it needs live checks. Instructions cannot reach the network on their own, and a skill running through the Claude API has no network access at all. Add the toolhq MCP server and these become real: mx_lookup, dns_lookup, reverse_dns, ip_reputation.

toolhq — mcp
$ claude mcp add --transport http toolhq https://toolhq.io/api/mcp

the instructions, in full

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.

Requires

The toolhq MCP server, for mx_lookup, dns_lookup, reverse_dns and ip_reputation:

claude mcp add --transport http toolhq https://toolhq.io/api/mcp

If those checks are unavailable, say so and stop. Do not describe what a record probably contains.

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:

A record ending ~all (softfail) rather than -all (fail) is not a bug. It is a deliberate and common choice, and it is not the cause of a rejection.

3. DKIM

DKIM lives at <selector>._domainkey.<domain> and selectors cannot be enumerated from DNS. 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. State which you established and which you assumed.

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>.

5. Transport and reputation

Only once the records above are correct.

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.

check its answers by hand

A skill that reasons well about wrong data is still wrong. For the first few cases, run the same question through a tool that cannot be mistaken.

Email Deliverability Score

Grade a domain’s email setup: MX, SPF, DKIM, DMARC, blacklists, and BIMI in one scored report.

Open the tool
SPF Record Generator

Build a valid SPF TXT record from includes, IP addresses, and an all mechanism.

Open the tool
DMARC Record Generator

Build a DMARC TXT record for _dmarc with policy, reporting, and alignment options.

Open the tool
Email Header Analyzer

Trace the delivery path, read SPF, DKIM, and DMARC verdicts, and spot spoofing signs in raw email headers. Parsed entirely in your browser.

Open the tool

why it is written this way

A complete SKILL.md for mail deliverability triage: the order to read records in, the traps that cause wrong diagnoses, and the live checks it depends on.

An agent skill for DNS and email debugging