hypermail-mcp 0.4.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -12
- package/dist/cli.js +801 -171
- package/dist/cli.js.map +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
A **Model Context Protocol** server that lets an agent operate any of the user's
|
|
4
4
|
inboxes through a single, unified tool surface.
|
|
5
5
|
|
|
6
|
+
> **v0.6.0** — Email watch notifications (polling-based), `signaturePath`
|
|
7
|
+
> support in `set_account_settings` for loading signatures from files,
|
|
8
|
+
> and a `check_notifications` tool for draining pending alerts.
|
|
9
|
+
>
|
|
10
|
+
> **v0.5.0** — Replaced optional `isHtml` boolean with required `format`
|
|
11
|
+
> parameter (`"html"` | `"markdown"`) on `send_email`, `draft_email`, and
|
|
12
|
+
> `edit_draft`. Markdown bodies are converted to HTML via `marked` so
|
|
13
|
+
> recipients always see clean HTML.
|
|
14
|
+
>
|
|
6
15
|
> **v0.4.3** — Upgraded Zod to v4.4.3. Fixed MCP SDK v1.29.0 compatibility
|
|
7
16
|
> by wrapping all tool schemas in `z.object()` and replacing discriminated
|
|
8
17
|
> union output schemas that caused `validateToolOutput` crashes.
|
|
@@ -63,16 +72,131 @@ hypermail-mcp --http --port 3000 --host 0.0.0.0
|
|
|
63
72
|
When hosted you **must** set `HYPERMAIL_MCP_KEY` so the account file is
|
|
64
73
|
reproducibly decryptable.
|
|
65
74
|
|
|
75
|
+
### Development (HTTP mode + email watch)
|
|
76
|
+
|
|
77
|
+
To test the email watch feature locally:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Terminal 1: auto-rebuild TypeScript on save
|
|
81
|
+
pnpm dev
|
|
82
|
+
|
|
83
|
+
# Terminal 2: start HTTP server with dev config (10s poll, separate data dir)
|
|
84
|
+
pnpm dev:http
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The server listens on `http://127.0.0.1:3000/mcp`. Pi connects via the
|
|
88
|
+
`.pi/mcp.json` config (read by `pi-mcp-adapter`). Tools appear as
|
|
89
|
+
`hypermail_http_*` (e.g. `hypermail_http_check_notifications`).
|
|
90
|
+
|
|
91
|
+
The dev config (`hypermail-config.http.json`) uses a separate data dir
|
|
92
|
+
(`~/.hypermail-mcp-dev`) and a 10-second poll interval for fast feedback.
|
|
93
|
+
|
|
94
|
+
## Modes: stdio vs HTTP
|
|
95
|
+
|
|
96
|
+
The server runs in one of two modes — the choice affects session management,
|
|
97
|
+
security, and which features are available.
|
|
98
|
+
|
|
99
|
+
| | stdio (default) | HTTP (`--http`) |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| **Transport** | stdin/stdout | HTTP (Streamable HTTP MCP) |
|
|
102
|
+
| **Lifecycle** | Per-invocation (lazy) — spawned on demand by the MCP host | Long-lived server process |
|
|
103
|
+
| **Session model** | One `McpServer` instance for all invocations | One `McpServer` per MCP session (multi-tenant) |
|
|
104
|
+
| **Key management** | Auto-generated, stored in OS keychain or `master.key` file | Requires `HYPERMAIL_MCP_KEY` env var (32-byte key for AES-256-GCM) |
|
|
105
|
+
| **Email watch** | ❌ Not available | ✅ Polls inbox every N seconds for new mail |
|
|
106
|
+
| **`check_notifications`** | ❌ Not registered | ✅ Drains pending new-mail alerts |
|
|
107
|
+
| **Agent multi-tenancy** | ❌ Unrestricted access | ✅ Per-agent API keys, account allowlists, provisioning control (via `agents.yaml`) |
|
|
108
|
+
| **Pi tool naming** | `hyper_*` | `hypermail_http_*` |
|
|
109
|
+
|
|
110
|
+
**When to use HTTP mode:**
|
|
111
|
+
- You need email watch / push notifications
|
|
112
|
+
- You want to expose the server to multiple agents with different permissions
|
|
113
|
+
- You're hosting the server as a service (Docker, cloud)
|
|
114
|
+
|
|
115
|
+
**When to use stdio mode:**
|
|
116
|
+
- Single-user local development with a desktop MCP client (Claude, Pi)
|
|
117
|
+
- You don't need email watch or multi-agent access control
|
|
118
|
+
|
|
119
|
+
## Agent multi-tenancy
|
|
120
|
+
|
|
121
|
+
In HTTP mode, the server can be shared across multiple agents with
|
|
122
|
+
different permissions. Agent identity and authorization are defined in an
|
|
123
|
+
`agents.yaml` file.
|
|
124
|
+
|
|
125
|
+
### agents.yaml
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
agents:
|
|
129
|
+
- id: my-assistant
|
|
130
|
+
api_key: hm_sk_<64-hex-chars>
|
|
131
|
+
name: My Email Assistant
|
|
132
|
+
accounts: # which email addresses this agent can access
|
|
133
|
+
- alice@example.com
|
|
134
|
+
- bob@example.com
|
|
135
|
+
provisioning: false # can this agent add/remove accounts?
|
|
136
|
+
|
|
137
|
+
- id: admin-agent
|
|
138
|
+
api_key: hm_sk_<64-hex-chars>
|
|
139
|
+
name: Admin Agent
|
|
140
|
+
accounts: [] # empty = all accounts
|
|
141
|
+
provisioning: true
|
|
142
|
+
|
|
143
|
+
# Optional: pre-declare email accounts with provider hints
|
|
144
|
+
email_accounts:
|
|
145
|
+
alice@example.com:
|
|
146
|
+
provider: outlook
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Agent ID:** lowercase letters, digits, hyphens, underscores. No spaces.
|
|
150
|
+
|
|
151
|
+
**API key format:** `hm_sk_` prefix + 64 hex characters. Generate with:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
hypermail-mcp generate-key
|
|
155
|
+
# => hm_sk_a1b2c3d4...
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The API key is hashed (SHA-256) before storage — the plaintext is never
|
|
159
|
+
written to disk. Agents authenticate by passing the key in the
|
|
160
|
+
`Authorization: Bearer hm_sk_...` header.
|
|
161
|
+
|
|
162
|
+
**accounts:** An allowlist of email addresses the agent can operate on.
|
|
163
|
+
If empty or omitted, the agent can access all configured accounts.
|
|
164
|
+
|
|
165
|
+
**provisioning:** When `true`, the agent can call `add_account` and
|
|
166
|
+
`remove_account`. Defaults to `false`.
|
|
167
|
+
|
|
168
|
+
### Configuration
|
|
169
|
+
|
|
170
|
+
Point the server at your agents.yaml:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Via CLI flag
|
|
174
|
+
hypermail-mcp --http --agents-config ./agents.yaml
|
|
175
|
+
|
|
176
|
+
# Via env var
|
|
177
|
+
export HYPERMAIL_AGENTS_CONFIG=/etc/hypermail/agents.yaml
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The server watches `agents.yaml` for changes and reloads automatically
|
|
181
|
+
(live reload — no restart needed). Agents removed from the file lose
|
|
182
|
+
access on their next request.
|
|
183
|
+
|
|
184
|
+
In **stdio mode**, agent multi-tenancy is not available — the server runs
|
|
185
|
+
with unrestricted access (the local user _is_ the agent).
|
|
186
|
+
|
|
66
187
|
## Configuration
|
|
67
188
|
|
|
68
189
|
| Env var | Purpose | Default |
|
|
69
190
|
| --- | --- | --- |
|
|
70
191
|
| `HYPERMAIL_MCP_DATA_DIR` | Where to keep the encrypted accounts blob | `~/.hypermail-mcp` |
|
|
71
192
|
| `HYPERMAIL_MCP_KEY` | 32-byte AES-256-GCM key (hex, base64, or any passphrase — derived via SHA-256). Required for hosted deployments. | auto-generated, stored via OS keychain (`keytar`) or a local `master.key` file |
|
|
193
|
+
| `HYPERMAIL_AGENTS_CONFIG` | Path to `agents.yaml` for HTTP multi-tenant mode (see Agent multi-tenancy above). | — (multi-tenancy disabled) |
|
|
72
194
|
| `MS_CLIENT_ID` | Azure Entra public client (application) id used for device-code login | placeholder — **set your own for production** |
|
|
73
195
|
| `MS_TENANT_ID` | Tenant for the authority URL | `common` |
|
|
74
196
|
|
|
75
|
-
CLI flags: `--http`, `--port`, `--host`, `--data-dir`, `--read-only`, `--help`.
|
|
197
|
+
CLI flags: `--http`, `--port`, `--host`, `--data-dir`, `--agents-config`, `--read-only`, `--help`.
|
|
198
|
+
|
|
199
|
+
Subcommands: `hypermail-mcp generate-key` — generate an `hm_sk_` API key for agents.yaml.
|
|
76
200
|
|
|
77
201
|
### Config file (`hypermail-config.json`)
|
|
78
202
|
|
|
@@ -112,7 +236,7 @@ account store.
|
|
|
112
236
|
| `add_account` | `provider`, `email?`, `config?` | Starts device-code (Outlook). Returns `{handle, verification:{userCode, verificationUri, expiresAt}}`. |
|
|
113
237
|
| `complete_add_account` | `provider`, `handle` | Returns `pending` / `ready` / `expired` / `error`. |
|
|
114
238
|
| `get_account_settings` | `account` | Get signature (HTML) and style preferences for an account. |
|
|
115
|
-
| `set_account_settings` | `account`, `signature?`, `style?` | Set signature HTML and font preferences. Disabled under `--read-only`. |
|
|
239
|
+
| `set_account_settings` | `account`, `signature?`, `signaturePath?`, `style?` | Set signature HTML (inline or via file path) and font preferences. Disabled under `--read-only`. |
|
|
116
240
|
| `remove_account` | `email` | Deletes tokens for the account. |
|
|
117
241
|
| `list_emails` | `account`, `folder?`, `limit?`, `unreadOnly?`, `skip?` | Defaults: folder=`inbox`, limit=25. Supports pagination via `skip` — response includes `hasMore`. |
|
|
118
242
|
| `search_emails` | `account`, `query`, `limit?` | KQL on Outlook. |
|
|
@@ -121,17 +245,54 @@ account store.
|
|
|
121
245
|
| `archive_email` | `account`, `id` | Move a message to the Archive folder. Disabled under `--read-only`. |
|
|
122
246
|
| `trash_email` | `account`, `id` | Move a message to Deleted Items (trash). Disabled under `--read-only`. |
|
|
123
247
|
| `move_email` | `account`, `id`, `destination` | Move to any folder by well-known name (`inbox`, `drafts`, etc.) or custom folder ID. Disabled under `--read-only`. |
|
|
124
|
-
| `send_email` | `account`, `to[]`, `cc?`, `bcc?`, `subject`, `body`, `
|
|
125
|
-
| `draft_email` | `account`, `to[]`, `cc?`, `bcc?`, `subject`, `body`, `
|
|
126
|
-
| `edit_draft` | `account`, `id`, `to?`, `cc?`, `bcc?`, `subject?`, `body?`, `
|
|
248
|
+
| `send_email` | `account`, `to[]`, `cc?`, `bcc?`, `subject`, `body`, `format`, `include_signature?`, `inReplyTo?`, `replyAll?`, `forwardMessageId?` | Send an email. `format` (`"html"` or `"markdown"`) controls body format — Markdown is converted to HTML via `marked`. Appends signature when `include_signature` is true. `inReplyTo` sends as threaded reply; `forwardMessageId` sends as forward. Disabled under `--read-only`. |
|
|
249
|
+
| `draft_email` | `account`, `to[]`, `cc?`, `bcc?`, `subject`, `body`, `format`, `include_signature?`, `inReplyTo?`, `replyAll?`, `forwardMessageId?` | Save as draft instead of sending. `format` (`"html"` or `"markdown"`) controls body format — Markdown is converted to HTML via `marked`. Returns the draft message ID and HTML body (`draftHtml`). Disabled under `--read-only`. |
|
|
250
|
+
| `edit_draft` | `account`, `id`, `to?`, `cc?`, `bcc?`, `subject?`, `body?`, `format?`, `include_signature?` | Edit an existing draft by ID. Only provided fields are updated. `format` only meaningful when `body` is provided. Returns the updated draft ID and HTML body (`draftHtml`). Disabled under `--read-only`. |
|
|
127
251
|
| `send_draft` | `account`, `id` | Send an existing draft email by ID. Use with draft IDs returned by `draft_email` or `edit_draft`. Disabled under `--read-only`. |
|
|
128
|
-
| `add_attachment_to_draft` | `account`, `id`, `
|
|
252
|
+
| `add_attachment_to_draft` | `account`, `id`, `name`, `contentBytes`, `contentType?` | Attach a base64-encoded file to an existing draft email by ID. Disabled under `--read-only`. |
|
|
129
253
|
| `list_folders` | `account`, `parentFolderId?` | List available mail folders. Returns top-level folders by default, or children of `parentFolderId`. |
|
|
130
254
|
| `create_folder` | `account`, `displayName`, `parentFolderId?` | Create a new mail folder under root (default) or the given parent. Disabled under `--read-only`. |
|
|
131
255
|
| `delete_folder` | `account`, `folderId` | Delete a mail folder by ID. Disabled under `--read-only`. |
|
|
132
256
|
| `rename_folder` | `account`, `folderId`, `newName` | Rename an existing mail folder. Disabled under `--read-only`. |
|
|
133
257
|
| `mark_read` | `account`, `id` | Mark a message as read. Disabled under `--read-only`. |
|
|
134
258
|
| `mark_unread` | `account`, `id` | Mark a message as unread. Disabled under `--read-only`. |
|
|
259
|
+
| `check_notifications` | — | Returns pending email-watch notifications (new-email alerts, auth failures). Drains the buffer on read. Only registered in HTTP mode. |
|
|
260
|
+
|
|
261
|
+
## Email Watch
|
|
262
|
+
|
|
263
|
+
When running in **HTTP mode** (`--http`), the server polls all configured
|
|
264
|
+
accounts every N seconds for new inbox mail. Detected emails and auth failures
|
|
265
|
+
are delivered through two channels:
|
|
266
|
+
|
|
267
|
+
- **Push** — `notifications/message` sent over the MCP stream. Compatible
|
|
268
|
+
clients (e.g. Mastra) receive these in real time.
|
|
269
|
+
- **Poll** — `check_notifications` tool drains an in-memory buffer. Works with
|
|
270
|
+
**any** MCP client, even those that don't maintain an SSE listener.
|
|
271
|
+
|
|
272
|
+
**Configuration** (in `hypermail-config.json`):
|
|
273
|
+
|
|
274
|
+
```jsonc
|
|
275
|
+
{
|
|
276
|
+
"watch": {
|
|
277
|
+
"enabled": true, // default true
|
|
278
|
+
"pollIntervalSeconds": 60 // default 60 (min 10, max 3600)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Behavior:**
|
|
284
|
+
|
|
285
|
+
- Only the **inbox** folder is watched. All stored accounts are polled by default.
|
|
286
|
+
- On first poll per account, the server records the newest email as a baseline
|
|
287
|
+
(no notifications). Only emails arriving after baseline trigger alerts.
|
|
288
|
+
- Baselines (`lastSeenAt`) persist in the account store — they survive server
|
|
289
|
+
restarts.
|
|
290
|
+
- Each poll paginates through the inbox (25 items per page) to catch email
|
|
291
|
+
bursts without missing messages.
|
|
292
|
+
- Auth failures (e.g. expired OAuth tokens) generate immediate notifications.
|
|
293
|
+
|
|
294
|
+
**Not supported in stdio mode.** The watcher requires a long-lived server
|
|
295
|
+
process. In stdio mode the `check_notifications` tool is not registered.
|
|
135
296
|
|
|
136
297
|
### Add-account flow (Outlook)
|
|
137
298
|
|
|
@@ -158,16 +319,21 @@ account store.
|
|
|
158
319
|
|
|
159
320
|
- Threading / conversations.
|
|
160
321
|
- Calendar integration.
|
|
161
|
-
- Webhook / push notifications for new mail.
|
|
162
322
|
|
|
163
323
|
## Project layout
|
|
164
324
|
|
|
165
325
|
```
|
|
166
326
|
src/
|
|
167
327
|
cli.ts # arg parsing + entry
|
|
168
|
-
server.ts # MCP server, stdio + HTTP transports
|
|
169
|
-
version.ts
|
|
170
|
-
|
|
328
|
+
server.ts # MCP server, stdio + HTTP transports, session management
|
|
329
|
+
version.ts # version constant
|
|
330
|
+
config.ts # hypermail-config.json schema + resolution
|
|
331
|
+
config/
|
|
332
|
+
agents-config.ts # agents.yaml schema, validation, live-reload watcher
|
|
333
|
+
store/
|
|
334
|
+
account-store.ts # encrypted multi-account store (AES-256-GCM)
|
|
335
|
+
agent-store.ts # agent identity + credentials store (HTTP multi-tenant)
|
|
336
|
+
crypto.ts # AES-256-GCM encrypt/decrypt, key resolution, atomic writes
|
|
171
337
|
providers/
|
|
172
338
|
types.ts # EmailProvider interface + shared DTOs
|
|
173
339
|
registry.ts # routes account email → provider
|
|
@@ -180,8 +346,20 @@ src/
|
|
|
180
346
|
auth.ts # Google OAuth device-code flow
|
|
181
347
|
client.ts # Gmail API (googleapis)
|
|
182
348
|
index.ts # GmailProvider implementation
|
|
183
|
-
shared/ #
|
|
184
|
-
|
|
349
|
+
shared/ # shared utilities across providers
|
|
350
|
+
watcher/
|
|
351
|
+
manager.ts # inbox poller + notification buffer
|
|
352
|
+
index.ts # watcher public API
|
|
353
|
+
tools/
|
|
354
|
+
index.ts # MCP tool registrations
|
|
355
|
+
agent-context.ts # agent authorization guards (checkAccountAccess, checkProvisioning)
|
|
356
|
+
accounts.ts # list/add/remove/complete-add account tools
|
|
357
|
+
browse.ts # list/search/read email tools
|
|
358
|
+
compose.ts # send/draft/edit/send-draft/add-attachment tools
|
|
359
|
+
folders.ts # list/create/delete/rename folder tools
|
|
360
|
+
notifications.ts # check_notifications tool (HTTP only)
|
|
361
|
+
organize.ts # archive/trash/move/mark-read/mark-unread tools
|
|
362
|
+
shared.ts # shared tool helpers
|
|
185
363
|
```
|
|
186
364
|
|
|
187
365
|
## License
|