gogcli-mcp-gmail 2.0.3
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 +113 -0
- package/SKILL.md +87 -0
- package/dist/index.js +31508 -0
- package/manifest.json +108 -0
- package/package.json +41 -0
- package/src/index.ts +12 -0
- package/src/tools/gmail-extra.ts +450 -0
- package/tests/tools/gmail-extra.test.ts +606 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +17 -0
package/manifest.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/anthropics/dxt/main/dist/mcpb-manifest.schema.json",
|
|
3
|
+
"manifest_version": "0.3",
|
|
4
|
+
"name": "gogcli-mcp-gmail",
|
|
5
|
+
"display_name": "gogcli (Gmail)",
|
|
6
|
+
"version": "2.0.3",
|
|
7
|
+
"description": "Extended Gmail for Claude via gogcli — auth + full Gmail support (threads, labels, drafts, attachments, forward, autoreply, bulk operations)",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Chris Hall",
|
|
10
|
+
"email": "chris.c.hall@gmail.com",
|
|
11
|
+
"url": "https://github.com/chrischall"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/chrischall/gogcli-mcp"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/chrischall/gogcli-mcp",
|
|
18
|
+
"support": "https://github.com/chrischall/gogcli-mcp/issues",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"google",
|
|
22
|
+
"gmail",
|
|
23
|
+
"email",
|
|
24
|
+
"gogcli",
|
|
25
|
+
"gog",
|
|
26
|
+
"threads",
|
|
27
|
+
"labels",
|
|
28
|
+
"drafts",
|
|
29
|
+
"autoreply"
|
|
30
|
+
],
|
|
31
|
+
"server": {
|
|
32
|
+
"type": "node",
|
|
33
|
+
"entry_point": "dist/index.js",
|
|
34
|
+
"mcp_config": {
|
|
35
|
+
"command": "node",
|
|
36
|
+
"args": [
|
|
37
|
+
"${__dirname}/dist/index.js"
|
|
38
|
+
],
|
|
39
|
+
"env": {
|
|
40
|
+
"GOG_ACCOUNT": "${user_config.gog_account}",
|
|
41
|
+
"GOG_PATH": "${user_config.gog_path}"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"user_config": {
|
|
46
|
+
"gog_account": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"title": "Google Account",
|
|
49
|
+
"description": "Default Google account email (optional — uses gogcli's configured default if omitted)",
|
|
50
|
+
"required": false
|
|
51
|
+
},
|
|
52
|
+
"gog_path": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"title": "gog Executable Path",
|
|
55
|
+
"description": "Path to the gog executable (optional — defaults to 'gog' on your PATH)",
|
|
56
|
+
"required": false
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"tools": [
|
|
60
|
+
{ "name": "gog_auth_add", "description": "Authorize a Google account via browser-based OAuth" },
|
|
61
|
+
{ "name": "gog_auth_list", "description": "List all Google accounts stored in gogcli" },
|
|
62
|
+
{ "name": "gog_auth_status", "description": "Show gogcli auth configuration" },
|
|
63
|
+
{ "name": "gog_auth_services", "description": "List Google services and OAuth scopes supported by gogcli" },
|
|
64
|
+
{ "name": "gog_auth_run", "description": "Run any gog auth subcommand (escape hatch)" },
|
|
65
|
+
{ "name": "gog_gmail_search", "description": "Search Gmail threads using Gmail query syntax" },
|
|
66
|
+
{ "name": "gog_gmail_get", "description": "Get a Gmail message by ID" },
|
|
67
|
+
{ "name": "gog_gmail_send", "description": "Send an email" },
|
|
68
|
+
{ "name": "gog_gmail_run", "description": "Run any gog gmail subcommand (escape hatch)" },
|
|
69
|
+
{ "name": "gog_gmail_raw", "description": "Dump raw Gmail API JSON for a message" },
|
|
70
|
+
{ "name": "gog_gmail_attachment", "description": "Download a single attachment from a message" },
|
|
71
|
+
{ "name": "gog_gmail_url", "description": "Print Gmail web URLs for one or more threads" },
|
|
72
|
+
{ "name": "gog_gmail_history", "description": "List Gmail history events since a historyId" },
|
|
73
|
+
{ "name": "gog_gmail_archive", "description": "Archive messages by IDs or by query" },
|
|
74
|
+
{ "name": "gog_gmail_mark_read", "description": "Mark messages as read by IDs or by query" },
|
|
75
|
+
{ "name": "gog_gmail_mark_unread", "description": "Mark messages as unread by IDs or by query" },
|
|
76
|
+
{ "name": "gog_gmail_trash", "description": "Move messages to trash by IDs or by query" },
|
|
77
|
+
{ "name": "gog_gmail_message_modify", "description": "Modify labels on a single message" },
|
|
78
|
+
{ "name": "gog_gmail_batch_delete", "description": "Permanently delete multiple messages (irreversible)" },
|
|
79
|
+
{ "name": "gog_gmail_batch_modify", "description": "Modify labels on multiple messages at once" },
|
|
80
|
+
{ "name": "gog_gmail_thread_get", "description": "Get a thread with all messages" },
|
|
81
|
+
{ "name": "gog_gmail_thread_modify", "description": "Modify labels on all messages in a thread" },
|
|
82
|
+
{ "name": "gog_gmail_thread_attachments", "description": "List or download all attachments in a thread" },
|
|
83
|
+
{ "name": "gog_gmail_labels_list", "description": "List all Gmail labels" },
|
|
84
|
+
{ "name": "gog_gmail_labels_get", "description": "Get label details and counts" },
|
|
85
|
+
{ "name": "gog_gmail_labels_create", "description": "Create a new Gmail label" },
|
|
86
|
+
{ "name": "gog_gmail_labels_rename", "description": "Rename a Gmail label" },
|
|
87
|
+
{ "name": "gog_gmail_labels_delete", "description": "Delete a Gmail label" },
|
|
88
|
+
{ "name": "gog_gmail_labels_modify", "description": "Modify labels on one or more threads" },
|
|
89
|
+
{ "name": "gog_gmail_drafts_list", "description": "List Gmail drafts" },
|
|
90
|
+
{ "name": "gog_gmail_drafts_get", "description": "Get a Gmail draft by ID" },
|
|
91
|
+
{ "name": "gog_gmail_drafts_create", "description": "Create a new Gmail draft" },
|
|
92
|
+
{ "name": "gog_gmail_drafts_update", "description": "Update an existing Gmail draft" },
|
|
93
|
+
{ "name": "gog_gmail_drafts_delete", "description": "Delete a Gmail draft" },
|
|
94
|
+
{ "name": "gog_gmail_drafts_send", "description": "Send an existing Gmail draft" },
|
|
95
|
+
{ "name": "gog_gmail_forward", "description": "Forward a Gmail message to new recipients" },
|
|
96
|
+
{ "name": "gog_gmail_autoreply", "description": "Reply once to all messages matching a Gmail search query" }
|
|
97
|
+
],
|
|
98
|
+
"compatibility": {
|
|
99
|
+
"platforms": [
|
|
100
|
+
"darwin",
|
|
101
|
+
"linux",
|
|
102
|
+
"win32"
|
|
103
|
+
],
|
|
104
|
+
"runtimes": {
|
|
105
|
+
"node": ">=18.0.0"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gogcli-mcp-gmail",
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"mcpName": "io.github.chrischall/gogcli-mcp-gmail",
|
|
5
|
+
"description": "Extended Gmail MCP server via gogcli — auth + full Gmail support (threads, labels, drafts, attachments, forward, autoreply, bulk operations)",
|
|
6
|
+
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/chrischall/gogcli-mcp"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": {
|
|
13
|
+
"gogcli-mcp-gmail": "dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --noEmit && npm run bundle",
|
|
20
|
+
"bundle": "node ../../scripts/bundle.js src/index.ts dist/index.js",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
+
"zod": "^4.4.2"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mcp",
|
|
33
|
+
"model-context-protocol",
|
|
34
|
+
"claude",
|
|
35
|
+
"ai",
|
|
36
|
+
"google",
|
|
37
|
+
"gogcli",
|
|
38
|
+
"gmail",
|
|
39
|
+
"email"
|
|
40
|
+
]
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createServer, registerAuthTools, registerGmailTools } from '../../gogcli-mcp/src/lib.js';
|
|
4
|
+
import { registerExtraGmailTools } from './tools/gmail-extra.js';
|
|
5
|
+
|
|
6
|
+
const server = createServer({ name: 'gogcli-gmail' });
|
|
7
|
+
registerAuthTools(server);
|
|
8
|
+
registerGmailTools(server);
|
|
9
|
+
registerExtraGmailTools(server);
|
|
10
|
+
|
|
11
|
+
const transport = new StdioServerTransport();
|
|
12
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { accountParam, runOrDiagnose } from '../../../gogcli-mcp/src/lib.js';
|
|
4
|
+
|
|
5
|
+
export function registerExtraGmailTools(server: McpServer): void {
|
|
6
|
+
server.registerTool('gog_gmail_raw', {
|
|
7
|
+
description: 'Dump the raw Gmail API response as JSON (lossless; for scripting and LLM consumption).',
|
|
8
|
+
annotations: { readOnlyHint: true },
|
|
9
|
+
inputSchema: {
|
|
10
|
+
messageId: z.string().describe('Gmail message ID'),
|
|
11
|
+
format: z.enum(['full', 'metadata', 'minimal', 'raw']).optional().describe('Gmail format (default: full)'),
|
|
12
|
+
pretty: z.boolean().optional().describe('Pretty-print JSON (default: compact single-line)'),
|
|
13
|
+
account: accountParam,
|
|
14
|
+
},
|
|
15
|
+
}, async ({ messageId, format, pretty, account }) => {
|
|
16
|
+
const args = ['gmail', 'raw', messageId];
|
|
17
|
+
if (format) args.push(`--format=${format}`);
|
|
18
|
+
if (pretty) args.push('--pretty');
|
|
19
|
+
return runOrDiagnose(args, { account });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
server.registerTool('gog_gmail_attachment', {
|
|
23
|
+
description: 'Download a single attachment from a Gmail message.',
|
|
24
|
+
annotations: { readOnlyHint: true },
|
|
25
|
+
inputSchema: {
|
|
26
|
+
messageId: z.string().describe('Gmail message ID'),
|
|
27
|
+
attachmentId: z.string().describe('Attachment ID (from the message payload)'),
|
|
28
|
+
out: z.string().optional().describe('Output file path (default: gogcli config dir)'),
|
|
29
|
+
name: z.string().optional().describe('Filename (used when --out is empty or points to a directory)'),
|
|
30
|
+
account: accountParam,
|
|
31
|
+
},
|
|
32
|
+
}, async ({ messageId, attachmentId, out, name, account }) => {
|
|
33
|
+
const args = ['gmail', 'attachment', messageId, attachmentId];
|
|
34
|
+
if (out) args.push(`--out=${out}`);
|
|
35
|
+
if (name) args.push(`--name=${name}`);
|
|
36
|
+
return runOrDiagnose(args, { account });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
server.registerTool('gog_gmail_url', {
|
|
40
|
+
description: 'Print Gmail web URLs for one or more threads.',
|
|
41
|
+
annotations: { readOnlyHint: true },
|
|
42
|
+
inputSchema: {
|
|
43
|
+
threadIds: z.array(z.string()).min(1).describe('One or more thread IDs'),
|
|
44
|
+
account: accountParam,
|
|
45
|
+
},
|
|
46
|
+
}, async ({ threadIds, account }) => {
|
|
47
|
+
return runOrDiagnose(['gmail', 'url', ...threadIds], { account });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
server.registerTool('gog_gmail_history', {
|
|
51
|
+
description: 'List Gmail history events since a given historyId (for syncing).',
|
|
52
|
+
annotations: { readOnlyHint: true },
|
|
53
|
+
inputSchema: {
|
|
54
|
+
since: z.string().optional().describe('Start history ID'),
|
|
55
|
+
max: z.number().optional().describe('Max results (default: 100)'),
|
|
56
|
+
page: z.string().optional().describe('Page token'),
|
|
57
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
58
|
+
account: accountParam,
|
|
59
|
+
},
|
|
60
|
+
}, async ({ since, max, page, all, account }) => {
|
|
61
|
+
const args = ['gmail', 'history'];
|
|
62
|
+
if (since) args.push(`--since=${since}`);
|
|
63
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
64
|
+
if (page) args.push(`--page=${page}`);
|
|
65
|
+
if (all) args.push('--all');
|
|
66
|
+
return runOrDiagnose(args, { account });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const bulkActions: Array<{ tool: string; cmd: string; description: string }> = [
|
|
70
|
+
{
|
|
71
|
+
tool: 'gog_gmail_archive',
|
|
72
|
+
cmd: 'archive',
|
|
73
|
+
description: 'Archive messages (remove from inbox). Pass either messageIds or a Gmail search query.',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
tool: 'gog_gmail_mark_read',
|
|
77
|
+
cmd: 'mark-read',
|
|
78
|
+
description: 'Mark messages as read. Pass either messageIds or a Gmail search query.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
tool: 'gog_gmail_mark_unread',
|
|
82
|
+
cmd: 'unread',
|
|
83
|
+
description: 'Mark messages as unread. Pass either messageIds or a Gmail search query.',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
tool: 'gog_gmail_trash',
|
|
87
|
+
cmd: 'trash',
|
|
88
|
+
description: 'Move messages to trash. Pass either messageIds or a Gmail search query.',
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
for (const { tool, cmd, description } of bulkActions) {
|
|
93
|
+
server.registerTool(tool, {
|
|
94
|
+
description,
|
|
95
|
+
annotations: { destructiveHint: true },
|
|
96
|
+
inputSchema: {
|
|
97
|
+
messageIds: z.array(z.string()).optional().describe('Specific message IDs to act on'),
|
|
98
|
+
query: z.string().optional().describe('Gmail search query (alternative to messageIds; acts on all matching)'),
|
|
99
|
+
max: z.number().optional().describe('Max messages when using --query (default: 100)'),
|
|
100
|
+
account: accountParam,
|
|
101
|
+
},
|
|
102
|
+
}, async ({ messageIds, query, max, account }) => {
|
|
103
|
+
const args = ['gmail', cmd];
|
|
104
|
+
if (messageIds) args.push(...messageIds);
|
|
105
|
+
if (query) args.push(`--query=${query}`);
|
|
106
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
107
|
+
return runOrDiagnose(args, { account });
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
server.registerTool('gog_gmail_message_modify', {
|
|
112
|
+
description: 'Modify labels on a single message (add and/or remove labels).',
|
|
113
|
+
annotations: { destructiveHint: true },
|
|
114
|
+
inputSchema: {
|
|
115
|
+
messageId: z.string().describe('Gmail message ID'),
|
|
116
|
+
add: z.string().optional().describe('Labels to add (comma-separated, name or ID)'),
|
|
117
|
+
remove: z.string().optional().describe('Labels to remove (comma-separated, name or ID)'),
|
|
118
|
+
account: accountParam,
|
|
119
|
+
},
|
|
120
|
+
}, async ({ messageId, add, remove, account }) => {
|
|
121
|
+
const args = ['gmail', 'messages', 'modify', messageId];
|
|
122
|
+
if (add) args.push(`--add=${add}`);
|
|
123
|
+
if (remove) args.push(`--remove=${remove}`);
|
|
124
|
+
return runOrDiagnose(args, { account });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
server.registerTool('gog_gmail_batch_delete', {
|
|
128
|
+
description: 'Permanently delete multiple messages (requires the broader Gmail scope). Use gog_gmail_trash for normal deletes.',
|
|
129
|
+
annotations: { destructiveHint: true },
|
|
130
|
+
inputSchema: {
|
|
131
|
+
messageIds: z.array(z.string()).min(1).describe('Message IDs to permanently delete'),
|
|
132
|
+
account: accountParam,
|
|
133
|
+
},
|
|
134
|
+
}, async ({ messageIds, account }) => {
|
|
135
|
+
return runOrDiagnose(['gmail', 'batch', 'delete', ...messageIds], { account });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
server.registerTool('gog_gmail_batch_modify', {
|
|
139
|
+
description: 'Modify labels on multiple messages in one call (add and/or remove labels).',
|
|
140
|
+
annotations: { destructiveHint: true },
|
|
141
|
+
inputSchema: {
|
|
142
|
+
messageIds: z.array(z.string()).min(1).describe('Message IDs to modify'),
|
|
143
|
+
add: z.string().optional().describe('Labels to add (comma-separated, name or ID)'),
|
|
144
|
+
remove: z.string().optional().describe('Labels to remove (comma-separated, name or ID)'),
|
|
145
|
+
account: accountParam,
|
|
146
|
+
},
|
|
147
|
+
}, async ({ messageIds, add, remove, account }) => {
|
|
148
|
+
const args = ['gmail', 'batch', 'modify', ...messageIds];
|
|
149
|
+
if (add) args.push(`--add=${add}`);
|
|
150
|
+
if (remove) args.push(`--remove=${remove}`);
|
|
151
|
+
return runOrDiagnose(args, { account });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
server.registerTool('gog_gmail_thread_get', {
|
|
155
|
+
description: 'Get a Gmail thread with all messages, optionally downloading attachments and sanitizing content for agent consumption.',
|
|
156
|
+
annotations: { readOnlyHint: true },
|
|
157
|
+
inputSchema: {
|
|
158
|
+
threadId: z.string().describe('Gmail thread ID'),
|
|
159
|
+
download: z.boolean().optional().describe('Download all attachments'),
|
|
160
|
+
full: z.boolean().optional().describe('Show full message bodies'),
|
|
161
|
+
sanitizeContent: z.boolean().optional().describe('Strip HTML, remove URLs, omit raw payloads from JSON'),
|
|
162
|
+
outDir: z.string().optional().describe('Directory to write attachments to (default: current directory)'),
|
|
163
|
+
account: accountParam,
|
|
164
|
+
},
|
|
165
|
+
}, async ({ threadId, download, full, sanitizeContent, outDir, account }) => {
|
|
166
|
+
const args = ['gmail', 'thread', 'get', threadId];
|
|
167
|
+
if (download) args.push('--download');
|
|
168
|
+
if (full) args.push('--full');
|
|
169
|
+
if (sanitizeContent) args.push('--sanitize-content');
|
|
170
|
+
if (outDir) args.push(`--out-dir=${outDir}`);
|
|
171
|
+
return runOrDiagnose(args, { account });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
server.registerTool('gog_gmail_thread_modify', {
|
|
175
|
+
description: 'Modify labels on all messages in a thread (add and/or remove labels).',
|
|
176
|
+
annotations: { destructiveHint: true },
|
|
177
|
+
inputSchema: {
|
|
178
|
+
threadId: z.string().describe('Gmail thread ID'),
|
|
179
|
+
add: z.string().optional().describe('Labels to add (comma-separated, name or ID)'),
|
|
180
|
+
remove: z.string().optional().describe('Labels to remove (comma-separated, name or ID)'),
|
|
181
|
+
account: accountParam,
|
|
182
|
+
},
|
|
183
|
+
}, async ({ threadId, add, remove, account }) => {
|
|
184
|
+
const args = ['gmail', 'thread', 'modify', threadId];
|
|
185
|
+
if (add) args.push(`--add=${add}`);
|
|
186
|
+
if (remove) args.push(`--remove=${remove}`);
|
|
187
|
+
return runOrDiagnose(args, { account });
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
server.registerTool('gog_gmail_thread_attachments', {
|
|
191
|
+
description: 'List all attachments in a Gmail thread, optionally downloading them.',
|
|
192
|
+
annotations: { readOnlyHint: true },
|
|
193
|
+
inputSchema: {
|
|
194
|
+
threadId: z.string().describe('Gmail thread ID'),
|
|
195
|
+
download: z.boolean().optional().describe('Download all attachments'),
|
|
196
|
+
outDir: z.string().optional().describe('Directory to write attachments to (default: current directory)'),
|
|
197
|
+
account: accountParam,
|
|
198
|
+
},
|
|
199
|
+
}, async ({ threadId, download, outDir, account }) => {
|
|
200
|
+
const args = ['gmail', 'thread', 'attachments', threadId];
|
|
201
|
+
if (download) args.push('--download');
|
|
202
|
+
if (outDir) args.push(`--out-dir=${outDir}`);
|
|
203
|
+
return runOrDiagnose(args, { account });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
server.registerTool('gog_gmail_labels_list', {
|
|
207
|
+
description: 'List all Gmail labels for the account.',
|
|
208
|
+
annotations: { readOnlyHint: true },
|
|
209
|
+
inputSchema: {
|
|
210
|
+
account: accountParam,
|
|
211
|
+
},
|
|
212
|
+
}, async ({ account }) => {
|
|
213
|
+
return runOrDiagnose(['gmail', 'labels', 'list'], { account });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
server.registerTool('gog_gmail_labels_get', {
|
|
217
|
+
description: 'Get label details, including message and thread counts.',
|
|
218
|
+
annotations: { readOnlyHint: true },
|
|
219
|
+
inputSchema: {
|
|
220
|
+
labelIdOrName: z.string().describe('Label ID or name (e.g. INBOX, STARRED, or a user-created label)'),
|
|
221
|
+
account: accountParam,
|
|
222
|
+
},
|
|
223
|
+
}, async ({ labelIdOrName, account }) => {
|
|
224
|
+
return runOrDiagnose(['gmail', 'labels', 'get', labelIdOrName], { account });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
server.registerTool('gog_gmail_labels_create', {
|
|
228
|
+
description: 'Create a new Gmail label.',
|
|
229
|
+
inputSchema: {
|
|
230
|
+
name: z.string().describe('Label name'),
|
|
231
|
+
account: accountParam,
|
|
232
|
+
},
|
|
233
|
+
}, async ({ name, account }) => {
|
|
234
|
+
return runOrDiagnose(['gmail', 'labels', 'create', name], { account });
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
server.registerTool('gog_gmail_labels_rename', {
|
|
238
|
+
description: 'Rename a Gmail label.',
|
|
239
|
+
annotations: { destructiveHint: true },
|
|
240
|
+
inputSchema: {
|
|
241
|
+
labelIdOrName: z.string().describe('Current label ID or name'),
|
|
242
|
+
newName: z.string().describe('New label name'),
|
|
243
|
+
account: accountParam,
|
|
244
|
+
},
|
|
245
|
+
}, async ({ labelIdOrName, newName, account }) => {
|
|
246
|
+
return runOrDiagnose(['gmail', 'labels', 'rename', labelIdOrName, newName], { account });
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
server.registerTool('gog_gmail_labels_delete', {
|
|
250
|
+
description: 'Delete a Gmail label.',
|
|
251
|
+
annotations: { destructiveHint: true },
|
|
252
|
+
inputSchema: {
|
|
253
|
+
labelIdOrName: z.string().describe('Label ID or name to delete'),
|
|
254
|
+
account: accountParam,
|
|
255
|
+
},
|
|
256
|
+
}, async ({ labelIdOrName, account }) => {
|
|
257
|
+
return runOrDiagnose(['gmail', 'labels', 'delete', labelIdOrName], { account });
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
server.registerTool('gog_gmail_labels_modify', {
|
|
261
|
+
description: 'Modify labels on one or more threads (add and/or remove labels).',
|
|
262
|
+
annotations: { destructiveHint: true },
|
|
263
|
+
inputSchema: {
|
|
264
|
+
threadIds: z.array(z.string()).min(1).describe('One or more thread IDs'),
|
|
265
|
+
add: z.string().optional().describe('Labels to add (comma-separated, name or ID)'),
|
|
266
|
+
remove: z.string().optional().describe('Labels to remove (comma-separated, name or ID)'),
|
|
267
|
+
account: accountParam,
|
|
268
|
+
},
|
|
269
|
+
}, async ({ threadIds, add, remove, account }) => {
|
|
270
|
+
const args = ['gmail', 'labels', 'modify', ...threadIds];
|
|
271
|
+
if (add) args.push(`--add=${add}`);
|
|
272
|
+
if (remove) args.push(`--remove=${remove}`);
|
|
273
|
+
return runOrDiagnose(args, { account });
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
server.registerTool('gog_gmail_drafts_list', {
|
|
277
|
+
description: 'List Gmail drafts.',
|
|
278
|
+
annotations: { readOnlyHint: true },
|
|
279
|
+
inputSchema: {
|
|
280
|
+
max: z.number().optional().describe('Max results (default: 20)'),
|
|
281
|
+
page: z.string().optional().describe('Page token'),
|
|
282
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
283
|
+
account: accountParam,
|
|
284
|
+
},
|
|
285
|
+
}, async ({ max, page, all, account }) => {
|
|
286
|
+
const args = ['gmail', 'drafts', 'list'];
|
|
287
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
288
|
+
if (page) args.push(`--page=${page}`);
|
|
289
|
+
if (all) args.push('--all');
|
|
290
|
+
return runOrDiagnose(args, { account });
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
server.registerTool('gog_gmail_drafts_get', {
|
|
294
|
+
description: 'Get a Gmail draft by ID.',
|
|
295
|
+
annotations: { readOnlyHint: true },
|
|
296
|
+
inputSchema: {
|
|
297
|
+
draftId: z.string().describe('Draft ID'),
|
|
298
|
+
download: z.boolean().optional().describe('Download draft attachments'),
|
|
299
|
+
account: accountParam,
|
|
300
|
+
},
|
|
301
|
+
}, async ({ draftId, download, account }) => {
|
|
302
|
+
const args = ['gmail', 'drafts', 'get', draftId];
|
|
303
|
+
if (download) args.push('--download');
|
|
304
|
+
return runOrDiagnose(args, { account });
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const draftWriteSchema = {
|
|
308
|
+
to: z.string().optional().describe('Recipients (comma-separated)'),
|
|
309
|
+
cc: z.string().optional().describe('CC recipients (comma-separated)'),
|
|
310
|
+
bcc: z.string().optional().describe('BCC recipients (comma-separated)'),
|
|
311
|
+
subject: z.string().describe('Subject'),
|
|
312
|
+
body: z.string().describe('Body (plain text)'),
|
|
313
|
+
bodyHtml: z.string().optional().describe('Body (HTML; optional)'),
|
|
314
|
+
replyToMessageId: z.string().optional().describe('Reply to Gmail message ID (sets In-Reply-To/References and thread)'),
|
|
315
|
+
replyTo: z.string().optional().describe('Reply-To header address'),
|
|
316
|
+
quote: z.boolean().optional().describe('Include quoted original message in reply (requires replyToMessageId)'),
|
|
317
|
+
attach: z.array(z.string()).optional().describe('Attachment file paths (repeatable)'),
|
|
318
|
+
from: z.string().optional().describe('Send from this email address (must be a verified send-as alias)'),
|
|
319
|
+
account: accountParam,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
type DraftFlags = {
|
|
323
|
+
to?: string;
|
|
324
|
+
cc?: string;
|
|
325
|
+
bcc?: string;
|
|
326
|
+
subject: string;
|
|
327
|
+
body: string;
|
|
328
|
+
bodyHtml?: string;
|
|
329
|
+
replyToMessageId?: string;
|
|
330
|
+
replyTo?: string;
|
|
331
|
+
quote?: boolean;
|
|
332
|
+
attach?: string[];
|
|
333
|
+
from?: string;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
function appendDraftFlags(args: string[], f: DraftFlags): void {
|
|
337
|
+
if (f.to) args.push(`--to=${f.to}`);
|
|
338
|
+
if (f.cc) args.push(`--cc=${f.cc}`);
|
|
339
|
+
if (f.bcc) args.push(`--bcc=${f.bcc}`);
|
|
340
|
+
args.push(`--subject=${f.subject}`);
|
|
341
|
+
args.push(`--body=${f.body}`);
|
|
342
|
+
if (f.bodyHtml) args.push(`--body-html=${f.bodyHtml}`);
|
|
343
|
+
if (f.replyToMessageId) args.push(`--reply-to-message-id=${f.replyToMessageId}`);
|
|
344
|
+
if (f.replyTo) args.push(`--reply-to=${f.replyTo}`);
|
|
345
|
+
if (f.quote) args.push('--quote');
|
|
346
|
+
if (f.attach) for (const path of f.attach) args.push(`--attach=${path}`);
|
|
347
|
+
if (f.from) args.push(`--from=${f.from}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
server.registerTool('gog_gmail_drafts_create', {
|
|
351
|
+
description: 'Create a new Gmail draft.',
|
|
352
|
+
inputSchema: draftWriteSchema,
|
|
353
|
+
}, async ({ account, ...flags }) => {
|
|
354
|
+
const args = ['gmail', 'drafts', 'create'];
|
|
355
|
+
appendDraftFlags(args, flags);
|
|
356
|
+
return runOrDiagnose(args, { account });
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
server.registerTool('gog_gmail_drafts_update', {
|
|
360
|
+
description: 'Update an existing Gmail draft.',
|
|
361
|
+
annotations: { destructiveHint: true },
|
|
362
|
+
inputSchema: {
|
|
363
|
+
draftId: z.string().describe('Draft ID'),
|
|
364
|
+
...draftWriteSchema,
|
|
365
|
+
},
|
|
366
|
+
}, async ({ draftId, account, ...flags }) => {
|
|
367
|
+
const args = ['gmail', 'drafts', 'update', draftId];
|
|
368
|
+
appendDraftFlags(args, flags);
|
|
369
|
+
return runOrDiagnose(args, { account });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
server.registerTool('gog_gmail_drafts_delete', {
|
|
373
|
+
description: 'Delete a Gmail draft.',
|
|
374
|
+
annotations: { destructiveHint: true },
|
|
375
|
+
inputSchema: {
|
|
376
|
+
draftId: z.string().describe('Draft ID'),
|
|
377
|
+
account: accountParam,
|
|
378
|
+
},
|
|
379
|
+
}, async ({ draftId, account }) => {
|
|
380
|
+
return runOrDiagnose(['gmail', 'drafts', 'delete', draftId], { account });
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
server.registerTool('gog_gmail_drafts_send', {
|
|
384
|
+
description: 'Send an existing Gmail draft.',
|
|
385
|
+
annotations: { destructiveHint: true },
|
|
386
|
+
inputSchema: {
|
|
387
|
+
draftId: z.string().describe('Draft ID to send'),
|
|
388
|
+
account: accountParam,
|
|
389
|
+
},
|
|
390
|
+
}, async ({ draftId, account }) => {
|
|
391
|
+
return runOrDiagnose(['gmail', 'drafts', 'send', draftId], { account });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
server.registerTool('gog_gmail_forward', {
|
|
395
|
+
description: 'Forward an existing Gmail message to new recipients.',
|
|
396
|
+
annotations: { destructiveHint: true },
|
|
397
|
+
inputSchema: {
|
|
398
|
+
messageId: z.string().describe('Gmail message ID to forward'),
|
|
399
|
+
to: z.string().describe('Recipients (comma-separated; required)'),
|
|
400
|
+
cc: z.string().optional().describe('CC recipients (comma-separated)'),
|
|
401
|
+
bcc: z.string().optional().describe('BCC recipients (comma-separated)'),
|
|
402
|
+
note: z.string().optional().describe('Introductory text above the forwarded message'),
|
|
403
|
+
from: z.string().optional().describe('Send from this email address (must be a verified send-as alias)'),
|
|
404
|
+
skipAttachments: z.boolean().optional().describe('Do not include original attachments'),
|
|
405
|
+
account: accountParam,
|
|
406
|
+
},
|
|
407
|
+
}, async ({ messageId, to, cc, bcc, note, from, skipAttachments, account }) => {
|
|
408
|
+
const args = ['gmail', 'forward', messageId, `--to=${to}`];
|
|
409
|
+
if (cc) args.push(`--cc=${cc}`);
|
|
410
|
+
if (bcc) args.push(`--bcc=${bcc}`);
|
|
411
|
+
if (note) args.push(`--note=${note}`);
|
|
412
|
+
if (from) args.push(`--from=${from}`);
|
|
413
|
+
if (skipAttachments) args.push('--skip-attachments');
|
|
414
|
+
return runOrDiagnose(args, { account });
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
server.registerTool('gog_gmail_autoreply', {
|
|
418
|
+
description: 'Reply once to all messages matching a Gmail search query. Use the label flag to dedupe across runs.',
|
|
419
|
+
annotations: { destructiveHint: true },
|
|
420
|
+
inputSchema: {
|
|
421
|
+
query: z.string().describe('Gmail search query'),
|
|
422
|
+
max: z.number().optional().describe('Max matching messages to inspect (default: 20)'),
|
|
423
|
+
subject: z.string().optional().describe('Override reply subject (default: Re: original subject)'),
|
|
424
|
+
body: z.string().optional().describe('Reply body (plain text; required unless bodyHtml is set)'),
|
|
425
|
+
bodyHtml: z.string().optional().describe('Reply body HTML'),
|
|
426
|
+
from: z.string().optional().describe('Send from this email address (must be a verified send-as alias)'),
|
|
427
|
+
replyTo: z.string().optional().describe('Reply-To header address'),
|
|
428
|
+
label: z.string().optional().describe('Label to add after replying (used for dedupe; default: AutoReplied)'),
|
|
429
|
+
archive: z.boolean().optional().describe('Archive threads after auto-replying'),
|
|
430
|
+
markRead: z.boolean().optional().describe('Mark threads as read after auto-replying'),
|
|
431
|
+
skipBulk: z.boolean().optional().describe('Skip auto-generated/list mail'),
|
|
432
|
+
allowSelf: z.boolean().optional().describe('Allow replying to messages sent by your own address'),
|
|
433
|
+
account: accountParam,
|
|
434
|
+
},
|
|
435
|
+
}, async ({ query, max, subject, body, bodyHtml, from, replyTo, label, archive, markRead, skipBulk, allowSelf, account }) => {
|
|
436
|
+
const args = ['gmail', 'autoreply', query];
|
|
437
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
438
|
+
if (subject) args.push(`--subject=${subject}`);
|
|
439
|
+
if (body) args.push(`--body=${body}`);
|
|
440
|
+
if (bodyHtml) args.push(`--body-html=${bodyHtml}`);
|
|
441
|
+
if (from) args.push(`--from=${from}`);
|
|
442
|
+
if (replyTo) args.push(`--reply-to=${replyTo}`);
|
|
443
|
+
if (label) args.push(`--label=${label}`);
|
|
444
|
+
if (archive) args.push('--archive');
|
|
445
|
+
if (markRead) args.push('--mark-read');
|
|
446
|
+
if (skipBulk) args.push('--skip-bulk');
|
|
447
|
+
if (allowSelf) args.push('--allow-self');
|
|
448
|
+
return runOrDiagnose(args, { account });
|
|
449
|
+
});
|
|
450
|
+
}
|