Docker Setup

Overview

The project includes Docker support for running Elasticsearch and the MCP server. This is the recommended way to run the infrastructure.

Quick Start

Download the docker-compose.yml and start all services:

# Create a directory and download docker-compose.yml
mkdir mail-mcp && cd mail-mcp
curl -O https://raw.githubusercontent.com/support-and-care-labs/mail-mcp/main/docker-compose.yml

# Create data directory
mkdir -p data

# Start all services
docker compose up -d

# Wait for services to be healthy
docker compose ps

# Check MCP server health
curl http://localhost:58080/health

The docker-compose.yml is also available for download: docker-compose.yml

Prerequisites

Docker Desktop

Version with Docker Compose V2 integrated

This project uses docker compose (Compose V2, integrated into Docker Desktop), not the older standalone docker-compose.

Services

Elasticsearch

Storage backend for indexed email data.

Image

docker.elastic.co/elasticsearch/elasticsearch:8.11.0

Ports

9200 (HTTP API), 9300 (transport)

Configuration

Single-node cluster, security disabled for local development

Kibana (Optional)

Web UI for exploring and visualizing Elasticsearch data. Kibana is not started by default - use the analysis profile to start it on demand.

Image

docker.elastic.co/kibana/kibana:8.11.0

Ports

5601 (HTTP)

Access

http://localhost:55601

Profile

analysis (start with docker compose --profile analysis up -d kibana)

Features
  • Discover - Search and filter documents

  • Visualizations - Create charts and graphs

  • Dashboards - Combined visualizations

  • Dev Tools - Run Elasticsearch queries directly

mail-mcp

MCP server application providing Streamable HTTP transport.

Image

ghcr.io/support-and-care-labs/mail-mcp:latest

Platforms

linux/amd64, linux/arm64

Ports

58080 (Streamable HTTP)

Access

http://localhost:58080/mcp (for MCP clients)

Health check

http://localhost:58080/health

scheduler

Background service for automatic mbox updates.

Image

ghcr.io/support-and-care-labs/mail-mcp:latest

Platforms

linux/amd64, linux/arm64

Schedule

Hourly updates of current month’s mbox

Function

Retrieves and re-indexes latest emails automatically

Docker Compose File

The complete docker-compose.yml for running all services:

docker-compose.yml
#
# Copyright 2025 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Docker Compose configuration for mail-mcp
#
# Quick Start:
#   mkdir -p data && docker compose up -d
#
# For development (build from source):
#   docker compose build && docker compose up -d

services:
  # Elasticsearch for storing and searching email data
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    container_name: mail-mcp-elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - cluster.name=mail-mcp-cluster
      - node.name=mail-mcp-node
    ports:
      - "59200:9200"
      - "59300:9300"
    volumes:
      - es-data:/usr/share/elasticsearch/data
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - mail-mcp-network
    restart: unless-stopped

  # Kibana for data visualization and exploration (optional, start manually)
  # Start with: docker compose --profile analysis up -d kibana
  kibana:
    profiles:
      - analysis
    image: docker.elastic.co/kibana/kibana:8.11.0
    container_name: mail-mcp-kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - SERVER_NAME=mail-mcp-kibana
      - SERVER_HOST=0.0.0.0
    ports:
      - "55601:5601"
    depends_on:
      elasticsearch:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:5601/api/status || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    networks:
      - mail-mcp-network
    restart: unless-stopped

  # mail-mcp MCP server
  mail-mcp:
    image: ghcr.io/support-and-care-labs/mail-mcp:latest
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mail-mcp-server
    environment:
      - MAIL_MCP_ELASTICSEARCH_URL=http://elasticsearch:9200
      - MAIL_MCP_ELASTICSEARCH_INDEX_PREFIX=maven
      - MAIL_MCP_DATA_PATH=/app/data
      - MAIL_MCP_LOG_LEVEL=INFO
      - PYTHONUNBUFFERED=1
    volumes:
      # Mount data directory for mbox files
      - ./data:/app/data:ro
      # Mount source code for development (optional)
      - ./src:/app/src:ro
      # Mount maven-jira-projects.toml
      - ./maven-jira-projects.toml:/app/maven-jira-projects.toml:ro
    depends_on:
      elasticsearch:
        condition: service_healthy
    networks:
      - mail-mcp-network
    restart: unless-stopped
    # HTTP transport exposed on port 58080
    ports:
      - "58080:8080"
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health').read()"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  # Scheduler for periodic mbox updates
  # Runs hourly to fetch the current month's mbox and re-index into Elasticsearch
  scheduler:
    image: ghcr.io/support-and-care-labs/mail-mcp:latest
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mail-mcp-scheduler
    environment:
      - MAIL_MCP_ELASTICSEARCH_URL=http://elasticsearch:9200
      - MAIL_MCP_ELASTICSEARCH_INDEX_PREFIX=maven
      - MAIL_MCP_DATA_PATH=/app/data
      - MAIL_MCP_MAILING_LISTS=dev@maven.apache.org,users@maven.apache.org
      - MAIL_MCP_LOG_LEVEL=INFO
      - PYTHONUNBUFFERED=1
    volumes:
      # Mount data directory as writable for scheduler to update mbox files
      - ./data:/app/data:rw
      # Mount source code for development (optional)
      - ./src:/app/src:ro
    depends_on:
      elasticsearch:
        condition: service_healthy
    networks:
      - mail-mcp-network
    restart: unless-stopped
    # Override default command to run supercronic scheduler
    command: ["/usr/local/bin/supercronic", "-json", "/app/crontab"]

volumes:
  es-data:
    driver: local

networks:
  mail-mcp-network:
    driver: bridge

Connecting MCP Clients

Once services are running, configure your MCP client:

The Mail MCP server runs as a persistent Docker service and is accessed via Streamable HTTP transport.

claude mcp add maven-mail --transport http http://localhost:58080/mcp

Manual JSON Configuration

For project-level configuration, add to .mcp.json in your project root:

{
  "mcpServers": {
    "maven-mail": {
      "type": "streamable-http",
      "url": "http://localhost:58080/mcp"
    }
  }
}
Ensure Docker services are running (docker compose up -d) before starting your MCP client.

Common Commands

Start Services

# Start all services (Elasticsearch, mail-mcp, scheduler)
# Note: Kibana is NOT started by default
docker compose up -d

# Start Elasticsearch only
docker compose up -d elasticsearch

# Start Kibana for data exploration (optional, on-demand)
docker compose --profile analysis up -d kibana

# View logs
docker compose logs -f mail-mcp
docker compose logs -f scheduler
docker compose logs -f elasticsearch

All services have restart: unless-stopped policy. They will automatically restart when Docker restarts, unless explicitly stopped.

Stop Services

# Stop all services
docker compose down

# Stop and remove volumes (clears all Elasticsearch data)
docker compose down -v

Check Status

# List running containers
docker compose ps

# Check mail-mcp health
curl http://localhost:58080/health

# Check Elasticsearch health
curl http://localhost:59200/_cluster/health

# View cluster info
curl http://localhost:59200/

# Check Kibana status (if running)
curl http://localhost:55601/api/status

# Open Kibana in browser
open http://localhost:55601

Configuration

Configuration is handled via environment variables in docker-compose.yml.

Elasticsearch Settings

environment:
  - discovery.type=single-node
  - xpack.security.enabled=false
  - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
  - cluster.name=mail-mcp-cluster
  - node.name=mail-mcp-node

mail-mcp Settings

environment:
  - MAIL_MCP_ELASTICSEARCH_URL=http://elasticsearch:9200
  - MAIL_MCP_ELASTICSEARCH_INDEX_PREFIX=maven
  - MAIL_MCP_DATA_PATH=/app/data

Data Persistence

Elasticsearch data is stored in a named Docker volume.

# List volumes
docker volume ls | grep mail-mcp

# Inspect volume
docker volume inspect mail-mcp_es-data

# Remove volume (deletes all indexed data)
docker volume rm mail-mcp_es-data

Using Kibana

Kibana provides a web-based interface for exploring your indexed email data.

Kibana is not started by default. Start it with: docker compose --profile analysis up -d kibana

First Time Setup

After starting Kibana and indexing some data:

  1. Access Kibana: Open http://localhost:55601 in your browser

  2. Create Data View:

    • Navigate to Management → Stack Management → Data Views

    • Click "Create data view"

    • Pattern: maven-* (matches all Maven indices)

    • Time field: date

    • Click "Save data view to Kibana"

Discover

Use Discover to search and explore emails:

  1. Navigate to Analytics → Discover

  2. Select your maven-* data view

  3. Use the search bar for queries:

    • subject:"release" - Find release discussions

    • from_name:"Tamás" - Emails from Tamás

    • has_vote:true - Emails with votes

    • jira_references:* - Emails mentioning JIRA issues

Dev Tools

Use Dev Tools for direct Elasticsearch queries:

  1. Navigate to Management → Dev Tools

  2. Run queries in the Console:

GET maven-dev/_search
{
  "query": {
    "match": {
      "subject": "maven 4"
    }
  }
}

Common Kibana Queries

Find emails by contributor:

GET maven-dev/_search
{
  "query": {
    "wildcard": {
      "from_address": "*cstamas*"
    }
  }
}

Find voting threads:

GET maven-dev/_search
{
  "query": {
    "bool": {
      "must": [
        { "term": { "has_vote": true } }
      ]
    }
  }
}

Find emails with JIRA references:

GET maven-dev/_search
{
  "query": {
    "exists": {
      "field": "jira_references"
    }
  }
}

Development Workflow

Typical Development Session

# Start core services (Elasticsearch, mail-mcp, scheduler)
docker compose up -d

# Wait for Elasticsearch to be ready
curl --retry 10 --retry-delay 2 http://localhost:59200/_cluster/health

# Work with the application
poetry shell
python -m mail_mcp.storage.elasticsearch  # Example

# Start Kibana for data exploration (when needed)
docker compose --profile analysis up -d kibana
curl --retry 10 --retry-delay 2 http://localhost:55601/api/status
open http://localhost:55601

# Stop Kibana when done with analysis
docker compose stop kibana

# Stop all services when done
docker compose down

Hot Reload for Development

The mail-mcp service mounts source code as a volume for live updates:

volumes:
  - ./src:/app/src:ro

Changes to Python files are reflected immediately (if using a hot-reload server).

Troubleshooting

Elasticsearch won’t start

Check Docker Desktop resources

Elasticsearch needs at least 512MB RAM.

View logs
docker compose logs elasticsearch

Port already in use

Check for existing Elasticsearch instances
lsof -i :9200
Change ports in docker-compose.yml
ports:
  - "9201:9200"  # Map to different host port

Container keeps restarting

Check health status
docker compose ps
View detailed logs
docker compose logs --tail=100 elasticsearch

Clear all data and restart

docker compose down -v
docker compose up -d

Production Deployment

The current Docker setup is optimized for local development. For production deployment:

  • Enable Elasticsearch security (xpack.security.enabled=true)

  • Use proper TLS certificates

  • Configure appropriate heap sizes

  • Set up proper networking

  • Use Docker secrets for sensitive configuration

  • Configure backup/restore procedures

See Elasticsearch documentation for production best practices.