Skip to content

Repository files navigation

vmail

A full-featured webmail client built with the Vio framework. Connect multiple Gmail and Outlook accounts, manage folders, search messages, view conversation threads, and integrate with AI agents through MCP devtools.

Features

  • Multi-account support — Connect Gmail and Outlook accounts via OAuth 2.0
  • Three-panel layout — Sidebar, message list, and message detail view
  • Local SQLite cache — Messages are cached locally for fast offline-capable browsing
  • Incremental sync — Background sync every 60 seconds using Gmail History API and Microsoft Delta API
  • Conversation threads — Group messages by thread with a stacked conversation view (toggle in settings)
  • Folder management — Create and delete custom folders/labels synced to your email provider
  • Move messages — Move messages between folders via a dropdown in the message view
  • Search — Search across subject, sender, and body text with a clear button to reset
  • Compose, reply, and forward — Full compose form with reply/forward prefilling
  • Star and read status — Toggle starred/read state with best-effort provider sync
  • MCP integration — AI agents can manage folders, move messages, and apply labels via vio-devtools
  • Permission model — Per-account MCP access levels: read_organize or full_control

Tech Stack

Layer Technology
Frontend Vio (reactive component framework)
Backend Fastify 5.x
Database SQLite via better-sqlite3
Gmail API googleapis
Outlook API @azure/msal-node + Microsoft Graph
Build Vite 7.x
Language TypeScript 5.x
Tests Vitest
DevTools vio-devtools (MCP integration)

Prerequisites

  • Node.js 20+
  • A Google Cloud project with the Gmail API enabled (for Gmail accounts)
  • A Microsoft Azure app registration with Mail.ReadWrite + Mail.Send permissions (for Outlook accounts)
  • The Vio framework built locally at ../vio-framework/packages/vio

Setup

1. Install dependencies

npm install

2. Configure environment variables

Create a .env file in the project root:

# Server
PORT=3001
DB_PATH=vmail.db

# Google OAuth (Gmail)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3001/auth/google/callback

# Microsoft OAuth (Outlook)
MICROSOFT_CLIENT_ID=your-microsoft-client-id
MICROSOFT_CLIENT_SECRET=your-microsoft-client-secret
MICROSOFT_REDIRECT_URI=http://localhost:3001/auth/microsoft/callback

3. Google Cloud setup

  1. Go to Google Cloud Console
  2. Create a project and enable the Gmail API
  3. Create OAuth 2.0 credentials (Web application)
  4. Add http://localhost:3001/auth/google/callback as an authorized redirect URI
  5. Copy the client ID and secret to your .env

Required scopes (requested automatically):

  • gmail.modify — Read and modify messages
  • gmail.compose — Create drafts
  • gmail.send — Send messages
  • userinfo.email / userinfo.profile — User info

4. Microsoft Azure setup

  1. Go to Azure Portal > App registrations
  2. Register a new application
  3. Add a Web redirect URI: http://localhost:3001/auth/microsoft/callback
  4. Under API permissions, add: Mail.ReadWrite, Mail.Send, openid, profile, email, offline_access
  5. Create a client secret and copy it along with the client ID to your .env

5. Start the development server

npm run dev

This starts both:

  • Backend on http://localhost:3001 (Fastify + SQLite)
  • Frontend on http://localhost:4005 (Vite dev server, proxies /api and /auth to backend)

Open http://localhost:4005 in your browser.

Usage

Adding an account

Click Add Gmail or Add Outlook in the sidebar. You'll be redirected to the provider's OAuth consent screen. After authorizing, vmail syncs your folders and fetches the 30 most recent inbox messages.

Navigating

  • Sidebar — Switch between accounts and folders
  • Message list — Click a message to view it; unread messages appear bold
  • Search — Type in the search bar and press Enter; click the x button to clear
  • Compose — Click the Compose button to open the compose form

Managing folders

  • Click the + button next to "Folders" in the sidebar to create a new folder
  • Type a name and press Enter (or Escape to cancel)
  • Hover over a custom folder to reveal the x delete button
  • Use the Move to... dropdown in the message view to move messages between folders

Conversation threads

  1. Go to Settings and enable conversation view
  2. Messages in the list are grouped by thread with a count badge
  3. Click a thread to see all messages stacked chronologically in the detail view

Settings

  • Conversation view — Toggle thread grouping on/off
  • MCP Access — Set per-account permission level for AI agent access
  • Remove Account — Delete an account and all its cached data

Architecture

src/
├── shared/
│   └── types.ts              # Shared TypeScript interfaces (Message, Folder, Account, etc.)
├── server/
│   ├── index.ts              # Fastify server entry point
│   ├── config.ts             # Environment configuration
│   ├── db/
│   │   ├── schema.ts         # SQLite schema (accounts, folders, messages, settings)
│   │   └── index.ts          # Database initialization + migrations
│   ├── providers/
│   │   ├── types.ts          # EmailProvider interface
│   │   ├── registry.ts       # Provider registry (lookup by name)
│   │   ├── google.ts         # Gmail provider (OAuth, labels, messages, sync)
│   │   └── microsoft.ts      # Outlook provider (MSAL, Graph API, delta sync)
│   └── routes/
│       ├── auth.ts           # OAuth flow (/auth/:provider, /auth/:provider/callback)
│       ├── accounts.ts       # Account CRUD (/api/accounts)
│       ├── emails.ts         # Messages, folders, search, sync, settings, MCP
│       └── static.ts         # Production static file serving
└── client/
    ├── main.ts               # App root, routing, mount, MCP dispatch bridge
    ├── api.ts                # Fetch wrapper for all API endpoints
    ├── styles.css            # Full application styles
    ├── store/
    │   └── actions.ts        # Vio store state + actions (reducers)
    └── components/
        ├── Sidebar.ts        # Account switcher, folder list, folder CRUD
        ├── MessageList.ts    # Message/thread list with search
        ├── MessageView.ts    # Message detail, thread view, action bar
        ├── ComposeView.ts    # Compose/reply/forward form
        └── SettingsView.ts   # Account settings, MCP permissions, display options

Database schema

Table Purpose
accounts OAuth credentials, sync tokens, user info
folders Email folders/labels per account (unique: account_id + provider_folder_id)
messages Full message cache with thread_id, indexed by account, folder, date, thread
attachments Message attachments (foreign key to messages)
drafts Unsent draft messages
mcp_settings MCP permission level per account
user_settings Key-value user preferences (e.g., conversation view toggle)

Sync strategy

  1. On auth — Fetch all folders + 30 recent inbox messages + initial sync token
  2. On folder open — If no cached messages, fetch from provider on demand
  3. Periodic sync — Every 60 seconds, incremental sync via Gmail History API or Microsoft Delta API
  4. Graceful degradation — If sync token expires, resets and re-syncs

API Reference

Authentication

Method Path Description
GET /auth/providers List available OAuth providers
GET /auth/:provider Redirect to provider OAuth consent screen
GET /auth/:provider/callback OAuth callback — exchanges code, creates account, initial sync

Accounts

Method Path Description
GET /api/accounts List all accounts
GET /api/accounts/:id Get single account
DELETE /api/accounts/:id Delete account (cascades)

Folders

Method Path Description
GET /api/accounts/:accountId/folders List folders for account
POST /api/accounts/:accountId/folders Create folder { name }
DELETE /api/accounts/:accountId/folders/:folderId Delete folder

Messages

Method Path Description
GET /api/accounts/:accountId/folders/:folderId/messages List messages (paginated: ?page=&limit=)
GET /api/accounts/:accountId/messages/:messageId Get full message
PATCH /api/accounts/:accountId/messages/:messageId Update flags { isRead, isStarred }
POST /api/accounts/:accountId/messages/:messageId/move Move message { folderId }
DELETE /api/accounts/:accountId/messages/:messageId Delete message
GET /api/accounts/:accountId/search?q= Search messages
GET /api/accounts/:accountId/threads/:threadId/messages Get all messages in a thread

Send

Method Path Description
POST /api/accounts/:accountId/send Send message { to, cc, bcc, subject, body, replyToId? }

Sync

Method Path Description
POST /api/accounts/:accountId/sync Trigger incremental sync

Settings

Method Path Description
GET /api/settings Get all user settings
PUT /api/settings Update user settings { key: value }
GET /api/accounts/:accountId/mcp-settings Get MCP permission level
PUT /api/accounts/:accountId/mcp-settings Set MCP permission { permissionLevel }

Health

Method Path Description
GET /api/health Health check { status: "ok" }

MCP Integration

vmail integrates with AI agents through vio-devtools. Agents can dispatch actions to the Vio store, which are intercepted by a bridge that triggers the corresponding API calls.

Available MCP actions

Action Payload Permission
mcpCreateFolder { name: string } full_control
mcpDeleteFolder { folderId: string } full_control
mcpMoveMessage { messageId: string, folderId: string } read_organize
mcpAddLabel { messageId: string, labelId: string } read_organize
mcpRemoveLabel { messageId: string, labelId: string } read_organize
mcpSendMessage { to, subject, body, ... } full_control
mcpDeleteMessage messageId full_control
mcpSaveDraft { to, subject, body, ... } full_control

Example (via vio-devtools MCP)

vio_dispatch('mcpCreateFolder', { name: 'Receipts' })
vio_dispatch('mcpMoveMessage', { messageId: '...', folderId: '...' })

Permission levels

  • read_organize — Read, search, star, move, delete messages; manage labels
  • full_control — All of the above plus send, reply, forward, create/delete folders, save drafts

Set per-account in Settings > MCP Access.

Testing

# Run all tests
npm test

# Watch mode
npm run test:watch

Tests cover:

  • Database creation and migrations
  • Provider registry
  • Google provider (mocked Gmail API)
  • Microsoft provider (mocked Graph API)
  • Server health endpoint
  • Account CRUD routes
  • Email routes (list, search, update, move, delete, send, sync)
  • Integration tests (full request lifecycle)

Production Build

npm run build

This compiles:

  • Clientdist/client/ (Vite build)
  • Serverdist/server/ (TypeScript compilation)

Run in production:

NODE_ENV=production node dist/server/index.js

The server serves the built client files and handles SPA fallback routing.

License

MIT

About

Full-featured webmail client built with the Vio framework. Multi-account Gmail/Outlook support, OAuth, local SQLite cache, conversation threads, folder management, and AI-agent integration via MCP devtools.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages