gmail-workspace-mcp-server 0.0.3 → 0.0.5
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 +145 -15
- package/build/index.integration-with-mock.js +140 -4
- package/build/index.js +23 -6
- package/package.json +1 -1
- package/shared/gmail-client/lib/drafts.d.ts +42 -0
- package/shared/gmail-client/lib/drafts.js +81 -0
- package/shared/gmail-client/lib/mime-utils.d.ts +20 -0
- package/shared/gmail-client/lib/mime-utils.js +38 -0
- package/shared/gmail-client/lib/modify-message.d.ts +9 -0
- package/shared/gmail-client/lib/modify-message.js +20 -0
- package/shared/gmail-client/lib/send-message.d.ts +18 -0
- package/shared/gmail-client/lib/send-message.js +40 -0
- package/shared/index.d.ts +2 -2
- package/shared/index.js +2 -2
- package/shared/server.d.ts +108 -1
- package/shared/server.js +43 -3
- package/shared/tools/change-email-conversation.d.ts +66 -0
- package/shared/tools/change-email-conversation.js +148 -0
- package/shared/tools/draft-email.d.ts +79 -0
- package/shared/tools/draft-email.js +150 -0
- package/shared/tools/{get-email.d.ts → get-email-conversation.d.ts} +9 -2
- package/shared/tools/{get-email.js → get-email-conversation.js} +59 -10
- package/shared/tools/{list-recent-emails.d.ts → list-email-conversations.d.ts} +28 -13
- package/shared/tools/list-email-conversations.js +150 -0
- package/shared/tools/search-email-conversations.d.ts +45 -0
- package/shared/tools/search-email-conversations.js +110 -0
- package/shared/tools/send-email.d.ts +104 -0
- package/shared/tools/send-email.js +181 -0
- package/shared/tools.d.ts +19 -2
- package/shared/tools.js +56 -8
- package/shared/utils/email-helpers.d.ts +4 -0
- package/shared/utils/email-helpers.js +15 -0
- package/shared/tools/list-recent-emails.js +0 -133
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Email } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sends an email directly
|
|
4
|
+
*/
|
|
5
|
+
export declare function sendMessage(baseUrl: string, headers: Record<string, string>, from: string, options: {
|
|
6
|
+
to: string;
|
|
7
|
+
subject: string;
|
|
8
|
+
body: string;
|
|
9
|
+
cc?: string;
|
|
10
|
+
bcc?: string;
|
|
11
|
+
threadId?: string;
|
|
12
|
+
inReplyTo?: string;
|
|
13
|
+
references?: string;
|
|
14
|
+
}): Promise<Email>;
|
|
15
|
+
/**
|
|
16
|
+
* Sends a draft (and deletes it)
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendDraft(baseUrl: string, headers: Record<string, string>, draftId: string): Promise<Email>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { handleApiError } from './api-errors.js';
|
|
2
|
+
import { buildMimeMessage, toBase64Url } from './mime-utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sends an email directly
|
|
5
|
+
*/
|
|
6
|
+
export async function sendMessage(baseUrl, headers, from, options) {
|
|
7
|
+
const url = `${baseUrl}/messages/send`;
|
|
8
|
+
const rawMessage = buildMimeMessage(from, options);
|
|
9
|
+
const encodedMessage = toBase64Url(rawMessage);
|
|
10
|
+
const requestBody = {
|
|
11
|
+
raw: encodedMessage,
|
|
12
|
+
};
|
|
13
|
+
if (options.threadId) {
|
|
14
|
+
requestBody.threadId = options.threadId;
|
|
15
|
+
}
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers,
|
|
19
|
+
body: JSON.stringify(requestBody),
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
handleApiError(response.status, 'sending message');
|
|
23
|
+
}
|
|
24
|
+
return (await response.json());
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sends a draft (and deletes it)
|
|
28
|
+
*/
|
|
29
|
+
export async function sendDraft(baseUrl, headers, draftId) {
|
|
30
|
+
const url = `${baseUrl}/drafts/send`;
|
|
31
|
+
const response = await fetch(url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers,
|
|
34
|
+
body: JSON.stringify({ id: draftId }),
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
handleApiError(response.status, 'sending draft', draftId);
|
|
38
|
+
}
|
|
39
|
+
return (await response.json());
|
|
40
|
+
}
|
package/shared/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createMCPServer, createDefaultClient, ServiceAccountGmailClient, type IGmailClient, type ClientFactory, type ServiceAccountCredentials, } from './server.js';
|
|
2
|
-
export { createRegisterTools, registerTools } from './tools.js';
|
|
1
|
+
export { createMCPServer, createDefaultClient, ServiceAccountGmailClient, type IGmailClient, type ClientFactory, type ServiceAccountCredentials, type CreateMCPServerOptions, type Draft, } from './server.js';
|
|
2
|
+
export { createRegisterTools, registerTools, parseEnabledToolGroups, getAvailableToolGroups, type ToolGroup, } from './tools.js';
|
|
3
3
|
export { logServerStart, logError, logWarning, logDebug } from './logging.js';
|
|
4
4
|
export type { Email, EmailListItem, EmailHeader, EmailPart, Label, Thread, PaginatedResponse, } from './types.js';
|
package/shared/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Main exports for Gmail MCP Server
|
|
2
2
|
// Server and client
|
|
3
3
|
export { createMCPServer, createDefaultClient, ServiceAccountGmailClient, } from './server.js';
|
|
4
|
-
// Tools
|
|
5
|
-
export { createRegisterTools, registerTools } from './tools.js';
|
|
4
|
+
// Tools and tool groups
|
|
5
|
+
export { createRegisterTools, registerTools, parseEnabledToolGroups, getAvailableToolGroups, } from './tools.js';
|
|
6
6
|
// Logging utilities
|
|
7
7
|
export { logServerStart, logError, logWarning, logDebug } from './logging.js';
|
package/shared/server.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
2
|
import type { Email, EmailListItem } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Draft message structure
|
|
5
|
+
*/
|
|
6
|
+
export interface Draft {
|
|
7
|
+
id: string;
|
|
8
|
+
message: Email;
|
|
9
|
+
}
|
|
3
10
|
/**
|
|
4
11
|
* Gmail API client interface
|
|
5
12
|
* Defines all methods for interacting with the Gmail API
|
|
@@ -25,6 +32,65 @@ export interface IGmailClient {
|
|
|
25
32
|
format?: 'minimal' | 'full' | 'raw' | 'metadata';
|
|
26
33
|
metadataHeaders?: string[];
|
|
27
34
|
}): Promise<Email>;
|
|
35
|
+
/**
|
|
36
|
+
* Modify labels on a message (add/remove labels)
|
|
37
|
+
*/
|
|
38
|
+
modifyMessage(messageId: string, options: {
|
|
39
|
+
addLabelIds?: string[];
|
|
40
|
+
removeLabelIds?: string[];
|
|
41
|
+
}): Promise<Email>;
|
|
42
|
+
/**
|
|
43
|
+
* Create a draft email
|
|
44
|
+
*/
|
|
45
|
+
createDraft(options: {
|
|
46
|
+
to: string;
|
|
47
|
+
subject: string;
|
|
48
|
+
body: string;
|
|
49
|
+
cc?: string;
|
|
50
|
+
bcc?: string;
|
|
51
|
+
threadId?: string;
|
|
52
|
+
inReplyTo?: string;
|
|
53
|
+
references?: string;
|
|
54
|
+
}): Promise<Draft>;
|
|
55
|
+
/**
|
|
56
|
+
* Get a draft by ID
|
|
57
|
+
*/
|
|
58
|
+
getDraft(draftId: string): Promise<Draft>;
|
|
59
|
+
/**
|
|
60
|
+
* List drafts
|
|
61
|
+
*/
|
|
62
|
+
listDrafts(options?: {
|
|
63
|
+
maxResults?: number;
|
|
64
|
+
pageToken?: string;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
drafts: Array<{
|
|
67
|
+
id: string;
|
|
68
|
+
message: EmailListItem;
|
|
69
|
+
}>;
|
|
70
|
+
nextPageToken?: string;
|
|
71
|
+
resultSizeEstimate?: number;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Delete a draft
|
|
75
|
+
*/
|
|
76
|
+
deleteDraft(draftId: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Send an email (either directly or from a draft)
|
|
79
|
+
*/
|
|
80
|
+
sendMessage(options: {
|
|
81
|
+
to: string;
|
|
82
|
+
subject: string;
|
|
83
|
+
body: string;
|
|
84
|
+
cc?: string;
|
|
85
|
+
bcc?: string;
|
|
86
|
+
threadId?: string;
|
|
87
|
+
inReplyTo?: string;
|
|
88
|
+
references?: string;
|
|
89
|
+
}): Promise<Email>;
|
|
90
|
+
/**
|
|
91
|
+
* Send a draft
|
|
92
|
+
*/
|
|
93
|
+
sendDraft(draftId: string): Promise<Email>;
|
|
28
94
|
}
|
|
29
95
|
/**
|
|
30
96
|
* Service account credentials structure
|
|
@@ -68,8 +134,49 @@ export declare class ServiceAccountGmailClient implements IGmailClient {
|
|
|
68
134
|
format?: 'minimal' | 'full' | 'raw' | 'metadata';
|
|
69
135
|
metadataHeaders?: string[];
|
|
70
136
|
}): Promise<Email>;
|
|
137
|
+
modifyMessage(messageId: string, options: {
|
|
138
|
+
addLabelIds?: string[];
|
|
139
|
+
removeLabelIds?: string[];
|
|
140
|
+
}): Promise<Email>;
|
|
141
|
+
createDraft(options: {
|
|
142
|
+
to: string;
|
|
143
|
+
subject: string;
|
|
144
|
+
body: string;
|
|
145
|
+
cc?: string;
|
|
146
|
+
bcc?: string;
|
|
147
|
+
threadId?: string;
|
|
148
|
+
inReplyTo?: string;
|
|
149
|
+
references?: string;
|
|
150
|
+
}): Promise<Draft>;
|
|
151
|
+
getDraft(draftId: string): Promise<Draft>;
|
|
152
|
+
listDrafts(options?: {
|
|
153
|
+
maxResults?: number;
|
|
154
|
+
pageToken?: string;
|
|
155
|
+
}): Promise<{
|
|
156
|
+
drafts: Array<{
|
|
157
|
+
id: string;
|
|
158
|
+
message: EmailListItem;
|
|
159
|
+
}>;
|
|
160
|
+
nextPageToken?: string;
|
|
161
|
+
resultSizeEstimate?: number;
|
|
162
|
+
}>;
|
|
163
|
+
deleteDraft(draftId: string): Promise<void>;
|
|
164
|
+
sendMessage(options: {
|
|
165
|
+
to: string;
|
|
166
|
+
subject: string;
|
|
167
|
+
body: string;
|
|
168
|
+
cc?: string;
|
|
169
|
+
bcc?: string;
|
|
170
|
+
threadId?: string;
|
|
171
|
+
inReplyTo?: string;
|
|
172
|
+
references?: string;
|
|
173
|
+
}): Promise<Email>;
|
|
174
|
+
sendDraft(draftId: string): Promise<Email>;
|
|
71
175
|
}
|
|
72
176
|
export type ClientFactory = () => IGmailClient;
|
|
177
|
+
export interface CreateMCPServerOptions {
|
|
178
|
+
version: string;
|
|
179
|
+
}
|
|
73
180
|
/**
|
|
74
181
|
* Creates the default Gmail client based on environment variables.
|
|
75
182
|
* Uses service account with domain-wide delegation:
|
|
@@ -78,7 +185,7 @@ export type ClientFactory = () => IGmailClient;
|
|
|
78
185
|
* - GMAIL_IMPERSONATE_EMAIL: Email address to impersonate
|
|
79
186
|
*/
|
|
80
187
|
export declare function createDefaultClient(): IGmailClient;
|
|
81
|
-
export declare function createMCPServer(): {
|
|
188
|
+
export declare function createMCPServer(options: CreateMCPServerOptions): {
|
|
82
189
|
server: Server<{
|
|
83
190
|
method: string;
|
|
84
191
|
params?: {
|
package/shared/server.js
CHANGED
|
@@ -16,7 +16,12 @@ export class ServiceAccountGmailClient {
|
|
|
16
16
|
this.jwtClient = new JWT({
|
|
17
17
|
email: credentials.client_email,
|
|
18
18
|
key: credentials.private_key,
|
|
19
|
-
scopes: [
|
|
19
|
+
scopes: [
|
|
20
|
+
'https://www.googleapis.com/auth/gmail.readonly',
|
|
21
|
+
'https://www.googleapis.com/auth/gmail.modify',
|
|
22
|
+
'https://www.googleapis.com/auth/gmail.compose',
|
|
23
|
+
'https://www.googleapis.com/auth/gmail.send',
|
|
24
|
+
],
|
|
20
25
|
subject: impersonateEmail,
|
|
21
26
|
});
|
|
22
27
|
}
|
|
@@ -59,6 +64,41 @@ export class ServiceAccountGmailClient {
|
|
|
59
64
|
const { getMessage } = await import('./gmail-client/lib/get-message.js');
|
|
60
65
|
return getMessage(this.baseUrl, headers, messageId, options);
|
|
61
66
|
}
|
|
67
|
+
async modifyMessage(messageId, options) {
|
|
68
|
+
const headers = await this.getHeaders();
|
|
69
|
+
const { modifyMessage } = await import('./gmail-client/lib/modify-message.js');
|
|
70
|
+
return modifyMessage(this.baseUrl, headers, messageId, options);
|
|
71
|
+
}
|
|
72
|
+
async createDraft(options) {
|
|
73
|
+
const headers = await this.getHeaders();
|
|
74
|
+
const { createDraft } = await import('./gmail-client/lib/drafts.js');
|
|
75
|
+
return createDraft(this.baseUrl, headers, this.impersonateEmail, options);
|
|
76
|
+
}
|
|
77
|
+
async getDraft(draftId) {
|
|
78
|
+
const headers = await this.getHeaders();
|
|
79
|
+
const { getDraft } = await import('./gmail-client/lib/drafts.js');
|
|
80
|
+
return getDraft(this.baseUrl, headers, draftId);
|
|
81
|
+
}
|
|
82
|
+
async listDrafts(options) {
|
|
83
|
+
const headers = await this.getHeaders();
|
|
84
|
+
const { listDrafts } = await import('./gmail-client/lib/drafts.js');
|
|
85
|
+
return listDrafts(this.baseUrl, headers, options);
|
|
86
|
+
}
|
|
87
|
+
async deleteDraft(draftId) {
|
|
88
|
+
const headers = await this.getHeaders();
|
|
89
|
+
const { deleteDraft } = await import('./gmail-client/lib/drafts.js');
|
|
90
|
+
return deleteDraft(this.baseUrl, headers, draftId);
|
|
91
|
+
}
|
|
92
|
+
async sendMessage(options) {
|
|
93
|
+
const headers = await this.getHeaders();
|
|
94
|
+
const { sendMessage } = await import('./gmail-client/lib/send-message.js');
|
|
95
|
+
return sendMessage(this.baseUrl, headers, this.impersonateEmail, options);
|
|
96
|
+
}
|
|
97
|
+
async sendDraft(draftId) {
|
|
98
|
+
const headers = await this.getHeaders();
|
|
99
|
+
const { sendDraft } = await import('./gmail-client/lib/send-message.js');
|
|
100
|
+
return sendDraft(this.baseUrl, headers, draftId);
|
|
101
|
+
}
|
|
62
102
|
}
|
|
63
103
|
/**
|
|
64
104
|
* Creates the default Gmail client based on environment variables.
|
|
@@ -98,10 +138,10 @@ export function createDefaultClient() {
|
|
|
98
138
|
};
|
|
99
139
|
return new ServiceAccountGmailClient(credentials, impersonateEmail);
|
|
100
140
|
}
|
|
101
|
-
export function createMCPServer() {
|
|
141
|
+
export function createMCPServer(options) {
|
|
102
142
|
const server = new Server({
|
|
103
143
|
name: 'gmail-workspace-mcp-server',
|
|
104
|
-
version:
|
|
144
|
+
version: options.version,
|
|
105
145
|
}, {
|
|
106
146
|
capabilities: {
|
|
107
147
|
tools: {},
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const ChangeEmailConversationSchema: z.ZodObject<{
|
|
5
|
+
email_id: z.ZodString;
|
|
6
|
+
status: z.ZodOptional<z.ZodEnum<["read", "unread", "archived"]>>;
|
|
7
|
+
labels: z.ZodOptional<z.ZodString>;
|
|
8
|
+
remove_labels: z.ZodOptional<z.ZodString>;
|
|
9
|
+
is_starred: z.ZodOptional<z.ZodBoolean>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
email_id: string;
|
|
12
|
+
labels?: string | undefined;
|
|
13
|
+
status?: "read" | "unread" | "archived" | undefined;
|
|
14
|
+
remove_labels?: string | undefined;
|
|
15
|
+
is_starred?: boolean | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
email_id: string;
|
|
18
|
+
labels?: string | undefined;
|
|
19
|
+
status?: "read" | "unread" | "archived" | undefined;
|
|
20
|
+
remove_labels?: string | undefined;
|
|
21
|
+
is_starred?: boolean | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function changeEmailConversationTool(server: Server, clientFactory: ClientFactory): {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: "object";
|
|
28
|
+
properties: {
|
|
29
|
+
email_id: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
status: {
|
|
34
|
+
type: string;
|
|
35
|
+
enum: string[];
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
labels: {
|
|
39
|
+
type: string;
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
remove_labels: {
|
|
43
|
+
type: string;
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
is_starred: {
|
|
47
|
+
type: string;
|
|
48
|
+
description: "Star or unstar the email. Set to true to star, false to unstar.";
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
required: string[];
|
|
52
|
+
};
|
|
53
|
+
handler: (args: unknown) => Promise<{
|
|
54
|
+
content: {
|
|
55
|
+
type: string;
|
|
56
|
+
text: string;
|
|
57
|
+
}[];
|
|
58
|
+
isError?: undefined;
|
|
59
|
+
} | {
|
|
60
|
+
content: {
|
|
61
|
+
type: string;
|
|
62
|
+
text: string;
|
|
63
|
+
}[];
|
|
64
|
+
isError: boolean;
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
email_id: 'The unique identifier of the email to modify. ' +
|
|
4
|
+
'Obtain this from list_email_conversations or search_email_conversations.',
|
|
5
|
+
status: 'Mark the email as read, unread, or archived. ' + 'Options: read, unread, archived.',
|
|
6
|
+
labels: 'Labels to add to the email. Comma-separated list. ' +
|
|
7
|
+
'Common labels: STARRED, IMPORTANT. User labels should be the label ID.',
|
|
8
|
+
remove_labels: 'Labels to remove from the email. Comma-separated list. ' +
|
|
9
|
+
'Common labels: STARRED, IMPORTANT, UNREAD. User labels should be the label ID.',
|
|
10
|
+
is_starred: 'Star or unstar the email. Set to true to star, false to unstar.',
|
|
11
|
+
};
|
|
12
|
+
export const ChangeEmailConversationSchema = z.object({
|
|
13
|
+
email_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.email_id),
|
|
14
|
+
status: z.enum(['read', 'unread', 'archived']).optional().describe(PARAM_DESCRIPTIONS.status),
|
|
15
|
+
labels: z.string().optional().describe(PARAM_DESCRIPTIONS.labels),
|
|
16
|
+
remove_labels: z.string().optional().describe(PARAM_DESCRIPTIONS.remove_labels),
|
|
17
|
+
is_starred: z.boolean().optional().describe(PARAM_DESCRIPTIONS.is_starred),
|
|
18
|
+
});
|
|
19
|
+
const TOOL_DESCRIPTION = `Modify an email conversation's status, labels, or starred state.
|
|
20
|
+
|
|
21
|
+
**Parameters:**
|
|
22
|
+
- email_id: The unique identifier of the email (required)
|
|
23
|
+
- status: Mark as "read", "unread", or "archived" (optional)
|
|
24
|
+
- labels: Comma-separated labels to add (optional)
|
|
25
|
+
- remove_labels: Comma-separated labels to remove (optional)
|
|
26
|
+
- is_starred: Set to true to star, false to unstar (optional)
|
|
27
|
+
|
|
28
|
+
**Label operations:**
|
|
29
|
+
- Adding STARRED marks the email as starred
|
|
30
|
+
- Removing INBOX archives the email
|
|
31
|
+
- Adding/removing UNREAD marks email as unread/read
|
|
32
|
+
|
|
33
|
+
**Use cases:**
|
|
34
|
+
- Mark an email as read/unread
|
|
35
|
+
- Star important emails
|
|
36
|
+
- Archive emails (remove from inbox)
|
|
37
|
+
- Apply custom labels for organization
|
|
38
|
+
|
|
39
|
+
**Note:** Get email_id from list_email_conversations or search_email_conversations first.`;
|
|
40
|
+
export function changeEmailConversationTool(server, clientFactory) {
|
|
41
|
+
return {
|
|
42
|
+
name: 'change_email_conversation',
|
|
43
|
+
description: TOOL_DESCRIPTION,
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
email_id: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: PARAM_DESCRIPTIONS.email_id,
|
|
50
|
+
},
|
|
51
|
+
status: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
enum: ['read', 'unread', 'archived'],
|
|
54
|
+
description: PARAM_DESCRIPTIONS.status,
|
|
55
|
+
},
|
|
56
|
+
labels: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: PARAM_DESCRIPTIONS.labels,
|
|
59
|
+
},
|
|
60
|
+
remove_labels: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: PARAM_DESCRIPTIONS.remove_labels,
|
|
63
|
+
},
|
|
64
|
+
is_starred: {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
description: PARAM_DESCRIPTIONS.is_starred,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ['email_id'],
|
|
70
|
+
},
|
|
71
|
+
handler: async (args) => {
|
|
72
|
+
try {
|
|
73
|
+
const parsed = ChangeEmailConversationSchema.parse(args ?? {});
|
|
74
|
+
const client = clientFactory();
|
|
75
|
+
const addLabelIds = [];
|
|
76
|
+
const removeLabelIds = [];
|
|
77
|
+
// Handle status
|
|
78
|
+
if (parsed.status === 'read') {
|
|
79
|
+
removeLabelIds.push('UNREAD');
|
|
80
|
+
}
|
|
81
|
+
else if (parsed.status === 'unread') {
|
|
82
|
+
addLabelIds.push('UNREAD');
|
|
83
|
+
}
|
|
84
|
+
else if (parsed.status === 'archived') {
|
|
85
|
+
removeLabelIds.push('INBOX');
|
|
86
|
+
}
|
|
87
|
+
// Handle is_starred
|
|
88
|
+
if (parsed.is_starred === true) {
|
|
89
|
+
addLabelIds.push('STARRED');
|
|
90
|
+
}
|
|
91
|
+
else if (parsed.is_starred === false) {
|
|
92
|
+
removeLabelIds.push('STARRED');
|
|
93
|
+
}
|
|
94
|
+
// Handle labels to add
|
|
95
|
+
if (parsed.labels) {
|
|
96
|
+
const labels = parsed.labels.split(',').map((l) => l.trim());
|
|
97
|
+
addLabelIds.push(...labels);
|
|
98
|
+
}
|
|
99
|
+
// Handle labels to remove
|
|
100
|
+
if (parsed.remove_labels) {
|
|
101
|
+
const labels = parsed.remove_labels.split(',').map((l) => l.trim());
|
|
102
|
+
removeLabelIds.push(...labels);
|
|
103
|
+
}
|
|
104
|
+
// Only make API call if there are changes to make
|
|
105
|
+
if (addLabelIds.length === 0 && removeLabelIds.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: 'No changes specified. Provide at least one of: status, labels, remove_labels, or is_starred.',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const updatedEmail = await client.modifyMessage(parsed.email_id, {
|
|
116
|
+
addLabelIds: addLabelIds.length > 0 ? addLabelIds : undefined,
|
|
117
|
+
removeLabelIds: removeLabelIds.length > 0 ? removeLabelIds : undefined,
|
|
118
|
+
});
|
|
119
|
+
const changes = [];
|
|
120
|
+
if (addLabelIds.length > 0) {
|
|
121
|
+
changes.push(`Added labels: ${addLabelIds.join(', ')}`);
|
|
122
|
+
}
|
|
123
|
+
if (removeLabelIds.length > 0) {
|
|
124
|
+
changes.push(`Removed labels: ${removeLabelIds.join(', ')}`);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
content: [
|
|
128
|
+
{
|
|
129
|
+
type: 'text',
|
|
130
|
+
text: `Email ${parsed.email_id} updated successfully.\n\n${changes.join('\n')}\n\nCurrent labels: ${updatedEmail.labelIds?.join(', ') || 'None'}`,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: `Error modifying email: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const DraftEmailSchema: z.ZodObject<{
|
|
5
|
+
to: z.ZodString;
|
|
6
|
+
subject: z.ZodString;
|
|
7
|
+
body: z.ZodString;
|
|
8
|
+
cc: z.ZodOptional<z.ZodString>;
|
|
9
|
+
bcc: z.ZodOptional<z.ZodString>;
|
|
10
|
+
thread_id: z.ZodOptional<z.ZodString>;
|
|
11
|
+
reply_to_email_id: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
body: string;
|
|
14
|
+
to: string;
|
|
15
|
+
subject: string;
|
|
16
|
+
cc?: string | undefined;
|
|
17
|
+
bcc?: string | undefined;
|
|
18
|
+
thread_id?: string | undefined;
|
|
19
|
+
reply_to_email_id?: string | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
body: string;
|
|
22
|
+
to: string;
|
|
23
|
+
subject: string;
|
|
24
|
+
cc?: string | undefined;
|
|
25
|
+
bcc?: string | undefined;
|
|
26
|
+
thread_id?: string | undefined;
|
|
27
|
+
reply_to_email_id?: string | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function draftEmailTool(server: Server, clientFactory: ClientFactory): {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object";
|
|
34
|
+
properties: {
|
|
35
|
+
to: {
|
|
36
|
+
type: string;
|
|
37
|
+
description: "Recipient email address(es). For multiple recipients, separate with commas.";
|
|
38
|
+
};
|
|
39
|
+
subject: {
|
|
40
|
+
type: string;
|
|
41
|
+
description: "Subject line of the email.";
|
|
42
|
+
};
|
|
43
|
+
body: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: "Plain text body content of the email.";
|
|
46
|
+
};
|
|
47
|
+
cc: {
|
|
48
|
+
type: string;
|
|
49
|
+
description: "CC recipient email address(es). For multiple, separate with commas.";
|
|
50
|
+
};
|
|
51
|
+
bcc: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: "BCC recipient email address(es). For multiple, separate with commas.";
|
|
54
|
+
};
|
|
55
|
+
thread_id: {
|
|
56
|
+
type: string;
|
|
57
|
+
description: string;
|
|
58
|
+
};
|
|
59
|
+
reply_to_email_id: {
|
|
60
|
+
type: string;
|
|
61
|
+
description: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
required: string[];
|
|
65
|
+
};
|
|
66
|
+
handler: (args: unknown) => Promise<{
|
|
67
|
+
content: {
|
|
68
|
+
type: string;
|
|
69
|
+
text: string;
|
|
70
|
+
}[];
|
|
71
|
+
isError?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
content: {
|
|
74
|
+
type: string;
|
|
75
|
+
text: string;
|
|
76
|
+
}[];
|
|
77
|
+
isError: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
};
|