@mindstone/mcp-server-microsoft-mail 0.1.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/dist/mail.js ADDED
@@ -0,0 +1,233 @@
1
+ const WELL_KNOWN_FOLDERS = {
2
+ inbox: 'inbox',
3
+ 'sent items': 'sentitems',
4
+ sent: 'sentitems',
5
+ 'deleted items': 'deleteditems',
6
+ trash: 'deleteditems',
7
+ 'junk email': 'junkemail',
8
+ junk: 'junkemail',
9
+ spam: 'junkemail',
10
+ drafts: 'drafts',
11
+ archive: 'archive',
12
+ outbox: 'outbox',
13
+ };
14
+ export function resolveFolder(folder) {
15
+ const trimmed = folder.trim();
16
+ return WELL_KNOWN_FOLDERS[trimmed.toLowerCase()] ?? trimmed;
17
+ }
18
+ function ensureArray(value) {
19
+ if (value == null)
20
+ return undefined;
21
+ return Array.isArray(value) ? value : [value];
22
+ }
23
+ export async function listEmails(client, args, signal) {
24
+ const folder = resolveFolder(args.folder ?? 'Inbox');
25
+ const top = Math.min(args.top ?? 25, 100);
26
+ let endpoint = `/me/mailFolders/${folder}/messages`;
27
+ const queryParams = [
28
+ `$top=${top}`,
29
+ '$select=id,subject,from,receivedDateTime,bodyPreview,isRead,hasAttachments,importance',
30
+ '$orderby=receivedDateTime desc',
31
+ ];
32
+ if (args.filter) {
33
+ queryParams.push(`$filter=${args.filter}`);
34
+ }
35
+ endpoint += '?' + queryParams.join('&');
36
+ const response = await client.api(endpoint).options({ signal }).get();
37
+ const emails = response.value ?? [];
38
+ const formatted = emails.map((email) => ({
39
+ id: email.id,
40
+ subject: email.subject,
41
+ from: email.from?.emailAddress?.address,
42
+ fromName: email.from?.emailAddress?.name,
43
+ receivedAt: email.receivedDateTime,
44
+ preview: email.bodyPreview?.substring(0, 200),
45
+ isRead: email.isRead,
46
+ hasAttachments: email.hasAttachments,
47
+ importance: email.importance,
48
+ }));
49
+ return {
50
+ count: formatted.length,
51
+ folder,
52
+ emails: formatted,
53
+ };
54
+ }
55
+ export async function getEmail(client, args, signal) {
56
+ const email = await client
57
+ .api(`/me/messages/${args.id}`)
58
+ .options({ signal })
59
+ .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,body,isRead,hasAttachments,importance')
60
+ .get();
61
+ return {
62
+ id: email.id,
63
+ subject: email.subject,
64
+ from: email.from?.emailAddress,
65
+ to: email.toRecipients?.map((r) => r.emailAddress),
66
+ cc: email.ccRecipients?.map((r) => r.emailAddress),
67
+ receivedAt: email.receivedDateTime,
68
+ body: email.body?.content,
69
+ bodyType: email.body?.contentType,
70
+ isRead: email.isRead,
71
+ hasAttachments: email.hasAttachments,
72
+ importance: email.importance,
73
+ };
74
+ }
75
+ export async function sendEmail(client, args, signal) {
76
+ const toList = ensureArray(args.to) ?? [];
77
+ const ccList = ensureArray(args.cc);
78
+ const message = {
79
+ subject: args.subject,
80
+ body: {
81
+ contentType: args.body.includes('<') ? 'HTML' : 'Text',
82
+ content: args.body,
83
+ },
84
+ toRecipients: toList.map((email) => ({
85
+ emailAddress: { address: email },
86
+ })),
87
+ ccRecipients: ccList?.map((email) => ({
88
+ emailAddress: { address: email },
89
+ })),
90
+ importance: args.importance ?? 'normal',
91
+ };
92
+ await client.api('/me/sendMail').options({ signal }).post({ message });
93
+ return {
94
+ success: true,
95
+ message: `Email sent to ${toList.join(', ')}`,
96
+ };
97
+ }
98
+ export async function searchEmails(client, args, signal) {
99
+ const top = Math.min(args.top ?? 25, 100);
100
+ // Note: Microsoft Graph API does not support $orderBy with $search.
101
+ // Search results are automatically sorted by date (newest first).
102
+ const response = await client
103
+ .api('/me/messages')
104
+ .options({ signal })
105
+ .search(`"${args.query}"`)
106
+ .top(top)
107
+ .select('id,subject,from,receivedDateTime,bodyPreview,isRead')
108
+ .get();
109
+ const emails = response.value ?? [];
110
+ const formatted = emails.map((email) => ({
111
+ id: email.id,
112
+ subject: email.subject,
113
+ from: email.from?.emailAddress?.address,
114
+ receivedAt: email.receivedDateTime,
115
+ preview: email.bodyPreview?.substring(0, 200),
116
+ isRead: email.isRead,
117
+ }));
118
+ return {
119
+ query: args.query,
120
+ count: formatted.length,
121
+ emails: formatted,
122
+ };
123
+ }
124
+ export async function replyToEmail(client, args, signal) {
125
+ const endpoint = args.replyAll
126
+ ? `/me/messages/${args.id}/replyAll`
127
+ : `/me/messages/${args.id}/reply`;
128
+ await client.api(endpoint).options({ signal }).post({
129
+ comment: args.body,
130
+ });
131
+ return {
132
+ success: true,
133
+ message: args.replyAll ? 'Reply sent to all recipients' : 'Reply sent',
134
+ };
135
+ }
136
+ export async function forwardEmail(client, args, signal) {
137
+ const toList = ensureArray(args.to) ?? [];
138
+ await client.api(`/me/messages/${args.id}/forward`).options({ signal }).post({
139
+ comment: args.comment ?? '',
140
+ toRecipients: toList.map((email) => ({
141
+ emailAddress: { address: email },
142
+ })),
143
+ });
144
+ return {
145
+ success: true,
146
+ message: `Email forwarded to ${toList.join(', ')}`,
147
+ };
148
+ }
149
+ export async function deleteEmail(client, args, signal) {
150
+ if (args.permanent) {
151
+ await client.api(`/me/messages/${args.id}`).options({ signal }).delete();
152
+ return { success: true, message: 'Email permanently deleted' };
153
+ }
154
+ await client.api(`/me/messages/${args.id}/move`).options({ signal }).post({
155
+ destinationId: 'deleteditems',
156
+ });
157
+ return { success: true, message: 'Email moved to Deleted Items' };
158
+ }
159
+ export async function listFolders(client, args, signal) {
160
+ let endpoint = '/me/mailFolders';
161
+ if (!args.includeHidden) {
162
+ endpoint += '?$filter=isHidden eq false';
163
+ }
164
+ const response = await client.api(endpoint).options({ signal }).get();
165
+ const folders = response.value ?? [];
166
+ const formatted = folders.map((folder) => ({
167
+ id: folder.id,
168
+ name: folder.displayName,
169
+ totalItems: folder.totalItemCount,
170
+ unreadItems: folder.unreadItemCount,
171
+ childFolders: folder.childFolderCount,
172
+ }));
173
+ return {
174
+ count: formatted.length,
175
+ folders: formatted,
176
+ };
177
+ }
178
+ export async function moveEmail(client, args, signal) {
179
+ const folderId = resolveFolder(args.destinationFolder);
180
+ await client.api(`/me/messages/${args.id}/move`).options({ signal }).post({
181
+ destinationId: folderId,
182
+ });
183
+ return {
184
+ success: true,
185
+ message: `Email moved to ${args.destinationFolder}`,
186
+ };
187
+ }
188
+ export async function createReplyDraft(client, args, signal) {
189
+ const endpoint = args.replyAll
190
+ ? `/me/messages/${args.id}/createReplyAll`
191
+ : `/me/messages/${args.id}/createReply`;
192
+ const requestBody = {};
193
+ if (args.body) {
194
+ requestBody.message = {
195
+ body: {
196
+ contentType: args.body.includes('<') ? 'HTML' : 'Text',
197
+ content: args.body,
198
+ },
199
+ };
200
+ }
201
+ const response = await client.api(endpoint).options({ signal }).post(requestBody);
202
+ return {
203
+ success: true,
204
+ draftId: response.id,
205
+ conversationId: response.conversationId,
206
+ subject: response.subject,
207
+ message: `Reply draft created${args.replyAll ? ' (reply-all)' : ''}. Open Outlook to review and send.`,
208
+ };
209
+ }
210
+ export async function createDraft(client, args, signal) {
211
+ const toList = ensureArray(args.to);
212
+ const ccList = ensureArray(args.cc);
213
+ const draft = {
214
+ subject: args.subject,
215
+ body: {
216
+ contentType: args.body.includes('<') ? 'HTML' : 'Text',
217
+ content: args.body,
218
+ },
219
+ toRecipients: toList?.map((email) => ({
220
+ emailAddress: { address: email },
221
+ })),
222
+ ccRecipients: ccList?.map((email) => ({
223
+ emailAddress: { address: email },
224
+ })),
225
+ };
226
+ const response = await client.api('/me/messages').options({ signal }).post(draft);
227
+ return {
228
+ success: true,
229
+ draftId: response.id,
230
+ message: 'Draft created successfully',
231
+ };
232
+ }
233
+ //# sourceMappingURL=mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createServer(): McpServer;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,YAAY,IAAI,SAAS,CAOxC"}
package/dist/server.js ADDED
@@ -0,0 +1,12 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SERVER_NAME, SERVER_VERSION } from './types.js';
3
+ import { registerMailTools } from './tools.js';
4
+ export function createServer() {
5
+ const server = new McpServer({
6
+ name: SERVER_NAME,
7
+ version: SERVER_VERSION,
8
+ });
9
+ registerMailTools(server);
10
+ return server;
11
+ }
12
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;KACxB,CAAC,CAAC;IACH,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerMailTools(server: McpServer): void;
3
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2CzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiczD"}
package/dist/tools.js ADDED
@@ -0,0 +1,359 @@
1
+ import { z } from 'zod';
2
+ import { callGraph } from './client.js';
3
+ import { authRequiredJson, errorResponse, successJson, withErrorHandling, } from './utils.js';
4
+ import { AUTH_TOOL_NAME } from './types.js';
5
+ import { createDraft, createReplyDraft, deleteEmail, forwardEmail, getEmail, listEmails, listFolders, moveEmail, replyToEmail, searchEmails, sendEmail, } from './mail.js';
6
+ const RecipientField = z.union([z.array(z.string()), z.string()]);
7
+ const ImportanceEnum = z.enum(['low', 'normal', 'high']);
8
+ // send_email accepts unknown keys so the handler can surface bundled-parity
9
+ // alias guidance (`recipient`/`recipients`, `message`/`content`/`text`)
10
+ // rather than letting Zod silently strip them.
11
+ const SendEmailSchema = z
12
+ .object({
13
+ to: RecipientField.optional().describe('Recipient email address(es). Use an array: ["alice@example.com"]'),
14
+ subject: z.string().optional().describe('Email subject'),
15
+ body: z.string().optional().describe('Email body (HTML supported)'),
16
+ cc: RecipientField.optional().describe('CC recipient(s)'),
17
+ importance: ImportanceEnum.optional().describe('Email importance'),
18
+ })
19
+ .passthrough();
20
+ export function registerMailTools(server) {
21
+ // ---------------------------------------------------------------------
22
+ // authenticate_microsoft_account
23
+ // ---------------------------------------------------------------------
24
+ server.registerTool(AUTH_TOOL_NAME, {
25
+ description: `Connect a Microsoft 365 account to enable email, calendar, files, and Teams access.
26
+
27
+ Call this tool when:
28
+ 1. Other Microsoft tools return authentication errors
29
+ 2. The user asks to connect or set up Microsoft 365
30
+
31
+ This tool returns a structured auth_required response. The host will
32
+ recognise it and dispatch the desktop OAuth flow. After the user completes
33
+ sign-in, Microsoft 365 tools become available.`,
34
+ inputSchema: z.object({}).strict().shape,
35
+ annotations: {
36
+ readOnlyHint: false,
37
+ destructiveHint: false,
38
+ openWorldHint: false,
39
+ },
40
+ }, withErrorHandling(async () => authRequiredJson()));
41
+ // ---------------------------------------------------------------------
42
+ // list_emails
43
+ // ---------------------------------------------------------------------
44
+ server.registerTool('list_emails', {
45
+ description: 'List emails from inbox or a specific folder. Returns subject, sender, date, and preview.',
46
+ inputSchema: z.object({
47
+ folder: z
48
+ .string()
49
+ .optional()
50
+ .describe('Well-known folder name (inbox, sentitems, drafts, deleteditems, junkemail, archive, outbox), display name (e.g. "Sent Items"), or folder ID from list_folders. Default: Inbox.'),
51
+ top: z.number().optional().describe('Number of emails to return (default: 25, max: 100)'),
52
+ filter: z
53
+ .string()
54
+ .optional()
55
+ .describe('OData filter expression (e.g., "isRead eq false")'),
56
+ }).shape,
57
+ annotations: {
58
+ readOnlyHint: true,
59
+ destructiveHint: false,
60
+ openWorldHint: true,
61
+ },
62
+ }, withErrorHandling(async (args, extra) => {
63
+ const result = await callGraph(extra, (c, signal) => listEmails(c, args, signal));
64
+ return successJson(result);
65
+ }));
66
+ // ---------------------------------------------------------------------
67
+ // get_email
68
+ // ---------------------------------------------------------------------
69
+ server.registerTool('get_email', {
70
+ description: 'Get full email content including body by message ID.',
71
+ inputSchema: z.object({
72
+ id: z.string().optional().describe('Email message ID'),
73
+ }).shape,
74
+ annotations: {
75
+ readOnlyHint: true,
76
+ destructiveHint: false,
77
+ openWorldHint: true,
78
+ },
79
+ }, withErrorHandling(async (args, extra) => {
80
+ if (!args.id) {
81
+ return errorResponse({
82
+ error: 'Missing required parameter: "id" (the email message ID). Example: { "id": "AAMkAGI2..." }. Use list_emails to find message IDs.',
83
+ action_required: 'Provide the message ID returned by list_emails.',
84
+ next_step: 'list_emails',
85
+ });
86
+ }
87
+ const result = await callGraph(extra, (c, signal) => getEmail(c, { id: args.id }, signal));
88
+ return successJson(result);
89
+ }));
90
+ // ---------------------------------------------------------------------
91
+ // send_email
92
+ // ---------------------------------------------------------------------
93
+ server.registerTool('send_email', {
94
+ description: 'Send a new email message. "to" and "cc" accept a string or an array of strings. Prefer arrays for multiple recipients (e.g. ["alice@example.com"]).',
95
+ inputSchema: SendEmailSchema,
96
+ annotations: {
97
+ readOnlyHint: false,
98
+ destructiveHint: false,
99
+ openWorldHint: true,
100
+ },
101
+ }, withErrorHandling(async (args, extra) => {
102
+ if ('recipient' in args || 'recipients' in args) {
103
+ return errorResponse({
104
+ error: 'Invalid parameter: Use "to" instead of "recipient"/"recipients". Example: { "to": ["alice@example.com"], "subject": "Hello", "body": "Message content" }',
105
+ action_required: 'Use the "to" parameter (string or array of strings).',
106
+ next_step: 'send_email',
107
+ });
108
+ }
109
+ if ('message' in args || 'content' in args || 'text' in args) {
110
+ return errorResponse({
111
+ error: 'Invalid parameter: Use "body" instead of "message"/"content"/"text". Example: { "to": ["alice@example.com"], "subject": "Hello", "body": "Message content" }',
112
+ action_required: 'Use the "body" parameter to provide the email content.',
113
+ next_step: 'send_email',
114
+ });
115
+ }
116
+ const toList = Array.isArray(args.to) ? args.to : args.to ? [args.to] : [];
117
+ if (!toList.length || !args.subject || !args.body) {
118
+ return errorResponse({
119
+ error: 'Missing required parameters. Required: "to" (string or array), "subject" (string), "body" (string). Example: { "to": ["recipient@example.com"], "subject": "Meeting Tomorrow", "body": "Hi, let\'s meet at 3pm." }',
120
+ action_required: 'Provide to, subject, and body fields.',
121
+ next_step: 'send_email',
122
+ });
123
+ }
124
+ const result = await callGraph(extra, (c, signal) => sendEmail(c, {
125
+ to: args.to,
126
+ subject: args.subject,
127
+ body: args.body,
128
+ cc: args.cc,
129
+ importance: args.importance,
130
+ }, signal));
131
+ return successJson(result);
132
+ }));
133
+ // ---------------------------------------------------------------------
134
+ // search_emails
135
+ // ---------------------------------------------------------------------
136
+ server.registerTool('search_emails', {
137
+ description: 'Search emails using Microsoft Search query syntax.',
138
+ inputSchema: z.object({
139
+ query: z
140
+ .string()
141
+ .optional()
142
+ .describe('Search query (e.g., "from:john subject:meeting")'),
143
+ top: z.number().optional().describe('Number of results (default: 25)'),
144
+ }).shape,
145
+ annotations: {
146
+ readOnlyHint: true,
147
+ destructiveHint: false,
148
+ openWorldHint: true,
149
+ },
150
+ }, withErrorHandling(async (args, extra) => {
151
+ if (!args.query) {
152
+ return errorResponse({
153
+ error: 'Missing required parameter: "query" (search text). Example: { "query": "project update", "top": 20 }',
154
+ action_required: 'Provide a non-empty query string.',
155
+ next_step: 'search_emails',
156
+ });
157
+ }
158
+ const result = await callGraph(extra, (c, signal) => searchEmails(c, { query: args.query, top: args.top }, signal));
159
+ return successJson(result);
160
+ }));
161
+ // ---------------------------------------------------------------------
162
+ // reply_to_email
163
+ // ---------------------------------------------------------------------
164
+ server.registerTool('reply_to_email', {
165
+ description: 'Reply to an email message.',
166
+ inputSchema: z.object({
167
+ id: z.string().optional().describe('Original email message ID'),
168
+ body: z.string().optional().describe('Reply body (HTML supported)'),
169
+ replyAll: z
170
+ .boolean()
171
+ .optional()
172
+ .describe('Reply to all recipients (default: false)'),
173
+ }).shape,
174
+ annotations: {
175
+ readOnlyHint: false,
176
+ destructiveHint: false,
177
+ openWorldHint: true,
178
+ },
179
+ }, withErrorHandling(async (args, extra) => {
180
+ if (!args.id || !args.body) {
181
+ return errorResponse({
182
+ error: 'Missing required parameters: "id" (email to reply to) and "body" (reply content). Example: { "id": "AAMkAGI2...", "body": "Thanks for your message!", "replyAll": false }',
183
+ action_required: 'Provide both id and body.',
184
+ next_step: 'reply_to_email',
185
+ });
186
+ }
187
+ const result = await callGraph(extra, (c, signal) => replyToEmail(c, { id: args.id, body: args.body, replyAll: args.replyAll }, signal));
188
+ return successJson(result);
189
+ }));
190
+ // ---------------------------------------------------------------------
191
+ // forward_email
192
+ // ---------------------------------------------------------------------
193
+ server.registerTool('forward_email', {
194
+ description: 'Forward an email to other recipients.',
195
+ inputSchema: z.object({
196
+ id: z.string().optional().describe('Email message ID to forward'),
197
+ to: RecipientField.optional().describe('Recipient(s) to forward to'),
198
+ comment: z.string().optional().describe('Optional comment to add'),
199
+ }).shape,
200
+ annotations: {
201
+ readOnlyHint: false,
202
+ destructiveHint: false,
203
+ openWorldHint: true,
204
+ },
205
+ }, withErrorHandling(async (args, extra) => {
206
+ const toList = Array.isArray(args.to) ? args.to : args.to ? [args.to] : [];
207
+ if (!args.id || !toList.length) {
208
+ return errorResponse({
209
+ error: 'Missing required parameters: "id" (email to forward) and "to" (string or array). Example: { "id": "AAMkAGI2...", "to": ["colleague@example.com"], "comment": "FYI" }',
210
+ action_required: 'Provide both id and to.',
211
+ next_step: 'forward_email',
212
+ });
213
+ }
214
+ const result = await callGraph(extra, (c, signal) => forwardEmail(c, { id: args.id, to: args.to, comment: args.comment }, signal));
215
+ return successJson(result);
216
+ }));
217
+ // ---------------------------------------------------------------------
218
+ // delete_email
219
+ // ---------------------------------------------------------------------
220
+ server.registerTool('delete_email', {
221
+ description: 'Delete or move an email to trash.',
222
+ inputSchema: z.object({
223
+ id: z.string().optional().describe('Email message ID'),
224
+ permanent: z
225
+ .boolean()
226
+ .optional()
227
+ .describe('Permanently delete (default: false, moves to Deleted Items)'),
228
+ }).shape,
229
+ annotations: {
230
+ readOnlyHint: false,
231
+ destructiveHint: true,
232
+ openWorldHint: true,
233
+ },
234
+ }, withErrorHandling(async (args, extra) => {
235
+ if (!args.id) {
236
+ return errorResponse({
237
+ error: 'Missing required parameter: "id" (email to delete). Example: { "id": "AAMkAGI2...", "permanent": false }. Set "permanent": true to permanently delete instead of moving to Deleted Items.',
238
+ action_required: 'Provide the message ID.',
239
+ next_step: 'delete_email',
240
+ });
241
+ }
242
+ const result = await callGraph(extra, (c, signal) => deleteEmail(c, { id: args.id, permanent: args.permanent }, signal));
243
+ return successJson(result);
244
+ }));
245
+ // ---------------------------------------------------------------------
246
+ // list_folders
247
+ // ---------------------------------------------------------------------
248
+ server.registerTool('list_folders', {
249
+ description: 'List mail folders (Inbox, Sent, Drafts, etc.). Returns folder IDs that can be used with list_emails and move_email.',
250
+ inputSchema: z.object({
251
+ includeHidden: z
252
+ .boolean()
253
+ .optional()
254
+ .describe('Include hidden folders (default: false)'),
255
+ }).shape,
256
+ annotations: {
257
+ readOnlyHint: true,
258
+ destructiveHint: false,
259
+ openWorldHint: true,
260
+ },
261
+ }, withErrorHandling(async (args, extra) => {
262
+ const result = await callGraph(extra, (c, signal) => listFolders(c, args, signal));
263
+ return successJson(result);
264
+ }));
265
+ // ---------------------------------------------------------------------
266
+ // move_email
267
+ // ---------------------------------------------------------------------
268
+ server.registerTool('move_email', {
269
+ description: 'Move an email to a different folder.',
270
+ inputSchema: z.object({
271
+ id: z.string().optional().describe('Email message ID'),
272
+ destinationFolder: z
273
+ .string()
274
+ .optional()
275
+ .describe('Well-known folder name (inbox, sentitems, drafts, deleteditems, junkemail, archive, outbox), display name, or folder ID from list_folders'),
276
+ }).shape,
277
+ annotations: {
278
+ readOnlyHint: false,
279
+ destructiveHint: false,
280
+ idempotentHint: true,
281
+ openWorldHint: true,
282
+ },
283
+ }, withErrorHandling(async (args, extra) => {
284
+ if (!args.id || !args.destinationFolder) {
285
+ return errorResponse({
286
+ error: 'Missing required parameters: "id" (email to move) and "destinationFolder" (target folder). Example: { "id": "AAMkAGI2...", "destinationFolder": "inbox" }. Use list_folders to find folder IDs.',
287
+ action_required: 'Provide id and destinationFolder.',
288
+ next_step: 'list_folders',
289
+ });
290
+ }
291
+ const result = await callGraph(extra, (c, signal) => moveEmail(c, { id: args.id, destinationFolder: args.destinationFolder }, signal));
292
+ return successJson(result);
293
+ }));
294
+ // ---------------------------------------------------------------------
295
+ // create_reply_draft
296
+ // ---------------------------------------------------------------------
297
+ server.registerTool('create_reply_draft', {
298
+ description: 'Create a draft reply to an existing email, threaded in the same conversation. The draft is saved in Drafts and can be reviewed in Outlook before sending.',
299
+ inputSchema: z.object({
300
+ id: z.string().optional().describe('Original email message ID to reply to'),
301
+ body: z
302
+ .string()
303
+ .optional()
304
+ .describe('Reply body (HTML supported). If omitted, creates a blank reply draft.'),
305
+ replyAll: z
306
+ .boolean()
307
+ .optional()
308
+ .describe('Reply to all recipients (default: false)'),
309
+ }).shape,
310
+ annotations: {
311
+ readOnlyHint: false,
312
+ destructiveHint: false,
313
+ openWorldHint: true,
314
+ },
315
+ }, withErrorHandling(async (args, extra) => {
316
+ if (!args.id) {
317
+ return errorResponse({
318
+ error: 'Missing required parameter: "id" (the email message ID to reply to). Example: { "id": "AAMkAGI2...", "body": "Thanks for your message!" }',
319
+ action_required: 'Provide the message ID.',
320
+ next_step: 'list_emails',
321
+ });
322
+ }
323
+ const result = await callGraph(extra, (c, signal) => createReplyDraft(c, { id: args.id, body: args.body, replyAll: args.replyAll }, signal));
324
+ return successJson(result);
325
+ }));
326
+ // ---------------------------------------------------------------------
327
+ // create_draft
328
+ // ---------------------------------------------------------------------
329
+ server.registerTool('create_draft', {
330
+ description: 'Create a new standalone draft email (saved but not sent). For replying to an existing thread, use create_reply_draft instead.',
331
+ inputSchema: z.object({
332
+ to: RecipientField.optional().describe('Recipient email address(es)'),
333
+ subject: z.string().optional().describe('Email subject'),
334
+ body: z.string().optional().describe('Email body (HTML supported)'),
335
+ cc: RecipientField.optional().describe('CC recipient(s)'),
336
+ }).shape,
337
+ annotations: {
338
+ readOnlyHint: false,
339
+ destructiveHint: false,
340
+ openWorldHint: true,
341
+ },
342
+ }, withErrorHandling(async (args, extra) => {
343
+ if (!args.subject || !args.body) {
344
+ return errorResponse({
345
+ error: 'Missing required parameters: "subject" and "body". Example: { "to": ["recipient@example.com"], "subject": "Draft Email", "body": "Content here..." }',
346
+ action_required: 'Provide subject and body.',
347
+ next_step: 'create_draft',
348
+ });
349
+ }
350
+ const result = await callGraph(extra, (c, signal) => createDraft(c, {
351
+ to: args.to,
352
+ subject: args.subject,
353
+ body: args.body,
354
+ cc: args.cc,
355
+ }, signal));
356
+ return successJson(result);
357
+ }));
358
+ }
359
+ //# sourceMappingURL=tools.js.map