- Multi-account email support (Yahoo + self-hosted IMAP/SMTP) - MCP tools: get_profile, search_messages, read_message, list_folders, create_draft, send_email - Streamable HTTP transport with session recovery - Docker + Kubernetes deployment configuration - Express server with health endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.8 KiB
5.8 KiB
Hermes MCP — Setup & Deployment
Hermes is a multi-account email MCP server for Claude AI. It supports Yahoo Mail (IMAP App Password) and any self-hosted mail server (Dovecot / Poste.io).
Local development
# 1. Install dependencies
npm install
# 2. Copy and fill in credentials
cp .env.example .env
# edit .env with your email credentials
# 3. Run in dev mode (hot-reload)
npm run dev
# 4. Test health
curl http://localhost:3456/health
# → {"status":"ok","service":"hermes-mcp"}
Production deployment (MicroK8s — current setup)
The server runs as a Kubernetes Deployment on 23.120.207.35 (MicroK8s single-node cluster).
SSL is handled by cert-manager with a Let's Encrypt cert for hermes.fetcherpay.com.
Prerequisites on the server
- MicroK8s with addons:
dns,ingress,registry,cert-manager - Local registry at
localhost:32000 - A
ClusterIssuernamedletsencrypt-prodalready configured
One-time: create K8s namespace secret
microk8s kubectl create namespace fetcherpay # if it doesn't exist
microk8s kubectl create secret generic hermes-mcp-env -n fetcherpay \
--from-literal=YAHOO_EMAIL=gheron01@yahoo.com \
--from-literal=YAHOO_APP_PASSWORD=lzlleytmslxocxae \
--from-literal=FETCHERPAY_EMAIL=garfield.heron@fetcherpay.com \
--from-literal=FETCHERPAY_PASSWORD=onelove \
--from-literal=FETCHERPAY_IMAP_HOST=23.120.207.35 \
--from-literal=FETCHERPAY_IMAP_PORT=30993 \
--from-literal=FETCHERPAY_SMTP_HOST=23.120.207.35 \
--from-literal=FETCHERPAY_SMTP_PORT=30587 \
--from-literal=PORT=3456
Build & push image
# From your local machine — SCP src to server first, then SSH in:
scp -P 2222 -r src/ package*.json tsconfig.json Dockerfile garfield@23.120.207.35:~/hermes-mcp/
ssh -p 2222 garfield@23.120.207.35
cd ~/hermes-mcp
docker build -t localhost:32000/hermes-mcp:latest .
docker push localhost:32000/hermes-mcp:latest
Apply K8s manifests (hermes-k8s.yaml on the server)
# ~/hermes-mcp/hermes-k8s.yaml (already applied — shown for reference)
apiVersion: apps/v1
kind: Deployment
metadata:
name: hermes-mcp
namespace: fetcherpay
spec:
replicas: 1
selector:
matchLabels:
app: hermes-mcp
template:
metadata:
labels:
app: hermes-mcp
spec:
containers:
- name: hermes-mcp
image: localhost:32000/hermes-mcp:latest
ports:
- containerPort: 3456
envFrom:
- secretRef:
name: hermes-mcp-env
readinessProbe:
httpGet:
path: /health
port: 3456
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: hermes-mcp
namespace: fetcherpay
spec:
selector:
app: hermes-mcp
ports:
- port: 3456
targetPort: 3456
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hermes-ingress
namespace: fetcherpay
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
rules:
- host: hermes.fetcherpay.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hermes-mcp
port:
number: 3456
tls:
- hosts:
- hermes.fetcherpay.com
secretName: hermes-fetcherpay-tls
Redeploy after code changes
# On your local machine:
scp -P 2222 src/imap.ts src/smtp.ts src/tools.ts src/index.ts \
garfield@23.120.207.35:~/hermes-mcp/src/
ssh -p 2222 garfield@23.120.207.35 "
cd ~/hermes-mcp &&
docker build -t localhost:32000/hermes-mcp:latest . &&
docker push localhost:32000/hermes-mcp:latest &&
microk8s kubectl rollout restart deployment/hermes-mcp -n fetcherpay &&
microk8s kubectl rollout status deployment/hermes-mcp -n fetcherpay
"
Useful commands
# Logs
microk8s kubectl logs -n fetcherpay -l app=hermes-mcp --tail=100 -f
# Pod status
microk8s kubectl get pods -n fetcherpay -l app=hermes-mcp
# Update a single env var without rebuild (takes effect on next rollout)
microk8s kubectl set env deployment/hermes-mcp -n fetcherpay KEY=value
microk8s kubectl rollout restart deployment/hermes-mcp -n fetcherpay
# Health check
curl https://hermes.fetcherpay.com/health
Add to Claude.ai
- Go to Claude.ai → Settings → Connectors → Add custom connector
- Enter URL:
https://hermes.fetcherpay.com/mcp - Click Connect
Available tools
| Tool | Description | Key params |
|---|---|---|
get_profile |
Get email address for an account | account |
search_messages |
Search INBOX by keyword/sender/subject | q, maxResults, account |
read_message |
Read full message body by UID | uid, account |
list_folders |
List all mailbox folders | account |
create_draft |
Save a draft to the Drafts folder | to, subject, body, account |
send_email |
Send an email | to, subject, body, account |
account is always optional — defaults to "yahoo". Set to "fetcherpay" for the FetcherPay mailbox.
Known issues & fixes
| Issue | Cause | Fix |
|---|---|---|
yahoo_read_message 5-min timeout |
source: true downloads full raw RFC822 |
Use bodyParts: ['TEXT'] |
messageFlagsAdd deadlock |
Called inside for await loop while FETCH active |
Moved to after the loop |
| Stale session after pod restart | !sessionId guard blocked re-initialize |
Accept initialize regardless of session ID |
FetcherPay EAI_AGAIN DNS |
K8s internal DNS cold-start for hostname | Use direct IP 23.120.207.35 |