A secure, containerized Model Context Protocol (MCP) server that integrates SpamAssassin for defensive email security analysis. This server provides Claude Code with comprehensive email analysis capabilities while maintaining strict security boundaries.
Defensive Operations Only - This server exclusively provides security analysis capabilities:
- β Email spam detection and analysis
- β Sender reputation checking
- β Rule testing and validation
- β Configuration inspection
- β NO email sending/relay capabilities
- β NO malicious content generation
- β NO offensive security tools
- Docker and Docker Compose
- Claude Code with MCP support
# Clone or create the project directory
cd spamassassin-mcp
# Optional: Copy and customize configuration
cp .env.example .env
# Edit .env to customize ports and settings
# Build and start the containers
docker compose up -d
# Check health
docker compose logs spamassassin-mcp# Pull the latest image from Docker Hub
docker pull your-dockerhub-username/spamassassin-mcp:latest
# Run the container
docker run -d \
--name spamassassin-mcp \
-p 8081:8080 \
your-dockerhub-username/spamassassin-mcp:latest# Connect to containerized server (SSE transport)
# Server URL: http://localhost:8081/mcp
# Or for direct connection (stdio transport)
./mcp-server# Scan a sample email
/scan_email --content "Subject: Test Email
This is a test email for spam analysis."
# Check sender reputation
/check_reputation --sender "test@example.com"
# Get current configuration
/get_configAnalyze email content for spam probability and rule matches.
Parameters:
content(required): Raw email content including headersheaders(optional): Additional headers to analyzecheck_bayes(optional): Include Bayesian analysisverbose(optional): Return detailed rule explanations
Example:
{
"content": "Subject: Urgent Action Required\\n\\nClick here to claim your prize!",
"verbose": true,
"check_bayes": true
}Check sender reputation and domain/IP blacklists.
Parameters:
sender(required): Email sender addressdomain(optional): Sender domainip(optional): Sender IP address
Explain how a spam score was calculated with detailed breakdown.
Retrieve current SpamAssassin configuration and status.
Update SpamAssassin rule definitions (defensive updates only).
Parameters:
source(optional): Rule source (official/custom)force(optional): Force update even if recent
Test custom rules against sample emails in a safe environment.
Parameters:
rules(required): Custom rule definitionstest_emails(required): Array of sample emails to test
spamassassin-mcp/
βββ main.go # MCP server entry point
βββ go.mod # Go module definition
βββ Dockerfile # Multi-stage container build
βββ docker-compose.yml # Service orchestration
βββ internal/
β βββ config/ # Configuration management
β βββ handlers/ # MCP tool handlers
β βββ spamassassin/ # SpamAssassin client wrapper
βββ configs/
β βββ config.yaml # Server configuration
βββ scripts/
β βββ entrypoint.sh # Container initialization
β βββ health-check.sh # Health monitoring
βββ README.md
| Variable | Default | Description |
|---|---|---|
SA_MCP_HOST_PORT |
8081 |
Host port for container deployment |
SA_MCP_LOG_LEVEL |
info |
Logging level (debug, info, warn, error) |
SA_MCP_SERVER_BIND_ADDR |
0.0.0.0:8080 |
Server bind address (container internal) |
SA_MCP_SPAMASSASSIN_HOST |
localhost |
SpamAssassin daemon host |
SA_MCP_SPAMASSASSIN_PORT |
783 |
SpamAssassin daemon port |
SA_MCP_SPAMASSASSIN_THRESHOLD |
5.0 |
Spam score threshold |
SA_MCP_SECURITY_MAX_EMAIL_SIZE |
10485760 |
Max email size (10MB) |
UPDATE_RULES |
false |
Update SpamAssassin rules on startup |
MCP_TRANSPORT |
auto |
Transport mode (auto, stdio, sse) |
The server includes comprehensive security measures:
- Rate Limiting: 60 requests/minute with burst of 10
- Input Validation: Email format and size validation
- Content Sanitization: Safe handling of email content
- Container Security: Non-root execution, read-only filesystem
- Network Isolation: Custom bridge network
- Resource Limits: Memory and CPU constraints
# Simple spam check
/scan_email --content "$(cat suspicious_email.eml)"
# Detailed analysis with Bayes
/scan_email --content "$(cat email.eml)" --verbose --check_bayes# Check sender reputation
/check_reputation --sender "unknown@suspicious-domain.com"
# Check domain and IP
/check_reputation --domain "suspicious-domain.com" --ip "192.168.1.100"# Test custom rules
/test_rules --rules "header LOCAL_TEST Subject =~ /test/i
describe LOCAL_TEST Test rule
score LOCAL_TEST 2.0" --test_emails '["Subject: test email\n\nThis is a test."]'# Get detailed score explanation
/explain_score --email_content "Subject: Free Money!\n\nClaim your prize now!"The container includes automated health checks:
# Check container health
docker-compose exec spamassassin-mcp /usr/local/bin/health-check.sh
# View health status
docker ps# View server logs
docker compose logs -f spamassassin-mcp
# Monitor resource usage
docker stats spamassassin-mcp
# Test MCP connectivity (container mode)
curl -v http://localhost:8081/mcp- Server exclusively provides analysis capabilities
- No email transmission or relay functionality
- Input validation and sanitization on all endpoints
- Rate limiting to prevent abuse
- Comprehensive logging for audit trails
- Runs as non-root user (
spamassassin) - Read-only root filesystem
- No new privileges allowed
- Resource limits enforced
- Network isolation
- No persistent storage of email content
- Temporary analysis only
- Configurable retention policies
- GDPR/privacy-compliant design
# Install dependencies
go mod download
# Build binary
go build -o mcp-server main.go
# Run locally (requires SpamAssassin)
./mcp-server# Run with testing profile (includes spamd)
docker compose --profile testing up -d
# Test SpamAssassin connectivity
docker compose exec spamassassin-mcp timeout 2 bash -c 'echo >/dev/tcp/localhost/783'
# Test MCP server health
docker compose exec spamassassin-mcp /usr/local/bin/health-check.shSymptoms: Container continuously restarts with "read error: EOF"
- Cause: stdio transport expects stdin input in container environment
- Solution: Server automatically detects container mode and uses SSE transport
- Verification: Check logs show "Starting MCP server with SSE transport"
Symptoms: "bind: address already in use"
- Solution: Modify
SA_MCP_HOST_PORTin.envfile - Default: Server uses port 8081 to avoid conflicts
Symptoms: "Pool overlaps with other one on this address space"
- Solution: docker-compose.yml uses 192.168.100.0/24 network
- Customization: Modify networks section if conflicts persist
Symptoms: Container marked unhealthy
- Verification: Run
/usr/local/bin/health-check.shmanually - Common Fix: Ensure SpamAssassin daemon is running
- Debug: Check
docker compose logs spamassassin-mcp
- API Reference - Complete MCP tools documentation
- Deployment Guide - Production deployment instructions
- Security Guide - Security architecture and best practices
- Development Guide - Contributing and development setup
- Configuration Reference - Complete configuration options
MIT License - See LICENSE file for details.
This project uses GitHub Actions for continuous integration and deployment:
- Docker Build and Push: Automatically builds and pushes Docker images to Docker Hub on pushes to
mainbranch and tags - Test Docker Image: Runs tests on the Docker image to ensure it builds and runs correctly
- Update Docker Hub Overview: Automatically updates the Docker Hub repository description when README.md changes
To use the Docker Hub publishing workflow:
-
Create a Docker Hub account if you don't already have one
-
Generate a Docker Hub access token:
- Log in to Docker Hub
- Go to Account Settings > Security
- Click "New Access Token"
- Give it a descriptive name (e.g., "GitHub Actions")
- Set permissions to "Read & Write"
- Copy the generated token (you won't see it again)
-
Set up Docker Hub credentials as GitHub Secrets:
- Go to your GitHub repository settings
- Click "Secrets and variables" > "Actions"
- Add two new repository secrets:
DOCKER_USERNAME: Your Docker Hub usernameDOCKER_PASSWORD: Your Docker Hub access token (the token you just created)
-
Push to the
mainbranch or create a tag starting withv(e.g.,v1.0.0)- The workflow will automatically build and push the image to Docker Hub
The published images will be available at https://hub.docker.com/r/YOUR_USERNAME/spamassassin-mcp where YOUR_USERNAME is your Docker Hub username.
Image tags:
latest- Latest build from the main branchvX.Y.Z- Specific version tags for releasescommit-SHA- Specific commit builds
You can also manually generate the Docker Hub overview:
# Generate Docker Hub overview
./scripts/extract-dockerhub-info.sh
# Or use the manual update script with your credentials
./scripts/update-dockerhub-manual.sh your-docker-username your-docker-access-tokenContributions welcome! Please read our Contributing Guide and ensure all changes maintain the security-first, defensive-only design principles.
For issues and questions:
- First Steps: Check the Troubleshooting Guide
- Logs:
docker-compose logs spamassassin-mcp - Health Check:
docker-compose exec spamassassin-mcp /usr/local/bin/health-check.sh - SpamAssassin Status:
docker-compose exec spamassassin-mcp pgrep spamd
- Model Context Protocol - Official MCP specifications
- Claude Code - Claude's official CLI
- SpamAssassin - Open source spam filtering