@hrmoller/maildrop-mcp
v0.2.0
Published
MCP server for maildrop.cc disposable inboxes: list, read, wait for, extract one-time codes from, and delete test mail via the public GraphQL API.
Maintainers
Readme
maildrop-mcp
An MCP server for maildrop.cc disposable inboxes. It lets an AI agent list a test mailbox, wait for a mail to arrive, pull the one-time code out of it, read a message as plain text, delete it, or fetch the mailbox alias, all through maildrop's public GraphQL API and without a browser.
Built for QA flows where test accounts use [email protected] addresses and the agent
needs the verification mail: login OTPs, sign-up confirmations, CRM/marketing-automation test
sends.
Why an MCP server
- Cheap reads.
list_inboxreturns one line per message and never fetches bodies.get_messagestrips tags, so a typical notification mail is a few hundred characters instead of the 15–25 KB HTML or raw source. No browser navigation or screenshots. - Waiting built in.
wait_for_messageandget_otppoll server-side at the interval maildrop asks for (10 s) and return once a matching mail lands. - Permission-friendly. Tool calls are governed by MCP allow rules in the client, so an agent can be allowed to read a test inbox without a shell-command classifier deciding that "extract digits from an email" looks suspicious.
Install
Requires Node 20 or newer. No API key: maildrop's API is public and unauthenticated.
The package is published to npm as
@hrmoller/maildrop-mcp, so clients
can run it with npx and nothing needs to be cloned.
Claude Code
claude mcp add --scope user maildrop -- npx -y @hrmoller/maildrop-mcpThen allow the tools so they run without prompting, either through /permissions or in
~/.claude/settings.json:
{ "permissions": { "allow": ["mcp__maildrop__*"] } }Start a new session; the tools appear as mcp__maildrop__list_inbox and so on.
Claude Desktop, Cursor, other clients
Add a stdio server entry to the client's MCP config:
{
"mcpServers": {
"maildrop": {
"command": "npx",
"args": ["-y", "@hrmoller/maildrop-mcp"]
}
}
}From source
git clone https://github.com/hrmoller/maildrop-mcp.git
cd maildrop-mcp
npm install && npm run build
claude mcp add --scope user maildrop -- node "$PWD/dist/index.js"Tools
| Tool | Arguments | Returns |
|---|---|---|
| list_inbox | mailbox | One line per message: id \| date \| from \| subject. Max 10 (maildrop's cap). |
| get_message | mailbox, id, format = text (default) / html / raw | text: headers + tag-stripped body. html: HTML body. raw: full RFC822 source. |
| wait_for_message | mailbox, subject?, from?, after?, timeoutSeconds (default 120, max 240) | The newest matching message as text once it arrives; error on timeout. |
| get_otp | same as wait_for_message plus digits? | Only the one-time code (4–8 digits by default, or exactly digits). |
| delete_message | mailbox, id | Confirmation. |
| get_alias | mailbox | The D-…@maildrop.cc alias that forwards to this mailbox. |
| service_status | none | Whether maildrop is operational, plus global counters. |
mailbox accepts name or [email protected]; the suffix is stripped because the API
returns an empty inbox when it is present. Names are case-insensitive.
subject and from are case-insensitive regular expressions. from is matched against both
the From: header and the envelope sender.
after narrows the search to messages dated after a point in time: an ISO-8601 timestamp,
the word now, or a number of seconds ago. Default is 120 seconds ago. Capture a timestamp
before triggering the mail and pass it, so two codes requested in quick succession are not
confused.
One-time-code extraction
get_otp converts the HTML body to text and looks for a digit run of the requested length
(4–8 when unspecified). A run that follows a cue word (code, kode, PIN, verif…,
adgangs…, otp) wins; otherwise the first standalone run is used, skipping four-digit
values that look like years. If nothing qualifies the tool errors with the message id so
the agent can fall back to get_message.
Errors
Tool errors are returned as MCP isError results with a prefix naming the kind:
not_found, timeout, no_code, api, network, usage.
API facts
Verified against the live API and docs.maildrop.cc on 2026-09-03.
| Fact | Value |
|---|---|
| Endpoint | POST https://api.maildrop.cc/graphql with content-type: application/json (400 without it; GET is rejected) |
| Auth | None. The docs mention bearer tokens as a possible future measure. |
| Rate limit | 50 requests per 10 seconds, burst 25; 429 until you back off; persistent offenders are blocked. Poll a mailbox no more than every 10 s. |
| Mailbox | Holds at most 10 messages, oldest evicted. Deleted after 24 hours of inactivity. |
| Mailbox name | Local part only, case-insensitive. Any name works without registration. |
| Sending | maildrop cannot send mail (SPF -all). |
| Greylisting | A first mail from an unknown sending server is deferred; it can take about 15 minutes to arrive. |
| Message fields | id ip helo date mailfrom rcptto headerfrom subject data html; data is the raw RFC822 source, html the decoded HTML body. |
Security notes
- Maildrop inboxes are public. Anyone who guesses a mailbox name can read it. Use it only
for test accounts whose mail may be public, never for anything real. Use unguessable names
(
purpose_<random digits>), and hand out the alias fromget_aliaswhen a third party logs recipient addresses. - Codes are secrets, briefly.
get_otpreturns a code so the agent can use it. Do not record codes in tickets, docs, or logs. - The server only talks to
api.maildrop.cc. It has no other network access, no filesystem access, and no configuration.
Releasing
Merging to main publishes the package when package.json carries a version that is not on
the registry yet; a merge that does not bump the version runs the tests and stops. The
workflow is .github/workflows/release.yml:
- Tests run on Node 20, 22 and 24.
- If they pass and the version is new,
npm publishruns and the commit is taggedvX.Y.Zwith a GitHub release.
So a release is: bump version in package.json, add a CHANGELOG.md entry, merge to
main.
Publishing authenticates with npm trusted publishing
over GitHub Actions OIDC, so no npm token is stored in the repository, and npm attaches
provenance to each published version automatically. The trusted publisher configured on
npmjs.com must name the workflow file release.yml.
Development
npm run typecheck # tsc --noEmit
npm test # build + node:test unit tests
npm run smoke # build, then drive dist/index.js over stdio: initialize, tools/list, two tool calls
npm run smoke -- some_mailboxLayout: src/maildrop.ts holds the API client and pure helpers (HTML-to-text, code
extraction, filters); src/index.ts registers the tools and carries the server instructions
that clients load at session start. Tests live next to the code and run
against the compiled dist/.
License
MIT.
