# UChecker MCP Server [![CI](https://github.com/uChecker-net/mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/uChecker-net/mcp/actions/workflows/ci.yml) An [MCP](https://modelcontextprotocol.io) server for the [UChecker](https://uchecker.net) email validation API. It lets an AI assistant validate email addresses, track validation tasks, read per-address results and account analytics, and export cleaned lists — without you writing any API glue. Ten tools, three resources and two guided prompts, over either stdio (local) or Streamable HTTP (remote). ## Quick start ### Local (stdio) You need a UChecker API key from [app.uchecker.net](https://app.uchecker.net). ```jsonc // Claude Desktop: claude_desktop_config.json // Claude Code: .mcp.json { "mcpServers": { "uchecker": { "command": "node", "args": ["/path/to/mcp/build/stdio.js"], "env": { "UCHECKER_API_KEY": "uk_..." } } } } ``` The key can also be passed as `--api-key=uk_...`. Build first with `npm ci && npm run build`. ### Remote (Streamable HTTP) A hosted instance runs at `https://api.uchecker.net/mcp`. It is stateless: the API key travels on every request, and no session is kept between calls. ```bash curl -X POST https://api.uchecker.net/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'x-api-key: uk_...' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"get_account_balance","arguments":{}}}' ``` `Authorization: Bearer uk_...` works as well. Health check: `GET /mcp/health`. To run your own instance, see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md). > The remote endpoint authenticates with an API key header, not OAuth, so > clients that can only do OAuth or cannot set custom headers need the stdio > build instead. ## Configuration | Variable | Mode | Default | Purpose | |---|---|---|---| | `UCHECKER_API_KEY` | stdio | — | Required. Also settable via `--api-key=`. | | `UCHECKER_API_URL` | both | `https://api.uchecker.net` | Upstream API. Also `--api-url=` in stdio. | | `UCHECKER_PUBLIC_API_URL` | http | `https://api.uchecker.net` | URL shown to users in download hints, when the server reaches the API over an internal network. | | `MCP_PORT` | http | `3009` | Listen port. | In HTTP mode the key is never configured server-side — it comes from the `x-api-key` or `Authorization: Bearer` header of each request, so one instance serves many accounts. ## Tools | Tool | What it does | Costs credits | |---|---|---| | `validate_email` | Queue one address for validation | 1 | | `validate_emails` | Queue a batch (max 10 000 per call) | 1 per address | | `get_task_status` | Status and progress of a task | — | | `wait_for_task` | Poll until completed/failed, with progress notifications | — | | `get_task_results` | Per-address results, paged and filterable | — | | `get_task_analytics` | Counts, deliverability %, rejection reasons | — | | `export_results` | Save the full result list to a file (stdio) or return a curl command (http) | — | | `list_tasks` | Paginated task history | — | | `get_account_balance` | Remaining credits | — | | `get_account_stats` | Account-wide totals and averages | — | Full parameter and output reference: [docs/TOOLS.md](docs/TOOLS.md). ### Resources - `uchecker://account/balance` — remaining credits - `uchecker://tasks` — 20 most recent tasks - `uchecker://tasks/{taskId}/analytics` — analytics for one task ### Prompts - `clean_email_list(source?)` — end-to-end workflow: check balance, validate in chunks, wait, export cleaned good/bad lists, report deliverability - `deliverability_report()` — deliverability trend across recent tasks ## How validation works Addresses are queued, not checked synchronously. `validate_email` and `validate_emails` return a `task_id` immediately; the task moves through `pending` → `processing` → `completed`. A five-address list typically finishes in well under a minute, but larger lists take proportionally longer, so: - prefer `wait_for_task` over a manual `get_task_status` loop — it emits MCP progress notifications while it waits and returns `timed_out: true` instead of hanging forever; - for long lists pass `webhook_url` and let the API call you back; - use `get_task_analytics` when you only need aggregates — it is far cheaper than pulling every row. Each address ends up `good`, `bad`, or `unknown`. `unknown` means the check could not reach a verdict (unreachable MX, greylisting, catch-all ambiguity) — it is not a synonym for invalid. ## Development The server runs on Node 20+; the test toolchain needs Node 22.12+. ```bash npm ci npm run build # tsc -> build/ npm test # vitest, no network ``` A live smoke test drives the built stdio server against a real API: ```bash UCHECKER_API_KEY=uk_... UCHECKER_API_URL=https://api.staging.uchecker.net \ node tests/live-staging.mjs [completedTaskId] ``` It spends one credit on a real `validate_email` call, so point it at staging unless you are deliberately verifying production. Architecture: `src/core/` holds the transport-independent server (API client, tools, resources, prompts); `src/stdio.ts` and `src/http.ts` are the two entry points. Adding a tool means touching `src/core/tools.ts` only. Further reading: - [docs/TOOLS.md](docs/TOOLS.md) — tool reference - [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) — self-hosting with Docker behind a reverse proxy - [docs/API-NOTES.md](docs/API-NOTES.md) — upstream API quirks this server absorbs - [CHANGELOG.md](CHANGELOG.md) ## License [MIT](LICENSE)