Email Validator for Agents
Server summary: An email-validation toolset for agents — free offline syntax checking plus a paid Apify Actor that scores deliverability risk from DNS/lists (no SMTP, no email sent).
check_email_syntax(free, offline): validates syntax and normalizes strings locally — extractsName <addr>, lowercases the domain, splits local part/domain; no network or APIFY_TOKEN.validate_emails(paid, via Apify Actorfairquery/email-validator-mx-disposable): full risk validation using DNS-over-HTTPS and bundled lists — requiresAPIFY_TOKEN.Syntax check (RFC-style, IDN → punycode)
Domain existence and MX records; detects null MX and A-record-only (implicit MX)
Disposable/temporary inbox detection (9,800+ domains) and shared disposable infrastructure
Role accounts (
info@,sales@), free webmail providers, common typos withdid_you_meansuggestionOptional SPF/DMARC presence and DMARC policy per domain
Returns per-address verdict (
valid|risky|invalid|unknown) with machine-readable reason codesMachine-readable error rows (
INPUT_EMPTY,MAX_EMAILS_EXCEEDED,DNS_LOOKUP_FAILED) that are never charged
Tunable behavior:
checkDns,checkSpfDmarc,allowDisposable,allowRoleAccounts,allowFreeProviders,maxEmails(up to 100,000).Typical uses: pre-filtering signup/lead/CRM lists before outreach, flagging throwaway or typo signups, parsing and normalizing address strings, B2B filtering of free webmail.
Cost / limits: syntax tool is free and deterministic; the validator is pay-per-event (~$0.00005 per run + per-email-row charge), duplicates (case-insensitive) are skipped, 1–10,000 emails per call, typical run 3–30 s.
Not supported: it does not send email or do SMTP mailbox probing, so it cannot confirm a specific mailbox exists; it is read-only, idempotent, and does not modify your data.
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., "@Email Validator for Agentscheck these emails before I import: jane@acme.com, bob@gmial.com"
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.
Email Validator for Agents (MX, Disposable, Typo, Risk Score v2)
$0.79 per 1,000 emails ($0.00079 per email). Error rows are free. Optional mailbox check with your own MillionVerifier key: +$0.19 per 1,000 checked emails ($0.00019 each), charged only when MillionVerifier returns a real result. You pay MillionVerifier for its credits directly.
Example: 5,000 signup emails cost 5,000 × $0.00079 = $3.95, plus Apify's $0.00005 Actor-start fee.
This Fairquery Apify Actor validates email addresses without sending any email and without SMTP probing. It checks syntax, DNS/MX, null MX, disposable domains (9,800+ bundled), role accounts, free providers, typos ("gamil.com" → "gmail.com"), the mail provider and SPF/DMARC. Each address gets a valid / risky / invalid / unknown verdict with machine-readable reason codes, plus a 0–100 Risk Score v2.
What's new in v2 (Risk Score v2)
All v1 fields are unchanged. v2 only adds fields:
risk_score(0–100, higher = riskier),risk_level(low/medium/high) andrisk_reasonsmx_provider_class:consumer_webmail,business_suite,security_gateway,hosting_or_forwarding,disposable_service,self_hosted_or_unrecognisedornone.mail_provider_idgives the provider (google,microsoft,yahoo,proofpoint, …).catch_all_likelihood:low/medium/high/unknown. This is an estimate from the mail provider type, not a measurement (catch_all_basis: "estimate_from_mx_provider"). See the table below.domain_age_daysanddomain_registered_atfrom RDAP. Domains under 30 days old getNEW_DOMAIN. If the age is unknown, these fields arenull.disposable_infrastructure: the domain's MX host is shared with known disposable-inbox services. This flags new throwaway domains that are not on the list yet.mailbox_verified: alwaysfalseunless the optional BYOK MillionVerifier check ran with a live key and returnedokorinvalid.
Related MCP server: inboxvalid-mcp
When to use this Actor (LLM / tool selection)
Use it when an agent needs to:
Pre-filter a lead / CRM / signup list before outreach, import or paid verification
Decide whether an address can receive mail (domain exists, has MX, no null MX)
Flag disposable inboxes (including new ones on disposable infrastructure), brand-new domains, role addresses and free webmail
Suggest a fix for a mistyped domain
See the mail provider, its type, and the DMARC policy
On its own it does not confirm that a specific mailbox exists. That needs an SMTP conversation, and this Actor does none (see the FAQ). If you need mailbox-level results, add your own MillionVerifier key (optional). valid means "well-formed, the domain can receive mail, no risk flags". It does not mean "mailbox confirmed".
How it works
Check | Source |
Syntax | Pragmatic RFC 5321/5322 subset; IDN domains → punycode |
MX / A / AAAA / TXT | DNS-over-HTTPS |
Disposable domains | Bundled CC0 list from |
Disposable infrastructure | 88 MX hosts that each served ≥5 listed disposable domains (resolved 2026-10-17), plus any MX host that is itself on the disposable list. Shared general-purpose infrastructure (Google, Microsoft, Cloudflare Email Routing, Mailgun, Amazon SES, registrar forwarding, hosting and parking MX) is excluded, so legitimate domains on those providers are never flagged. |
Domain age | RDAP (RFC 9083). Servers come only from the IANA RDAP bootstrap, with a bundled snapshot as fallback. 6 s timeout per request, at most 2 parallel requests per registry, one back-off on HTTP 429, a 120 s budget and 2,000 uncached lookups per run. Failures give |
Role / free provider / typo | Built-in lists, plus edit distance against popular providers. The disposable list is checked first: a listed domain (for example |
Catch-all estimate (prior, not measured)
MX provider |
| Basis |
Gmail / Google Workspace |
| Unknown recipients are rejected by default; catch-all routing must be switched on by an admin |
Microsoft 365 / Outlook.com |
| Published measurements show RCPT answers that vary from attempt to attempt, and many tenants accept mail and bounce it later |
Yahoo / AOL (including custom domains on Yahoo MX) |
| They accept every recipient at RCPT time and bounce later |
Proofpoint, Mimecast, Barracuda, Cisco and other security gateways |
| They often accept for the whole domain; this depends on configuration |
Disposable-inbox services |
| They accept any local part by design |
Everything else (self-hosted, Zoho, iCloud, Proton, hosting, forwarding…) |
| No evidence either way |
Risk score v2 (additive, capped 0–100)
Signal | Points |
| 100 (score fixed) |
Disposable domain ( | +70 |
Disposable infrastructure, not listed ( | +60 |
Possible typo ( | +45 |
Domain under 30 days old ( | +35 / +10 |
DNS not checked ( | +25 |
A record only, no MX ( | +20 |
Catch-all estimate | +15 / +8 / +5 |
Role account ( | +10 |
No SPF / no DMARC (custom domains only) | +5 / +3 |
BYOK result | +10 / +60 / −15 |
risk_level: low < 25 ≤ medium < 60 ≤ high. Treat this score as a pre-send filter, not as proof of deliverability. The verdict and reasons fields work exactly as in v1. The new signals only change them when the BYOK check runs: an invalid result from MillionVerifier makes the verdict invalid, and catch_all or disposable make it risky.
Input
Field | Type | Default | Description |
|
| – (required; sample addresses are only prefilled in the Console form) | Addresses. An entry may be a comma, semicolon or newline separated list, or |
|
|
| MX/A lookups (turn off for syntax-only checks) |
|
|
| SPF/DMARC presence and DMARC policy |
|
|
| If |
|
|
| If |
|
|
| If |
|
|
| Safety cap per run (1–100,000) |
|
|
| RDAP domain age (fails soft to |
|
|
| Keep RDAP registration dates for 30 days in a named key-value store, |
| secret | – | Optional. Your own MillionVerifier API key (BYOK). |
|
|
| Hard cap on MillionVerifier calls per run |
{ "emails": ["jane@acme.com", "info@apify.com", "bob@gamil.com"], "allowFreeProviders": false }Output
Every row has schema_version (1.1.0; v1 rows were 1.0.0, and v2 only adds fields), type (email | error), source_url (the DoH MX query URL) and checked_at.
Real row from beta run TzHumKQWvIqfKfdRm (2026-10-17):
{
"type": "email", "input": "someone@yahoo.com", "email": "someone@yahoo.com", "domain": "yahoo.com",
"verdict": "valid", "reasons": ["FREE_PROVIDER"],
"syntax_valid": true, "is_disposable": false, "is_role_account": false, "is_free_provider": true,
"did_you_mean": null, "domain_exists": true, "has_mx": true, "null_mx": false, "implicit_mx": false,
"mx_records": [{ "priority": 1, "exchange": "mta5.am0.yahoodns.net" }, …], "mail_provider": "Yahoo",
"has_spf": true, "has_dmarc": true, "dmarc_policy": "reject",
"risk_score": 15, "risk_level": "low", "risk_reasons": ["CATCH_ALL_LIKELY_ESTIMATE"], "risk_score_version": 2,
"mail_provider_id": "yahoo", "mx_provider_class": "consumer_webmail",
"catch_all_likelihood": "high", "catch_all_basis": "estimate_from_mx_provider",
"domain_registered_at": "1995-01-18T05:00:00.000Z", "domain_age_days": 11595,
"domain_age_source": "https://rdap.verisign.com/com/v1/domain/yahoo.com",
"disposable_infrastructure": false, "disposable_infrastructure_mx": null,
"mailbox_verified": false, "mailbox_check": "not_requested", "mailbox_status": null,
"mailbox_provider": null, "mailbox_raw": null, "mailbox_error": null
}v1 reason codes (reasons) are unchanged: INVALID_SYNTAX, DOMAIN_NOT_FOUND, NULL_MX and NO_MAIL_SERVER (→ invalid); DISPOSABLE_DOMAIN, ROLE_ACCOUNT, FREE_PROVIDER, POSSIBLE_TYPO and IMPLICIT_MX_ONLY (→ risky, depending on the allow* flags); DNS_NOT_CHECKED (→ unknown). With BYOK there are also MAILBOX_UNDELIVERABLE, MAILBOX_CATCH_ALL and MAILBOX_DISPOSABLE.
mailbox_check is one of not_requested, checked, skipped_invalid, skipped_disposable, skipped_limit or error. mailbox_status is one of deliverable, undeliverable, catch_all, disposable or unknown, mapped 1:1 from MillionVerifier's result field (ok, invalid, catch_all, disposable, unknown). mailbox_raw keeps result, resultcode, subresult, quality and livemode. It never contains your key.
Error rows (machine-readable, never charged)
{ "type": "error", "code": "DNS_LOOKUP_FAILED", "message": "DoH MX acme.com -> HTTP 503", "retryable": true, "input": "jane@acme.com", "domain": "acme.com" }Codes: INPUT_EMPTY, INPUT_TOO_LARGE, MAX_EMAILS_EXCEEDED, DNS_LOOKUP_FAILED, MAILBOX_KEY_INVALID_FORMAT and MAILBOX_CHECK_STOPPED (for example a wrong key or no credits left). Duplicate addresses (case-insensitive) are skipped and counted in the summary.
Run summary
The key-value store record RUN_SUMMARY has: emails checked, duplicates skipped, verdict counts, domains checked, DNS and RDAP request counts, RDAP cache hits, mailbox checks attempted, billable and failed, charged events, and the echoed input. The API key is never echoed; the summary shows only mailboxCheckEnabled: true/false.
Pricing
Event | Price | When |
| $0.00079 | Each email row with a verdict ( |
| $0.00019 | Only with your own MillionVerifier key, and only when MillionVerifier returns a real (live-key) |
| $0.00005 | Once per run, per GB of memory (Apify default event) |
These are never charged:
error rows
skipped duplicates
rows skipped before the mailbox check (already
invalid, disposable, or overmailboxCheckMaxPerRun)MillionVerifier
unknown/unverifiedresultsvendor errors, timeouts and rate limits
demo keys (
API_KEY_FOR_*,livemode: false)
Malformed addresses still get an invalid verdict row, and that row is charged like any other check.
Worked examples
5,000 addresses with 200 duplicates and 30 DNS failures: 4,770 × $0.00079 = $3.77, plus $0.00005.
5,000 addresses with BYOK, where 3,600 survivors get a real MillionVerifier result: 5,000 × $0.00079 + 3,600 × $0.00019 = $3.95 + $0.68 = $4.63, plus your MillionVerifier credits.
To cap spend, set a maximum cost per run in the run options; the Actor stops cleanly at the limit and does not start mailbox checks it could not charge for. maxEmails and mailboxCheckMaxPerRun cap each run.
Price comparison (Apify Store, checked 2026-10-17)
Prices are per email, read from each Actor's pricing through the Apify API (GET /v2/acts/{id}) on 2026-10-17. Tier prices depend on your Apify plan. Those Actors verify mailboxes over SMTP via their own off-Apify backends; this Actor does not. The comparison is meant for pre-filtering, not as a claim of equal accuracy.
Tool | What it does | FREE tier | BRONZE | GOLD | Source |
This Actor (Risk Score v2) | DNS/list risk score, no SMTP | $0.00079 | $0.00079 | $0.00079 | |
This Actor + BYOK MillionVerifier | Mailbox check through your own MillionVerifier account | $0.00079 + $0.00019 + your MillionVerifier credits | same | same | MillionVerifier: 50,000 credits for $89 ($0.00178/credit) or 1M for $449 (millionverifier.com) |
bounceverify/bounceverify-email-verifier | SMTP verification (own backend) | $0.00089 | $0.0008 | $0.00065 | |
sumitr_mardy/email-verifier | DNS/list checks; SMTP only through your own relay | $0.00088 | – | $0.00055 | |
blessiticus/email-verifier-pro | SMTP verification (external backend) | $0.00085 + $0.00005 start | – | $0.0006 | |
michael.g/email-verifier-validator | Verification (method not disclosed) | $0.10 | $0.001 | $0.0006 | |
fatihtahta/email-verifier-validator-pro | Resells the validatedmails.com API | $0.002 | – | $0.00089 |
"–" means the tier was not read for this table. We are not the cheapest mailbox verifier on higher Apify tiers, and we do not claim to be. The positioning is: a $0.79 per 1,000 pre-send risk score; send only the survivors to a mailbox verifier.
Optional mailbox check: bring your own MillionVerifier key
How: put your key from https://app.millionverifier.com/api into
millionVerifierApiKey. Only rows that are not alreadyinvalidor disposable are sent, which saves your credits. A free pre-flight call to MillionVerifier's credits endpoint stops the phase at once if the key is wrong or the account has no credits.API used: MillionVerifier Single API v3,
GET https://api.millionverifier.com/api/v3/?api=…&email=…&timeout=10(API docs, OpenAPI 3.1.0, checked 2026-10-17). We use a 15 s request timeout, at most 10 parallel requests and 2 per recipient domain, back-off and retry on HTTP 429/503, and the hardmailboxCheckMaxPerRuncap. Timeouts are not retried, because the vendor may already have used a credit.Vendor limits and billing (from MillionVerifier's help center, checked 2026-10-17):
The Single API is limited to 160 requests per second per account.
MillionVerifier does not deduct credits for risky (
unknown,catch_all) results.It does deduct credits again if the same address is verified again.
Credits used appear in your account in one-minute batches.
Source: Real-Time API.
Within one run, each address is checked once (duplicates are removed first). Nothing is cached across runs.
Terms (checked 2026-10-17):
MillionVerifier explicitly allows integrating its API into other products and reselling it (Resellers; the API docs say "You may also use our API to resell our services").
Its Terms of Service (published 20 July 2026) treat use through your API key or an integration as your use. You must protect your key and use the data lawfully.
The service is for business use only.
With BYOK you use your own account, so nothing is resold.
Your key:
It is a secret input, encrypted by Apify.
The Actor never logs it.
It is never written to the dataset, the key-value store or
RUN_SUMMARY.Error messages contain only our own codes, never the key.
Privacy: with BYOK, the email addresses you submit are sent to MillionVerifier, which acts as your processor under its DPA. Without a key, no full address leaves the Actor.
Demo keys: MillionVerifier's documented test keys (for example
API_KEY_FOR_OK) return fake results withlivemode: false. They never change the verdict, never setmailbox_verified, and are never charged."Deliverable" does not mean inbox placement.
Memory / runtime
Default 128 MB. No browser and no proxy. About 1,000 emails across a few hundred domains usually finish in about a minute. The first run on new domains is slower because of RDAP lookups; later runs reuse the 30-day cache.
MCP / Apify tools
Call from agents via Apify MCP (https://mcp.apify.com, authenticated with your Apify token), Actor fairquery/email-validator-mx-disposable:
{ "actor": "fairquery/email-validator-mx-disposable", "input": { "emails": ["jane@acme.com"] } }Read dataset items (type == "email") and the RUN_SUMMARY record. A standalone stdio MCP server lives in the GitHub repo under mcp/.
Local stdio MCP server
This repo ships a stdio MCP server (TypeScript, @modelcontextprotocol/sdk) under mcp/ exposing validate_emails (runs this Actor through the Apify API) and the free offline check_email_syntax. initialize and tools/list work without a token; tool calls require APIFY_TOKEN (billed per the Store pricing).
cd mcp && npm install && npm run build
node scripts/smoke-test.mjs # initialize + tools/list, no token
APIFY_TOKEN=... node scripts/smoke-test.mjs --call # real tool call{ "mcpServers": { "fairquery-email": { "command": "node", "args": ["/path/to/email-validator-mx-disposable/mcp/dist/index.js"], "env": { "APIFY_TOKEN": "<your Apify token>" } } } }Docker: docker build -t fairquery-email-mcp . (the repo-root Dockerfile builds this MCP server and is what Glama uses; the Apify Actor image is built from .actor/Dockerfile). glama.json lists fairquery as maintainer.
Works well after
Google Maps Scraper, Contact Details Scraper or Website Contact & Company Enricher: pass the emails they find into
emailsto drop dead domains, disposable inboxes and typos before outreach.Before a CRM import or a newsletter send: run your export through this Actor and drop
invalidrows. Review theriskyrows by hand.Then screen the people or companies with the Sanctions & Watchlist Screener if you onboard customers or suppliers.
Troubleshooting
Symptom | Cause and fix |
Run fails at once with |
|
Some rows are | The DNS-over-HTTPS lookup failed or timed out ( |
| You sent more addresses than |
| The TLD has no RDAP service in the IANA bootstrap (for example |
| Check your key or your MillionVerifier credits. Rows without a mailbox result were not charged the BYOK event. |
A |
|
Free webmail marked |
|
FAQ
Why no SMTP probing?
Three reasons.
It is not possible from Apify. We tested it on 2026-10-17 with a private, throwaway test Actor (run
OYO4B7Rqdl3lGAtJv, 128 MB, 33 s):TCP port 25 to Gmail, Outlook and Yahoo MX hosts timed out. Ports 587 and 465 timed out too.
Port 443 connected in 4 ms.
Apify Proxy refused
CONNECT host:25on both the datacenter group (590 UPSTREAM502) and the residential group (590 UPSTREAM453).The egress IP was in AWS address space, and AWS blocks outbound port 25 by default.
The probe only opened TCP connections and sent
QUIT. It never sentRCPT TO, and it has since been deleted.
The workarounds have real costs. You would need your own relay host or a rotating "SMTP proxy" pool, and that creates IP-reputation and blocklist risk. Spamhaus describes unsolicited probing as potentially hostile.
Even with port 25 open, many answers carry little information. Catch-all domains accept every recipient, Yahoo accepts every recipient at RCPT time, Microsoft's answers vary, and greylisting delays answers.
Mailbox-level verifiers on Apify generally run the SMTP step outside Apify. If you need that step, use the optional MillionVerifier BYOK check: the SMTP work happens on MillionVerifier's infrastructure, under your account.
Does it send email or connect to mail servers?
No. It uses DNS-over-HTTPS (MX, A/AAAA, TXT), RDAP (registry web APIs) and bundled lists. There is no SMTP handshake, and nothing is sent to the address. With BYOK, MillionVerifier does its own checks on its infrastructure.
Does valid or a low risk_score mean the mailbox exists?
No. mailbox_verified is false unless the BYOK check returned a live ok or invalid result.
Is catch_all_likelihood measured?
No. It is an estimate from the mail provider type (see the table above). Only the BYOK result catch_all comes from an actual check, done by MillionVerifier.
How much does a run cost?
$0.00079 per email row with a verdict, plus a $0.00005 start fee. 1,000 emails ≈ $0.79. BYOK adds $0.00019 per real MillionVerifier result. Error rows and skipped duplicates are free.
How do I limit spend?
Set a maximum cost per run in the run options, and/or lower maxEmails and mailboxCheckMaxPerRun.
Can an AI agent call it?
Yes. Use Apify MCP with Actor fairquery/email-validator-mx-disposable, or the Fairquery MCP server (npx -y @fairquery/agent-trust-mcp). The server's free email_check_syntax tool runs locally; its email_validate_deliverability tool calls this Actor.
Is my list stored?
Results are written only to your run's dataset and RUN_SUMMARY in your Apify account. Two things are sent elsewhere:
Domain names (not full addresses) go to the Cloudflare DNS-over-HTTPS resolver and to the registry RDAP servers.
If
cacheDomainAgeis on, registration dates for domains (no email addresses) are kept for 30 days in thefairquery-email-rdap-cachestore in your account.
Full addresses leave the Actor only when you add a MillionVerifier key.
Fairquery family
Other Fairquery tools on the Apify Store. Each one returns structured rows with machine-readable error codes, and error rows are never charged:
Website Contact & Company Enricher: domain in, public business emails, phones, socials and company details out; $3 per 1,000 domains
Website Tech, SEO & llms.txt Check for Agents: tech stack, SEO score, AI-crawler rules and llms.txt per site; $1.50 per 1,000 sites
Sanctions & Watchlist Screener (OFAC, EU, UK, UN): fuzzy name screening against official sanctions lists; $3 per 1,000 names + $0.007 per run
LLM Data Guard: PII & Secret Redactor: redact PII and secrets before text reaches an LLM; $1.50 per 1,000 documents + $0.20 per 1,000 findings
Dependency Vulnerability Check for Agents: packages and lockfiles against OSV.dev; $0.50 per 1,000 packages + $1.50 per 1,000 vulnerabilities
EDGAR Company Facts & Filings for Agents: SEC filings and XBRL facts by ticker, CIK or name; from $1.50 per 1,000 companies
Company Jobs Feed: Greenhouse, Lever & Ashby: live jobs from employers' own ATS boards with age and repost signals; $0.99 per 1,000 jobs + $0.0005 per board
MCP server
@fairquery/agent-trust-mcp: one MCP server for Claude Desktop, Cursor and VS Code (npx -y @fairquery/agent-trust-mcp). It has 7 free tools that need no token, plus paid tools that call the PII, email, sanctions and OSV Actors with your own Apify token.
All Fairquery Actors: https://apify.com/fairquery
Kits & templates
Optional guides from Fairquery for wiring this Actor into agents and automations (the Actor works fine without them):
AI Agent Data Toolkit Quick-Start (PDF): MCP setup for Claude Desktop and Cursor, API calls, recipes, cost estimates.
n8n + Zapier Workflow Template Pack: 10 importable n8n workflows for the four Fairquery Actors, plus Zapier setup guides.
Agent Prompt Pack for Research & Lead Ops: 69 prompts for company research, lead cleaning, SEO and security reviews.
Agent Starter Kit: 10 copy-paste MCP + n8n recipes ($5): 10 copy-paste MCP + n8n recipes, with ready-made Claude/Cursor configs and 3 starter workflows.
Disclaimer
DNS- and registry-based validation cannot guarantee that a mailbox exists or accepts mail. Use the results to reduce bounces, not as proof of deliverability. Comply with anti-spam law (CAN-SPAM, GDPR/ePrivacy) when you send email. MillionVerifier is a third-party service under its own terms; Fairquery is not affiliated with it.
Links
Apify Store: https://apify.com/fairquery/email-validator-mx-disposable
GitHub: https://github.com/fairquery/email-validator-mx-disposable
Contact: fairquery@proton.me
License: MIT
Development
Local development, tests and deployment notes are in DEVELOPMENT.md. The Apify Store README is .actor/README.md.
Available Tools
2 toolscheck_email_syntaxCheck email syntax offline (free)ARead-onlyIdempotentInspect
WHAT: Check whether strings are syntactically valid email addresses and normalize them (extracts "Name ", lowercases the domain), entirely locally. WHEN TO USE: fast pre-filtering or parsing before calling validate_emails, or when only the format matters. WHEN NOT TO USE: it does not check that the domain exists, has MX records, is disposable, a role account or a typo - use validate_emails (paid) for deliverability. COST: free; pure local function, no network, no APIFY_TOKEN. BEHAVIOR: synchronous and CPU-bound, no rate limit; 10,000 addresses return in well under a second. RETURNS: one "email_syntax" row per input element; reason is null or INVALID_SYNTAX. PARAMETERS: emails: each element is checked on its own and returns exactly one row, in input order (no splitting of comma/semicolon lists, no deduplication - unlike validate_emails). Normalization before checking: surrounding whitespace is trimmed, a trailing "Name " is reduced to addr, and the domain is lowercased (the local part keeps its case). Syntax rules: total <= 254 chars, local part <= 64 chars with no leading/trailing or double dot, domain labels of letters/digits/hyphens and a 2-63 letter TLD; quoted local parts, IP-literal domains and non-ASCII (IDN) addresses are reported as INVALID_SYNTAX here. input always echoes the original string so you can map rows back. EXAMPLE: {"emails":["Jane jane@Example.com"]} -> email "jane@example.com", syntax_valid true, domain "example.com".
| Name | Required | Description | Default |
|---|---|---|---|
| emails | Yes | Required. 1-10000 strings, one address per element; bare "jane@acme.com" or "Name <addr>" form (one row per element, no list splitting). Example: ["Jane <jane@Example.com>","not-an-email"] |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | Number of rows returned |
| items | Yes | Result rows; each has a "type" field (data rows or machine-readable "error" rows) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive/closed-world, and the description adds what they cannot: free, purely local, no APIFY_TOKEN, synchronous and CPU-bound, no rate limit, and a performance guarantee (10k addresses in well under a second). It also discloses the return shape and the INVALID_SYNTAX reason value.
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?
Strongly front-loaded with labeled sections (WHAT, WHEN, COST, BEHAVIOR, RETURNS, PARAMETERS, EXAMPLE) so it scans well, and no sentence is filler. It is nonetheless on the verbose end for a single-parameter tool, with the PARAMETERS block restating normalization details at 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?
Given a one-param, locally-executed tool with an output schema already present, the definition covers cost, performance, failure mode, normalization, ordering guarantees, and a worked example. An agent has everything needed to call it correctly and to interpret rows back to inputs.
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?
Goes far beyond the 100% schema coverage: per-element row semantics, no comma/semicolon splitting or deduplication (contrasted with validate_emails), the exact normalization order (trim, unwrap "Name <addr>", lowercase domain only), and concrete syntax limits (254 total, 64 local, 2-63 letter TLD, IDN/quoted/IP-literal rejected). Behavior an agent could not derive from the schema.
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 WHAT clause gives a specific verb and resource (check and normalize email address syntax) and immediately scopes it to local-only operation, which cleanly separates it from validate_emails. An agent can route between the two tools without opening either 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?
Explicit WHEN TO USE (fast pre-filtering before validate_emails, or when only format matters) and WHEN NOT TO USE (no MX, deliverability, disposable/role/typo checks - use validate_emails instead) with the alternative named. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_emailsValidate email addresses (syntax, MX, disposable, typo)ARead-onlyIdempotentInspect
WHAT: Validate email addresses for deliverability risk: RFC-style syntax, domain existence and MX records (DNS-over-HTTPS), null MX, disposable/temporary inbox domains, role accounts (info@, sales@), free webmail providers, common domain typos with a "did you mean" suggestion, and optional SPF/DMARC presence and policy. WHEN TO USE: cleaning signup or lead lists, checking an address before sending, flagging throwaway or typo signups. WHEN NOT TO USE: for a quick offline format check or parsing "Name " strings use check_email_syntax (free). Does NOT send email or do SMTP mailbox probing, so it cannot confirm that a specific mailbox exists. BEHAVIOR: runs the Fairquery Apify Actor fairquery/email-validator-mx-disposable via the Apify API (DNS lookups via Cloudflare DoH). Requires APIFY_TOKEN. Duplicates (case-insensitive) are skipped. Typical run 3-30 s. COST: paid (billed to your Apify account, pay-per-event): $0.00005 per run + $0.00079 per email row with a verdict; error rows are free. 100 emails ~ $0.08. RETURNS: one email row per unique address; verdict follows reasons[]: INVALID_SYNTAX, DOMAIN_NOT_FOUND, NULL_MX, NO_MAIL_SERVER -> invalid; DISPOSABLE_DOMAIN, ROLE_ACCOUNT, FREE_PROVIDER (per allow* flags), POSSIBLE_TYPO, IMPLICIT_MX_ONLY -> risky; DNS_NOT_CHECKED -> unknown. Error rows are free and never sit next to an email row for the same address: INPUT_EMPTY (nothing to check), MAX_EMAILS_EXCEEDED (once, for the overflow), DNS_LOOKUP_FAILED (one per address whose domain lookup failed, instead of its email row; retryable=true for transient DNS errors, so resend only those addresses). PARAMETERS: emails: entries are split on commas, semicolons and newlines, trimmed, reduced from "Name " to addr, and case-insensitive duplicates are skipped before checking, so a pasted list in one element works and you pay once per unique address; maxEmails caps that unique count (extras get one free MAX_EMAILS_EXCEEDED row), which makes it your spend cap. checkDns=false skips all DNS lookups: domain_exists / has_mx stay null and an address that is otherwise clean gets verdict "unknown" (DNS_NOT_CHECKED) instead of "valid" - it is still billed. checkSpfDmarc only adds has_spf / has_dmarc / dmarc_policy and never changes the verdict; it is ignored when checkDns=false. The three allow* flags only decide whether a condition downgrades an otherwise valid address to "risky": the reason code (ROLE_ACCOUNT, DISPOSABLE_DOMAIN, FREE_PROVIDER) is listed in reasons[] either way, and no flag can rescue an "invalid" address (bad syntax, DOMAIN_NOT_FOUND, NULL_MX, NO_MAIL_SERVER). POSSIBLE_TYPO and IMPLICIT_MX_ONLY are always "risky". Presets: B2B lead list = allowFreeProviders false + allowRoleAccounts false; consumer signups = defaults. EXAMPLE: {"emails":["someone@gamil.com"]} -> verdict "risky", reasons ["POSSIBLE_TYPO"], did_you_mean "someone@gmail.com".
| Name | Required | Description | Default |
|---|---|---|---|
| emails | Yes | Required. 1-10000 email addresses to validate. Entries may also be comma/semicolon/newline-separated lists or "Name <addr>" forms. Example: ["john.doe@gmail.com","someone@gamil.com"] | |
| checkDns | No | Optional boolean, default true. true looks up MX/A records (DNS-over-HTTPS) to confirm the domain can receive mail; false = syntax/list checks only, clean addresses then get verdict "unknown". Example: false | |
| maxEmails | No | Optional integer 1-100000, default 10000. Safety/spend cap on unique emails per run; addresses beyond it are skipped with one free MAX_EMAILS_EXCEEDED error row. Example: 500 | |
| checkSpfDmarc | No | Optional boolean, default true. true adds has_spf, has_dmarc and dmarc_policy (none|quarantine|reject) per domain; never changes the verdict; needs checkDns=true | |
| allowDisposable | No | Optional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get "risky"; true stops penalising them (DISPOSABLE_DOMAIN still reported) | |
| allowRoleAccounts | No | Optional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...; +tags ignored) from "valid" to "risky"; ROLE_ACCOUNT is reported in reasons either way | |
| allowFreeProviders | No | Optional boolean, default true. false downgrades free webmail (gmail.com, outlook.com, yahoo.com, ...) to "risky" - recommended for B2B lead lists |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | Number of rows returned |
| items | Yes | Result rows; each has a "type" field (data rows or machine-readable "error" rows) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover only the safety profile (readOnly, idempotent, openWorld, non-destructive), leaving the description to carry the operational burden, which it does: APIFY_TOKEN requirement, external Actor invocation, case-insensitive dedup and skip behavior, 3-30s runtime, paid pay-per-event pricing model, and the fact that some checks are billed even when skipped. It also flags what can never be confirmed (mailbox existence) and which error rows are retryable.
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 headed WHAT/WHEN/BEHAVIOR/COST/RETURNS/PARAMETERS/EXAMPLE layout is front-loaded and scannable, and for a 7-parameter tool with billing, cost, and verdict-tier complexity the length is largely earned. It is nonetheless verbose and the RETURNS and PARAMETERS sections restate overlapping verdict/flag behavior, so it is not maximally tight.
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?
Even though an output schema exists and return fields need not be documented, the description still maps verdict values to reason codes and explains which error rows replace email rows and why, which is exactly what an agent needs to interpret partial failures. Everything needed to call the tool correctly and read its results is present.
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 coverage is already 100%, yet the description still adds meaning the schema cannot express: allow* flags only downgrade an otherwise valid address to 'risky' and never rescue an 'invalid' one, the reason code is reported either way, checkDns=false still bills, checkSpfDmarc never alters the verdict and is ignored when checkDns=false, and maxEmails functions as the spend cap. It also supplies named presets (B2B vs consumer), which are not encoded anywhere in the schema.
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 WHAT section names a specific verb (validate) and resource (email addresses) and enumerates exactly which checks run: RFC syntax, MX/DNS-over-HTTPS, null MX, disposable domains, role accounts, free providers, typos, SPF/DMARC. It also draws an explicit boundary against the sibling check_email_syntax, so an agent can discriminate without opening either 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?
WHEN TO USE gives concrete scenarios (cleaning signup/lead lists, pre-send checks, throwaway/typo signups) and WHEN NOT TO USE names the exact alternative (check_email_syntax, free) plus its capability limits (no SMTP mailbox probing, cannot confirm a mailbox exists). Explicit and actionable.
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.
2 tool updates
- Changed
check_email_syntax1 field changed- changed
Input schema / properties / emails / descriptionPrevious value: -"Required. 1-10000 strings to check; each may be a bare address or \"Name <addr>\" form. Example: [\"Jane <jane@Example.com>\",\"not-an-email\"]"New value: +"Required. 1-10000 strings, one address per element; bare \"jane@acme.com\" or \"Name <addr>\" form (one row per element, no list splitting). Example: [\"Jane <jane@Example.com>\",\"not-an-email\"]"
- Changed
validate_emails7 fields changed- changed
Input schema / properties / allowDisposable / descriptionPrevious value: -"Default false (disposable domains are \"risky\"). Set true to not penalize disposable domains."New value: +"Optional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get \"risky\"; true stops penalising them (DISPOSABLE_DOMAIN still reported)" - changed
Input schema / properties / allowFreeProviders / descriptionPrevious value: -"Default true. If false, free webmail (gmail.com, outlook.com...) gets \"risky\" - useful for B2B lead lists."New value: +"Optional boolean, default true. false downgrades free webmail (gmail.com, outlook.com, yahoo.com, ...) to \"risky\" - recommended for B2B lead lists" - changed
Input schema / properties / allowRoleAccounts / descriptionPrevious value: -"Default true. If false, role addresses (info@, sales@, support@...) get verdict \"risky\"."New value: +"Optional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...; +tags ignored) from \"valid\" to \"risky\"; ROLE_ACCOUNT is reported in reasons either way" - changed
Input schema / properties / checkDns / descriptionPrevious value: -"Look up MX/A records to confirm the domain can receive mail (default true). false = syntax/list checks only, verdict may be \"unknown\"."New value: +"Optional boolean, default true. true looks up MX/A records (DNS-over-HTTPS) to confirm the domain can receive mail; false = syntax/list checks only, clean addresses then get verdict \"unknown\". Example: false" - changed
Input schema / properties / checkSpfDmarc / descriptionPrevious value: -"Also report SPF and DMARC presence and DMARC policy per domain (default true)"New value: +"Optional boolean, default true. true adds has_spf, has_dmarc and dmarc_policy (none|quarantine|reject) per domain; never changes the verdict; needs checkDns=true" - changed
Input schema / properties / emails / descriptionPrevious value: -"Required. 1-10000 email addresses to validate. Entries may also be comma/semicolon/newline-separated lists or \"Name <addr>\" forms. Example: [\"john.doe@gmail.com\",\"someone@gmial.com\"]"New value: +"Required. 1-10000 email addresses to validate. Entries may also be comma/semicolon/newline-separated lists or \"Name <addr>\" forms. Example: [\"john.doe@gmail.com\",\"someone@gamil.com\"]" - changed
Input schema / properties / maxEmails / descriptionPrevious value: -"Safety cap on unique emails per run (1-100000, default 10000); extras produce a MAX_EMAILS_EXCEEDED error row"New value: +"Optional integer 1-100000, default 10000. Safety/spend cap on unique emails per run; addresses beyond it are skipped with one free MAX_EMAILS_EXCEEDED error row. Example: 500"
2 tool updates
v1.0.1- Added
check_email_syntax - Changed
validate_emails9 fields changed- changed
Input schema / properties / allowDisposable / descriptionPrevious value: -"If true, disposable domains are not risky"New value: +"Default false (disposable domains are \"risky\"). Set true to not penalize disposable domains." - changed
Input schema / properties / allowFreeProviders / descriptionPrevious value: -"If false, free webmail becomes risky"New value: +"Default true. If false, free webmail (gmail.com, outlook.com...) gets \"risky\" - useful for B2B lead lists." - changed
Input schema / properties / allowRoleAccounts / descriptionPrevious value: -"If false, info@/sales@ etc. become risky"New value: +"Default true. If false, role addresses (info@, sales@, support@...) get verdict \"risky\"." - changed
Input schema / properties / checkDns / descriptionPrevious value: -"MX/A lookups (default true)"New value: +"Look up MX/A records to confirm the domain can receive mail (default true). false = syntax/list checks only, verdict may be \"unknown\"." - changed
Input schema / properties / checkSpfDmarc / descriptionPrevious value: -"Report SPF/DMARC (default true)"New value: +"Also report SPF and DMARC presence and DMARC policy per domain (default true)" - changed
Input schema / properties / emails / descriptionPrevious value: -"Email addresses to validate"New value: +"Required. 1-10000 email addresses to validate. Entries may also be comma/semicolon/newline-separated lists or \"Name <addr>\" forms. Example: [\"john.doe@gmail.com\",\"someone@gmial.com\"]" - added
Input schema / properties / emails / maxItemsAdded value: +10000 - added
Input schema / properties / maxEmails / descriptionAdded value: +"Safety cap on unique emails per run (1-100000, default 10000); extras produce a MAX_EMAILS_EXCEEDED error row" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "count": { + "description": "Number of rows returned", + "type": "integer" + }, + "items": { + "description": "Result rows; each has a \"type\" field (data rows or machine-readable \"error\" rows)", + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "count", + "items" + ], + "type": "object" +}
1 tool update
v0.1.0- First observed
validate_emails
TDQS
Scored across 2 tools
The two tools occupy clearly separated layers: check_email_syntax is a free, local, format-only check, while validate_emails is the paid network deliverability check. Each description explicitly states when to use the other instead, eliminating misselection risk.
Both names follow a consistent snake_case verb_noun pattern (validate_emails, check_email_syntax) with the subject ('emails') clearly identified. No mixed conventions or vague verbs.
Two tools is thin in absolute terms, but for a narrow email-validation purpose the free/paid split is a natural and justified decomposition rather than an under-built surface. Nothing appears to be padded or missing at the tool level.
The surface covers both the offline syntax tier and the full deliverability tier (syntax, DNS/MX, disposable, role, typo, SPF/DMARC) with documented verdicts and error rows. The only real gap is that mailbox existence/SMTP probing and any async run-status retrieval are absent, both explicitly acknowledged as out of scope.
Maintenance
Related MCP Connectors
DNS/MX bulk email verification via MCP with role, disposable and free-provider flags.
Validate emails (MX, disposable, role, typo) and phone numbers, singly or in batches, for AI agents.
Verify emails (valid/invalid/risky/unknown): MX, SMTP, catch-all, disposable. Free agent key.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceComprehensive email validation MCP server that checks syntax, MX records, disposable domains, role-based accounts, SPF/DKIM, typo suggestions, and risk scoring.MIT
- AlicenseAqualityBmaintenanceAn MCP server that exposes a verify_email tool for checking email syntax, disposable domains, and MX records, returning a structured validity result.1ISC
- AlicenseBqualityDmaintenanceAn MCP server that provides email verification, returning valid, invalid, or risky status with detailed checks and metadata. It enables verifying email addresses via a simple tool interface.3MIT
- FlicenseAqualityDmaintenanceAn MCP server that exposes a mock email verification tool over stdio transport, providing structured JSON results with statuses valid, invalid, or risky.1-