| deploy | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| email_server.py | ||
| README.md | ||
| requirements.txt | ||
mg-mcp — Mailgun MCP Server
A Streamable HTTPS MCP server that exposes a single send_email tool, backed by Mailgun (mg.oliver.solutions).
Deployed at: https://optical-dev.oliver.solutions/mg-mcp/
Architecture
LibreChat (or any MCP client)
│ HTTPS, header: Authorization: Bearer <MCP_BEARER_KEY>
▼
Apache shared vhost on optical-dev.oliver.solutions
│ ProxyPass /mg-mcp/ → http://127.0.0.1:${MG_MCP_PORT}/ (prefix stripped)
▼
Docker container `mg-mcp` → uvicorn → FastAPI → FastMCP
│ GET /api/health (no auth)
│ POST /mcp (MCP streamable HTTP, requires Bearer)
▼
Mailgun API
Stateless. No DB. No persistent volumes.
Local development
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then edit and fill in real values
export $(grep -v '^#' .env | xargs)
python email_server.py # serves on http://127.0.0.1:8000
Quick health check:
curl http://127.0.0.1:8000/api/health
Deploying to optical-dev.oliver.solutions
One-time setup:
-
From a laptop, push this repo to GitHub:
cd /Users/daveporter/Desktop/CODING-2024/MG-MCP git init && git add . && git commit -m "Initial mg-mcp" gh repo create OliverGroup/mg-mcp --private --source=. --push -
SSH into the dev server and clone to
/opt/mg-mcp/:ssh user@optical-dev.oliver.solutions sudo git clone git@github.com:OliverGroup/mg-mcp.git /opt/mg-mcp sudo chown -R $USER /opt/mg-mcp cd /opt/mg-mcp -
Create
.envfrom the template and fill in real values:cp .env.example .env nano .envMAILGUN_API_KEY— the existing Mailgun private API key formg.oliver.solutionsMAILGUN_DOMAIN—mg.oliver.solutionsMAILGUN_FROM—noreply@mg.oliver.solutions(or any verified address)MCP_BEARER_KEY— generate withopenssl rand -hex 32. Share with anyone configuring an MCP client.- Leave
MG_MCP_PORTblank —deploy.shwill auto-pick.
-
Run the deploy:
bash deploy/deploy.shIt will print the chosen port, the public URL, and an Apache
Includeline. -
Add the printed
Includeline inside</VirtualHost>of/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf, alongside the other app Includes. -
Reload Apache:
sudo apachectl configtest && sudo systemctl reload apache2
Re-deploy (after pushing changes to GitHub):
ssh user@optical-dev.oliver.solutions
cd /opt/mg-mcp
bash deploy/deploy.sh
Flags: --no-pull, --no-build, --logs.
Verification
From a laptop after deploy:
# 1. Health (no auth) — should return JSON
curl https://optical-dev.oliver.solutions/mg-mcp/api/health
# → {"status":"ok","service":"mg-mcp"}
# 2. MCP endpoint without auth — should 401
curl -i https://optical-dev.oliver.solutions/mg-mcp/mcp
# → HTTP/1.1 401, body {"error":"Missing Bearer token"}
# 3. MCP initialize handshake (replace TOKEN)
TOKEN=...your MCP_BEARER_KEY...
curl -i -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-X POST https://optical-dev.oliver.solutions/mg-mcp/mcp \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# → 200 with a streaming JSON response listing server capabilities
Configure a client (LibreChat)
Settings → MCP Connectors → Add:
| Field | Value |
|---|---|
| Name | mailgun |
| MCP Server URL | https://optical-dev.oliver.solutions/mg-mcp/mcp |
| Transport | Streamable HTTPS |
| Authentication | API Key |
| Header Format | Bearer |
| API Key | the value of MCP_BEARER_KEY from the server's .env |
Save, then prompt: "Send an email to me@example.com with subject 'mg-mcp test' and body 'It works.'"
Troubleshooting
/mcpreturns 404 — the FastMCP SDK version may serve at a different sub-path. Checkdocker compose logs appfor the actual route, or try/instead of/mcpin the client URL.- Streaming responses hang or truncate — confirm
flushpackets=onis in the renderedapache-mg-mcp.confand thatmod_proxy_httpis loaded (sudo a2enmod proxy proxy_http headers && sudo systemctl reload apache2). - Health 200 locally, 502 publicly — the Include line is missing from the vhost or Apache wasn't reloaded.
- Mailgun 401 inside the tool — the API key in
.envis wrong; restart the container after fixing (docker compose up -d).