littlebird-mail
by workslo
README.md
# littlebird-mail
Littlebird's independent mailbox - a Cloudflare Worker that acts as a remote MCP server for send, read, reply, and search.
This repo also carries `bin/mail`, the fleet's hardened AgentMail CLI (unrelated to the Worker itself) — see [docs/fleet-mail-check.md](docs/fleet-mail-check.md).
## What it does
- **Outbound**: Sends email as `littlebird-mail@workslo.ai` via Cloudflare's `send_email` Worker binding. DKIM-signed, so it lands as Littlebird - not Shane.
- **Inbound**: Receives email via Cloudflare Email Routing, parses with `postal-mime`, and stores in D1.
- **MCP Tools**: Six tools exposed over a stateless HTTP endpoint (`/mcp`):
- `send_email` - Send mail as Littlebird
- `reply` - Reply to an existing thread
- `list_inbox` - Paginated inbox list, optional unread-only filter
- `read_message` - Full message body + marks as read
- `search_inbox` - Text search across subject, body, and from
- `inbox_stats` - Total and unread counts
## Architecture
- **Stateless**: No Durable Objects or session state. All persistence is D1.
- **Auth**: `/mcp` accepts only `Authorization: Bearer <MCP_TOKEN>`. Unauthenticated requests return a plain `401`; there is no OAuth provider, authorization flow, token exchange, dynamic registration, or OAuth metadata.
- **Threading**: Unified `messages` table with `thread_id` and `in_reply_to`. Replies attach to the parent thread.
## Deploy
### 1. Domain setup (alive-node only)
- Onboard `workslo.ai` to **Cloudflare Email Service** (Dashboard: Compute & AI > Email Service > Onboard Domain).
- Add SPF + DKIM records as instructed.
### 2. Install & generate types
```bash
npm install
npx wrangler types # Generates worker-configuration.d.ts with runtime types
```
### 3. Deploy
```bash
npx wrangler deploy
```
### 4. Set the bearer secret for a new deployment
```bash
npx wrangler secret put MCP_TOKEN
# Enter a long random string. Littlebird uses this as the Remote MCP bearer token.
```
Existing deployments keep their current `MCP_TOKEN`; a normal code deploy does not expose, replace, or rotate it. Do not re-run this command unless you intentionally need to replace the secret.
### 5. Inbound routing rule
Dashboard: Email Service > Email Routing > Create rule
- **Custom address**: `littlebird-mail@workslo.ai`
- **Action**: **Send to a Worker**
- **Worker**: `littlebird-mail`
### 6. Wire up the MCP client
In **Littlebird Settings > Integrations**, add a Remote MCP server:
- **URL**: `https://birb.workslo.ai/mcp`
- **Auth**: Bearer token = the value you set in step 4
This is the service's only authentication path. OAuth endpoints such as `/authorize`, `/token`, and `/register` are intentionally not exposed.
### 7. Smoke check the deployed MCP endpoint
Use the same URL + token shape as Littlebird:
```bash
MCP_TOKEN=<token-from-step-4> npm run smoke:mcp
```
Expected result:
```text
MCP smoke passed for https://birb.workslo.ai/mcp
Tools: inbox_stats, list_inbox, read_message, reply, search_inbox, send_email
```
## Schema
D1 database `littlebird-mail` was provisioned manually. If you start fresh, run:
```sql
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
direction TEXT NOT NULL, -- 'inbound' or 'outbound'
from_address TEXT,
to_address TEXT,
subject TEXT,
body_text TEXT,
body_html TEXT,
thread_id TEXT,
in_reply_to TEXT,
message_id TEXT UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
read_at DATETIME,
labels TEXT,
raw_headers TEXT
);
CREATE INDEX IF NOT EXISTS idx_thread ON messages(thread_id);
CREATE INDEX IF NOT EXISTS idx_created ON messages(created_at);
CREATE INDEX IF NOT EXISTS idx_in_reply_to ON messages(in_reply_to);
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT
);
```
## D1 Database ID
- **Name**: `littlebird-mail`
- **ID**: `dbc3a61d-e581-4b03-9296-eee97dfb5851`
- **Region**: WNAM
## Environment
| Binding | Name | Purpose |
|---------|------|---------|
| D1 | `DB` | Message store + config |
| send_email | `EMAIL` | Outbound mail |
| var | `LB_ADDRESS` | Default From address |
| secret | `MCP_TOKEN` | Bearer auth for `/mcp` |
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing