Raised: $0
0% of monthly goal Help us cross the finish line!
Goal: $12,000
Raised: $0 Goal: $12,000
0% of monthly goal Help us cross the finish line!
Sponsor DDEV

If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.

ddev-maildev

MailDev for DDEV. Your coding agent reads the dev inbox over MCP.

add-on registry tests release last commit
DDEV v1.25.4 or newer MailDev 3.0.0-rc.3 MCP enabled Apache-2.0 licence

The idea

DDEV already catches mail with Mailpit. Catching mail is not the point.

The point is that you cannot ask Mailpit anything. A test sends a password reset, you want the link, so you open a browser and copy it out by hand. MailDev 3 answers over the Model Context Protocol, so your agent can search the inbox and read the message itself.

Install

ddev add-on get wazum/ddev-maildev
ddev restart

That is all. PHP mail now goes to MailDev, and the maildev server is in your .mcp.json. Claude Code reads that file on its next start and asks you to approve the server once. Other MCP clients use their own config file, so copy the entry there.

ddev maildev     # open the inbox

The inbox is at https://<project>.ddev.site:1081. The login is in .ddev/.env.maildev.local.

Mail your framework sends itself

PHP’s mail() is redirected for you. Frameworks that speak SMTP themselves never use sendmail_path, so they need one line. The web container has MAILDEV_SMTP_HOST and MAILDEV_SMTP_PORT, so nothing is hardcoded:

Framework Where What to set
Symfony .env.local MAILER_DSN=smtp://${MAILDEV_SMTP_HOST}:${MAILDEV_SMTP_PORT}
Laravel .env MAIL_MAILER=smtp, MAIL_HOST=${MAILDEV_SMTP_HOST}, MAIL_PORT=${MAILDEV_SMTP_PORT}
TYPO3 AdditionalConfiguration.php $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = 'smtp';
…['transport_smtp_server'] = getenv('MAILDEV_SMTP_HOST') . ':' . getenv('MAILDEV_SMTP_PORT');

Turn encryption and credentials off for these. Restart the workers or clear the config cache if your framework caches them.

Why the inbox has a password

MailDev has no authentication by default. The add-on generates a password into .ddev/.env.maildev.local, which DDEV’s own .gitignore covers, and both the API and /mcp then ask for it. Without a password the installation stops.

The password also goes into .mcp.json, as an Authorization header. A .mcp.json the add-on creates is readable only by you. One you already had keeps your permissions. People commit this file, so the installation warns you if Git tracks it. Untrack it, or keep the entry in your own MCP config instead.

How it works

MailDev listens on 1025 for SMTP and 1080 for the web interface. The DDEV router publishes the interface on 1081 over HTTPS. SMTP stays inside Docker.

.ddev/php/maildev.ini changes sendmail_path to relay through the mailpit binary that is already in the web image, so nothing new is installed. It uses the container name, not the service name: DDEV puts every service on one shared network, where a plain maildev is ambiguous as soon as a second project installs this add-on.

Mail lives in a Docker volume and survives ddev restart.

What it does to your files

File What happens
.mcp.json the maildev server is merged in, your other servers are left alone
.ddev/php/maildev.ini generated, and skipped on reinstall if you remove its #ddev-generated marker
.ddev/.env.web.maildev the SMTP host and port for the web container
.ddev/.env.maildev.local the password, and your MAILDEV_IMAGE override if you set one

The add-on records what it wrote in .ddev/maildev/mcp-state.json. Removal takes out that entry only, and only while it still matches. An entry you edited stays, and so does a .mcp.json that was there before you installed.

Known limits

The pin is a release candidate. MailDev 3 is a rewrite and the only line with an MCP server, so 3.0.0-rc.3 is what you get. Override it with MAILDEV_IMAGE.

Mailpit keeps running and stays empty. It cannot be removed from the web container, so ddev describe lists both.

Links in MCP replies point at http://localhost:1080. MailDev builds them from its own bind address and has no setting for the public URL, so they do not open. The message content is correct.

Two writes are not one transaction. If writing .mcp.json fails after the state was recorded, a reinstall or a removal recovers. Someone editing the file during that moment is not detected.

Anything on your machine can post to the inbox. DDEV puts every service on one shared Docker network, so another project’s container can reach this SMTP port, and MailDev accepts mail without a password, as mail servers do. Treat what comes out of the inbox as data, not as instructions.

Removing it

ddev add-on remove maildev
ddev restart

This removes the container, the volume, the generated files and the MCP entry. The restart sends PHP mail back to Mailpit. .ddev/.env.maildev.local stays, in case you put your own settings in it. Framework SMTP settings are yours to undo.

Licence

Apache-2.0

If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.