---
title: "Validate Email Addresses Over MCP"
description: "nobounce.dev exposes its whole validation surface as MCP tools over streamable HTTP — no local server, one JSON-RPC endpoint, eight tools."
slug: "validate-emails-over-mcp"
date: 2026-09-27
updated: 2026-09-27
last_tested: 2026-09-27
summary: "Call check_email, check_email_batch and friends as MCP tools from any MCP client: the key is a tool argument, results arrive as frozen verdict objects, and suggestions still recover the signup."
cluster: "Signup flows"
intent: how-to
sources:
  - title: "Model Context Protocol specification"
    url: "https://modelcontextprotocol.io/specification"
  - title: "nobounce.dev agent reference"
    url: "https://nobounce.dev/llms.txt"
  - title: "nobounce.dev OpenAPI specification"
    url: "https://nobounce.dev/openapi.json"
  - title: "nobounce.dev verdict taxonomy"
    url: "https://nobounce.dev/v1/config"
---

The usual way an agent integrates an API is a curl loop, and [Validate Email Addresses From an AI Agent](https://nobounce.dev/blog/validate-emails-from-an-agent/) walks that path. But if your host already speaks Model Context Protocol — Claude Desktop, an MCP-enabled IDE, an agent framework with a tool client — there is nothing to script. nobounce exposes the same surface as MCP tools at a single endpoint: `POST /mcp`, JSON-RPC 2.0 over streamable HTTP. No server to install, no configuration file to maintain, no transport to run locally.

This is the whole loop over MCP: discover, evaluate without credentials, get a key, check, branch on the verdict.

## Discover the tools

```bash
curl -X POST https://nobounce.dev/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Eight tools come back:

| tool | what it does | key? |
|---|---|---|
| `get_config` | verdict and reason enums, tier limits, full fixture list | no |
| `demo_check` | validate one address against the frozen fixture corpus | no |
| `redeem_access_code` | exchange an operator-minted access code for a live key | no |
| `hash_email` | SHA-256 an address in memory, for feedback | no |
| `check_email` | validate one address against live DNS | yes |
| `check_email_batch` | validate up to 1,000 addresses in one call | yes |
| `get_usage` | tier, checks used, remaining quota | yes |
| `report_feedback` | report bounced / delivered / opened, by hash only | yes |

Each tool's `inputSchema` is in the `tools/list` result, so an MCP host can present the tools without any hand-written glue.

## Evaluate with no credentials

`demo_check` needs no key and no account. Call it as a tool:

```bash
curl -X POST https://nobounce.dev/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"demo_check","arguments":{"email":"user@gmai.com"}}}'
```

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"status\": 200,\n  \"body\": {\n    \"verdict\": \"undeliverable\",\n    \"reason\": \"typosquat_mx\",\n    \"suggestion\": \"user@gmail.com\",\n    \"confidence\": 0.94,\n    \"checked\": { \"syntax\": true, \"mx\": true, \"typo\": true, \"disposable\": true },\n    \"cached\": false\n  }\n}"
      }
    ]
  }
}
```

Two things to note in that shape. The tool result wraps the underlying HTTP response as `{status, body}` inside a text content block, so parse the inner text and branch on `status` — a JSON-RPC `result` can still carry an HTTP 401 inside it. And `demo_check` resolves the frozen fixture corpus only, so it is an evaluation surface, not a production validator. It cannot check arbitrary live addresses; `get_config` lists every address it knows.

## Get a key

There is no free tier. Live DNS checks require a paid key, from $1/mo — hobby 1,000 checks, pro $19/100,000, scale $99/1.5M. The agent-friendly path is an operator-minted access code, and redemption is itself a tool call:

```json
{"jsonrpc":"2.0","id":3,"method":"tools/call",
 "params":{"name":"redeem_access_code","arguments":{"coupon":"NB-YOUR-CODE"}}}
```

One call, no browser, no captcha, no email. The key is returned exactly once — store it immediately. The self-serve card and stablecoin rails also exist, via `POST /v1/keys` over plain HTTP, and land in the same entitlement.

Here is the difference from the raw HTTP API: on `/v1/check` the key travels in the `Authorization` header and never in a URL. Over MCP there is no header juggling for the model to get wrong — the key is a declared argument of `check_email`, `check_email_batch`, `get_usage` and `report_feedback`, right in the tool schema. The host asks the model to fill `api_key`, and the schema enforces that it is present.

## The verdict handling is still your job

MCP changes the transport, not the contract. The `body` of `check_email` is the same frozen verdict object the REST API returns, and the mapping is the same:

- `undeliverable` with a `suggestion` — this is not an error. The domain the user typed is not the domain they meant, and the suggestion names the correction. Offer it with a one-click accept and a real "keep what I typed" option; the mechanics are in [Suggest the Correction Instead of Rejecting the Signup](https://nobounce.dev/blog/suggest-the-correction-not-rejection/).
- `undeliverable` without a suggestion — reject, with `reason` naming the cause (`domain_not_found`, `null_mx`, `syntax_invalid`, …).
- `risky` — disposable domain or role account such as `admin@`. Proceed; blocking is your product decision, made by branching on `reason`.
- `unknown` — the check did not complete. `checked.mx: false` is the fail-open signal: record it and proceed, never reject. An agent that treats a validator outage as a bad address will confidently discard good signups — the reasoning is in [Fail Open: Email Validation Must Never Block a Signup](https://nobounce.dev/blog/fail-open-email-validation-signup/).

## Quota, batches and feedback

Before a bulk run, call `get_usage` and compare remaining quota against the list size — the batch endpoint refuses the whole call with 429 rather than half-processing it. `check_email_batch` takes up to 1,000 addresses and keys each result by `email_sha256`, so logs stay free of plaintext addresses; the chunking loop and hash join are in [How to Batch-Validate a List of Email Addresses](https://nobounce.dev/blog/batch-validate-email-list/). Cache hits still count against the monthly quota.

When you later learn an address bounced, report it: `hash_email` computes the digest (in memory — no plaintext address is ever persisted; anything stored is SHA-256, domains in clear), then `report_feedback` takes the hash with the event. Sending a raw address to that tool fails.

## When to use MCP, and when not to

Use MCP when the caller is an MCP client: an assistant wiring validation into a repo, an agent evaluating the API, a chat-driven ops loop. Use raw HTTP for your signup server — the language how-tos cover that path — because a server-side handler wants a bounded timeout and its own secret management, not a tool call. Both surfaces return the identical verdict object, so the branching logic you write once carries over.

For the ordered, end-to-end integration procedure, hand your agent [/integrate.md](https://nobounce.dev/integrate.md) — it is written to be fetched and followed.
