Files
hermes-mcp/DEPLOY.md
garfieldheron 9ecb02785c Remove specific account references from public documentation
- Replace FETCHERPAY with generic CUSTOM account examples
- Update README.md, .env.example, and DEPLOY.md with generic configurations
- Remove hardcoded IPs, email addresses, and domain names
- Update package.json description to be more generic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-05 13:37:51 -05:00

5.6 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 custom IMAP/SMTP server.


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 (Kubernetes example)

Example deployment using MicroK8s single-node cluster. SSL is handled by cert-manager with a Let's Encrypt certificate.

Prerequisites on the server

  • MicroK8s with addons: dns, ingress, registry, cert-manager
  • Local registry at localhost:32000
  • A ClusterIssuer named letsencrypt-prod already configured

One-time: create K8s namespace and secret

microk8s kubectl create namespace hermes-mcp   # if it doesn't exist

microk8s kubectl create secret generic hermes-mcp-env -n hermes-mcp \
  --from-literal=YAHOO_EMAIL=your@yahoo.com \
  --from-literal=YAHOO_APP_PASSWORD=your-app-password \
  --from-literal=CUSTOM_EMAIL=your@domain.com \
  --from-literal=CUSTOM_PASSWORD=your-password \
  --from-literal=CUSTOM_IMAP_HOST=mail.yourdomain.com \
  --from-literal=CUSTOM_IMAP_PORT=993 \
  --from-literal=CUSTOM_SMTP_HOST=mail.yourdomain.com \
  --from-literal=CUSTOM_SMTP_PORT=587 \
  --from-literal=PORT=3456

Build & push image

# Option 1: Build locally and push to your registry
docker build -t localhost:32000/hermes-mcp:latest .
docker push localhost:32000/hermes-mcp:latest

# Option 2: Copy to server and build there
scp -r src/ package*.json tsconfig.json Dockerfile user@your-server:~/hermes-mcp/
ssh user@your-server "cd ~/hermes-mcp && docker build -t localhost:32000/hermes-mcp:latest . && docker push localhost:32000/hermes-mcp:latest"

Apply K8s manifests

# hermes-k8s.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hermes-mcp
  namespace: hermes-mcp
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: hermes-mcp
spec:
  selector:
    app: hermes-mcp
  ports:
    - port: 3456
      targetPort: 3456
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hermes-ingress
  namespace: hermes-mcp
  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.yourdomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hermes-mcp
                port:
                  number: 3456
  tls:
    - hosts:
        - hermes.yourdomain.com
      secretName: hermes-tls

Apply the manifests:

microk8s kubectl apply -f hermes-k8s.yaml

Redeploy after code changes

# Rebuild and push the image
docker build -t localhost:32000/hermes-mcp:latest .
docker push localhost:32000/hermes-mcp:latest

# Restart the deployment
microk8s kubectl rollout restart deployment/hermes-mcp -n hermes-mcp
microk8s kubectl rollout status deployment/hermes-mcp -n hermes-mcp

Useful commands

# Logs
microk8s kubectl logs -n hermes-mcp -l app=hermes-mcp --tail=100 -f

# Pod status
microk8s kubectl get pods -n hermes-mcp -l app=hermes-mcp

# Update a single env var without rebuild (takes effect on next rollout)
microk8s kubectl set env deployment/hermes-mcp -n hermes-mcp KEY=value
microk8s kubectl rollout restart deployment/hermes-mcp -n hermes-mcp

# Health check
curl https://hermes.yourdomain.com/health

Add to Claude.ai

  1. Go to Claude.ai → Settings → Connectors → Add custom connector
  2. Enter URL: https://hermes.yourdomain.com/mcp (or your server's URL)
  3. 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 and defaults to "yahoo". Configure your second account in the code if needed.


Known issues & fixes

Issue Cause Fix
read_message timeout source: true downloads full raw RFC822 Use bodyParts: ['TEXT'] instead
messageFlagsAdd deadlock Called inside for await loop while FETCH active Moved to after the loop
Stale session after pod restart Session ID guard blocked re-initialize Accept initialize regardless of session ID
EAI_AGAIN DNS errors K8s internal DNS resolution issues Use direct IP instead of hostname