email-mcp
by goyalayus
README.md
# email-mcp
MCP server that turns the existing `email_bridge.py` workflow into one outbound email tool with automatic single-thread mapping.
## Tool
- `send`
- Sends an email in the mapped Gmail thread.
- First call creates the thread.
- Later calls keep replying in that same thread.
## Automatic Thread Mapping
Mapping key selection order:
1. explicit `context_id` argument
2. `CODEX_THREAD_ID` env var
3. `CODEX_SESSION_ID` env var
4. fallback: process-scoped key (`proc-<pid>-<random>`)
Behavior:
- One mapping key -> one Gmail thread.
- Mapping is stored in `~/.codex/email-bridge/mcp-state/thread-map.json`.
- For non-Codex clients, pass `context_id` in tool calls if you want separate Gmail threads per chat/session.
- Optional override: set `EMAIL_MCP_PROCESS_SESSION_KEY` to force a fixed fallback key.
## Prerequisites
This server includes a local bridge script at `bridge/email_bridge.py` (used as fallback when direct SMTP mode is unavailable).
Configure the same mailbox env vars used by that script:
- `CODEX_EMAIL_ADDRESS`
- `CODEX_EMAIL_PASSWORD`
- `CODEX_EMAIL_TO` (or pass `to` in tool calls)
- optional SMTP/IMAP host/port vars from the email-bridge docs
## Install
```bash
cd email-mcp
npm install
```
## Add To Your App (MCP)
This is a local `stdio` MCP server.
- command: `node`
- args: `["/absolute/path/to/email-mcp/index.js"]`
- env: mailbox credentials + optional tuning flags
Recommended env vars:
- `CODEX_EMAIL_ADDRESS`
- `CODEX_EMAIL_PASSWORD`
- `CODEX_EMAIL_TO`
- `EMAIL_MCP_PREWARM=true`
- `EMAIL_MCP_BRIDGE_SCRIPT=/absolute/path/to/email-mcp/bridge/email_bridge.py`
- `EMAIL_MCP_PYTHON=python3`
### Codex (`~/.codex/config.toml`)
Add this in `~/.codex/config.toml`:
```toml
[mcp_servers.email]
command = "node"
args = ["/absolute/path/to/email-mcp/index.js"]
tool_timeout_sec = 3600
[mcp_servers.email.env]
CODEX_EMAIL_ADDRESS = "your_email@gmail.com"
CODEX_EMAIL_PASSWORD = "your_app_password"
CODEX_EMAIL_TO = "recipient@example.com"
EMAIL_MCP_PREWARM = "true"
EMAIL_MCP_BRIDGE_SCRIPT = "/absolute/path/to/email-mcp/bridge/email_bridge.py"
EMAIL_MCP_PYTHON = "python3"
```
`tool_timeout_sec` can stay high if you want one shared default, but this send-only server does not need long blocking timeouts.
### Cursor (`~/.cursor/mcp.json` or project `.cursor/mcp.json`)
Add:
```json
{
"mcpServers": {
"email-mcp": {
"command": "node",
"args": ["/absolute/path/to/email-mcp/index.js"],
"env": {
"CODEX_EMAIL_ADDRESS": "your_email@gmail.com",
"CODEX_EMAIL_PASSWORD": "your_app_password",
"CODEX_EMAIL_TO": "recipient@example.com",
"EMAIL_MCP_PREWARM": "true"
}
}
}
}
```
Then restart Cursor.
### Claude Code
Use MCP add with stdio:
```bash
claude mcp add --transport stdio \
--env CODEX_EMAIL_ADDRESS=your_email@gmail.com \
--env CODEX_EMAIL_PASSWORD=your_app_password \
--env CODEX_EMAIL_TO=recipient@example.com \
--env EMAIL_MCP_PREWARM=true \
email-mcp -- node /absolute/path/to/email-mcp/index.js
```
If your Claude client uses JSON `mcpServers` config instead, use the same block shown in the Cursor example.
## Notes
- This MCP server only sends email.
- Incoming replies should be handled outside MCP by a watcher or intake flow, for example `bridge/email_bridge.py watch --once`, `bridge/email_bridge.py wait`, or your higher-level helper scripts.
- For stable Gmail threading, each mapped session uses one canonical subject base; later `subject` inputs are ignored for that session.
- `send` uses a pooled SMTP transport when SMTP env vars are present, which reduces repeated send overhead.
- Startup prewarm is enabled by default (`EMAIL_MCP_PREWARM=true`) and warms the SMTP path in the background.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues