MCPcopy
hub / github.com/Gururagavendra/gmail-cleaner

github.com/Gururagavendra/gmail-cleaner @v1.3.0 sqlite

repository ↗ · DeepWiki ↗ · release v1.3.0 ↗
486 symbols 1,000 edges 49 files 286 documented · 59%
README

Gmail Bulk Unsubscribe & Cleanup Tool

A free, privacy-focused tool to bulk unsubscribe from emails, delete emails by sender, and mark emails as read. No subscriptions, no data collection - runs 100% on your machine.

Python Docker Gmail API License GitHub stars

No Subscription Required - Free Forever

Features

Feature Description
Bulk Unsubscribe Find newsletters and unsubscribe with one click
Delete by Sender Scan and see who sends you the most emails, delete in bulk
Bulk Delete Multiple Senders Delete emails from multiple senders simultaneously with progress tracking
Mark as Read Bulk mark thousands of unread emails as read
Archive Emails Archive emails from selected senders (remove from inbox)
Label Management Create, delete, and apply/remove labels to emails from specific senders
Mark Important Mark or unmark emails from selected senders as important
Email Download Download email metadata for selected senders as CSV
Smart Filters Filter by date range, email size, category (Promotions, Social, Updates, Forums, Primary), sender, and labels
Privacy First Runs locally - your data never leaves your machine
Super Fast Gmail API with batch requests (100 emails per API call)
Gmail-style UI Clean, familiar interface with real-time progress tracking

Platform Support

Works on all major platforms - both Docker and local installation:

Platform Docker Local (Python)
Linux (x86_64) Native Native
Windows (x86_64) Native Native
macOS Intel Native Native
macOS Apple Silicon (M1/M2/M3/M4) Native Native

Security & Privacy

  • 100% Local - No external servers, no data collection
  • Open Source - Inspect all the code yourself
  • Minimal Permissions - Only requests read + modify (for mark as read)
  • Your Credentials - You control your own Google OAuth app
  • Gitignored Secrets - credentials.json and token.json never get committed

🆘 Need Help Setting Up?

A few people reached out to me on Reddit and via email saying they love the idea, but don’t have the technical expertise to run this software themselves. I’d also like to grow the project further, so support would really help make the time I invest in it more worthwhile.

Struggling with Docker, Google Cloud Console, or credentials.json? I can help you set it up personally!

I offer a 1-on-1 Setup Service ($8) where we hop on a Google Meet, you share your screen, and I guide you through the entire installation until it's working perfectly.

  • Secure: I guide you; I never see your passwords.
  • Fast: We'll get it running in under 20 minutes.
  • Support the Project: Your $8 helps keep this tool free and open source.

Book a Setup Session Here - mail me at guruvelu85@gmail.com, i will reply and setup an gmeet call

Demo

Gmail Cleaner Demo

Watch Setup Video on YouTube - Step-by-step video on how to setup the repo and run the project locally.

Feature Requests

Lets make this tool a better one by improving as much as possible, All features are welcome, To request a feature, open a GitHub issue.

Prerequisites

Setup

Important: You must create your OWN Google Cloud credentials. This app doesn't include pre-configured OAuth - that's what makes it privacy-focused! Each user runs their own instance with their own credentials.

1. Get Google OAuth Credentials

Video Tutorial: Watch on YouTube for a visual walkthrough

  1. Go to Google Cloud Console
  2. Create a new project (or select existing)
  3. Search for "Gmail API" and Enable it
  4. Go to Google Auth Platform → Click "Get started"
  5. Fill in the wizard:
  6. App Information: Enter app name (e.g., "Gmail Cleanup"), select your email
  7. Audience: Select External
  8. Contact Information: Add your email address
  9. Click Create
  10. Go to Audience (left sidebar) → Scroll to Test users
  11. Click Add Users → Add your Gmail address → Save
  12. Go to Clients (left sidebar) → Create Client
  13. Choose the application type based on your setup:
Setup Application Type Redirect URI
Local/Desktop (Python with browser) Desktop app Not needed
Docker (localhost) Web application http://localhost:8767/
Docker/Remote Server (public domain) Web application http://YOUR_PUBLIC_DOMAIN:8767/

⚠️ Important: Redirect URIs must use a domain name (e.g., gmail.example.com), NOT an IP address (e.g., 192.168.1.100). Google OAuth does not allow IP addresses. If you need to use a server IP, use a Dynamic DNS service to get a domain name.

  • Name: "Gmail Cleanup" (or anything)
  • Click Create
  • Click Download (downloads JSON file)
  • Rename the downloaded file to credentials.json

💡 Which should I choose? - Running locally with Python (uv run python main.py)? → Desktop app - Running with Docker or on a remote server? → Web application

Note: If using custom port mapping or a custom domain, see Advanced Configuration for redirect URI details.

2. Clone the Repository

  1. Clone the repo:
git clone https://github.com/Gururagavendra/gmail-cleaner.git
  1. Navigate to the folder:
cd gmail-cleaner
  1. Put your credentials.json file in the project folder.

Usage

Option A: Docker (Recommended)

  1. Pull the latest image and start the container:
docker compose pull && docker compose up
  1. Open the app in your browser:
http://localhost:8766
  1. Click "Sign In" button in the web UI

  2. Check logs for the OAuth URL (only after clicking Sign In!):

docker logs $(docker ps -q --filter ancestor=ghcr.io/gururagavendra/gmail-cleaner)

Or if you built locally:

docker logs $(docker ps -q --filter name=gmail-cleaner)
  1. Copy the Google OAuth URL from logs, open in browser, and authorize:
  2. Choose your Google account
  3. "Google hasn't verified this app" → Click Continue > This warning appears because you created your own OAuth app (not published to Google). This is expected and safe - you control the app!
  4. Grant permissions → Click Continue
  5. Done! You'll see "Authentication flow has completed"

🌐 Using a custom domain, remote server, or custom port mapping? See Advanced Configuration for setup instructions.

Persisting Authentication (Data Directory)

The docker-compose.yml includes a data directory volume mount that automatically persists your authentication token.

How it works:

  • The ./data directory on your host is mounted to /app/data in the container
  • When you authenticate, token.json is automatically saved to /app/data/token.json inside the container
  • This file is persisted to ./data/token.json on your host filesystem
  • On subsequent container restarts, your authentication persists automatically

No manual steps required!

  • ✅ First-time setup: Just run docker compose up - the data directory is created automatically
  • ✅ Authentication persists: Your token is saved to ./data/token.json on the host
  • ✅ Container restarts: Your authentication is automatically loaded from the persisted file

To reset authentication:

If you need to sign in with a different account or reset authentication:

# Stop the container
docker compose down

# Remove the token file
rm -f ./data/token.json

# Start again (will prompt for new authentication)
docker compose up

Option B: Python (with uv)

uv sync
uv run python main.py

The app opens at http://localhost:8766

FAQ

Q: Why do I need to create my own Google Cloud project?

Because this app accesses your Gmail. By using your own OAuth credentials, you have full control and don't need to trust a third party.

Q: Is this safe?

Yes! The code is open source - you can inspect it. Your emails are processed locally on your machine.

Q: Can I use this for multiple Gmail accounts?

Yes! Click "Sign Out" and sign in with a different account. Each account needs to be added as a test user in your Google Cloud project.

Q: Emails went to Trash, can I recover them?

Yes! The delete feature moves emails to Trash. Go to Gmail → Trash to recover within 30 days.

Q: Can't delete or modify files in the ./data directory?

Docker containers run as root by default, so files created in ./data (like token.json) are owned by root. To fix permissions: bash sudo chown -R $USER:$USER ./data/ Or to delete a specific file: bash sudo rm ./data/token.json This is a common Docker behavior - the files are safe, just owned by root for security reasons.

Q: Having OAuth authentication issues?

Check the Troubleshooting section for common solutions.

Advanced Configuration

Custom Port Mapping / Docker Port Override

If you're using custom port mappings in Docker (e.g., mapping 18766:8766 and 18767:8767):

  1. Update docker-compose.yml:

yaml services: gmail-cleaner: ports: - "18766:8766" # Web UI (external:internal) - "18767:8767" # OAuth callback (external:internal) environment: - WEB_AUTH=true - OAUTH_EXTERNAL_PORT=18767 # External port that browser will use

  1. Update Google Cloud Console redirect URI:
  2. Go to Clients → Your OAuth client → Authorized redirect URIs
  3. Update to: http://localhost:18767/ (or http://YOUR_DOMAIN:18767/ if using custom domain)
  4. Note: Must be a domain name, not an IP address

  5. Restart the container:

bash docker compose down && docker compose up

💡 How it works: The app listens on port 8767 inside the container, but sets the OAuth redirect URI to use port 18767 (the external port). Docker forwards the external port to the internal port.

Custom Domain / Reverse Proxy / Remote Server

If you're accessing via a custom domain (e.g., gmail.example.com) instead of localhost:

⚠️ Important: - Use Web application credentials (not Desktop app) for remote server setups. See Step 7 in Get Google OAuth Credentials. - IP addresses are NOT allowed in Google OAuth redirect URIs. You must use a domain name (e.g., gmail.example.com), not an IP address (e.g., 192.168.1.100). - Google requires redirect URIs to use a public top-level domain (.com, .org, .net, etc.)

Allowed redirect URIs: - ✅ http://localhost:8767/ (for local access) - ✅ http://gmail.example.com:8767/ (custom domain) - ✅ http://mygmail.duckdns.org:8767/ (dynamic DNS) - ❌ http://192.168.1.100:8767/ (IP addresses not allowed) - ❌ http://10.0.0.5:8767/ (private IPs not allowed)

If you need to use a server IP: - Use a dynamic DNS service (free options: DuckDNS, No-IP, Dynu) - Point the domain to your server's IP address - Use the domain name in OAuth (e.g., http://mygmail.duckdns.org:8767/)

  1. Update Google Cloud Console:
  2. Go to Clients → Your OAuth client → Authorized redirect URIs
  3. Add: http://YOUR_DOMAIN:8767/ (or external port if using custom mapping)
  4. Must be a domain name, not an IP address

  5. Update docker-compose.yml:

yaml environment: - WEB_AUTH=true - OAUTH_HOST=gmail.example.com # Just the hostname - NO http:// or https:// # Optional: If using custom port mapping - OAUTH_EXTERNAL_PORT=18767

⚠️ Common mistakes: - Use only the hostname (e.g., gmail.example.com), NOT the full URL (e.g., ~~https://gmail.example.com~~) - Use a domain name, NOT an IP address (e.g., ~~192.168.1.100~~)

  1. For HTTPS with reverse proxy:
  2. The OAuth callback uses HTTP on port 8767 internally
  3. Your reverse proxy should forward port 8767 for the OAuth callback
  4. The Authorized redirect URI in Google Cloud must be http://YOUR_DOMAIN:8767/ (HTTP, not HTTPS) or use the external port if mapped
  5. Proxy both port 8766 (app) and port 8767 (OAuth callback) through your reverse proxy

Troubleshooting

OAuth & Authentication Issues

"Access blocked: Gmail Cleanup has not completed the Google verification process"

Your app is missing test users in the OAuth setup:

  1. Go to Google Cloud Console → Your Project
  2. Go to APIs & ServicesOAuth consent screen
  3. Scroll down to Test users
  4. Click Add Users and add your Gmail address
  5. Try signing in again

Why? Since your app is in "Testing" mode, only emails listed as test users can sign in. This is normal and expected!

"Error 403: access_denied"

  1. Make

Core symbols most depended-on inside this repo

get_gmail_service
called by 13
app/services/auth.py
build_gmail_query
called by 12
app/services/gmail/helpers.py
_is_file_empty
called by 5
app/services/auth.py
is_web_auth_mode
called by 4
app/services/auth.py
reset_scan
called by 3
app/core/state.py
reset_delete_scan
called by 3
app/core/state.py
reset_mark_read
called by 3
app/core/state.py
decode_base64_content
called by 3
app/services/gmail/download.py

Shape

Function 180
Method 165
Route 83
Class 58

Languages

Python81%
TypeScript19%

Modules by API surface

tests/unit/api/test_api_actions.py37 symbols
tests/unit/services/auth/test_sign_in_api.py35 symbols
tests/unit/models/test_schemas.py35 symbols
tests/unit/services/auth/test_token_management_complete.py33 symbols
tests/unit/services/auth/test_credentials_handling_complete.py32 symbols
app/api/status.py30 symbols
app/api/actions.py30 symbols
tests/unit/services/gmail/test_gmail_service.py29 symbols
static/js/labels.js25 symbols
app/models/schemas.py25 symbols
static/js/delete.js23 symbols
tests/unit/services/auth/test_oauth_flow_complete.py18 symbols

Dependencies from manifests, versioned

fastapi0.122.0 · 1×
google-api-python-client2.100.0 · 1×
google-auth2.23.0 · 1×
google-auth-oauthlib1.1.0 · 1×
jinja23.1.6 · 1×
pydantic-settings2.11.0 · 1×
uvicorn0.38.0 · 1×

For agents

$ claude mcp add gmail-cleaner \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact