Skip to main content
Glama
naman-upreti

Email Verification MCP Server

by naman-upreti
README.md
# Email Verification MCP Server

A lightweight **Model Context Protocol (MCP) server** that exposes email verification as a structured tool that can be consumed by an MCP client or AI agent.

The server exposes a single tool:

```text
verify_email(address)
```

The implementation performs email syntax validation, domain verification, and disposable-domain risk detection using a mock verification provider.

---

## Overview

The goal of this implementation is to provide a clean interface through which another system or agent can request email verification and receive a structured result.

The implementation is intentionally modular:

```text
MCP Client
    |
    | MCP / STDIO
    v
+----------------------+
|     MCP Server       |
|                      |
|    verify_email      |
+----------+-----------+
           |
           v
+----------------------+
| Verification Service |
|                      |
| - normalization      |
| - syntax check       |
| - domain check       |
| - risk check         |
+----------+-----------+
           |
           v
+-----------------------+
| Verification Provider |
|                       |
| Mock Provider         |
+-----------------------+
```

The MCP layer is kept thin, while the verification logic and provider dependency are separated from the transport layer.

---

## Features

- MCP server using STDIO transport
- `verify_email` MCP tool
- Structured verification result
- Email normalization
- Email syntax validation
- Domain verification
- Disposable-domain detection
- Explicit provider error handling
- Mock provider for deterministic testing
- Unit tests
- MCP client integration test
- MCP Inspector demonstration

---

## Tool Interface

### `verify_email`

The MCP server exposes:

```text
verify_email(address: string)
```

### Input

```json
{
  "address": "user@example.com"
}
```

### Output

```json
{
  "email": "user@example.com",
  "status": "valid",
  "reason": "Email syntax and domain checks passed.",
  "checks": {
    "syntax": true,
    "domain": true,
    "risk": false
  }
}
```

### Possible statuses

| Status | Meaning |
|---|---|
| `valid` | Syntax and domain checks passed |
| `invalid` | Email syntax or domain verification failed |
| `risky` | The domain is identified as disposable |
| `error` | The verification provider could not complete the check |

The structured response allows an MCP client or agent to consume the verification result programmatically instead of parsing an unstructured text response.

---

## Validation Flow

```text
Input email
    |
    v
Normalize
(trim + lowercase)
    |
    v
Syntax validation
    |
    +------ invalid ------> invalid
    |
    v
Extract domain
    |
    v
Provider verification
    |
    +---- provider error ---> error
    |
    v
Domain exists?
    |
    +--------- no ---------> invalid
    |
    v
Disposable domain?
    |
    +--------- yes --------> risky
    |
    v
valid
```

### 1. Normalization

The input is stripped of surrounding whitespace and converted to lowercase.

For example:

```text
  USER@EXAMPLE.COM
```

becomes:

```text
user@example.com
```

### 2. Syntax Validation

The service first performs a lightweight syntax check.

Invalid syntax is rejected before making a provider call.

### 3. Domain Verification

After syntax validation, the domain is passed to the verification provider.

The mock provider simulates whether the domain exists.

### 4. Disposable-Domain Detection

The provider identifies domains included in the configured disposable-domain list.

A disposable domain produces:

```text
status = risky
```

rather than `invalid`.

---

## Architecture and Design Decisions

### Thin MCP Layer

The MCP server is responsible for exposing the tool and passing the input to the verification service.

It does not contain the verification business rules.

This keeps the protocol layer simple and makes the core verification logic independently testable.

### Verification Service

The verification service contains the application logic:

- normalization
- syntax validation
- provider invocation
- domain evaluation
- risk evaluation
- structured result creation

This separation means the service can be tested without requiring an MCP client.

### Provider Separation

The verification service delegates domain verification to a provider.

The provider is currently implemented as a mock provider for deterministic testing.

```text
Verification Service
        |
        v
MockEmailVerificationProvider
```

A real provider can later replace the mock without changing the MCP tool contract.

This keeps the external verification dependency replaceable and avoids coupling the MCP interface to a specific provider.

---

## Error Handling

The implementation distinguishes between validation failures and provider failures.

### Invalid Input

For malformed email syntax, the service returns an `invalid` result.

### Invalid Domain

If the provider reports that the domain cannot be verified, the service returns an `invalid` result.

### Disposable Domain

If the provider identifies the domain as disposable, the service returns a `risky` result.

### Provider Failure

If the verification provider cannot complete the request, the service returns an `error` result.

This distinction is important because an invalid email and an unavailable verification service represent different conditions.

The implementation handles the expected `ProviderError` explicitly rather than broadly catching every exception and hiding unexpected programming errors.

---

## Retry and Backoff

The current provider is a local mock and therefore does not require network retries.

For a production provider, retries would be appropriate only for transient failures such as:

- connection failures
- HTTP 429 responses
- temporary provider failures
- HTTP 5xx responses

Permanent validation failures should not be retried.

A production retry strategy could use bounded exponential backoff:

```text
Request
   |
   v
Attempt 1
   |
   +---- success ---> result
   |
   +---- transient failure
              |
              v
           backoff
              |
              v
           Attempt 2
              |
              +---- transient failure
                         |
                         v
                      backoff
                         |
                         v
                      Attempt 3
                         |
                         +---- failure ---> error
```

The retry count and delays should be bounded to avoid increasing load on the provider during an outage.

---

## Assumptions

The following assumptions were made for this implementation:

1. A real email verification API is not required for the implementation.
2. The external verification dependency is therefore represented by a mock provider.
3. Domain existence is simulated by the mock provider.
4. Disposable-domain detection uses a small predefined domain list.
5. SMTP mailbox verification is outside the scope of this implementation.
6. The MCP server is designed around STDIO transport for local MCP client integration.

These assumptions keep the implementation focused on the MCP integration and verification pipeline.

---

## Testing

The project contains unit tests covering:

- valid email
- invalid email syntax
- disposable email
- email normalization
- invalid domain
- disposable domain
- provider failure

Run:

```bash
pytest
```

Current result:

```text
7 passed
```

### MCP Integration Test

The project also contains an MCP client integration test.

Run:

```bash
python tests/test_mcp_server.py
```

This verifies that:

1. The MCP server can be started.
2. An MCP session can be initialized.
3. The `verify_email` tool is discovered.
4. The tool can be invoked.
5. A structured verification result is returned.

---

## MCP Inspector Demo

The implementation was tested using MCP Inspector.

### 1. Server Connection

The MCP server successfully connects using STDIO transport.

![MCP server connected](screenshots/01-server-connected.png)

### 2. Tool Discovery

The MCP Inspector discovers the `verify_email` tool and exposes its required `address` input.

![verify_email tool discovery](screenshots/02-tool-discovery.png)

### 3. Tool Execution

The tool was invoked with:

```text
user@example.com
```

and returned a structured verification result.

![verify_email tool execution](screenshots/03-tool-execution.png)

### 4. Automated Tests

The verification test suite passes all seven test cases.

![Automated tests passing](screenshots/04-tests-passing.png)

---

## Project Structure

```text
email-verification-mcp/
│
├── src/
│   └── email_mcp/
│       ├── __init__.py
│       ├── models.py
│       ├── provider.py
│       ├── server.py
│       └── verifier.py
│
├── tests/
│   ├── test_verifier.py
│   └── test_mcp_server.py
│
├── screenshots/
│   ├── 01-server-connected.png
│   ├── 02-tool-discovery.png
│   ├── 03-tool-execution.png
│   └── 04-tests-passing.png
│
├── .gitignore
├── pyproject.toml
├── requirements.txt
├── uv.lock
└── README.md
```

---

## Setup

### Prerequisites

- Python 3.14+
- pip
- MCP Python SDK

### 1. Clone the Repository

```bash
git clone <repository-url>
cd email-verification-mcp
```

### 2. Create a Virtual Environment

On Windows:

```bash
python -m venv .venv
.venv\Scripts\activate
```

### 3. Install Dependencies

```bash
pip install -r requirements.txt
```

### 4. Run Tests

```bash
pytest
```

### 5. Run the MCP Server

```bash
python -m email_mcp.server
```

The server uses STDIO transport and waits for an MCP-compatible client.

### 6. Run with MCP Inspector

```bash
mcp dev src/email_mcp/server.py --with .
```

---

## Trade-offs

### What I Optimized For

- Simplicity
- Modularity
- Testability
- Clear separation of concerns
- Minimal infrastructure
- Replaceable provider implementation

### What I Intentionally Did Not Add

- Database
- Redis
- Message queue
- Web framework
- Real external verification API
- Complex deployment infrastructure

These components are not necessary to demonstrate the requested MCP interface and would add complexity to the current implementation.

The provider boundary leaves room to introduce production infrastructure when actual scale and reliability requirements justify it.

---

## Scalability Considerations

The current implementation is intentionally small, but the architecture allows the verification pipeline to evolve without changing the MCP tool contract.

A production implementation could add:

- Real verification provider
- DNS/MX verification
- Provider timeouts
- Retry and backoff
- Rate limiting
- Domain-result caching
- Structured logging
- Metrics and monitoring
- Provider fallback
- Asynchronous processing for high-volume workloads

The important architectural decision is that these additions can be made behind the service/provider boundary rather than requiring a rewrite of the MCP interface.

---

## Future Improvements

If this implementation were extended for production, I would prioritize:

1. Replace the mock provider with a real email verification API.
2. Add provider timeouts and bounded retries.
3. Add caching for repeated domain checks.
4. Add rate limiting.
5. Add structured logging and metrics.
6. Add integration tests for the real provider.
7. Benchmark concurrent verification requests.
8. Add provider fallback for improved availability.

---

## Conclusion

The implementation provides a small, modular MCP server exposing email verification through a structured `verify_email` tool.

The main design goal was to keep the system:

- easy to understand
- independently testable
- modular
- lightweight
- replaceable at the provider layer

while leaving a clear path toward a production verification backend.