@forwardemail/mcp-server 1.0.2 → 1.0.4

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 CHANGED
@@ -1,211 +0,0 @@
1
- # @forwardemail/mcp-server
2
-
3
- [Model Context Protocol](https://github.com/modelcontextprotocol/specification) (MCP) server for [Forward Email](https://forwardemail.net). Connect AI assistants to your inbox.
4
-
5
- ## Overview
6
-
7
- This package provides a local MCP server that wraps the [Forward Email API](https://forwardemail.net/email-api). It lets AI agents (Claude, ChatGPT, Cursor, Windsurf, and others) send email, manage domains, and read your inbox through natural language.
8
-
9
- The server runs locally on your machine via `stdio`. Your API key never leaves your device.
10
-
11
- ## Install
12
-
13
- ```bash
14
- npm install -g @forwardemail/mcp-server
15
- ```
16
-
17
- Or run directly with `npx`:
18
-
19
- ```bash
20
- npx @forwardemail/mcp-server
21
- ```
22
-
23
- ## Setup
24
-
25
- ### 1. Get your API key
26
-
27
- Log in at <https://forwardemail.net/my-account/security> and copy your API key.
28
-
29
- ### 2. Set the environment variable
30
-
31
- ```bash
32
- export FORWARD_EMAIL_API_KEY=your-api-key
33
- ```
34
-
35
- ### 3. Configure your AI client
36
-
37
- #### Claude Desktop
38
-
39
- Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
40
-
41
- ```json
42
- {
43
- "mcpServers": {
44
- "forwardemail": {
45
- "command": "npx",
46
- "args": ["@forwardemail/mcp-server"],
47
- "env": {
48
- "FORWARD_EMAIL_API_KEY": "your-api-key"
49
- }
50
- }
51
- }
52
- }
53
- ```
54
-
55
- #### Cursor
56
-
57
- Add to `.cursor/mcp.json` in your project:
58
-
59
- ```json
60
- {
61
- "mcpServers": {
62
- "forwardemail": {
63
- "command": "npx",
64
- "args": ["@forwardemail/mcp-server"],
65
- "env": {
66
- "FORWARD_EMAIL_API_KEY": "your-api-key"
67
- }
68
- }
69
- }
70
- }
71
- ```
72
-
73
- ## Available Tools
74
-
75
- This server exposes the complete [Forward Email API](https://forwardemail.net/email-api) as MCP tools. Every endpoint is covered.
76
-
77
- ### Account
78
-
79
- * `getAccount` - Get your account details
80
- * `updateAccount` - Update your account
81
-
82
- ### Logs
83
-
84
- * `downloadLogs` - Download email delivery logs
85
-
86
- ### Contacts (CardDAV)
87
-
88
- * `listContacts` - List all contacts
89
- * `createContact` - Create a contact
90
- * `getContact` - Get a contact by ID
91
- * `updateContact` - Update a contact
92
- * `deleteContact` - Delete a contact
93
-
94
- ### Calendars (CalDAV)
95
-
96
- * `listCalendars` - List all calendars
97
- * `createCalendar` - Create a calendar
98
- * `getCalendar` - Get a calendar by ID
99
- * `updateCalendar` - Update a calendar
100
- * `deleteCalendar` - Delete a calendar
101
-
102
- ### Calendar Events (CalDAV)
103
-
104
- * `listCalendarEvents` - List all calendar events
105
- * `createCalendarEvent` - Create a calendar event
106
- * `getCalendarEvent` - Get a calendar event by ID
107
- * `updateCalendarEvent` - Update a calendar event
108
- * `deleteCalendarEvent` - Delete a calendar event
109
-
110
- ### Domains
111
-
112
- * `listDomains` - List all domains
113
- * `createDomain` - Create a new domain
114
- * `getDomain` - Get a domain by ID or name
115
- * `updateDomain` - Update a domain
116
- * `deleteDomain` - Delete a domain
117
- * `verifyDomainRecords` - Verify domain DNS records
118
- * `verifySmtpRecords` - Verify domain SMTP records
119
- * `testS3Connection` - Test custom S3 connection for a domain
120
-
121
- ### Domain Catch-All Passwords
122
-
123
- * `listCatchAllPasswords` - List domain-wide catch-all passwords
124
- * `createCatchAllPassword` - Create a domain-wide catch-all password
125
- * `deleteCatchAllPassword` - Remove a domain-wide catch-all password
126
-
127
- ### Domain Invites
128
-
129
- * `acceptDomainInvite` - Accept a domain invite
130
- * `createDomainInvite` - Invite a user to a domain
131
- * `removeDomainInvite` - Remove a domain invite
132
-
133
- ### Domain Members
134
-
135
- * `updateDomainMember` - Update a domain member role
136
- * `removeDomainMember` - Remove a member from a domain
137
-
138
- ### Aliases
139
-
140
- * `listAliases` - List aliases for a domain
141
- * `createAlias` - Create a new alias
142
- * `getAlias` - Get an alias by ID
143
- * `updateAlias` - Update an alias
144
- * `deleteAlias` - Delete an alias
145
- * `generateAliasPassword` - Generate or set a password for an alias
146
-
147
- ### Sieve Scripts
148
-
149
- * `listSieveScripts` - List Sieve scripts for an alias
150
- * `createSieveScript` - Create a Sieve script for an alias
151
- * `getSieveScript` - Get a Sieve script by ID
152
- * `updateSieveScript` - Update a Sieve script
153
- * `deleteSieveScript` - Delete a Sieve script
154
- * `activateSieveScript` - Activate a Sieve script
155
-
156
- ### Sieve Scripts (Alias Auth)
157
-
158
- * `listSieveScriptsAliasAuth` - List Sieve scripts (alias auth)
159
- * `createSieveScriptAliasAuth` - Create a Sieve script (alias auth)
160
- * `getSieveScriptAliasAuth` - Get a Sieve script (alias auth)
161
- * `updateSieveScriptAliasAuth` - Update a Sieve script (alias auth)
162
- * `deleteSieveScriptAliasAuth` - Delete a Sieve script (alias auth)
163
- * `activateSieveScriptAliasAuth` - Activate a Sieve script (alias auth)
164
-
165
- ### Emails (Outbound SMTP)
166
-
167
- * `listEmails` - List outbound SMTP emails
168
- * `sendEmail` - Send an email via outbound SMTP
169
- * `getEmailLimit` - Get outbound SMTP email sending limit
170
- * `getEmail` - Get an outbound SMTP email by ID
171
- * `deleteEmail` - Delete an outbound SMTP email
172
-
173
- ### Messages (IMAP)
174
-
175
- * `listMessages` - List and search messages in a folder
176
- * `createMessage` - Create a new message (draft)
177
- * `getMessage` - Get a message by ID
178
- * `updateMessage` - Update a message
179
- * `deleteMessage` - Delete a message
180
-
181
- ### Folders (IMAP)
182
-
183
- * `listFolders` - List all folders
184
- * `createFolder` - Create a new folder
185
- * `getFolder` - Get a folder by ID
186
- * `updateFolder` - Update a folder
187
- * `deleteFolder` - Delete a folder
188
-
189
- ### Encrypt
190
-
191
- * `encryptRecord` - Encrypt a plaintext Forward Email TXT record
192
-
193
- ## Development
194
-
195
- ```bash
196
- git clone https://github.com/forwardemail/mcp-server.git
197
- cd mcp-server
198
- pnpm install
199
- pnpm test
200
- ```
201
-
202
- ## License
203
-
204
- [BUSL-1.1](LICENSE)
205
-
206
- ## Links
207
-
208
- * [Forward Email](https://forwardemail.net)
209
- * [Forward Email API Docs](https://forwardemail.net/email-api)
210
- * [MCP Specification](https://github.com/modelcontextprotocol/specification)
211
- * [MCP Landing Page](https://forwardemail.net/mcp)
package/lib/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export type McpServerOptions = {
2
2
  apiKey?: string;
3
3
  baseURL?: string;
4
+ aliasUser?: string;
5
+ aliasPassword?: string;
4
6
  };
5
7
 
6
8
  export class McpServer {
package/lib/tools.js CHANGED
@@ -1,43 +1,130 @@
1
1
  const axios = require('axios');
2
2
 
3
3
  const getTools = (options = {}) => {
4
- const client = axios.create({
5
- baseURL: options.baseURL || 'https://api.forwardemail.net',
6
- headers: {
7
- Authorization: `Bearer ${options.apiKey || process.env.FORWARD_EMAIL_API_KEY}`,
8
- },
4
+ const baseURL =
5
+ options.baseURL ||
6
+ process.env.FORWARD_EMAIL_API_URL ||
7
+ 'https://api.forwardemail.net';
8
+ const apiKey = options.apiKey || process.env.FORWARD_EMAIL_API_KEY;
9
+
10
+ // Create an axios client for API-key-authenticated requests
11
+ // (Basic auth: apiKey as username, empty password)
12
+ const apiKeyClient = axios.create({
13
+ baseURL,
14
+ auth: apiKey ? {username: apiKey, password: ''} : undefined,
9
15
  });
10
16
 
17
+ // Create an axios client for alias-authenticated requests
18
+ // (Basic auth: alias email as username, generated password as password)
19
+ const createAliasClient = (username, password) =>
20
+ axios.create({
21
+ baseURL,
22
+ auth: {username, password},
23
+ });
24
+
25
+ // Default alias credentials from options or environment
26
+ const defaultAliasUser =
27
+ options.aliasUser || process.env.FORWARD_EMAIL_ALIAS_USER || '';
28
+ const defaultAliasPassword =
29
+ options.aliasPassword || process.env.FORWARD_EMAIL_ALIAS_PASSWORD || '';
30
+
31
+ //
32
+ // auth types:
33
+ // 'apiKey' – uses API key via Basic auth (username=apiKey, password='')
34
+ // 'aliasAuth' – uses alias credentials via Basic auth
35
+ // (username=alias email, password=generated password)
36
+ // 'both' – accepts either; uses alias credentials if provided,
37
+ // otherwise falls back to API key
38
+ // 'none' – no authentication required
39
+ //
11
40
  const createTool = (spec) => ({
12
41
  toolSpec: {
13
42
  name: spec.name,
14
43
  description: spec.description,
15
44
  input: {
16
45
  type: 'object',
17
- properties: spec.inputs || {},
46
+ properties: {
47
+ ...spec.inputs,
48
+ // Inject alias credential inputs for alias-auth and both-auth tools
49
+
50
+ ...((spec.auth === 'aliasAuth' || spec.auth === 'both') && {
51
+ alias_username: {
52
+ type: 'string',
53
+ description:
54
+ 'Alias email address for authentication (e.g. user@example.com). ' +
55
+ 'Required for alias-authenticated endpoints. ' +
56
+ 'Falls back to FORWARD_EMAIL_ALIAS_USER env var.',
57
+ },
58
+ alias_password: {
59
+ type: 'string',
60
+ description:
61
+ 'Generated alias password for authentication. ' +
62
+ 'Required for alias-authenticated endpoints. ' +
63
+ 'Falls back to FORWARD_EMAIL_ALIAS_PASSWORD env var. ' +
64
+ 'Generate one with the generateAliasPassword tool.',
65
+ },
66
+ }),
67
+ },
18
68
  },
19
69
  },
70
+ auth: spec.auth || 'apiKey',
20
71
  async invoke(arguments_) {
21
72
  let {path} = spec;
22
73
  const pathParameters = path.match(/{(\w+)}/g) || [];
23
74
  const queryArguments = {};
24
75
  const bodyArguments = {};
25
76
 
77
+ // Extract alias credentials from arguments (don't send them to the API)
78
+ const aliasUser = arguments_.alias_username || defaultAliasUser;
79
+ const aliasPass = arguments_.alias_password || defaultAliasPassword;
80
+
26
81
  for (const key in arguments_) {
27
- if (Object.hasOwn(arguments_, key)) {
28
- if (pathParameters.includes(`{${key}}`)) {
29
- path = path.replace(`{${key}}`, arguments_[key]);
30
- } else if (spec.query && spec.query.includes(key)) {
31
- queryArguments[key] = arguments_[key];
32
- } else {
33
- bodyArguments[key] = arguments_[key];
34
- }
82
+ if (!Object.hasOwn(arguments_, key)) continue;
83
+ // Skip credential fields
84
+ if (key === 'alias_username' || key === 'alias_password') continue;
85
+
86
+ if (pathParameters.includes(`{${key}}`)) {
87
+ path = path.replace(`{${key}}`, arguments_[key]);
88
+ } else if (spec.query && spec.query.includes(key)) {
89
+ queryArguments[key] = arguments_[key];
90
+ } else {
91
+ bodyArguments[key] = arguments_[key];
35
92
  }
36
93
  }
37
94
 
38
95
  const config = {params: queryArguments};
39
96
  const hasBody = Object.keys(bodyArguments).length > 0;
40
97
 
98
+ // Choose the right client based on auth type
99
+ let client;
100
+ switch (spec.auth) {
101
+ case 'aliasAuth': {
102
+ client = createAliasClient(aliasUser, aliasPass);
103
+
104
+ break;
105
+ }
106
+
107
+ case 'both': {
108
+ // Use alias credentials if provided, otherwise fall back to API key
109
+ client =
110
+ aliasUser && aliasPass
111
+ ? createAliasClient(aliasUser, aliasPass)
112
+ : apiKeyClient;
113
+
114
+ break;
115
+ }
116
+
117
+ case 'none': {
118
+ client = axios.create({baseURL});
119
+
120
+ break;
121
+ }
122
+
123
+ default: {
124
+ client = apiKeyClient;
125
+ }
126
+ }
127
+
41
128
  let response;
42
129
  if (spec.method === 'get' || spec.method === 'delete') {
43
130
  response = await client[spec.method](path, config);
@@ -55,29 +142,36 @@ const getTools = (options = {}) => {
55
142
 
56
143
  const tools = {
57
144
  //
58
- // Account
145
+ // Account (supports both API key and alias auth)
59
146
  //
60
147
  getAccount: createTool({
61
148
  name: 'getAccount',
62
- description: 'Get your account details',
149
+ description:
150
+ 'Get your account details. With API key auth returns user account info. ' +
151
+ 'With alias auth returns alias/mailbox info including storage quota.',
63
152
  method: 'get',
64
153
  path: '/v1/account',
154
+ auth: 'both',
65
155
  }),
66
156
  updateAccount: createTool({
67
157
  name: 'updateAccount',
68
- description: 'Update your account',
158
+ description:
159
+ 'Update your account. With API key auth updates user profile. ' +
160
+ 'With alias auth updates alias-scoped settings.',
69
161
  method: 'put',
70
162
  path: '/v1/account',
163
+ auth: 'both',
71
164
  }),
72
165
 
73
166
  //
74
- // Logs
167
+ // Logs (API key auth)
75
168
  //
76
169
  downloadLogs: createTool({
77
170
  name: 'downloadLogs',
78
171
  description: 'Download email delivery logs',
79
172
  method: 'get',
80
173
  path: '/v1/logs/download',
174
+ auth: 'apiKey',
81
175
  query: [
82
176
  'domain',
83
177
  'q',
@@ -88,115 +182,138 @@ const getTools = (options = {}) => {
88
182
  }),
89
183
 
90
184
  //
91
- // Contacts (CardDAV)
185
+ // Contacts (CardDAV) — alias auth required
92
186
  //
93
187
  listContacts: createTool({
94
188
  name: 'listContacts',
95
- description: 'List all contacts',
189
+ description:
190
+ 'List all contacts for the authenticated alias. ' +
191
+ 'Requires alias credentials (alias_username and alias_password).',
96
192
  method: 'get',
97
193
  path: '/v1/contacts',
194
+ auth: 'aliasAuth',
98
195
  }),
99
196
  createContact: createTool({
100
197
  name: 'createContact',
101
- description: 'Create a contact',
198
+ description:
199
+ 'Create a contact for the authenticated alias. Requires alias credentials.',
102
200
  method: 'post',
103
201
  path: '/v1/contacts',
202
+ auth: 'aliasAuth',
104
203
  }),
105
204
  getContact: createTool({
106
205
  name: 'getContact',
107
- description: 'Get a contact by ID',
206
+ description: 'Get a contact by ID. Requires alias credentials.',
108
207
  method: 'get',
109
208
  path: '/v1/contacts/{id}',
209
+ auth: 'aliasAuth',
110
210
  }),
111
211
  updateContact: createTool({
112
212
  name: 'updateContact',
113
- description: 'Update a contact',
213
+ description: 'Update a contact. Requires alias credentials.',
114
214
  method: 'put',
115
215
  path: '/v1/contacts/{id}',
216
+ auth: 'aliasAuth',
116
217
  }),
117
218
  deleteContact: createTool({
118
219
  name: 'deleteContact',
119
- description: 'Delete a contact',
220
+ description: 'Delete a contact. Requires alias credentials.',
120
221
  method: 'delete',
121
222
  path: '/v1/contacts/{id}',
223
+ auth: 'aliasAuth',
122
224
  }),
123
225
 
124
226
  //
125
- // Calendars (CalDAV)
227
+ // Calendars (CalDAV) — alias auth required
126
228
  //
127
229
  listCalendars: createTool({
128
230
  name: 'listCalendars',
129
- description: 'List all calendars',
231
+ description:
232
+ 'List all calendars for the authenticated alias. ' +
233
+ 'Requires alias credentials (alias_username and alias_password).',
130
234
  method: 'get',
131
235
  path: '/v1/calendars',
236
+ auth: 'aliasAuth',
132
237
  }),
133
238
  createCalendar: createTool({
134
239
  name: 'createCalendar',
135
- description: 'Create a calendar',
240
+ description: 'Create a calendar. Requires alias credentials.',
136
241
  method: 'post',
137
242
  path: '/v1/calendars',
243
+ auth: 'aliasAuth',
138
244
  }),
139
245
  getCalendar: createTool({
140
246
  name: 'getCalendar',
141
- description: 'Get a calendar by ID',
247
+ description: 'Get a calendar by ID. Requires alias credentials.',
142
248
  method: 'get',
143
249
  path: '/v1/calendars/{id}',
250
+ auth: 'aliasAuth',
144
251
  }),
145
252
  updateCalendar: createTool({
146
253
  name: 'updateCalendar',
147
- description: 'Update a calendar',
254
+ description: 'Update a calendar. Requires alias credentials.',
148
255
  method: 'put',
149
256
  path: '/v1/calendars/{id}',
257
+ auth: 'aliasAuth',
150
258
  }),
151
259
  deleteCalendar: createTool({
152
260
  name: 'deleteCalendar',
153
- description: 'Delete a calendar',
261
+ description: 'Delete a calendar. Requires alias credentials.',
154
262
  method: 'delete',
155
263
  path: '/v1/calendars/{id}',
264
+ auth: 'aliasAuth',
156
265
  }),
157
266
 
158
267
  //
159
- // Calendar Events (CalDAV)
268
+ // Calendar Events (CalDAV) — alias auth required
160
269
  //
161
270
  listCalendarEvents: createTool({
162
271
  name: 'listCalendarEvents',
163
- description: 'List all calendar events',
272
+ description:
273
+ 'List all calendar events for the authenticated alias. ' +
274
+ 'Requires alias credentials (alias_username and alias_password).',
164
275
  method: 'get',
165
276
  path: '/v1/calendar-events',
277
+ auth: 'aliasAuth',
166
278
  }),
167
279
  createCalendarEvent: createTool({
168
280
  name: 'createCalendarEvent',
169
- description: 'Create a calendar event',
281
+ description: 'Create a calendar event. Requires alias credentials.',
170
282
  method: 'post',
171
283
  path: '/v1/calendar-events',
284
+ auth: 'aliasAuth',
172
285
  }),
173
286
  getCalendarEvent: createTool({
174
287
  name: 'getCalendarEvent',
175
- description: 'Get a calendar event by ID',
288
+ description: 'Get a calendar event by ID. Requires alias credentials.',
176
289
  method: 'get',
177
290
  path: '/v1/calendar-events/{id}',
291
+ auth: 'aliasAuth',
178
292
  }),
179
293
  updateCalendarEvent: createTool({
180
294
  name: 'updateCalendarEvent',
181
- description: 'Update a calendar event',
295
+ description: 'Update a calendar event. Requires alias credentials.',
182
296
  method: 'put',
183
297
  path: '/v1/calendar-events/{id}',
298
+ auth: 'aliasAuth',
184
299
  }),
185
300
  deleteCalendarEvent: createTool({
186
301
  name: 'deleteCalendarEvent',
187
- description: 'Delete a calendar event',
302
+ description: 'Delete a calendar event. Requires alias credentials.',
188
303
  method: 'delete',
189
304
  path: '/v1/calendar-events/{id}',
305
+ auth: 'aliasAuth',
190
306
  }),
191
307
 
192
308
  //
193
- // Domains
309
+ // Domains (API key auth)
194
310
  //
195
311
  listDomains: createTool({
196
312
  name: 'listDomains',
197
313
  description: 'List all domains',
198
314
  method: 'get',
199
315
  path: '/v1/domains',
316
+ auth: 'apiKey',
200
317
  query: ['sort', 'page', 'limit'],
201
318
  }),
202
319
  createDomain: createTool({
@@ -204,112 +321,128 @@ const getTools = (options = {}) => {
204
321
  description: 'Create a new domain',
205
322
  method: 'post',
206
323
  path: '/v1/domains',
324
+ auth: 'apiKey',
207
325
  }),
208
326
  getDomain: createTool({
209
327
  name: 'getDomain',
210
328
  description: 'Get a domain by ID or name',
211
329
  method: 'get',
212
330
  path: '/v1/domains/{domain_id}',
331
+ auth: 'apiKey',
213
332
  }),
214
333
  updateDomain: createTool({
215
334
  name: 'updateDomain',
216
335
  description: 'Update a domain',
217
336
  method: 'put',
218
337
  path: '/v1/domains/{domain_id}',
338
+ auth: 'apiKey',
219
339
  }),
220
340
  deleteDomain: createTool({
221
341
  name: 'deleteDomain',
222
342
  description: 'Delete a domain',
223
343
  method: 'delete',
224
344
  path: '/v1/domains/{domain_id}',
345
+ auth: 'apiKey',
225
346
  }),
226
347
  verifyDomainRecords: createTool({
227
348
  name: 'verifyDomainRecords',
228
349
  description: 'Verify domain DNS records',
229
350
  method: 'get',
230
351
  path: '/v1/domains/{domain_id}/verify-records',
352
+ auth: 'apiKey',
231
353
  }),
232
354
  verifySmtpRecords: createTool({
233
355
  name: 'verifySmtpRecords',
234
356
  description: 'Verify domain SMTP records',
235
357
  method: 'get',
236
358
  path: '/v1/domains/{domain_id}/verify-smtp',
359
+ auth: 'apiKey',
237
360
  }),
238
361
  testS3Connection: createTool({
239
362
  name: 'testS3Connection',
240
363
  description: 'Test custom S3 connection for a domain',
241
364
  method: 'post',
242
365
  path: '/v1/domains/{domain_id}/test-s3-connection',
366
+ auth: 'apiKey',
243
367
  }),
244
368
 
245
369
  //
246
- // Domain Catch-All Passwords
370
+ // Domain Catch-All Passwords (API key auth)
247
371
  //
248
372
  listCatchAllPasswords: createTool({
249
373
  name: 'listCatchAllPasswords',
250
374
  description: 'List domain-wide catch-all passwords',
251
375
  method: 'get',
252
376
  path: '/v1/domains/{domain_id}/catch-all-passwords',
377
+ auth: 'apiKey',
253
378
  }),
254
379
  createCatchAllPassword: createTool({
255
380
  name: 'createCatchAllPassword',
256
381
  description: 'Create a domain-wide catch-all password',
257
382
  method: 'post',
258
383
  path: '/v1/domains/{domain_id}/catch-all-passwords',
384
+ auth: 'apiKey',
259
385
  }),
260
386
  deleteCatchAllPassword: createTool({
261
387
  name: 'deleteCatchAllPassword',
262
388
  description: 'Remove a domain-wide catch-all password',
263
389
  method: 'delete',
264
390
  path: '/v1/domains/{domain_id}/catch-all-passwords/{token_id}',
391
+ auth: 'apiKey',
265
392
  }),
266
393
 
267
394
  //
268
- // Domain Invites
395
+ // Domain Invites (API key auth)
269
396
  //
270
397
  acceptDomainInvite: createTool({
271
398
  name: 'acceptDomainInvite',
272
399
  description: 'Accept a domain invite',
273
400
  method: 'get',
274
401
  path: '/v1/domains/{domain_id}/invites',
402
+ auth: 'apiKey',
275
403
  }),
276
404
  createDomainInvite: createTool({
277
405
  name: 'createDomainInvite',
278
406
  description: 'Invite a user to a domain',
279
407
  method: 'post',
280
408
  path: '/v1/domains/{domain_id}/invites',
409
+ auth: 'apiKey',
281
410
  }),
282
411
  removeDomainInvite: createTool({
283
412
  name: 'removeDomainInvite',
284
413
  description: 'Remove a domain invite',
285
414
  method: 'delete',
286
415
  path: '/v1/domains/{domain_id}/invites',
416
+ auth: 'apiKey',
287
417
  }),
288
418
 
289
419
  //
290
- // Domain Members
420
+ // Domain Members (API key auth)
291
421
  //
292
422
  updateDomainMember: createTool({
293
423
  name: 'updateDomainMember',
294
424
  description: 'Update a domain member role (admin or user)',
295
425
  method: 'put',
296
426
  path: '/v1/domains/{domain_id}/members/{member_id}',
427
+ auth: 'apiKey',
297
428
  }),
298
429
  removeDomainMember: createTool({
299
430
  name: 'removeDomainMember',
300
431
  description: 'Remove a member from a domain',
301
432
  method: 'delete',
302
433
  path: '/v1/domains/{domain_id}/members/{member_id}',
434
+ auth: 'apiKey',
303
435
  }),
304
436
 
305
437
  //
306
- // Aliases
438
+ // Aliases (API key auth)
307
439
  //
308
440
  listAliases: createTool({
309
441
  name: 'listAliases',
310
442
  description: 'List aliases for a domain',
311
443
  method: 'get',
312
444
  path: '/v1/domains/{domain_id}/aliases',
445
+ auth: 'apiKey',
313
446
  query: ['sort', 'page', 'limit'],
314
447
  }),
315
448
  createAlias: createTool({
@@ -317,155 +450,193 @@ const getTools = (options = {}) => {
317
450
  description: 'Create a new alias',
318
451
  method: 'post',
319
452
  path: '/v1/domains/{domain_id}/aliases',
453
+ auth: 'apiKey',
320
454
  }),
321
455
  getAlias: createTool({
322
456
  name: 'getAlias',
323
457
  description: 'Get an alias by ID',
324
458
  method: 'get',
325
459
  path: '/v1/domains/{domain_id}/aliases/{alias_id}',
460
+ auth: 'apiKey',
326
461
  }),
327
462
  updateAlias: createTool({
328
463
  name: 'updateAlias',
329
464
  description: 'Update an alias',
330
465
  method: 'put',
331
466
  path: '/v1/domains/{domain_id}/aliases/{alias_id}',
467
+ auth: 'apiKey',
332
468
  }),
333
469
  deleteAlias: createTool({
334
470
  name: 'deleteAlias',
335
471
  description: 'Delete an alias',
336
472
  method: 'delete',
337
473
  path: '/v1/domains/{domain_id}/aliases/{alias_id}',
474
+ auth: 'apiKey',
338
475
  }),
339
476
  generateAliasPassword: createTool({
340
477
  name: 'generateAliasPassword',
341
- description: 'Generate or set a password for an alias',
478
+ description:
479
+ 'Generate or set a password for an alias. Returns the alias username ' +
480
+ 'and password needed for alias-authenticated endpoints (messages, ' +
481
+ 'folders, contacts, calendars, sieve scripts).',
342
482
  method: 'post',
343
483
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/generate-password',
484
+ auth: 'apiKey',
344
485
  }),
345
486
 
346
487
  //
347
- // Sieve Scripts (domain-scoped, authenticated via API key)
488
+ // Sieve Scripts — domain-scoped (API key auth)
348
489
  //
349
490
  listSieveScripts: createTool({
350
491
  name: 'listSieveScripts',
351
- description: 'List Sieve scripts for an alias',
492
+ description: 'List Sieve scripts for an alias (API key auth)',
352
493
  method: 'get',
353
494
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve',
495
+ auth: 'apiKey',
354
496
  }),
355
497
  createSieveScript: createTool({
356
498
  name: 'createSieveScript',
357
- description: 'Create a Sieve script for an alias',
499
+ description: 'Create a Sieve script for an alias (API key auth)',
358
500
  method: 'post',
359
501
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve',
502
+ auth: 'apiKey',
360
503
  }),
361
504
  getSieveScript: createTool({
362
505
  name: 'getSieveScript',
363
- description: 'Get a Sieve script by ID',
506
+ description: 'Get a Sieve script by ID (API key auth)',
364
507
  method: 'get',
365
508
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
509
+ auth: 'apiKey',
366
510
  }),
367
511
  updateSieveScript: createTool({
368
512
  name: 'updateSieveScript',
369
- description: 'Update a Sieve script',
513
+ description: 'Update a Sieve script (API key auth)',
370
514
  method: 'put',
371
515
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
516
+ auth: 'apiKey',
372
517
  }),
373
518
  deleteSieveScript: createTool({
374
519
  name: 'deleteSieveScript',
375
- description: 'Delete a Sieve script',
520
+ description: 'Delete a Sieve script (API key auth)',
376
521
  method: 'delete',
377
522
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
523
+ auth: 'apiKey',
378
524
  }),
379
525
  activateSieveScript: createTool({
380
526
  name: 'activateSieveScript',
381
- description: 'Activate a Sieve script',
527
+ description: 'Activate a Sieve script (API key auth)',
382
528
  method: 'post',
383
529
  path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}/activate',
530
+ auth: 'apiKey',
384
531
  }),
385
532
 
386
533
  //
387
- // Sieve Scripts (alias auth, authenticated via alias credentials)
534
+ // Sieve Scripts — alias-scoped (alias auth)
388
535
  //
389
536
  listSieveScriptsAliasAuth: createTool({
390
537
  name: 'listSieveScriptsAliasAuth',
391
- description: 'List Sieve scripts (alias auth)',
538
+ description:
539
+ 'List Sieve scripts for the authenticated alias. ' +
540
+ 'Requires alias credentials.',
392
541
  method: 'get',
393
542
  path: '/v1/sieve-scripts',
543
+ auth: 'aliasAuth',
394
544
  }),
395
545
  createSieveScriptAliasAuth: createTool({
396
546
  name: 'createSieveScriptAliasAuth',
397
- description: 'Create a Sieve script (alias auth)',
547
+ description:
548
+ 'Create a Sieve script for the authenticated alias. ' +
549
+ 'Requires alias credentials.',
398
550
  method: 'post',
399
551
  path: '/v1/sieve-scripts',
552
+ auth: 'aliasAuth',
400
553
  }),
401
554
  getSieveScriptAliasAuth: createTool({
402
555
  name: 'getSieveScriptAliasAuth',
403
- description: 'Get a Sieve script (alias auth)',
556
+ description:
557
+ 'Get a Sieve script by ID (alias auth). Requires alias credentials.',
404
558
  method: 'get',
405
559
  path: '/v1/sieve-scripts/{script_id}',
560
+ auth: 'aliasAuth',
406
561
  }),
407
562
  updateSieveScriptAliasAuth: createTool({
408
563
  name: 'updateSieveScriptAliasAuth',
409
- description: 'Update a Sieve script (alias auth)',
564
+ description:
565
+ 'Update a Sieve script (alias auth). Requires alias credentials.',
410
566
  method: 'put',
411
567
  path: '/v1/sieve-scripts/{script_id}',
568
+ auth: 'aliasAuth',
412
569
  }),
413
570
  deleteSieveScriptAliasAuth: createTool({
414
571
  name: 'deleteSieveScriptAliasAuth',
415
- description: 'Delete a Sieve script (alias auth)',
572
+ description:
573
+ 'Delete a Sieve script (alias auth). Requires alias credentials.',
416
574
  method: 'delete',
417
575
  path: '/v1/sieve-scripts/{script_id}',
576
+ auth: 'aliasAuth',
418
577
  }),
419
578
  activateSieveScriptAliasAuth: createTool({
420
579
  name: 'activateSieveScriptAliasAuth',
421
- description: 'Activate a Sieve script (alias auth)',
580
+ description:
581
+ 'Activate a Sieve script (alias auth). Requires alias credentials.',
422
582
  method: 'post',
423
583
  path: '/v1/sieve-scripts/{script_id}/activate',
584
+ auth: 'aliasAuth',
424
585
  }),
425
586
 
426
587
  //
427
- // Emails (Outbound SMTP)
588
+ // Emails — Outbound SMTP
428
589
  //
429
590
  listEmails: createTool({
430
591
  name: 'listEmails',
431
592
  description: 'List outbound SMTP emails',
432
593
  method: 'get',
433
594
  path: '/v1/emails',
595
+ auth: 'apiKey',
434
596
  query: ['q', 'domain', 'is_scheduled', 'sort', 'page', 'limit'],
435
597
  }),
436
598
  sendEmail: createTool({
437
599
  name: 'sendEmail',
438
- description: 'Send an email via outbound SMTP',
600
+ description:
601
+ 'Send an email via outbound SMTP. ' +
602
+ 'Supports both API key and alias auth.',
439
603
  method: 'post',
440
604
  path: '/v1/emails',
605
+ auth: 'both',
441
606
  }),
442
607
  getEmailLimit: createTool({
443
608
  name: 'getEmailLimit',
444
609
  description: 'Get outbound SMTP email sending limit',
445
610
  method: 'get',
446
611
  path: '/v1/emails/limit',
612
+ auth: 'apiKey',
447
613
  }),
448
614
  getEmail: createTool({
449
615
  name: 'getEmail',
450
616
  description: 'Get an outbound SMTP email by ID',
451
617
  method: 'get',
452
618
  path: '/v1/emails/{id}',
619
+ auth: 'apiKey',
453
620
  }),
454
621
  deleteEmail: createTool({
455
622
  name: 'deleteEmail',
456
623
  description: 'Delete an outbound SMTP email',
457
624
  method: 'delete',
458
625
  path: '/v1/emails/{id}',
626
+ auth: 'apiKey',
459
627
  }),
460
628
 
461
629
  //
462
- // Messages (IMAP)
630
+ // Messages — IMAP (alias auth required)
463
631
  //
464
632
  listMessages: createTool({
465
633
  name: 'listMessages',
466
- description: 'List and search messages in a folder',
634
+ description:
635
+ 'List and search messages in a folder. ' +
636
+ 'Requires alias credentials (alias_username and alias_password).',
467
637
  method: 'get',
468
638
  path: '/v1/messages',
639
+ auth: 'aliasAuth',
469
640
  query: [
470
641
  'folder',
471
642
  'is_unread',
@@ -500,74 +671,92 @@ const getTools = (options = {}) => {
500
671
  }),
501
672
  createMessage: createTool({
502
673
  name: 'createMessage',
503
- description: 'Create a new message (draft)',
674
+ description:
675
+ 'Create a new message (draft) in a folder. ' +
676
+ 'Requires alias credentials.',
504
677
  method: 'post',
505
678
  path: '/v1/messages',
679
+ auth: 'aliasAuth',
506
680
  }),
507
681
  getMessage: createTool({
508
682
  name: 'getMessage',
509
- description: 'Get a message by ID',
683
+ description: 'Get a message by ID. Requires alias credentials.',
510
684
  method: 'get',
511
685
  path: '/v1/messages/{id}',
686
+ auth: 'aliasAuth',
512
687
  query: ['eml', 'nodemailer', 'attachments', 'raw'],
513
688
  }),
514
689
  updateMessage: createTool({
515
690
  name: 'updateMessage',
516
- description: 'Update a message',
691
+ description:
692
+ 'Update a message (flags, labels, move to folder). ' +
693
+ 'Requires alias credentials.',
517
694
  method: 'put',
518
695
  path: '/v1/messages/{id}',
696
+ auth: 'aliasAuth',
519
697
  query: ['eml'],
520
698
  }),
521
699
  deleteMessage: createTool({
522
700
  name: 'deleteMessage',
523
- description: 'Delete a message',
701
+ description: 'Delete a message permanently. Requires alias credentials.',
524
702
  method: 'delete',
525
703
  path: '/v1/messages/{id}',
704
+ auth: 'aliasAuth',
526
705
  }),
527
706
 
528
707
  //
529
- // Folders (IMAP)
708
+ // Folders — IMAP (alias auth required)
530
709
  //
531
710
  listFolders: createTool({
532
711
  name: 'listFolders',
533
- description: 'List all folders',
712
+ description:
713
+ 'List all IMAP folders. ' +
714
+ 'Requires alias credentials (alias_username and alias_password).',
534
715
  method: 'get',
535
716
  path: '/v1/folders',
717
+ auth: 'aliasAuth',
536
718
  query: ['subscribed'],
537
719
  }),
538
720
  createFolder: createTool({
539
721
  name: 'createFolder',
540
- description: 'Create a new folder',
722
+ description: 'Create a new IMAP folder. Requires alias credentials.',
541
723
  method: 'post',
542
724
  path: '/v1/folders',
725
+ auth: 'aliasAuth',
543
726
  }),
544
727
  getFolder: createTool({
545
728
  name: 'getFolder',
546
- description: 'Get a folder by ID',
729
+ description: 'Get a folder by ID or path. Requires alias credentials.',
547
730
  method: 'get',
548
731
  path: '/v1/folders/{id}',
732
+ auth: 'aliasAuth',
549
733
  }),
550
734
  updateFolder: createTool({
551
735
  name: 'updateFolder',
552
- description: 'Update a folder',
736
+ description: 'Rename a folder. Requires alias credentials.',
553
737
  method: 'put',
554
738
  path: '/v1/folders/{id}',
739
+ auth: 'aliasAuth',
555
740
  }),
556
741
  deleteFolder: createTool({
557
742
  name: 'deleteFolder',
558
- description: 'Delete a folder',
743
+ description:
744
+ 'Delete a folder and all messages in it. ' +
745
+ 'Requires alias credentials.',
559
746
  method: 'delete',
560
747
  path: '/v1/folders/{id}',
748
+ auth: 'aliasAuth',
561
749
  }),
562
750
 
563
751
  //
564
- // Encrypt
752
+ // Encrypt (no auth required)
565
753
  //
566
754
  encryptRecord: createTool({
567
755
  name: 'encryptRecord',
568
756
  description: 'Encrypt a plaintext Forward Email TXT record',
569
757
  method: 'post',
570
758
  path: '/v1/encrypt',
759
+ auth: 'none',
571
760
  }),
572
761
  };
573
762
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@forwardemail/mcp-server",
3
3
  "description": "Model Context Protocol (MCP) server for Forward Email. Connect AI to your inbox.",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "author": "Forward Email LLC <support@forwardemail.net> (https://forwardemail.net)",
6
6
  "bin": {
7
7
  "mcp-server": "bin/mcp-server.js"
@@ -59,6 +59,9 @@
59
59
  "tabWidth": 2,
60
60
  "singleQuote": true
61
61
  },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
62
65
  "repository": {
63
66
  "type": "git",
64
67
  "url": "git+https://github.com/forwardemail/mcp-server.git"
@@ -70,7 +73,13 @@
70
73
  "rules": {
71
74
  "unicorn/prefer-module": "off",
72
75
  "import-x/extensions": "off",
73
- "n/prefer-global/process": "off"
76
+ "n/prefer-global/process": "off",
77
+ "camelcase": [
78
+ "error",
79
+ {
80
+ "properties": "never"
81
+ }
82
+ ]
74
83
  }
75
84
  },
76
85
  "scripts": {