{"id":48213379,"url":"https://github.com/factorial-io/imap-mcp","last_synced_at":"2026-04-04T18:53:19.735Z","repository":{"id":344491515,"uuid":"1180299491","full_name":"factorial-io/imap-mcp","owner":"factorial-io","description":"A mcp server exposing imap server with oauth","archived":false,"fork":false,"pushed_at":"2026-03-26T17:52:17.000Z","size":696,"stargazers_count":2,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T17:53:23.729Z","etag":null,"topics":["imap","mcp","mcp-server","mcp-servers","oidc"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/factorial-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-12T22:47:08.000Z","updated_at":"2026-03-26T17:47:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/factorial-io/imap-mcp","commit_stats":null,"previous_names":["factorial-io/imap-mcp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/factorial-io/imap-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorial-io%2Fimap-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorial-io%2Fimap-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorial-io%2Fimap-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorial-io%2Fimap-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/factorial-io","download_url":"https://codeload.github.com/factorial-io/imap-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorial-io%2Fimap-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31409470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["imap","mcp","mcp-server","mcp-servers","oidc"],"created_at":"2026-04-04T18:53:19.245Z","updated_at":"2026-04-04T18:53:19.719Z","avatar_url":"https://github.com/factorial-io.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IMAP MCP Server\n\n[![CI](https://github.com/factorial-io/imap-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/factorial-io/imap-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nA self-hosted Rust service that acts as a multi-tenant IMAP MCP server for claude.ai, using any OpenID Connect provider for authentication.\n\nUsers authenticate via their OIDC provider (e.g. GitLab, Keycloak, Auth0), enter their IMAP password once, and then claude.ai accesses their email through the MCP protocol using a Bearer token.\n\n## Prerequisites\n\n- Docker and Docker Compose\n- An OpenID Connect provider (GitLab, Keycloak, Auth0, etc.) with an OAuth application configured\n- An IMAP mail server (TLS, port 993)\n- Traefik reverse proxy (for HTTPS) or equivalent\n\n## OIDC Provider Setup\n\nCreate an OAuth/OIDC application in your identity provider:\n\n- **Redirect URI**: `https://\u003cYOUR_DOMAIN\u003e/auth/callback`\n- **Scopes**: `openid`, `profile`, `email`\n- **Confidential**: Yes\n\nNote the **Client ID**, **Client Secret**, and **Issuer URL** (e.g. `https://gitlab.example.com`).\n\n## Deployment\n\n1. Clone this repository:\n\n```bash\ngit clone \u003crepo-url\u003e \u0026\u0026 cd imap-mcp\n```\n\n2. Create your `.env` file:\n\n```bash\ncp .env.example .env\n```\n\n3. Fill in the values:\n\n```bash\n# Generate an encryption key\nopenssl rand -base64 32\n# Paste it as ENCRYPTION_KEY in .env\n```\n\nEdit `.env` with your OIDC credentials, IMAP host, public domain, etc.\n\n4. Deploy with Docker Compose:\n\n```bash\ndocker compose up -d\n```\n\nThe service will be available at `https://\u003cYOUR_DOMAIN\u003e`.\n\n## Adding the MCP Server in claude.ai\n\n1. Open [claude.ai](https://claude.ai)\n2. Go to **Settings \u003e Integrations \u003e Add MCP Server**\n3. Enter the URL: `https://\u003cYOUR_DOMAIN\u003e/mcp`\n4. Claude.ai handles the OAuth flow automatically:\n   - Registers itself as an OAuth client (RFC 7591 dynamic registration)\n   - Opens a popup where you log in via your OIDC provider\n   - After login, enter your IMAP password in the setup form\n   - The form validates your credentials against the IMAP server\n   - On success, claude.ai receives an access token via OAuth code exchange\n5. The MCP server is now connected — claude.ai can read your email\n\n## Available MCP Tools\n\n| Tool | Description |\n|------|-------------|\n| `list_folders` | List all IMAP mailbox folders |\n| `list_emails` | List emails in a folder (uid, date, from, subject, seen flag) |\n| `get_email` | Fetch full email by UID (headers + plain text body, S/MIME signed supported) |\n| `search_emails` | Search emails using IMAP SEARCH criteria |\n| `mark_read` | Set \\Seen flag on an email by UID |\n| `mark_unread` | Unset \\Seen flag on an email by UID |\n\n## Architecture\n\n```\nclaude.ai → POST /register (dynamic client registration)\n         → GET  /auth/login (OAuth authorize → redirects to OIDC provider)\n                    → OIDC provider login\n                    → GET /auth/callback (show IMAP password form)\n                    → POST /auth/setup (validate IMAP, generate auth code)\n                    → redirect to claude.ai with authorization code\n         → POST /auth/token (exchange code for access token, PKCE verified)\n         → POST /mcp (Bearer token → MCP JSON-RPC)\n                    ↓\n           Validate token (Redis)\n                    ↓\n           Decrypt IMAP password (AES-256-GCM)\n                    ↓\n           Connect to IMAP server (TLS)\n                    ↓\n           Execute MCP tool\n                    ↓\n           Return results to claude.ai\n```\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `OIDC_ISSUER_URL` | Yes | OIDC provider URL (e.g. `https://gitlab.example.com`) |\n| `OIDC_CLIENT_ID` | Yes | OIDC application client ID |\n| `OIDC_CLIENT_SECRET` | Yes | OIDC application client secret |\n| `IMAP_HOST` | Yes | IMAP server hostname |\n| `IMAP_PORT` | No | IMAP port (default: 993) |\n| `BASE_URL` | Yes | Public URL of this service, no trailing slash |\n| `REDIS_URL` | Yes | Redis connection URL |\n| `ENCRYPTION_KEY` | Yes | 32-byte AES-256 key, base64-encoded |\n| `RUST_LOG` | No | Log level (default: info) |\n| `BIND_ADDR` | No | Listen address (default: 0.0.0.0:8080) |\n\n## Kubernetes Deployment\n\nYou can also host imap-mcp on Kubernetes. The container image is published to `ghcr.io/factorial-io/imap-mcp`.\n\n### Requirements\n\n- A Kubernetes cluster (1.24+)\n- An **Ingress controller** (e.g. nginx-ingress, Traefik) with TLS termination — claude.ai requires HTTPS\n- A **Redis** instance accessible from the cluster (e.g. via [Bitnami Redis Helm chart](https://github.com/bitnami/charts/tree/main/bitnami/redis) or a managed service)\n- An **OIDC provider** with a configured OAuth application (see [OIDC Provider Setup](#oidc-provider-setup))\n- A **Secret** containing the environment variables listed in [Environment Variables](#environment-variables)\n\n### Minimal manifests\n\nCreate a Secret with your configuration:\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: imap-mcp\nstringData:\n  OIDC_ISSUER_URL: \"https://gitlab.example.com\"\n  OIDC_CLIENT_ID: \"your-client-id\"\n  OIDC_CLIENT_SECRET: \"your-client-secret\"\n  IMAP_HOST: \"mail.example.com\"\n  BASE_URL: \"https://imap-mcp.example.com\"\n  REDIS_URL: \"redis://redis:6379\"\n  ENCRYPTION_KEY: \"\u003coutput of: openssl rand -base64 32\u003e\"\n```\n\nDeploy the application:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: imap-mcp\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: imap-mcp\n  template:\n    metadata:\n      labels:\n        app: imap-mcp\n    spec:\n      containers:\n        - name: imap-mcp\n          image: ghcr.io/factorial-io/imap-mcp:latest\n          ports:\n            - containerPort: 8080\n          envFrom:\n            - secretRef:\n                name: imap-mcp\n          resources:\n            requests:\n              cpu: 50m\n              memory: 32Mi\n            limits:\n              cpu: 200m\n              memory: 128Mi\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: imap-mcp\nspec:\n  selector:\n    app: imap-mcp\n  ports:\n    - port: 80\n      targetPort: 8080\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: imap-mcp\nspec:\n  tls:\n    - hosts:\n        - imap-mcp.example.com\n      secretName: imap-mcp-tls\n  rules:\n    - host: imap-mcp.example.com\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: imap-mcp\n                port:\n                  number: 80\n```\n\nAdjust the Ingress annotations for your ingress controller and TLS setup. The container runs as a non-root user and listens on port 8080.\n\n## Local Development\n\nA `docker-compose.dev.yml` is provided for local testing with claude.ai using ngrok as a tunnel.\n\n### Prerequisites\n\n- [1Password CLI](https://developer.1password.com/docs/cli/) (`op`) — secrets are stored in 1Password\n- An [ngrok](https://ngrok.com/) account (free tier works, paid gives a stable domain)\n\n### Setup\n\n1. Copy the example env file and fill in your OIDC credentials in 1Password (vault: **Employee**, item: **IMAP MCP Server**):\n\n```bash\ncp .env.example .env\n```\n\n2. Set `BASE_URL` in `.env` to your ngrok URL (see step 4).\n\n3. Configure the OIDC provider's redirect URI to `https://\u003cNGROK_URL\u003e/auth/callback`.\n\n4. Start all services:\n\n```bash\nop run --env-file=.env -- docker compose -f docker-compose.dev.yml up --build\n```\n\n5. Get the ngrok public URL:\n\n```bash\ncurl -s http://localhost:4040/api/tunnels | jq '.tunnels[0].public_url'\n```\n\nOr open http://localhost:4040 in your browser.\n\n6. Update `BASE_URL` in `.env` with the ngrok URL and restart the app:\n\n```bash\nop run --env-file=.env -- docker compose -f docker-compose.dev.yml restart imap-mcp\n```\n\nIf you have a reserved ngrok domain, set `NGROK_DOMAIN` in `.env` to skip steps 5-6.\n\n### Connect claude.ai\n\n1. Go to **Settings \u003e Integrations \u003e Add MCP Server** on [claude.ai](https://claude.ai)\n2. Enter the URL: `https://\u003cNGROK_URL\u003e/mcp`\n3. Authenticate via your OIDC provider and enter your IMAP password\n4. Try prompts like \"List my email folders\" or \"Show my latest emails\"\n\n## Security\n\n- OAuth 2.0 with PKCE (S256) for the full authorization flow\n- Dynamic client registration (RFC 7591) — redirect URIs must use HTTPS\n- IMAP passwords are encrypted at rest with AES-256-GCM and wrapped in `SecretString` in memory\n- IMAP passwords are never logged or returned in responses\n- Session tokens are opaque UUIDs (not JWTs)\n- Sessions expire after 30 days, refreshed on each use\n- Authorization codes are single-use with 5-minute TTL\n- IMAP input validation prevents command injection via folder names and search queries\n- HTML output is escaped to prevent XSS\n- CORS restricted to required methods and headers\n- Container runs as non-root user\n- IMAP connections are opened per-request (no persistent pool)\n\n## License\n\nMIT -- see [LICENSE](LICENSE).\n\n## Acknowledgements\n\nDevelopment time and API tokens sponsored by [Factorial.io](https://www.factorial.io/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffactorial-io%2Fimap-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffactorial-io%2Fimap-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffactorial-io%2Fimap-mcp/lists"}