@forwardemail/mcp-server 1.0.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/LICENSE +22 -0
- package/README.md +211 -0
- package/bin/mcp-server.js +5 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +63 -0
- package/lib/tools.js +577 -0
- package/package.json +83 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: Forward Email LLC
|
|
6
|
+
Licensed Work: @forwardemail/mcp-server
|
|
7
|
+
The Licensed Work is (c) 2025 Forward Email LLC.
|
|
8
|
+
Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
9
|
+
you may not use the Licensed Work for an Email Service
|
|
10
|
+
that is Competitive with the Licensed Work.
|
|
11
|
+
|
|
12
|
+
A "Competitive" service is a service that offers
|
|
13
|
+
email forwarding, email hosting, or email API services
|
|
14
|
+
that are substantially similar to those offered by
|
|
15
|
+
Forward Email (https://forwardemail.net).
|
|
16
|
+
|
|
17
|
+
Change Date: Four years from the date the Licensed Work is published.
|
|
18
|
+
|
|
19
|
+
Change License: MIT
|
|
20
|
+
|
|
21
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
22
|
+
please visit: https://forwardemail.net
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
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
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const readline = require('node:readline');
|
|
2
|
+
const {getTools} = require('./tools.js');
|
|
3
|
+
|
|
4
|
+
class McpServer {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.tools = getTools(options);
|
|
7
|
+
this.rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
terminal: false,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
listen() {
|
|
15
|
+
this.rl.on('line', async (line) => {
|
|
16
|
+
try {
|
|
17
|
+
const request = JSON.parse(line);
|
|
18
|
+
await this.handleRequest(request);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
this.sendError(error.message);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async handleRequest(request) {
|
|
26
|
+
if (request.type === 'listTools') {
|
|
27
|
+
return this.sendResponse(request.id, {
|
|
28
|
+
tools: Object.values(this.tools).map((tool) => tool.toolSpec),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (request.type === 'invokeTool') {
|
|
33
|
+
const tool = this.tools[request.name];
|
|
34
|
+
if (tool) {
|
|
35
|
+
try {
|
|
36
|
+
const result = await tool.invoke(request.arguments);
|
|
37
|
+
return this.sendResponse(request.id, {result});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const errorMessage = error.response?.data?.message || error.message;
|
|
40
|
+
return this.sendError(errorMessage, request.id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.sendError(`Tool not found: ${request.name}`, request.id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.sendError(`Unknown request type: ${request.type}`, request.id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sendResponse(id, payload) {
|
|
51
|
+
this.send({type: 'toolResponse', id, ...payload});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
sendError(message, id = null) {
|
|
55
|
+
this.send({type: 'error', id, message});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
send(data) {
|
|
59
|
+
process.stdout.write(JSON.stringify(data) + '\n');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = {McpServer};
|
package/lib/tools.js
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
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
|
+
},
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const createTool = (spec) => ({
|
|
12
|
+
toolSpec: {
|
|
13
|
+
name: spec.name,
|
|
14
|
+
description: spec.description,
|
|
15
|
+
input: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: spec.inputs || {},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
async invoke(arguments_) {
|
|
21
|
+
let {path} = spec;
|
|
22
|
+
const pathParameters = path.match(/{(\w+)}/g) || [];
|
|
23
|
+
const queryArguments = {};
|
|
24
|
+
const bodyArguments = {};
|
|
25
|
+
|
|
26
|
+
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
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const config = {params: queryArguments};
|
|
39
|
+
const hasBody = Object.keys(bodyArguments).length > 0;
|
|
40
|
+
|
|
41
|
+
let response;
|
|
42
|
+
if (spec.method === 'get' || spec.method === 'delete') {
|
|
43
|
+
response = await client[spec.method](path, config);
|
|
44
|
+
} else {
|
|
45
|
+
response = await client[spec.method](
|
|
46
|
+
path,
|
|
47
|
+
hasBody ? bodyArguments : undefined,
|
|
48
|
+
config,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return response.data;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const tools = {
|
|
57
|
+
//
|
|
58
|
+
// Account
|
|
59
|
+
//
|
|
60
|
+
getAccount: createTool({
|
|
61
|
+
name: 'getAccount',
|
|
62
|
+
description: 'Get your account details',
|
|
63
|
+
method: 'get',
|
|
64
|
+
path: '/v1/account',
|
|
65
|
+
}),
|
|
66
|
+
updateAccount: createTool({
|
|
67
|
+
name: 'updateAccount',
|
|
68
|
+
description: 'Update your account',
|
|
69
|
+
method: 'put',
|
|
70
|
+
path: '/v1/account',
|
|
71
|
+
}),
|
|
72
|
+
|
|
73
|
+
//
|
|
74
|
+
// Logs
|
|
75
|
+
//
|
|
76
|
+
downloadLogs: createTool({
|
|
77
|
+
name: 'downloadLogs',
|
|
78
|
+
description: 'Download email delivery logs',
|
|
79
|
+
method: 'get',
|
|
80
|
+
path: '/v1/logs/download',
|
|
81
|
+
query: [
|
|
82
|
+
'domain',
|
|
83
|
+
'q',
|
|
84
|
+
'bounce_category',
|
|
85
|
+
'response_code',
|
|
86
|
+
'always_send_email',
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
|
|
90
|
+
//
|
|
91
|
+
// Contacts (CardDAV)
|
|
92
|
+
//
|
|
93
|
+
listContacts: createTool({
|
|
94
|
+
name: 'listContacts',
|
|
95
|
+
description: 'List all contacts',
|
|
96
|
+
method: 'get',
|
|
97
|
+
path: '/v1/contacts',
|
|
98
|
+
}),
|
|
99
|
+
createContact: createTool({
|
|
100
|
+
name: 'createContact',
|
|
101
|
+
description: 'Create a contact',
|
|
102
|
+
method: 'post',
|
|
103
|
+
path: '/v1/contacts',
|
|
104
|
+
}),
|
|
105
|
+
getContact: createTool({
|
|
106
|
+
name: 'getContact',
|
|
107
|
+
description: 'Get a contact by ID',
|
|
108
|
+
method: 'get',
|
|
109
|
+
path: '/v1/contacts/{id}',
|
|
110
|
+
}),
|
|
111
|
+
updateContact: createTool({
|
|
112
|
+
name: 'updateContact',
|
|
113
|
+
description: 'Update a contact',
|
|
114
|
+
method: 'put',
|
|
115
|
+
path: '/v1/contacts/{id}',
|
|
116
|
+
}),
|
|
117
|
+
deleteContact: createTool({
|
|
118
|
+
name: 'deleteContact',
|
|
119
|
+
description: 'Delete a contact',
|
|
120
|
+
method: 'delete',
|
|
121
|
+
path: '/v1/contacts/{id}',
|
|
122
|
+
}),
|
|
123
|
+
|
|
124
|
+
//
|
|
125
|
+
// Calendars (CalDAV)
|
|
126
|
+
//
|
|
127
|
+
listCalendars: createTool({
|
|
128
|
+
name: 'listCalendars',
|
|
129
|
+
description: 'List all calendars',
|
|
130
|
+
method: 'get',
|
|
131
|
+
path: '/v1/calendars',
|
|
132
|
+
}),
|
|
133
|
+
createCalendar: createTool({
|
|
134
|
+
name: 'createCalendar',
|
|
135
|
+
description: 'Create a calendar',
|
|
136
|
+
method: 'post',
|
|
137
|
+
path: '/v1/calendars',
|
|
138
|
+
}),
|
|
139
|
+
getCalendar: createTool({
|
|
140
|
+
name: 'getCalendar',
|
|
141
|
+
description: 'Get a calendar by ID',
|
|
142
|
+
method: 'get',
|
|
143
|
+
path: '/v1/calendars/{id}',
|
|
144
|
+
}),
|
|
145
|
+
updateCalendar: createTool({
|
|
146
|
+
name: 'updateCalendar',
|
|
147
|
+
description: 'Update a calendar',
|
|
148
|
+
method: 'put',
|
|
149
|
+
path: '/v1/calendars/{id}',
|
|
150
|
+
}),
|
|
151
|
+
deleteCalendar: createTool({
|
|
152
|
+
name: 'deleteCalendar',
|
|
153
|
+
description: 'Delete a calendar',
|
|
154
|
+
method: 'delete',
|
|
155
|
+
path: '/v1/calendars/{id}',
|
|
156
|
+
}),
|
|
157
|
+
|
|
158
|
+
//
|
|
159
|
+
// Calendar Events (CalDAV)
|
|
160
|
+
//
|
|
161
|
+
listCalendarEvents: createTool({
|
|
162
|
+
name: 'listCalendarEvents',
|
|
163
|
+
description: 'List all calendar events',
|
|
164
|
+
method: 'get',
|
|
165
|
+
path: '/v1/calendar-events',
|
|
166
|
+
}),
|
|
167
|
+
createCalendarEvent: createTool({
|
|
168
|
+
name: 'createCalendarEvent',
|
|
169
|
+
description: 'Create a calendar event',
|
|
170
|
+
method: 'post',
|
|
171
|
+
path: '/v1/calendar-events',
|
|
172
|
+
}),
|
|
173
|
+
getCalendarEvent: createTool({
|
|
174
|
+
name: 'getCalendarEvent',
|
|
175
|
+
description: 'Get a calendar event by ID',
|
|
176
|
+
method: 'get',
|
|
177
|
+
path: '/v1/calendar-events/{id}',
|
|
178
|
+
}),
|
|
179
|
+
updateCalendarEvent: createTool({
|
|
180
|
+
name: 'updateCalendarEvent',
|
|
181
|
+
description: 'Update a calendar event',
|
|
182
|
+
method: 'put',
|
|
183
|
+
path: '/v1/calendar-events/{id}',
|
|
184
|
+
}),
|
|
185
|
+
deleteCalendarEvent: createTool({
|
|
186
|
+
name: 'deleteCalendarEvent',
|
|
187
|
+
description: 'Delete a calendar event',
|
|
188
|
+
method: 'delete',
|
|
189
|
+
path: '/v1/calendar-events/{id}',
|
|
190
|
+
}),
|
|
191
|
+
|
|
192
|
+
//
|
|
193
|
+
// Domains
|
|
194
|
+
//
|
|
195
|
+
listDomains: createTool({
|
|
196
|
+
name: 'listDomains',
|
|
197
|
+
description: 'List all domains',
|
|
198
|
+
method: 'get',
|
|
199
|
+
path: '/v1/domains',
|
|
200
|
+
query: ['sort', 'page', 'limit'],
|
|
201
|
+
}),
|
|
202
|
+
createDomain: createTool({
|
|
203
|
+
name: 'createDomain',
|
|
204
|
+
description: 'Create a new domain',
|
|
205
|
+
method: 'post',
|
|
206
|
+
path: '/v1/domains',
|
|
207
|
+
}),
|
|
208
|
+
getDomain: createTool({
|
|
209
|
+
name: 'getDomain',
|
|
210
|
+
description: 'Get a domain by ID or name',
|
|
211
|
+
method: 'get',
|
|
212
|
+
path: '/v1/domains/{domain_id}',
|
|
213
|
+
}),
|
|
214
|
+
updateDomain: createTool({
|
|
215
|
+
name: 'updateDomain',
|
|
216
|
+
description: 'Update a domain',
|
|
217
|
+
method: 'put',
|
|
218
|
+
path: '/v1/domains/{domain_id}',
|
|
219
|
+
}),
|
|
220
|
+
deleteDomain: createTool({
|
|
221
|
+
name: 'deleteDomain',
|
|
222
|
+
description: 'Delete a domain',
|
|
223
|
+
method: 'delete',
|
|
224
|
+
path: '/v1/domains/{domain_id}',
|
|
225
|
+
}),
|
|
226
|
+
verifyDomainRecords: createTool({
|
|
227
|
+
name: 'verifyDomainRecords',
|
|
228
|
+
description: 'Verify domain DNS records',
|
|
229
|
+
method: 'get',
|
|
230
|
+
path: '/v1/domains/{domain_id}/verify-records',
|
|
231
|
+
}),
|
|
232
|
+
verifySmtpRecords: createTool({
|
|
233
|
+
name: 'verifySmtpRecords',
|
|
234
|
+
description: 'Verify domain SMTP records',
|
|
235
|
+
method: 'get',
|
|
236
|
+
path: '/v1/domains/{domain_id}/verify-smtp',
|
|
237
|
+
}),
|
|
238
|
+
testS3Connection: createTool({
|
|
239
|
+
name: 'testS3Connection',
|
|
240
|
+
description: 'Test custom S3 connection for a domain',
|
|
241
|
+
method: 'post',
|
|
242
|
+
path: '/v1/domains/{domain_id}/test-s3-connection',
|
|
243
|
+
}),
|
|
244
|
+
|
|
245
|
+
//
|
|
246
|
+
// Domain Catch-All Passwords
|
|
247
|
+
//
|
|
248
|
+
listCatchAllPasswords: createTool({
|
|
249
|
+
name: 'listCatchAllPasswords',
|
|
250
|
+
description: 'List domain-wide catch-all passwords',
|
|
251
|
+
method: 'get',
|
|
252
|
+
path: '/v1/domains/{domain_id}/catch-all-passwords',
|
|
253
|
+
}),
|
|
254
|
+
createCatchAllPassword: createTool({
|
|
255
|
+
name: 'createCatchAllPassword',
|
|
256
|
+
description: 'Create a domain-wide catch-all password',
|
|
257
|
+
method: 'post',
|
|
258
|
+
path: '/v1/domains/{domain_id}/catch-all-passwords',
|
|
259
|
+
}),
|
|
260
|
+
deleteCatchAllPassword: createTool({
|
|
261
|
+
name: 'deleteCatchAllPassword',
|
|
262
|
+
description: 'Remove a domain-wide catch-all password',
|
|
263
|
+
method: 'delete',
|
|
264
|
+
path: '/v1/domains/{domain_id}/catch-all-passwords/{token_id}',
|
|
265
|
+
}),
|
|
266
|
+
|
|
267
|
+
//
|
|
268
|
+
// Domain Invites
|
|
269
|
+
//
|
|
270
|
+
acceptDomainInvite: createTool({
|
|
271
|
+
name: 'acceptDomainInvite',
|
|
272
|
+
description: 'Accept a domain invite',
|
|
273
|
+
method: 'get',
|
|
274
|
+
path: '/v1/domains/{domain_id}/invites',
|
|
275
|
+
}),
|
|
276
|
+
createDomainInvite: createTool({
|
|
277
|
+
name: 'createDomainInvite',
|
|
278
|
+
description: 'Invite a user to a domain',
|
|
279
|
+
method: 'post',
|
|
280
|
+
path: '/v1/domains/{domain_id}/invites',
|
|
281
|
+
}),
|
|
282
|
+
removeDomainInvite: createTool({
|
|
283
|
+
name: 'removeDomainInvite',
|
|
284
|
+
description: 'Remove a domain invite',
|
|
285
|
+
method: 'delete',
|
|
286
|
+
path: '/v1/domains/{domain_id}/invites',
|
|
287
|
+
}),
|
|
288
|
+
|
|
289
|
+
//
|
|
290
|
+
// Domain Members
|
|
291
|
+
//
|
|
292
|
+
updateDomainMember: createTool({
|
|
293
|
+
name: 'updateDomainMember',
|
|
294
|
+
description: 'Update a domain member role (admin or user)',
|
|
295
|
+
method: 'put',
|
|
296
|
+
path: '/v1/domains/{domain_id}/members/{member_id}',
|
|
297
|
+
}),
|
|
298
|
+
removeDomainMember: createTool({
|
|
299
|
+
name: 'removeDomainMember',
|
|
300
|
+
description: 'Remove a member from a domain',
|
|
301
|
+
method: 'delete',
|
|
302
|
+
path: '/v1/domains/{domain_id}/members/{member_id}',
|
|
303
|
+
}),
|
|
304
|
+
|
|
305
|
+
//
|
|
306
|
+
// Aliases
|
|
307
|
+
//
|
|
308
|
+
listAliases: createTool({
|
|
309
|
+
name: 'listAliases',
|
|
310
|
+
description: 'List aliases for a domain',
|
|
311
|
+
method: 'get',
|
|
312
|
+
path: '/v1/domains/{domain_id}/aliases',
|
|
313
|
+
query: ['sort', 'page', 'limit'],
|
|
314
|
+
}),
|
|
315
|
+
createAlias: createTool({
|
|
316
|
+
name: 'createAlias',
|
|
317
|
+
description: 'Create a new alias',
|
|
318
|
+
method: 'post',
|
|
319
|
+
path: '/v1/domains/{domain_id}/aliases',
|
|
320
|
+
}),
|
|
321
|
+
getAlias: createTool({
|
|
322
|
+
name: 'getAlias',
|
|
323
|
+
description: 'Get an alias by ID',
|
|
324
|
+
method: 'get',
|
|
325
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}',
|
|
326
|
+
}),
|
|
327
|
+
updateAlias: createTool({
|
|
328
|
+
name: 'updateAlias',
|
|
329
|
+
description: 'Update an alias',
|
|
330
|
+
method: 'put',
|
|
331
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}',
|
|
332
|
+
}),
|
|
333
|
+
deleteAlias: createTool({
|
|
334
|
+
name: 'deleteAlias',
|
|
335
|
+
description: 'Delete an alias',
|
|
336
|
+
method: 'delete',
|
|
337
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}',
|
|
338
|
+
}),
|
|
339
|
+
generateAliasPassword: createTool({
|
|
340
|
+
name: 'generateAliasPassword',
|
|
341
|
+
description: 'Generate or set a password for an alias',
|
|
342
|
+
method: 'post',
|
|
343
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/generate-password',
|
|
344
|
+
}),
|
|
345
|
+
|
|
346
|
+
//
|
|
347
|
+
// Sieve Scripts (domain-scoped, authenticated via API key)
|
|
348
|
+
//
|
|
349
|
+
listSieveScripts: createTool({
|
|
350
|
+
name: 'listSieveScripts',
|
|
351
|
+
description: 'List Sieve scripts for an alias',
|
|
352
|
+
method: 'get',
|
|
353
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve',
|
|
354
|
+
}),
|
|
355
|
+
createSieveScript: createTool({
|
|
356
|
+
name: 'createSieveScript',
|
|
357
|
+
description: 'Create a Sieve script for an alias',
|
|
358
|
+
method: 'post',
|
|
359
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve',
|
|
360
|
+
}),
|
|
361
|
+
getSieveScript: createTool({
|
|
362
|
+
name: 'getSieveScript',
|
|
363
|
+
description: 'Get a Sieve script by ID',
|
|
364
|
+
method: 'get',
|
|
365
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
|
|
366
|
+
}),
|
|
367
|
+
updateSieveScript: createTool({
|
|
368
|
+
name: 'updateSieveScript',
|
|
369
|
+
description: 'Update a Sieve script',
|
|
370
|
+
method: 'put',
|
|
371
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
|
|
372
|
+
}),
|
|
373
|
+
deleteSieveScript: createTool({
|
|
374
|
+
name: 'deleteSieveScript',
|
|
375
|
+
description: 'Delete a Sieve script',
|
|
376
|
+
method: 'delete',
|
|
377
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}',
|
|
378
|
+
}),
|
|
379
|
+
activateSieveScript: createTool({
|
|
380
|
+
name: 'activateSieveScript',
|
|
381
|
+
description: 'Activate a Sieve script',
|
|
382
|
+
method: 'post',
|
|
383
|
+
path: '/v1/domains/{domain_id}/aliases/{alias_id}/sieve/{script_id}/activate',
|
|
384
|
+
}),
|
|
385
|
+
|
|
386
|
+
//
|
|
387
|
+
// Sieve Scripts (alias auth, authenticated via alias credentials)
|
|
388
|
+
//
|
|
389
|
+
listSieveScriptsAliasAuth: createTool({
|
|
390
|
+
name: 'listSieveScriptsAliasAuth',
|
|
391
|
+
description: 'List Sieve scripts (alias auth)',
|
|
392
|
+
method: 'get',
|
|
393
|
+
path: '/v1/sieve-scripts',
|
|
394
|
+
}),
|
|
395
|
+
createSieveScriptAliasAuth: createTool({
|
|
396
|
+
name: 'createSieveScriptAliasAuth',
|
|
397
|
+
description: 'Create a Sieve script (alias auth)',
|
|
398
|
+
method: 'post',
|
|
399
|
+
path: '/v1/sieve-scripts',
|
|
400
|
+
}),
|
|
401
|
+
getSieveScriptAliasAuth: createTool({
|
|
402
|
+
name: 'getSieveScriptAliasAuth',
|
|
403
|
+
description: 'Get a Sieve script (alias auth)',
|
|
404
|
+
method: 'get',
|
|
405
|
+
path: '/v1/sieve-scripts/{script_id}',
|
|
406
|
+
}),
|
|
407
|
+
updateSieveScriptAliasAuth: createTool({
|
|
408
|
+
name: 'updateSieveScriptAliasAuth',
|
|
409
|
+
description: 'Update a Sieve script (alias auth)',
|
|
410
|
+
method: 'put',
|
|
411
|
+
path: '/v1/sieve-scripts/{script_id}',
|
|
412
|
+
}),
|
|
413
|
+
deleteSieveScriptAliasAuth: createTool({
|
|
414
|
+
name: 'deleteSieveScriptAliasAuth',
|
|
415
|
+
description: 'Delete a Sieve script (alias auth)',
|
|
416
|
+
method: 'delete',
|
|
417
|
+
path: '/v1/sieve-scripts/{script_id}',
|
|
418
|
+
}),
|
|
419
|
+
activateSieveScriptAliasAuth: createTool({
|
|
420
|
+
name: 'activateSieveScriptAliasAuth',
|
|
421
|
+
description: 'Activate a Sieve script (alias auth)',
|
|
422
|
+
method: 'post',
|
|
423
|
+
path: '/v1/sieve-scripts/{script_id}/activate',
|
|
424
|
+
}),
|
|
425
|
+
|
|
426
|
+
//
|
|
427
|
+
// Emails (Outbound SMTP)
|
|
428
|
+
//
|
|
429
|
+
listEmails: createTool({
|
|
430
|
+
name: 'listEmails',
|
|
431
|
+
description: 'List outbound SMTP emails',
|
|
432
|
+
method: 'get',
|
|
433
|
+
path: '/v1/emails',
|
|
434
|
+
query: ['q', 'domain', 'is_scheduled', 'sort', 'page', 'limit'],
|
|
435
|
+
}),
|
|
436
|
+
sendEmail: createTool({
|
|
437
|
+
name: 'sendEmail',
|
|
438
|
+
description: 'Send an email via outbound SMTP',
|
|
439
|
+
method: 'post',
|
|
440
|
+
path: '/v1/emails',
|
|
441
|
+
}),
|
|
442
|
+
getEmailLimit: createTool({
|
|
443
|
+
name: 'getEmailLimit',
|
|
444
|
+
description: 'Get outbound SMTP email sending limit',
|
|
445
|
+
method: 'get',
|
|
446
|
+
path: '/v1/emails/limit',
|
|
447
|
+
}),
|
|
448
|
+
getEmail: createTool({
|
|
449
|
+
name: 'getEmail',
|
|
450
|
+
description: 'Get an outbound SMTP email by ID',
|
|
451
|
+
method: 'get',
|
|
452
|
+
path: '/v1/emails/{id}',
|
|
453
|
+
}),
|
|
454
|
+
deleteEmail: createTool({
|
|
455
|
+
name: 'deleteEmail',
|
|
456
|
+
description: 'Delete an outbound SMTP email',
|
|
457
|
+
method: 'delete',
|
|
458
|
+
path: '/v1/emails/{id}',
|
|
459
|
+
}),
|
|
460
|
+
|
|
461
|
+
//
|
|
462
|
+
// Messages (IMAP)
|
|
463
|
+
//
|
|
464
|
+
listMessages: createTool({
|
|
465
|
+
name: 'listMessages',
|
|
466
|
+
description: 'List and search messages in a folder',
|
|
467
|
+
method: 'get',
|
|
468
|
+
path: '/v1/messages',
|
|
469
|
+
query: [
|
|
470
|
+
'folder',
|
|
471
|
+
'is_unread',
|
|
472
|
+
'is_flagged',
|
|
473
|
+
'is_deleted',
|
|
474
|
+
'is_draft',
|
|
475
|
+
'is_junk',
|
|
476
|
+
'is_copied',
|
|
477
|
+
'is_encrypted',
|
|
478
|
+
'is_searchable',
|
|
479
|
+
'is_expired',
|
|
480
|
+
'has_attachments',
|
|
481
|
+
'has_attachment',
|
|
482
|
+
'subject',
|
|
483
|
+
'body',
|
|
484
|
+
'text',
|
|
485
|
+
'headers',
|
|
486
|
+
'message_id',
|
|
487
|
+
'search',
|
|
488
|
+
'q',
|
|
489
|
+
'since',
|
|
490
|
+
'before',
|
|
491
|
+
'min_size',
|
|
492
|
+
'max_size',
|
|
493
|
+
'from',
|
|
494
|
+
'to',
|
|
495
|
+
'cc',
|
|
496
|
+
'bcc',
|
|
497
|
+
'date',
|
|
498
|
+
'reply-to',
|
|
499
|
+
],
|
|
500
|
+
}),
|
|
501
|
+
createMessage: createTool({
|
|
502
|
+
name: 'createMessage',
|
|
503
|
+
description: 'Create a new message (draft)',
|
|
504
|
+
method: 'post',
|
|
505
|
+
path: '/v1/messages',
|
|
506
|
+
}),
|
|
507
|
+
getMessage: createTool({
|
|
508
|
+
name: 'getMessage',
|
|
509
|
+
description: 'Get a message by ID',
|
|
510
|
+
method: 'get',
|
|
511
|
+
path: '/v1/messages/{id}',
|
|
512
|
+
query: ['eml', 'nodemailer', 'attachments', 'raw'],
|
|
513
|
+
}),
|
|
514
|
+
updateMessage: createTool({
|
|
515
|
+
name: 'updateMessage',
|
|
516
|
+
description: 'Update a message',
|
|
517
|
+
method: 'put',
|
|
518
|
+
path: '/v1/messages/{id}',
|
|
519
|
+
query: ['eml'],
|
|
520
|
+
}),
|
|
521
|
+
deleteMessage: createTool({
|
|
522
|
+
name: 'deleteMessage',
|
|
523
|
+
description: 'Delete a message',
|
|
524
|
+
method: 'delete',
|
|
525
|
+
path: '/v1/messages/{id}',
|
|
526
|
+
}),
|
|
527
|
+
|
|
528
|
+
//
|
|
529
|
+
// Folders (IMAP)
|
|
530
|
+
//
|
|
531
|
+
listFolders: createTool({
|
|
532
|
+
name: 'listFolders',
|
|
533
|
+
description: 'List all folders',
|
|
534
|
+
method: 'get',
|
|
535
|
+
path: '/v1/folders',
|
|
536
|
+
query: ['subscribed'],
|
|
537
|
+
}),
|
|
538
|
+
createFolder: createTool({
|
|
539
|
+
name: 'createFolder',
|
|
540
|
+
description: 'Create a new folder',
|
|
541
|
+
method: 'post',
|
|
542
|
+
path: '/v1/folders',
|
|
543
|
+
}),
|
|
544
|
+
getFolder: createTool({
|
|
545
|
+
name: 'getFolder',
|
|
546
|
+
description: 'Get a folder by ID',
|
|
547
|
+
method: 'get',
|
|
548
|
+
path: '/v1/folders/{id}',
|
|
549
|
+
}),
|
|
550
|
+
updateFolder: createTool({
|
|
551
|
+
name: 'updateFolder',
|
|
552
|
+
description: 'Update a folder',
|
|
553
|
+
method: 'put',
|
|
554
|
+
path: '/v1/folders/{id}',
|
|
555
|
+
}),
|
|
556
|
+
deleteFolder: createTool({
|
|
557
|
+
name: 'deleteFolder',
|
|
558
|
+
description: 'Delete a folder',
|
|
559
|
+
method: 'delete',
|
|
560
|
+
path: '/v1/folders/{id}',
|
|
561
|
+
}),
|
|
562
|
+
|
|
563
|
+
//
|
|
564
|
+
// Encrypt
|
|
565
|
+
//
|
|
566
|
+
encryptRecord: createTool({
|
|
567
|
+
name: 'encryptRecord',
|
|
568
|
+
description: 'Encrypt a plaintext Forward Email TXT record',
|
|
569
|
+
method: 'post',
|
|
570
|
+
path: '/v1/encrypt',
|
|
571
|
+
}),
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
return tools;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
module.exports = {getTools};
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forwardemail/mcp-server",
|
|
3
|
+
"description": "Model Context Protocol (MCP) server for Forward Email. Connect AI to your inbox.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": "Forward Email LLC <support@forwardemail.net> (https://forwardemail.net)",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-server": "./bin/mcp-server.js"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/forwardemail/mcp-server/issues"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"axios": "^1.7.2",
|
|
14
|
+
"cosmiconfig": "^9.0.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@commitlint/cli": "^19.3.0",
|
|
18
|
+
"@commitlint/config-conventional": "^19.2.2",
|
|
19
|
+
"c8": "^10.1.2",
|
|
20
|
+
"fixpack": "^4.0.0",
|
|
21
|
+
"husky": "^9.0.11",
|
|
22
|
+
"lint-staged": "^15.2.2",
|
|
23
|
+
"prettier": "^3.2.5",
|
|
24
|
+
"remark-cli": "^12.0.1",
|
|
25
|
+
"remark-preset-lint-recommended": "^7.0.1",
|
|
26
|
+
"xo": "^0.58.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": "./lib/index.js",
|
|
34
|
+
"require": "./lib/index.js",
|
|
35
|
+
"types": "./lib/index.d.ts"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"lib/",
|
|
40
|
+
"bin/",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"homepage": "https://forwardemail.net/mcp",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"ai",
|
|
47
|
+
"automation",
|
|
48
|
+
"chatgpt",
|
|
49
|
+
"claude",
|
|
50
|
+
"email",
|
|
51
|
+
"forward-email",
|
|
52
|
+
"llm",
|
|
53
|
+
"mcp",
|
|
54
|
+
"model-context-protocol"
|
|
55
|
+
],
|
|
56
|
+
"license": "BUSL-1.1",
|
|
57
|
+
"main": "lib/index.js",
|
|
58
|
+
"prettier": {
|
|
59
|
+
"useTabs": false,
|
|
60
|
+
"tabWidth": 2,
|
|
61
|
+
"singleQuote": true
|
|
62
|
+
},
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "https://github.com/forwardemail/mcp-server.git"
|
|
66
|
+
},
|
|
67
|
+
"types": "lib/index.d.ts",
|
|
68
|
+
"xo": {
|
|
69
|
+
"space": 2,
|
|
70
|
+
"prettier": true,
|
|
71
|
+
"rules": {
|
|
72
|
+
"unicorn/prefer-module": "off",
|
|
73
|
+
"import-x/extensions": "off",
|
|
74
|
+
"n/prefer-global/process": "off"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"lint": "fixpack && remark . -qfo && xo --fix",
|
|
79
|
+
"pre-commit": "lint-staged",
|
|
80
|
+
"pretest": "pnpm run lint",
|
|
81
|
+
"test": "c8 --reporter=text --reporter=html node --test"
|
|
82
|
+
}
|
|
83
|
+
}
|