Version: 3.1.0-alpha
Last updated: August 10, 2026
Status: Production-ready autonomous email analysis backend (Node.js + Ollama)
Model: phi4-mini:latest (3.8B, local, free) via Ollama
Most people get too many emails. Some are urgent, some are boring, some contain files or images, some ask you to do something later, and some are only promotional noise.
Gmail Agent MCP is an autonomous email-analysis agent that sits beside your inbox and does the boring parts of email work for you. It reads each email with a local LLM, summarizes it, extracts key points, detects priority/importance, identifies action items, searches related past emails, reads attachments, and can even create calendar events — all without sending your data to a cloud API.
The entire pipeline runs locally (Gmail/Google Calendar API for data access only; all AI inference happens on your machine via Ollama). There are no per-token costs.
| Component | Technology | Purpose |
|---|---|---|
| Language | Node.js (ES Modules, type: module) | Backend runtime |
| Runtime version | Node.js ≥ 18.17.0 | Minimum supported |
| Package manager | npm | Dependency management |
| Dev server | nodemon | Auto-restart on file change during development |
| Tests | Vitest 4.x | Unit + integration testing |
| Component | Technology | Purpose |
|---|---|---|
| Primary model | phi4-mini:latest (Microsoft, 3.8B) | Local tool-calling + analysis. Default via OLLAMA_MODEL env. |
| Vision model | qwen3-vl:8b-instruct | Optional multimodal path for inline images |
| LLM server | Ollama (local) | http://localhost:11434 |
| Cloud fallback | OpenRouter API | Optional; free models (Gemma 2/3, etc.) |
| Embedding model | bge-m3 (1024-dim) | Via Ollama /api/embeddings |
| AI client | core/ai-client.js | Unified interface; raw /api/generate for phi4-mini |
| Prompt formats | JSON, YAML, XML, FUNCTION_CALL, `< | tool_call |
| Parser stack | 5-tier flexible parser + YAML/JSON fallback | Handles model output variations |
| Component | Technology | Purpose |
|---|---|---|
| Vector index | FAISS (faiss-node) | IndexFlatL2, 1024-dim BGE-M3 embeddings |
| Relational store | SQLite (better-sqlite3, sqlite3) | Email embeddings, document chunks, metadata |
| Document DB | documents.db (SQLite) | Attachment text, chunks, OCR results |
| JSON state files | data/*.json | Summaries, core memory, metadata, tracking |
| NDJSON logs | data/analysis_detail_log.ndjson | Per-iteration analysis traces |
| Component | Technology | Purpose |
|---|---|---|
| Gmail API | googleapis (v1) + google-auth-library | OAuth2 email fetch |
| Google Calendar | googleapis | Event create/read |
| WebSocket bridge | ws (port 8081) | Chrome extension communication |
| Component | Technology | Purpose |
|---|---|---|
pdf-parse, pdfkit | Text extraction | |
| Office docs | mammoth (Word), xlsx (Excel), officegen | Docx/xlsx read/write |
| Images / OCR | sharp, tesseract.js | Image resize + OCR |
| Archives | adm-zip | Zip extraction |
| Rich text | rtf-stream-parser | RTF extraction |
| Markdown | marked | Rendering |
| Vision captions | moondream (via Ollama) | Image description |
| Component | Technology | Purpose |
|---|---|---|
| Config | dotenv | Environment variables |
| Logging | winston | Structured logs |
| Validation | joi, validator | Input validation |
| Concurrency | async-mutex, lru-cache, p-timeout | Thread safety, caching, timeouts |
| Queue | bull | Background job queue (Redis-backed) |
| CLI progress | cli-progress | Terminal progress bars |
| MCP SDK | @modelcontextprotocol/sdk | Model Context Protocol server |
| Containerization | Docker (multi-stage, node:18-alpine) | Production deployment |
| HTML cleaning | Custom regex-based stripper | 92% payload reduction |
| Resource management | core/resource-registry.js | Timer/interval cleanup to prevent leaks |
| Error handling | core/error-handler.js | Standardized error codes and formatting |
| Model management | core/model-manager.js | GPU/memory-aware model switching |
| API tracking | core/api-tracker.js | Rate limit and usage monitoring |
| Timeout fetch | utils/timeout-free-fetch.js | Bypasses undici 300s timeout for Ollama |
| Security helpers | utils/security-helpers.js | Bounded maps/sets, token encryption |
| Email utilities | utils/email-text-utils.js | HTML stripping, entity decoding, truncation |
| Document processing | utils/document-processor.js | Multi-format file handling utilities |
| Chunk embedder | utils/chunk-embedder.js | 512-token chunks with 128 overlap, MD5 cache |
| CLI UI | utils/cli-ui.js | Colored output, progress bars, status updates |
The system is a modular, layered Node.js backend with an autonomous analysis loop, Chrome extension frontend, and an optional Python FastAPI backend.
┌─────────────────────────────────────────────────────────────────┐
│ Chrome Extension │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ content.js — sidebar UI, email monitoring, WebSocket │ │
│ │ background.js — service worker, reconnection logic │ │
│ │ popup.js — connection status, trigger analysis │ │
│ │ safe-html.js — XSS prevention, DOM escaping │ │
│ └───────────────────────────┬───────────────────────────────┘ │
└───────────────────────────────┼──────────────────────────────────┘
│ ws://localhost:8081
┌───────────────────────────────▼──────────────────────────────────┐
│ server.js (entry point) │
│ - Initialization workflow (fetch → clean → embed → analyze) │
│ - WebSocket bridge startup │
│ - Periodic analysis scheduler │
│ - AutonomousAgent class with crash recovery │
└───────────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────────▼──────────────────────────────────┐
│ server/gmail-server.js (~8.5k lines) │
│ - Gmail/Calendar API orchestration │
│ - Incremental fetch, analysis batching │
│ - Tool execution routing │
│ - Autonomous loop (snooze, follow-up, digest) │
│ - FAISS lazy initialization with promise deduplication │
└──────┬──────────────────────┬────────────────────┬───────────────┘
│ │ │
┌──────▼──────┐ ┌──────────▼─────────┐ ┌──────▼──────────────┐
│ core/ │ │ tools/ │ │ services/ │
│ ai-client │ │ attachment-tools │ │ analysis-manager │
│ ai-models │ │ calendar-tools │ │ calendar-manager │
│ model-mgr │ │ semantic-search │ │ core-memory │
│ constants │ │ memory-tools │ │ document-orchestrator│
│ error-hdlr │ │ classification │ │ query-router │
│ api-tracker│ │ enrichment │ │ follow-up-tracker │
│ resource │ │ extraction │ │ smart-snooze │
│ registry │ │ action-tools │ │ daily-digest │
│ │ │ system-tools │ │ relationship-graph │
│ │ │ email-analysis │ │ sentiment-timeline │
│ │ │ tool-call-logger │ │ response-intelligence│
│ │ │ tool-debug-dash │ │ user-embeddings │
│ │ │ inline-image-svc │ │ offline-calendar-queue│
│ │ │ qwen-image-db │ │ file-reader │
└─────────────┘ └───────────────────┘ └─────────────────────┘
│ │ │
┌──────▼──────────────────────▼────────────────────▼───────────────┐
│ Data Layer (SQLite + JSON) │
│ gmail_embeddings.db │ documents.db │ email_summaries.json │
│ core_memory.json │ cleaned_emails.json │ raw_emails.json │
│ analysis_detail_log.ndjson │ retry_attempts.json │
└───────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ Python Backend (FastAPI) │
│ backend/main.py — Uvicorn server, CORS, lifespan │
│ backend/services/ — Gmail, AI, Calendar, Vector Search │
│ backend/agents/autonomous_agent.py — Python port of agent loop │
│ backend/tools/ — Tool registry + email tool handlers │
│ backend/api/router.py — Health, email, analysis endpoints │
└───────────────────────────────────────────────────────────────────┘
Key design principles:
node server.js)cleaned_emails.json + gmail_embeddings.db are missing → full init.history.list) when possible, or full mailbox fetch on fresh start. Concurrent fetching with progress bars.documents.db.Email → Build prompt (with tools + memory context) → LLM → Parse response
├─ Tool call? → Execute tool → Feed result back → LLM again
└─ Final analysis? → Validate → Save summary → Update vector DB → Next email
The loop runs up to 10 iterations per email, with:
Promise.raceglobal.gc()Gmail API → Fetch → Clean → Embed (BGE-M3) → Analyze (phi4-mini/qwen3-vl) → Store
↓
Tool Calls (vector_search, calendar, attachments)
↓
Core Memory Learning
↓
Chrome Extension UI
core/ai-client.js — Unified AI InterfacechatOllama() — routes to Ollama (native tools or raw /api/generate)_chatPhi4Raw() — bypasses broken Ollama template for phi4-minianalyzeEmail() — main entry: builds prompt, runs tool-calling loop, returns structured analysisparseToolCallFlexible() — 5-tier parser (FUNCTION_CALL, <|tool_call|>, <tool_call>, JSON, raw)extractYAML() / extractJSON() — response parsing with fallback_isValidAnalysisJSON() — rejects non-analysis output (tool calls, arrays, empty)| Model | Use Case | Status |
|---|---|---|
phi4-mini:latest | Default — text analysis + tool calling | ✅ Active |
qwen3-vl:8b-instruct | Vision (inline images) | Optional |
| Gemma 2/3 (OpenRouter) | Cloud fallback | Optional |
Why phi4-mini? Best small model (<4B) for tool calling (67.1%) and logic/math (88.6%). Proven in production with 0 errors across 83 emails.
bge-m3 (1024-dimensional vectors)IndexFlatL2 (exact nearest-neighbor)gmail_embeddings.db (SQLite, embedding as BLOB)The LLM can call these tools during analysis:
| Tool | Purpose |
|---|---|
vector_search_emails | Find related past emails (temporal-aware) |
calendar_get_events | Check schedule for conflicts |
calendar_add_event | Create calendar event from email |
read_attachment | Extract text from PDF/doc/xlsx/image attachment |
query_document | Semantic search inside a document attachment |
view_inline_image | (Vision models) view inline image |
Defensive guards:
IMAGE_* IDs) are already in context — read_attachment rejects them with a clear message.subject + snippet for embedding.core_memory.json) — user preferences, patterns, sender historyepisodic_memory.json) — past interactionsBackground services (started at init):
| Metric | Value |
|---|---|
| Emails analyzed | 83 / 83 (100%) |
| Errors | 0 (0% error rate) |
| Average time/email | ~88.7s (range 23.5s – 220.3s) |
| Model | phi4-mini:latest (Ollama, local) |
| Priority detection accuracy | 88.7% |
| Tool efficiency | 86.7% |
| Status | ✅ Production Ready |
| Iterations | % of Emails |
|---|---|
| 1 (first try) | 78% |
| 2 (formatting fix) | 18% |
| 3+ (edge case) | 4% |
| Avg iterations | 1.25–1.4 |
| Error Type | JSON Failure | YAML Failure |
|---|---|---|
| Unescaped quotes | 8.2% | 0% |
| Trailing commas | 5.1% | 0% |
| Missing quotes | 4.3% | 0% |
| Invalid nesting | 3.9% | 0% |
| Overall | ~24% | ~0.5% |
| Tool | Calls | Notes |
|---|---|---|
vector_search_emails | 1 | Found 10 related emails in 0.53s |
read_attachment | 2 | PDF bus ticket, book PDF |
query_document | 1 | Document search |
| Emails using tools | ~6% | 94% analyzed without tools |
cleaned_emails.json (O(1) lookups)ollama serve)phi4-mini:latest and bge-m3 pulled:
ollama pull phi4-mini:latest
ollama pull bge-m3
npm install
node authenticate.js
This opens a browser for Gmail + Google Calendar OAuth consent and saves tokens to data/user_tokens.json.
nodemon server.js
Server starts on default port, WebSocket bridge on ws://localhost:8081.
docker build -t gmail-agent-mcp .
docker run -p 8081:8081 gmail-agent-mcp
npm test # all tests
npm run test:unit # unit only
npm run test:integration # integration only
.env)| Variable | Default | Purpose |
|---|---|---|
OLLAMA_MODEL / MODEL_NAME | phi4-mini:latest | Active LLM |
VISION_MODEL | qwen3-vl:8b-instruct | Vision model |
OLLAMA_URL | http://localhost:11434 | Ollama endpoint |
OPENROUTER_API_KEY | — | Optional cloud fallback |
GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET | — | OAuth credentials |
core/ai-client.js)maxIterations = 10 — max tool-calling loops per emailANALYSIS_TIMEOUT_MS = 5 * 60 * 1000 — per-email timeoutMAX_PROMPT_TOKENS = 10000 — prompt size capTOOL_REFRESH_INTERVAL = 100 — re-fetch tool defs every N emailsgmail-agent-mcp/
├── server.js # Entry point + init workflow + WebSocket
├── server/
│ └── gmail-server.js # Main orchestration (~8.5k lines)
├── core/
│ ├── ai-client.js # LLM interface + tool-calling loop
│ ├── ai-models.js # Model registry (OpenRouter)
│ ├── model-manager.js # Model init/selection
│ ├── constants.js
│ ├── error-handler.js
│ ├── api-tracker.js
│ └── resource-registry.js
├── tools/
│ ├── attachment-tools.js # read_attachment, query_document
│ ├── calendar-tools.js
│ ├── semantic-search.js # FAISS + BGE-M3
│ ├── memory-tools.js
│ ├── classification-rules.js
│ └── ... (10+ tool modules)
├── services/
│ ├── analysis-manager.js
│ ├── calendar-manager.js
│ ├── core-memory.js
│ ├── document-orchestrator.js
│ ├── query-router.js
│ ├── follow-up-tracker.js
│ ├── smart-snooze.js
│ ├── daily-digest.js
│ ├── relationship-graph.js
│ ├── sentiment-timeline.js
│ ├── response-intelligence.js
│ └── ... (20+ services)
├── config/
│ └── prompts.js # Prompt builders (phi4-mini, Hermes)
├── data/ # Runtime state (JSON + SQLite)
├── docs/ # Documentation + benchmark reports
├── extension/ # Chrome extension frontend
├── tests/ # Vitest suites
├── scripts/ # Utility scripts
├── Dockerfile
├── package.json
└── README.md
Note:
_legacy_node_backend/andpython_backend//backend/are preserved for reference only. The active code is at the root (server.js,core/,tools/,services/).
| Bug | Fix |
|---|---|
A — Flat FUNCTION_CALL misparsed as final YAML | Normalized flat format; YAML parser strips FUNCTION_CALL blocks; validation requires non-empty summary |
| B — Fetch capped at 100 / 24h window | Incremental fetch from latest analyzed timestamp; full mailbox on fresh start |
| C — Periodic loop analyzed 0 emails | Synchronous analysis via working performSingleEmailAnalysis path |
| D — No per-email timeout | 5-minute Promise.race wrapper around analysis |
| E — Empty-body vector throw | Fallback to subject + snippet; skip gracefully if still empty |
F — Inline-image IDs passed to read_attachment | Prompt instruction + defensive guard (rejects IMAGE_* IDs) |
G — vectorSearchRelevance empty | Backfilled from real search hits (relevantReplies) |
| Latent — bge-m3 context overflow | Truncate embedding input to 32K chars (both init + analysis paths) |
qwen3-vl (optional); phi4-mini is text-only.Last updated: August 10, 2026 — Documentation refreshed to reflect the active Node.js backend, current model (phi4-mini:latest), and production benchmarks from the April 18, 2026 run (83 emails, 0 errors, 88.7% accuracy).
11 commits
JavaScript
86.5%
Python
11.7%
HTML
1.5%
Version: 3.1.0-alpha
Last updated: August 10, 2026
Status: Production-ready autonomous email analysis backend (Node.js + Ollama)
Model: phi4-mini:latest (3.8B, local, free) via Ollama
Most people get too many emails. Some are urgent, some are boring, some contain files or images, some ask you to do something later, and some are only promotional noise.
Gmail Agent MCP is an autonomous email-analysis agent that sits beside your inbox and does the boring parts of email work for you. It reads each email with a local LLM, summarizes it, extracts key points, detects priority/importance, identifies action items, searches related past emails, reads attachments, and can even create calendar events — all without sending your data to a cloud API.
The entire pipeline runs locally (Gmail/Google Calendar API for data access only; all AI inference happens on your machine via Ollama). There are no per-token costs.
| Component | Technology | Purpose |
|---|---|---|
| Language | Node.js (ES Modules, type: module) | Backend runtime |
| Runtime version | Node.js ≥ 18.17.0 | Minimum supported |
| Package manager | npm | Dependency management |
| Dev server | nodemon | Auto-restart on file change during development |
| Tests | Vitest 4.x | Unit + integration testing |
| Component | Technology | Purpose |
|---|---|---|
| Primary model | phi4-mini:latest (Microsoft, 3.8B) | Local tool-calling + analysis. Default via OLLAMA_MODEL env. |
| Vision model | qwen3-vl:8b-instruct | Optional multimodal path for inline images |
| LLM server | Ollama (local) | http://localhost:11434 |
| Cloud fallback | OpenRouter API | Optional; free models (Gemma 2/3, etc.) |
| Embedding model | bge-m3 (1024-dim) | Via Ollama /api/embeddings |
| AI client | core/ai-client.js | Unified interface; raw /api/generate for phi4-mini |
| Prompt formats | JSON, YAML, XML, FUNCTION_CALL, `< | tool_call |
| Parser stack | 5-tier flexible parser + YAML/JSON fallback | Handles model output variations |
| Component | Technology | Purpose |
|---|---|---|
| Vector index | FAISS (faiss-node) | IndexFlatL2, 1024-dim BGE-M3 embeddings |
| Relational store | SQLite (better-sqlite3, sqlite3) | Email embeddings, document chunks, metadata |
| Document DB | documents.db (SQLite) | Attachment text, chunks, OCR results |
| JSON state files | data/*.json | Summaries, core memory, metadata, tracking |
| NDJSON logs | data/analysis_detail_log.ndjson | Per-iteration analysis traces |
| Component | Technology | Purpose |
|---|---|---|
| Gmail API | googleapis (v1) + google-auth-library | OAuth2 email fetch |
| Google Calendar | googleapis | Event create/read |
| WebSocket bridge | ws (port 8081) | Chrome extension communication |
| Component | Technology | Purpose |
|---|---|---|
pdf-parse, pdfkit | Text extraction | |
| Office docs | mammoth (Word), xlsx (Excel), officegen | Docx/xlsx read/write |
| Images / OCR | sharp, tesseract.js | Image resize + OCR |
| Archives | adm-zip | Zip extraction |
| Rich text | rtf-stream-parser | RTF extraction |
| Markdown | marked | Rendering |
| Vision captions | moondream (via Ollama) | Image description |
| Component | Technology | Purpose |
|---|---|---|
| Config | dotenv | Environment variables |
| Logging | winston | Structured logs |
| Validation | joi, validator | Input validation |
| Concurrency | async-mutex, lru-cache, p-timeout | Thread safety, caching, timeouts |
| Queue | bull | Background job queue (Redis-backed) |
| CLI progress | cli-progress | Terminal progress bars |
| MCP SDK | @modelcontextprotocol/sdk | Model Context Protocol server |
| Containerization | Docker (multi-stage, node:18-alpine) | Production deployment |
| HTML cleaning | Custom regex-based stripper | 92% payload reduction |
| Resource management | core/resource-registry.js | Timer/interval cleanup to prevent leaks |
| Error handling | core/error-handler.js | Standardized error codes and formatting |
| Model management | core/model-manager.js | GPU/memory-aware model switching |
| API tracking | core/api-tracker.js | Rate limit and usage monitoring |
| Timeout fetch | utils/timeout-free-fetch.js | Bypasses undici 300s timeout for Ollama |
| Security helpers | utils/security-helpers.js | Bounded maps/sets, token encryption |
| Email utilities | utils/email-text-utils.js | HTML stripping, entity decoding, truncation |
| Document processing | utils/document-processor.js | Multi-format file handling utilities |
| Chunk embedder | utils/chunk-embedder.js | 512-token chunks with 128 overlap, MD5 cache |
| CLI UI | utils/cli-ui.js | Colored output, progress bars, status updates |
The system is a modular, layered Node.js backend with an autonomous analysis loop, Chrome extension frontend, and an optional Python FastAPI backend.
┌─────────────────────────────────────────────────────────────────┐
│ Chrome Extension │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ content.js — sidebar UI, email monitoring, WebSocket │ │
│ │ background.js — service worker, reconnection logic │ │
│ │ popup.js — connection status, trigger analysis │ │
│ │ safe-html.js — XSS prevention, DOM escaping │ │
│ └───────────────────────────┬───────────────────────────────┘ │
└───────────────────────────────┼──────────────────────────────────┘
│ ws://localhost:8081
┌───────────────────────────────▼──────────────────────────────────┐
│ server.js (entry point) │
│ - Initialization workflow (fetch → clean → embed → analyze) │
│ - WebSocket bridge startup │
│ - Periodic analysis scheduler │
│ - AutonomousAgent class with crash recovery │
└───────────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────────▼──────────────────────────────────┐
│ server/gmail-server.js (~8.5k lines) │
│ - Gmail/Calendar API orchestration │
│ - Incremental fetch, analysis batching │
│ - Tool execution routing │
│ - Autonomous loop (snooze, follow-up, digest) │
│ - FAISS lazy initialization with promise deduplication │
└──────┬──────────────────────┬────────────────────┬───────────────┘
│ │ │
┌──────▼──────┐ ┌──────────▼─────────┐ ┌──────▼──────────────┐
│ core/ │ │ tools/ │ │ services/ │
│ ai-client │ │ attachment-tools │ │ analysis-manager │
│ ai-models │ │ calendar-tools │ │ calendar-manager │
│ model-mgr │ │ semantic-search │ │ core-memory │
│ constants │ │ memory-tools │ │ document-orchestrator│
│ error-hdlr │ │ classification │ │ query-router │
│ api-tracker│ │ enrichment │ │ follow-up-tracker │
│ resource │ │ extraction │ │ smart-snooze │
│ registry │ │ action-tools │ │ daily-digest │
│ │ │ system-tools │ │ relationship-graph │
│ │ │ email-analysis │ │ sentiment-timeline │
│ │ │ tool-call-logger │ │ response-intelligence│
│ │ │ tool-debug-dash │ │ user-embeddings │
│ │ │ inline-image-svc │ │ offline-calendar-queue│
│ │ │ qwen-image-db │ │ file-reader │
└─────────────┘ └───────────────────┘ └─────────────────────┘
│ │ │
┌──────▼──────────────────────▼────────────────────▼───────────────┐
│ Data Layer (SQLite + JSON) │
│ gmail_embeddings.db │ documents.db │ email_summaries.json │
│ core_memory.json │ cleaned_emails.json │ raw_emails.json │
│ analysis_detail_log.ndjson │ retry_attempts.json │
└───────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ Python Backend (FastAPI) │
│ backend/main.py — Uvicorn server, CORS, lifespan │
│ backend/services/ — Gmail, AI, Calendar, Vector Search │
│ backend/agents/autonomous_agent.py — Python port of agent loop │
│ backend/tools/ — Tool registry + email tool handlers │
│ backend/api/router.py — Health, email, analysis endpoints │
└───────────────────────────────────────────────────────────────────┘
Key design principles:
node server.js)cleaned_emails.json + gmail_embeddings.db are missing → full init.history.list) when possible, or full mailbox fetch on fresh start. Concurrent fetching with progress bars.documents.db.Email → Build prompt (with tools + memory context) → LLM → Parse response
├─ Tool call? → Execute tool → Feed result back → LLM again
└─ Final analysis? → Validate → Save summary → Update vector DB → Next email
The loop runs up to 10 iterations per email, with:
Promise.raceglobal.gc()Gmail API → Fetch → Clean → Embed (BGE-M3) → Analyze (phi4-mini/qwen3-vl) → Store
↓
Tool Calls (vector_search, calendar, attachments)
↓
Core Memory Learning
↓
Chrome Extension UI
core/ai-client.js — Unified AI InterfacechatOllama() — routes to Ollama (native tools or raw /api/generate)_chatPhi4Raw() — bypasses broken Ollama template for phi4-minianalyzeEmail() — main entry: builds prompt, runs tool-calling loop, returns structured analysisparseToolCallFlexible() — 5-tier parser (FUNCTION_CALL, <|tool_call|>, <tool_call>, JSON, raw)extractYAML() / extractJSON() — response parsing with fallback_isValidAnalysisJSON() — rejects non-analysis output (tool calls, arrays, empty)| Model | Use Case | Status |
|---|---|---|
phi4-mini:latest | Default — text analysis + tool calling | ✅ Active |
qwen3-vl:8b-instruct | Vision (inline images) | Optional |
| Gemma 2/3 (OpenRouter) | Cloud fallback | Optional |
Why phi4-mini? Best small model (<4B) for tool calling (67.1%) and logic/math (88.6%). Proven in production with 0 errors across 83 emails.
bge-m3 (1024-dimensional vectors)IndexFlatL2 (exact nearest-neighbor)gmail_embeddings.db (SQLite, embedding as BLOB)The LLM can call these tools during analysis:
| Tool | Purpose |
|---|---|
vector_search_emails | Find related past emails (temporal-aware) |
calendar_get_events | Check schedule for conflicts |
calendar_add_event | Create calendar event from email |
read_attachment | Extract text from PDF/doc/xlsx/image attachment |
query_document | Semantic search inside a document attachment |
view_inline_image | (Vision models) view inline image |
Defensive guards:
IMAGE_* IDs) are already in context — read_attachment rejects them with a clear message.subject + snippet for embedding.core_memory.json) — user preferences, patterns, sender historyepisodic_memory.json) — past interactionsBackground services (started at init):
| Metric | Value |
|---|---|
| Emails analyzed | 83 / 83 (100%) |
| Errors | 0 (0% error rate) |
| Average time/email | ~88.7s (range 23.5s – 220.3s) |
| Model | phi4-mini:latest (Ollama, local) |
| Priority detection accuracy | 88.7% |
| Tool efficiency | 86.7% |
| Status | ✅ Production Ready |
| Iterations | % of Emails |
|---|---|
| 1 (first try) | 78% |
| 2 (formatting fix) | 18% |
| 3+ (edge case) | 4% |
| Avg iterations | 1.25–1.4 |
| Error Type | JSON Failure | YAML Failure |
|---|---|---|
| Unescaped quotes | 8.2% | 0% |
| Trailing commas | 5.1% | 0% |
| Missing quotes | 4.3% | 0% |
| Invalid nesting | 3.9% | 0% |
| Overall | ~24% | ~0.5% |
| Tool | Calls | Notes |
|---|---|---|
vector_search_emails | 1 | Found 10 related emails in 0.53s |
read_attachment | 2 | PDF bus ticket, book PDF |
query_document | 1 | Document search |
| Emails using tools | ~6% | 94% analyzed without tools |
cleaned_emails.json (O(1) lookups)ollama serve)phi4-mini:latest and bge-m3 pulled:
ollama pull phi4-mini:latest
ollama pull bge-m3
npm install
node authenticate.js
This opens a browser for Gmail + Google Calendar OAuth consent and saves tokens to data/user_tokens.json.
nodemon server.js
Server starts on default port, WebSocket bridge on ws://localhost:8081.
docker build -t gmail-agent-mcp .
docker run -p 8081:8081 gmail-agent-mcp
npm test # all tests
npm run test:unit # unit only
npm run test:integration # integration only
.env)| Variable | Default | Purpose |
|---|---|---|
OLLAMA_MODEL / MODEL_NAME | phi4-mini:latest | Active LLM |
VISION_MODEL | qwen3-vl:8b-instruct | Vision model |
OLLAMA_URL | http://localhost:11434 | Ollama endpoint |
OPENROUTER_API_KEY | — | Optional cloud fallback |
GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET | — | OAuth credentials |
core/ai-client.js)maxIterations = 10 — max tool-calling loops per emailANALYSIS_TIMEOUT_MS = 5 * 60 * 1000 — per-email timeoutMAX_PROMPT_TOKENS = 10000 — prompt size capTOOL_REFRESH_INTERVAL = 100 — re-fetch tool defs every N emailsgmail-agent-mcp/
├── server.js # Entry point + init workflow + WebSocket
├── server/
│ └── gmail-server.js # Main orchestration (~8.5k lines)
├── core/
│ ├── ai-client.js # LLM interface + tool-calling loop
│ ├── ai-models.js # Model registry (OpenRouter)
│ ├── model-manager.js # Model init/selection
│ ├── constants.js
│ ├── error-handler.js
│ ├── api-tracker.js
│ └── resource-registry.js
├── tools/
│ ├── attachment-tools.js # read_attachment, query_document
│ ├── calendar-tools.js
│ ├── semantic-search.js # FAISS + BGE-M3
│ ├── memory-tools.js
│ ├── classification-rules.js
│ └── ... (10+ tool modules)
├── services/
│ ├── analysis-manager.js
│ ├── calendar-manager.js
│ ├── core-memory.js
│ ├── document-orchestrator.js
│ ├── query-router.js
│ ├── follow-up-tracker.js
│ ├── smart-snooze.js
│ ├── daily-digest.js
│ ├── relationship-graph.js
│ ├── sentiment-timeline.js
│ ├── response-intelligence.js
│ └── ... (20+ services)
├── config/
│ └── prompts.js # Prompt builders (phi4-mini, Hermes)
├── data/ # Runtime state (JSON + SQLite)
├── docs/ # Documentation + benchmark reports
├── extension/ # Chrome extension frontend
├── tests/ # Vitest suites
├── scripts/ # Utility scripts
├── Dockerfile
├── package.json
└── README.md
Note:
_legacy_node_backend/andpython_backend//backend/are preserved for reference only. The active code is at the root (server.js,core/,tools/,services/).
| Bug | Fix |
|---|---|
A — Flat FUNCTION_CALL misparsed as final YAML | Normalized flat format; YAML parser strips FUNCTION_CALL blocks; validation requires non-empty summary |
| B — Fetch capped at 100 / 24h window | Incremental fetch from latest analyzed timestamp; full mailbox on fresh start |
| C — Periodic loop analyzed 0 emails | Synchronous analysis via working performSingleEmailAnalysis path |
| D — No per-email timeout | 5-minute Promise.race wrapper around analysis |
| E — Empty-body vector throw | Fallback to subject + snippet; skip gracefully if still empty |
F — Inline-image IDs passed to read_attachment | Prompt instruction + defensive guard (rejects IMAGE_* IDs) |
G — vectorSearchRelevance empty | Backfilled from real search hits (relevantReplies) |
| Latent — bge-m3 context overflow | Truncate embedding input to 32K chars (both init + analysis paths) |
qwen3-vl (optional); phi4-mini is text-only.Last updated: August 10, 2026 — Documentation refreshed to reflect the active Node.js backend, current model (phi4-mini:latest), and production benchmarks from the April 18, 2026 run (83 emails, 0 errors, 88.7% accuracy).
11 commits
JavaScript
86.5%
Python
11.7%
HTML
1.5%