GroundEffect

Hyper-fast, local Gmail and Google Calendar indexing for Claude Code, available as a Skill or MCP Server.

GroundEffect

Hyper-fast, local Gmail and Google Calendar indexing for Claude Code.

GroundEffect is a local headless IMAP/CalDav client, Claude Code skill, and MCP server built in Rust with LanceDB.

Features

  • Hybrid Search: BM25 full-text + vector semantic search with Reciprocal Rank Fusion
  • Local Embeddings: Runs nomic-embed-text-v1.5 locally via Candle with Metal acceleration
  • Multi-Account: Connect unlimited Gmail/GCal accounts with independent sync
  • MCP Integration: Exposes email and calendar tools directly to Claude Code
  • Real-time Sync: IMAP IDLE for instant email notifications, CalDAV polling for calendar
  • HTML Text Extraction: Automatically converts HTML emails to clean plain text using html2text
  • Pluggable Token Storage: File-based (default) or PostgreSQL with AES-256-GCM encryption

Quick Start

1. Install

brew tap jamiequint/groundeffect
brew install groundeffect

This automatically:

  • Installs the daemon (starts at login)
  • Installs the Claude Code skill
  • Adds permissions to run groundeffect commands

2. Configure OAuth

Create a Google Cloud project with OAuth credentials:

  1. Go to Google Cloud Console
  2. Create a project and enable Gmail API and Google Calendar API
  3. Go to APIs & Services > Credentials
  4. Create OAuth client ID (Desktop app type)
  5. Add your credentials:
echo 'export GROUNDEFFECT_GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"' >> ~/.zshrc
echo 'export GROUNDEFFECT_GOOGLE_CLIENT_SECRET="your-client-secret"' >> ~/.zshrc
source ~/.zshrc

3. Add an Account

groundeffect account add

This opens a browser for Google OAuth. After authentication, the daemon syncs automatically.

That's it! Ask Claude Code to search your emails and calendar.

CLI Reference

All commands output JSON by default. Add --human for readable output.

Account Commands

CommandDescription
account listList all connected accounts
account show <account>Show account details and sync status
account addAdd new Google account via OAuth
account delete <account>Remove account and all synced data
account configure <account>Update account settings (alias, attachments)

Parameters for add:

ParameterDescriptionDefault
--yearsYears of email history to sync (1-20 or "all")1
--attachmentsEnable automatic attachment downloadoff
--aliasFriendly name for the account-

Email Commands

CommandDescription
email search <query>Hybrid BM25 + semantic search
email listList recent emails
email show <id>Show full email content
email thread <thread_id>Show all emails in a thread
email sendCompose and send email
email attachment <id>Get attachment content
email foldersList IMAP folders

Parameters for search:

ParameterDescriptionDefault
--accountFilter to specific accountall
--limitMax results (max: 100)10
--fromFilter by sender-
--toFilter by recipient-
--date-fromFilter after date (YYYY-MM-DD)-
--date-toFilter before date (YYYY-MM-DD)-
--has-attachmentFilter emails with attachments-

Parameters for send:

ParameterDescription
--fromAccount to send from (required)
--toRecipient(s) (required)
--subjectEmail subject (required)
--bodyEmail body (required)
--ccCC recipients
--bccBCC recipients
--reply-toEmail ID to reply to (for threading)
--htmlForce HTML format (auto-detected from markdown/URLs)
--save-as-draftSave as draft instead of sending
--confirmSend immediately (without: preview only)

Draft Commands

CommandDescription
email draft createCreate a new email draft
email draft listList all drafts for an account
email draft show <id>Show full draft content
email draft update <id>Update an existing draft
email draft send <id>Send a draft
email draft delete <id>Delete a draft

Parameters for draft create:

ParameterDescription
--fromAccount to create draft in (required)
--toRecipient(s)
--subjectEmail subject
--bodyEmail body
--ccCC recipients
--bccBCC recipients
--htmlForce HTML format
--reply-toEmail ID to reply to (for threading)

Calendar Commands

CommandDescription
calendar eventsList events in a date range (no query required)
calendar search <query>Search events with semantic search
calendar show <id>Show event details
calendar createCreate new event

Parameters for events:

ParameterDescriptionDefault
--fromStart date (YYYY-MM-DD)today
--toEnd date (YYYY-MM-DD)7 days from start
--accountFilter to specific account(s)all
--limitMax results (max: 200)50
--humanHuman-readable output grouped by date-

Use calendar events to answer questions like "what's on my calendar tomorrow" or "show me my meetings next week" without requiring a search query.

Parameters for create:

ParameterDescriptionDefault
--accountAccount to create event in (required)-
--summaryEvent title (required)-
--startStart time (ISO 8601) (required)-
--endEnd time (ISO 8601) (required)-
--descriptionEvent description-
--locationEvent location-
--attendeesAttendee emails (repeatable)-
--calendarCalendar IDprimary

Sync Commands

CommandDescription
sync statusShow sync status for all accounts
sync reset --account <a> --confirmClear all synced data
sync extend --account <a> --target-date <d>Sync older emails back to date
sync resume-from --account <a> --target-date <d>Force sync to resume from date
sync download-attachments --account <a>Download pending attachments

Daemon Commands

CommandDescription
daemon installInstall launchd agent (auto-start at login)
daemon uninstallRemove launchd agent
daemon statusCheck if daemon is running
daemon restartRestart the daemon

Config Commands

CommandDescription
config settingsView/modify daemon settings
config add-permissionsAdd to Claude Code allowlist
config remove-permissionsRemove from Claude Code allowlist

Parameters for settings:

ParameterDescriptionDefault
--loggingEnable/disable file loggingoff
--email-intervalEmail poll interval in seconds (60-3600)300
--calendar-intervalCalendar poll interval in seconds (60-3600)300
--max-fetchesMax concurrent fetches (1-50)10
--timezoneUser timezone for date parsing (e.g., America/Los_Angeles)UTC

MCP Integration (Alternative)

If you prefer MCP over the CLI skill, add to ~/.claude.json:

{
  "mcpServers": {
    "groundeffect": {
      "type": "stdio",
      "command": "groundeffect-mcp",
      "env": {
        "GROUNDEFFECT_GOOGLE_CLIENT_ID": "${GROUNDEFFECT_GOOGLE_CLIENT_ID}",
        "GROUNDEFFECT_GOOGLE_CLIENT_SECRET": "${GROUNDEFFECT_GOOGLE_CLIENT_SECRET}"
      }
    }
  }
}

The skill is faster (direct CLI calls vs MCP JSON-RPC overhead) but MCP works with other MCP-compatible clients.

Build from Source

git clone https://github.com/jamiequint/groundeffect.git
cd groundeffect
cargo build --release

Binaries:

  • target/release/groundeffect - CLI
  • target/release/groundeffect-daemon - Background sync daemon
  • target/release/groundeffect-mcp - MCP server

Install manually:

# Install binaries
sudo cp target/release/groundeffect* /usr/local/bin/

# Install skill
cp -r skill ~/.claude/skills/groundeffect

# Install daemon
groundeffect daemon install

# Add permissions
groundeffect config add-permissions

Data Storage

~/.config/groundeffect/
├── config.toml            # Main configuration
├── daemon.toml            # Daemon configuration
└── tokens/                # OAuth tokens (file provider)

~/.local/share/groundeffect/
├── lancedb/               # LanceDB database
├── attachments/           # Downloaded attachments
├── models/                # Embedding model files
├── logs/                  # Log files
└── cache/
    └── sync_state/        # Sync state

~/.claude/skills/groundeffect/   # Claude Code skill

Token Storage

By default, OAuth tokens are stored in ~/.config/groundeffect/tokens/ as encrypted JSON files.

For server deployments or ephemeral containers, you can store tokens in PostgreSQL instead. This requires the postgres feature.

PostgreSQL Token Storage

  1. Build with the postgres feature:

    cargo build --release --features postgres
    
  2. Create the tokens table:

    CREATE TABLE IF NOT EXISTS groundeffect_tokens (
        email VARCHAR(255) PRIMARY KEY,
        encrypted_tokens BYTEA NOT NULL,
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    );
    
  3. Configure in ~/.config/groundeffect/config.toml:

    [tokens]
    provider = "postgres"
    database_url_env = "DATABASE_URL"
    encryption_key_env = "GE_TOKEN_ENCRYPTION_KEY"
    # table_name = "groundeffect_tokens"  # optional
    
  4. Set environment variables:

    export DATABASE_URL="postgres://user:pass@localhost/mydb"
    export GE_TOKEN_ENCRYPTION_KEY="your-secret-key-here"
    

Tokens are encrypted at rest using AES-256-GCM with a key derived from your encryption key via HKDF-SHA256.

Troubleshooting

"OAuth token expired"

Re-authenticate:

groundeffect account add

Daemon not running

Check status and restart:

groundeffect daemon status
groundeffect daemon restart

Or check launchd:

launchctl list | grep groundeffect

View logs

tail -f ~/.local/share/groundeffect/logs/daemon.log

Enable logging if disabled:

groundeffect config settings --logging true
groundeffect daemon restart

High memory usage

Embedding model uses ~500MB-1GB during active embedding. Normal when idle.

Architecture

┌──────────────┐      ┌──────────────────┐
│ Claude Code  │─────►│ groundeffect-mcp │────────┐
│ (MCP Host)   │stdio │                  │        │
└──────────────┘      └──────────────────┘        │
                                                  ▼
┌──────────────┐      ┌──────────────────┐    ┌─────────┐
│ Claude Code  │─────►│ groundeffect     │───►│ LanceDB │
│ (Skill/Bash) │      │ (CLI)            │    └─────────┘
└──────────────┘      └──────────────────┘         ▲
                                                   │
                      ┌──────────────────┐         │
                      │ groundeffect-    │─────────┘
                      │ daemon           │◄──── IMAP/CalDAV
                      └──────────────────┘

License

MIT

Related Servers