mailmux
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mailmuxWhat's unread across all my mailboxes?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Boxaide
Free, self-hosted multi-mailbox agentic inbox.
Connect any IMAP/SMTP mail. One unified inbox in the browser. One MCP surface for every agent.
On top of the inbox: a CRM derived from your own mail, scheduled agent automations, and outreach that no agent can send on its own. See Agent work platform.
No paid SaaS required for core receive + send. MIT licensed.
Formerly Sley, then Mailmux. The repo is Remedy92/boxaide.
Quick start
cd Projects/boxaide # or your clone path
npm install
./scripts/start.sh --fixture
# equivalent: npm run dev -- --fixtureOpen http://127.0.0.1:8787 — on localhost the UI auto-loads the bearer token.
Fixture mode seeds two demo mailboxes (personal, work) so you can try the UI and MCP without real credentials.
Real mail
npm run devIn the UI: Connect mailbox → pick a preset (Gmail / Fastmail / Outlook / iCloud) or enter IMAP/SMTP hosts → use an app password where required.
Production-ish start
npm run build
npm start
# or: node dist/cli.js serveRelated MCP server: mail-mcp
Using the hosted interface
Boxaide has one web interface: the Next.js app in apps/web, built as a static export. You can serve it from your own process or host it elsewhere. Either way it talks to the Boxaide server on your machine, and it never sends your mail or your token anywhere else.
The public site is https://boxaide.vercel.app.
Local (recommended — works in every browser)
npm run build
npm startnpm run build compiles the server, builds apps/web, and copies the export to web-next/. Open the URL it prints, normally http://127.0.0.1:8787. Same origin as the API, so nothing else is needed.
To run it on its own during development:
cd apps/web && npm install && npm run dev # http://localhost:3000
cd apps/web && npm run build && npm run serve # the real static exportHosted (deployed somewhere else)
The page runs entirely in your browser and fetches mail directly from your machine.
Three routes exist in the export. Locally they are one screen and two spares; on a deployment they are the whole difference between a stranger and a user:
Route | What it is |
| The inbox. On a deployment |
| The download page. The desktop installer for the visitor's operating system, and the clone-and-run command behind a link. |
| The inbox again, at an address the redirect does not touch. This is the hosted interface; use it wherever this section says "the deployed page". |
1. Deploy apps/web. On Vercel and equivalents, set the project's Root Directory to apps/web and leave the build and output commands on auto. That setting is what keeps the CLI's better-sqlite3 out of the front-end install; it lives in the dashboard and cannot be expressed in a file. Do not add a workspaces key to the root package.json.
No environment variable is required. One optional, non-secret variable exists:
Name | Value | Purpose |
|
| The pre-filled Server URL on a browser with nothing in localStorage. Public by definition — it is inlined into the bundle at build time. Omit it and the same default comes from |
2. Allow the origin. On your machine, not on the host:
BOXAIDE_ALLOWED_ORIGINS=https://boxaide.vercel.app boxaide serveSee the rules and the cost of doing this under Browser origins below.
3. Copy the token. boxaide serve prints it on first run; it is also in bearer.token inside your data directory (~/.boxaide by default, or ~/.mailmux if that folder exists and ~/.boxaide does not).
4. Point the page at your server. Open the deployed page at /app, click Set up Boxaide, and enter the Server URL and the token. Both are stored in your browser's localStorage (boxaide.*) and are sent only to the server URL you entered. A first run still reads leftover mailmux.* and mailmux_token keys once.
5. Allow local network access (Chrome, Edge, Brave). Since Chromium 142 the browser asks permission before a website may reach 127.0.0.1. Allow it when prompted; if you dismissed the prompt, re-enable it under Site settings → Apps on device.
Safari, and the mixed-content limit
A page served over https cannot reach an http address. For 127.0.0.1 and localhost Chromium and Firefox make an exception; WebKit does not, and there is no workaround (WebKit bug 171934, still open). This is also why BOXAIDE_ALLOWED_ORIGINS drops http:// entries: the configuration that would avoid the block is the one that makes the allowlist spoofable.
If your server is not on loopback, put it behind https or reach it over a tunnel. Otherwise use the local build — run boxaide serve and open http://127.0.0.1:8787 directly. It is the same interface.
What the host can see
Nothing. The deployed page has no server-side code: no API routes, no server actions, no proxy, no middleware. Your token, your mail credentials and every message body travel only between your browser and your own machine.
Desktop app
A window instead of a terminal, for people who do not want either. apps/desktop is an Electron shell: it starts the same server inside its own process, binds 127.0.0.1, and uses the same ~/.boxaide data directory, master key and bearer token. An account connected in the desktop app is the same account your agents reach over MCP.
On macOS the app also lives in the menu bar. Click the mark for a popover —
recent mail, whether an agent is listening, one button into the app; the
popover is the /tray/ route of the same web export. Right-click for a menu:
open Boxaide, install the Claude connector (opens the bundled .mcpb in
Claude Desktop), Start at login (packaged app only — it registers a macOS
login item), quit. The menu bar icon stays as long as the app runs, including
with the window closed.
npm run desktopThat compiles the server, installs Electron deps only when the lockfile changed, downloads the Electron binary once, copies dist/ and web-next/ into the app if they changed, and opens the window. A second run skips the install and the download. The web UI rebuilds only when apps/web/src no longer matches the last web:sync stamp.
npm run desktop:dist # same prepare, then signed mac dmg in apps/desktop/release/On mac, signing uses a Developer ID certificate pinned by hash in apps/desktop/scripts/sign-mac.sh (electron-builder's by-name signing is ambiguous when the keychain holds two same-named certificates). Notarization is a separate, credential-holding step; the commands are at the top of that script. The port follows BOXAIDE_PORT (default 8787); if something already holds it — boxaide serve in a terminal, or a second copy of the app — the window does not open and the app says so.
The install button serves GitHub releases/latest. CI does not upload a dmg. After a merge to master, from the main checkout (not a worktree):
./scripts/ship_status.sh # is origin/master what a visitor downloads?
./scripts/ship.sh # bump, pack, sign, publish latest
./scripts/install-hooks.sh # once: remind on pull/checkout of mastership.sh is the only publisher. A git hook only prints the status; it never packs. Pass --dry-run to see the plan.
ship.sh needs APPLE_KEYCHAIN_PROFILE and refuses to run without it. Apple must notarize every macOS download, or the installer says it cannot verify the app. Mint the profile once. The keychain profile name is historical:
xcrun notarytool store-credentials mailmux-notary --apple-id <apple-id> --team-id 22DPQ7YCASAgent MCP (any client)
Claude Desktop — one click
With Boxaide running, open Connect agent in the UI and press Download for
Claude Desktop, or fetch http://127.0.0.1:8787/boxaide.mcpb directly.
Double-click the file; Claude Desktop installs it. Nothing to configure: the
connector is a tiny stdio→HTTP proxy (apps/mcpb) that finds your local server
and reads the token from ~/.boxaide/bearer.token itself. It is built into
web-next/boxaide.mcpb by npm run build (npm run mcpb:build on its own).
HTTP MCP (Cursor / remote-capable clients)
{
"mcpServers": {
"boxaide": {
"url": "http://127.0.0.1:8787/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}Token lives in ~/.boxaide/bearer.token (or BOXAIDE_TOKEN).
stdio MCP (Claude Code / manual Claude Desktop)
npm run mcp
# or: npx tsx src/cli.ts mcp{
"mcpServers": {
"boxaide": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/boxaide/src/cli.ts", "mcp"],
"env": {
"BOXAIDE_DATA_DIR": "/Users/you/.boxaide"
}
}
}
}Agents that speak TOML use [mcp_servers.boxaide]. Tool calls show up as mcp__boxaide__*.
Tools
Tool | Purpose |
| Connected aliases |
| Inbox list ( |
| Free-text search |
| Full body |
| Send (confirm in your agent) |
| Wait for the user's next message in the Boxaide window |
| Answer them there |
| Post a one-line "here is what I am doing" |
| Re-read the conversation |
The platform modules add their own tools. Full list in Agent work platform.
Group | Tools |
CRM |
|
Automations |
|
Outreach |
|
There is no tool that approves, rejects or sends an outbox row. That is a human action in the web UI.
Accounts are connected once in the web UI (or API). Agents reuse the same store — no per-agent OAuth.
Talking to your agent inside Boxaide
The Agent view is the app's first screen, and Boxaide runs no model behind it.
The agent is whichever MCP client you already use — Claude Code, Codex, Cursor,
Claude Desktop — and the four chat_* tools above are how it holds the
conversation in the Boxaide window instead of in its own terminal. There is no
per-client integration: a long-polling tool call is the one capability every MCP
client has.
Connect the client as above, then say this to it once, in its own window:
You are my Boxaide inbox agent. Use the Boxaide MCP tools.
Loop: call chat_await_message, do the work, post the answer with chat_say, then
call chat_await_message again. Keep going until I tell you to stop.
Everything I read appears in the Boxaide window, so every answer must go through
chat_say — do not answer here. A chat_await_message that returns no message is
normal; call it again. Use chat_activity for anything slow. Draft rather than
send unless I ask you to send.The kickoff is not optional and cannot be automated away: MCP is client-driven, so nothing on the Boxaide side can make an agent start listening. Anything you type before one does is queued and delivered when it arrives.
Notes on what the UI claims. "Listening" means an agent is parked in an open
chat_await_message — a request that is open right now, not an inference. It
never says "connected", because a stateless POST /mcp cannot tell a configured
client from one that was never started. Each message goes to exactly one agent,
so do not point two at the same server. The conversation is stored in
~/.boxaide/boxaide.db, encrypted with the same master key as the account
passwords, because an agent summarising an inbox puts mail content in those rows.
Agent work platform — CRM, automations, outreach
Three modules ship with the inbox. They are free, MIT, and run only on your machine. There is no sync, no tracking pixel, no click redirect, and no account anywhere else.
Module | What it is | Where you see it |
CRM | Contacts, organisations, notes, an interaction timeline and a deal pipeline, all derived from mail you already have. | People and Pipeline views |
Automations | Named prompts on a cron. Each run is a one-shot headless agent with the Boxaide tools and no user to talk to. | Automations view |
Outreach | Campaigns of timed steps that produce drafts. Every draft waits for you. | Outreach view |
CRM: derived, not entered
You do not type your contacts in. crm_sync walks INBOX and the Sent folder of each account and records who you actually mail with: contact per address, one interaction row per message, organisation per non-free email domain. It runs every 10 minutes while boxaide serve is up, and on demand from the tool or POST /api/crm/sync.
Free-provider domains (gmail.com, outlook.com, proton.me, …) never create an organisation. Automated senders (no-reply@, postmaster@, bounce addresses) are skipped. You can still add or correct anything by hand, or ask the agent to.
Automations are created by talking to the agent
The Automations view has no create form. This is deliberate: an automation is a prompt, and writing a good one is a conversation, not a text field.
Say what you want to whichever MCP client you already use:
Every weekday at 8, look at yesterday's unread mail, update the CRM, and queue a follow-up draft for anyone in the "warm" tag I have not mailed in two weeks.
The agent calls automation_create with a name, a 5-field cron and the prompt it just wrote for a future run of itself. The view then owns the automation: enable and disable it, see next and last run, run it now, read the log of any past run.
What a run may do:
Can | read mail, search, read and write CRM, save drafts, queue outreach into the outbox |
Cannot | talk to you (no chat tools — there is no one at the window), call |
Limits | one run at a time, queued if another is going; 15-minute hard timeout, then killed |
Run logs are stored encrypted, like everything else mail-derived.
Importing Claude Desktop scheduled tasks
Claude Desktop keeps each scheduled task as a folder under ~/.claude/scheduled-tasks/<name>/SKILL.md, with a name and description in front matter and the instructions in the body. Those are exactly the two things automation_create needs, minus a schedule.
Ask the agent to do the move:
Read ~/.claude/scheduled-tasks/*/SKILL.md and recreate each one as a Boxaide automation.
It reads the folder itself with its own file tools and calls automation_create per task: name from the front matter, prompt from the body. A SKILL.md does not carry a cron, so the agent asks you for the schedule of each one, or proposes one from the description. Nothing is imported silently, and nothing is deleted on the Claude Desktop side.
Why this is a conversation and not an importer: the two systems do not have the same permissions. A Claude Desktop task can talk to you and reach everything on your machine. A Boxaide automation cannot talk to anyone and works through the Boxaide tools. A task that assumed it could ask a question needs rewriting before it makes sense on a cron here, and the agent that reads it is the thing best placed to rewrite it.
No auto-send
No agent sends outreach. Not a scheduled one, not the one you are chatting to, not by mistake.
An agent's only route toward delivery is outbox_queue_draft, and the outreach engine's timed steps use the same table. Both land as pending rows in the outbox. The Outreach view shows each one in full — recipient, subject, body — with Approve, Edit or Reject. Approval is REST only, from the browser, by you.
The MCP surface has no approve, reject or send tool at all. This is not a permission you can grant; the tool does not exist.
Step | Who |
Write the draft | agent |
Queue it into the outbox | agent |
Read it, edit it, approve or reject it | you, in the browser |
Put it on the wire | server, after approval |
The rail badges the pending count, and the desktop app raises a notification and a dock badge when it rises. You are told about waiting drafts; you are never told after the fact about sent ones.
Sending is throttled server-side even after approval: at least 60 seconds between engine sends with jitter, and at most BOXAIDE_SEND_DAILY_CAP (default 50) per account per UTC day. Over the cap, an approved row simply goes out the next day.
Suppression is a server rule, not a checkbox
suppression is a table of addresses that must not be mailed. The check lives inside MailService.sendMessage, so it applies to every path — outreach, a manual compose, an agent's message_send. A suppressed recipient fails the send with recipient suppressed: <email>.
Reason | How an address gets there |
| Someone replied "stop", "unsubscribe" or "opt out" to a campaign. Detected on the inbound message; the campaign contact stops immediately. |
| You added it in the Outreach view. |
| A send failed hard. |
| An agent added it with |
Only a human can override, and only through REST: POST /api/messages/send accepts overrideSuppression: true. The MCP message_send tool does not expose the flag, so no agent can override a suppression at all.
Every outreach step, including the first, ends with a plain-text opt-out line telling the recipient to reply with "stop". There are no open pixels and no click-tracking links — they conflict with the privacy posture and are out of scope on purpose.
Everything stays on your machine
Same store, same master key, same file as the rest of Boxaide: ~/.boxaide/boxaide.db.
Data | At rest |
Note text, interaction subjects and snippets, campaign step subjects and bodies, outbox subjects and bodies, automation run logs | encrypted, AES-256-GCM, same master key as your mail passwords |
Contact email and name, organisation name and domain, tags, deal titles, suppression addresses | plaintext — they are CRM identity, needed for UNIQUE and for search |
Automation prompts | plaintext — you wrote them, they are not mail content |
Nothing leaves the process. There is no sync, no telemetry and no hosted component. Back up ~/.boxaide and you have backed up all of it.
Install options
Method | Command |
Dev |
|
Fixture demo |
|
Built |
|
Init data dir |
|
Env
Each BOXAIDE_* name is preferred. The matching MAILMUX_* name is still read when the Boxaide name is unset.
Variable | Default | Meaning |
|
| SQLite + keys. Uses |
|
| Bind address — see below |
|
| Port |
| auto file | API/MCP bearer |
| auto file | AES key for passwords — see below |
| off | Demo provider |
| empty | Extra browser origins allowed to call the API — see below |
|
| Approved outreach sends per account per UTC day |
Bind address (BOXAIDE_HOST)
The default binds to loopback, so only your own machine can reach the server. Change it and the server answers on the network, where the bearer token is the only thing between a stranger and your mail.
One behaviour changes on a non-loopback bind: /api/local-bootstrap, which hands out the bearer token in plaintext, answers 404 and hands out nothing. Its Host and Origin checks are browser guards, and a remote client picks both headers itself. Paste the token in by hand instead; it is in ~/.boxaide/bearer.token.
Master key (BOXAIDE_MASTER_KEY)
This key encrypts your stored mail passwords. Leave it unset and Boxaide generates a random one in ~/.boxaide/master.key.
Set it to 64 hex characters — a full random 32-byte key:
openssl rand -hex 32Any other value is treated as a passphrase and stretched with scrypt (N=2¹⁷, r=8 — 128 MB per attempt, about 0.2s once at startup). The salt is random per install and stored in ~/.boxaide/master.salt, so no precomputed table applies and the same passphrase on two machines produces two different keys. A passphrase still holds far less entropy than a random key, so prefer the hex form.
Back up master.salt with your data directory. Lose it and a passphrase no longer derives the key that encrypted your stored mail passwords.
Upgrading: passphrases used to be hashed once with SHA-256. The scrypt change means a passphrase set before this version derives a different key, and stored mail passwords no longer decrypt. Re-enter each account's password once, or keep the old key by setting BOXAIDE_MASTER_KEY to the hex of sha256(<your passphrase>).
Browser origins (BOXAIDE_ALLOWED_ORIGINS)
By default Boxaide accepts browser requests only from your own machine. A page on any other origin gets 403 {"error":"forbidden origin"}. Leave the variable unset and nothing changes.
Set it when you want a web interface hosted somewhere else — a deployment of apps/web, for example — to talk to your local server. The page still runs entirely in your browser and still fetches mail directly from your machine; the variable only tells your server which page origins it will answer. See Using the hosted interface for the full walkthrough.
BOXAIDE_ALLOWED_ORIGINS=https://boxaide.vercel.app boxaide serveRules:
Comma-separated, exact origins.
https://a.example.com,https://b.example.com.Only
https://entries are kept. A plaintext origin is trivially spoofed on a hostile network, sohttp://entries are dropped.Path, query and case are stripped:
https://A.App/xbecomeshttps://a.app. A port must match exactly —https://a.appdoes not allowhttps://a.app:8443.*is ignored on purpose. Boxaide holds your mail credentials; an any-origin allowlist would let any page you visit probe your server.Loopback (
127.0.0.1,localhost,::1) always passes, so the self-hosted UI needs no configuration.Requests with no
Originheader (curl, MCP clients) are unaffected.
What enabling this costs you. The origin check is the last defence-in-depth layer in front of a service that holds decrypted IMAP passwords. Adding an origin means:
Anyone who can serve a page at that exact hostname can reach your server if they also have your bearer token. On shared hosting platforms that includes preview deployments and anyone with deploy access. Prefer a custom domain you control over a platform-assigned hostname.
It is the only remaining barrier against a DNS-rebinding page reaching your loopback service, so the list should stay as short as you can make it.
The token is still required on every request.
Access-Control-Allow-Credentialsis never sent — Boxaide authenticates by header, never by cookie — so no page can ride ambient credentials./api/local-bootstrap, which hands out the bearer token in plaintext, is not widened by this variable. It stays loopback-only. A remote page must have its token pasted in by a human.
Architecture
See docs/ARCHITECTURE.md.
One Node process:
/— web UI (theapps/webexport, served fromweb-next/)/api/*— REST (same mail core)/mcp— JSON-RPC MCPboxaide mcp— stdio MCP
IMAP via ImapFlow, SMTP via Nodemailer, secrets AES-256-GCM, state SQLite.
Tests
npm testTests call shipped MailService, crypto, HTTP app, and MCP handlers with an in-memory FixtureProvider — no live mail accounts required.
Security notes
Default bind is localhost.
Browser requests are loopback-only unless
BOXAIDE_ALLOWED_ORIGINSnames another origin. Default is closed.Passwords encrypted at rest; master key in
~/.boxaide/master.key(mode 0600). A passphrase inBOXAIDE_MASTER_KEYis stretched with scrypt against~/.boxaide/master.salt.Prefer app passwords over primary account passwords.
Keep
message_sendbehind agent confirmation.Outreach cannot be sent by an agent. Approval is REST-only and human; no MCP tool approves, rejects or sends an outbox row.
Suppression is enforced inside
MailService.sendMessage, so it covers every send path. Only REST can override it.Mail-derived text in CRM, outreach and automation rows is encrypted with the same master key as your passwords.
License
MIT — free to use, modify, and redistribute.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityDmaintenanceAn MCP server that enables AI models to read, search, and send emails via IMAP and SMTP protocols. It supports various providers like Gmail and Outlook, allowing for tasks such as retrieving unread messages, searching by sender, and managing mailbox folders.
- Alicense-qualityDmaintenanceA generic IMAP and SMTP MCP server that enables AI agents to interact with email accounts for reading, searching, and sending messages. It provides high-level tools for managing email workflows like daily digests and folder organization across any standard email provider.1MIT
- Alicense-qualityAmaintenanceAn open-source MCP server that provides AI agents with secure access to read, search, and manage emails via Microsoft 365 and Gmail. It features security-first defaults like recipient allowlists and markdown content conversion to facilitate safe agent interaction with mailboxes.4Apache 2.0
- Alicense-qualityAmaintenanceA self-hosted MCP server that gives AI agents full email superpowers.1MIT
Related MCP Connectors
Hosted email MCP for AI agents with inboxes, send/receive, memory, recovery, and credits.
Shipmail MCP server for AI agent custom-domain email inboxes with REST API and webhooks.
Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Remedy92/boxaide'
If you have feedback or need assistance with the MCP directory API, please join our Discord server