@joshluedeman/m365-mcp 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/index.js ADDED
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ acquireToken,
4
+ runAuthCommand
5
+ } from "./chunk-JEMHJMEL.js";
6
+
7
+ // src/index.ts
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+
10
+ // src/server.ts
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+
13
+ // src/tools/tasks/schemas.ts
14
+ import { z } from "zod";
15
+ var ListTasksSchema = {
16
+ listId: z.string().describe("The ID of the To Do task list"),
17
+ filter: z.string().optional().describe(`OData $filter expression (e.g. "status eq 'notStarted'")`),
18
+ top: z.number().int().min(1).max(100).optional().describe("Maximum number of tasks to return (1-100)")
19
+ };
20
+ var GetTaskSchema = {
21
+ listId: z.string().describe("The ID of the To Do task list"),
22
+ taskId: z.string().describe("The ID of the task")
23
+ };
24
+ var CreateTaskSchema = {
25
+ listId: z.string().describe("The ID of the To Do task list"),
26
+ title: z.string().describe("Task title"),
27
+ body: z.string().optional().describe("Task body / notes (plain text)"),
28
+ dueDate: z.string().optional().describe("Due date in ISO 8601 format (e.g. 2026-06-01)"),
29
+ importance: z.enum(["low", "normal", "high"]).optional().describe("Task importance level")
30
+ };
31
+ var UpdateTaskSchema = {
32
+ listId: z.string().describe("The ID of the To Do task list"),
33
+ taskId: z.string().describe("The ID of the task"),
34
+ title: z.string().optional().describe("Updated task title"),
35
+ body: z.string().optional().describe("Updated task body / notes"),
36
+ dueDate: z.string().optional().describe("Updated due date in ISO 8601 format"),
37
+ importance: z.enum(["low", "normal", "high"]).optional().describe("Updated importance level")
38
+ };
39
+ var CompleteTaskSchema = {
40
+ listId: z.string().describe("The ID of the To Do task list"),
41
+ taskId: z.string().describe("The ID of the task to mark complete")
42
+ };
43
+ var DeleteTaskSchema = {
44
+ listId: z.string().describe("The ID of the To Do task list"),
45
+ taskId: z.string().describe("The ID of the task to delete")
46
+ };
47
+
48
+ // src/graph/client.ts
49
+ import { Client } from "@microsoft/microsoft-graph-client";
50
+ function getGraphClient(accessToken) {
51
+ const authProvider = {
52
+ getAccessToken: async () => accessToken
53
+ };
54
+ return Client.initWithMiddleware({ authProvider });
55
+ }
56
+
57
+ // src/utils/pagination.ts
58
+ async function collectAllPages(client, url) {
59
+ const results = [];
60
+ let nextUrl = url;
61
+ while (nextUrl !== void 0) {
62
+ const response = await client.api(nextUrl).get();
63
+ if (Array.isArray(response.value)) {
64
+ results.push(...response.value);
65
+ }
66
+ nextUrl = response["@odata.nextLink"];
67
+ }
68
+ return results;
69
+ }
70
+
71
+ // src/utils/formatting.ts
72
+ function formatResponse(data) {
73
+ return {
74
+ content: [
75
+ {
76
+ type: "text",
77
+ text: JSON.stringify(data, null, 2)
78
+ }
79
+ ]
80
+ };
81
+ }
82
+ function formatError(message) {
83
+ return {
84
+ content: [
85
+ {
86
+ type: "text",
87
+ text: message
88
+ }
89
+ ],
90
+ isError: true
91
+ };
92
+ }
93
+
94
+ // src/utils/errors.ts
95
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
96
+ function parseGraphError(err) {
97
+ if (err instanceof Error) {
98
+ const graphErr = err;
99
+ if (graphErr.statusCode === 429) {
100
+ let retryAfter = "unknown";
101
+ try {
102
+ if (typeof graphErr.body === "string") {
103
+ const parsed = JSON.parse(graphErr.body);
104
+ retryAfter = parsed.error?.innerError?.["retry-after"] ?? "unknown";
105
+ } else if (typeof graphErr.body === "object" && graphErr.body !== null) {
106
+ retryAfter = graphErr.body.error?.innerError?.["retry-after"] ?? "unknown";
107
+ }
108
+ } catch {
109
+ }
110
+ return `Graph API rate limit exceeded (429). Retry after ${retryAfter} seconds.`;
111
+ }
112
+ if (graphErr.statusCode !== void 0 && graphErr.body !== void 0) {
113
+ try {
114
+ const body = typeof graphErr.body === "string" ? JSON.parse(graphErr.body) : graphErr.body;
115
+ const code = body.error?.code ?? "UnknownError";
116
+ const message = body.error?.message ?? graphErr.message;
117
+ return `Graph API error [${graphErr.statusCode}] ${code}: ${message}`;
118
+ } catch {
119
+ return `Graph API error [${graphErr.statusCode}]: ${graphErr.message}`;
120
+ }
121
+ }
122
+ return `Error: ${err.message}`;
123
+ }
124
+ return `Unexpected error: ${String(err)}`;
125
+ }
126
+
127
+ // src/tools/tasks/handlers.ts
128
+ async function handleListTaskLists() {
129
+ try {
130
+ const token = await acquireToken();
131
+ const client = getGraphClient(token);
132
+ const lists = await collectAllPages(client, "/me/todo/lists");
133
+ return formatResponse(lists);
134
+ } catch (err) {
135
+ return formatError(parseGraphError(err));
136
+ }
137
+ }
138
+ async function handleListTasks(listId, filter, top) {
139
+ try {
140
+ const token = await acquireToken();
141
+ const client = getGraphClient(token);
142
+ let request = client.api(`/me/todo/lists/${listId}/tasks`);
143
+ if (filter !== void 0) {
144
+ request = request.filter(filter);
145
+ }
146
+ if (top !== void 0) {
147
+ request = request.top(top);
148
+ }
149
+ const response = await request.get();
150
+ const tasks = response.value ?? [];
151
+ if (top === void 0 && response["@odata.nextLink"] !== void 0) {
152
+ const remaining = await collectAllPages(
153
+ client,
154
+ response["@odata.nextLink"]
155
+ );
156
+ tasks.push(...remaining);
157
+ }
158
+ return formatResponse(tasks);
159
+ } catch (err) {
160
+ return formatError(parseGraphError(err));
161
+ }
162
+ }
163
+ async function handleGetTask(listId, taskId) {
164
+ try {
165
+ const token = await acquireToken();
166
+ const client = getGraphClient(token);
167
+ const task = await client.api(`/me/todo/lists/${listId}/tasks/${taskId}`).get();
168
+ return formatResponse(task);
169
+ } catch (err) {
170
+ return formatError(parseGraphError(err));
171
+ }
172
+ }
173
+ async function handleCreateTask(params) {
174
+ try {
175
+ const token = await acquireToken();
176
+ const client = getGraphClient(token);
177
+ const body = {
178
+ title: params.title
179
+ };
180
+ if (params.body !== void 0) {
181
+ body.body = { content: params.body, contentType: "text" };
182
+ }
183
+ if (params.dueDate !== void 0) {
184
+ body.dueDateTime = { dateTime: `${params.dueDate}T00:00:00`, timeZone: "UTC" };
185
+ }
186
+ if (params.importance !== void 0) {
187
+ body.importance = params.importance;
188
+ }
189
+ const created = await client.api(`/me/todo/lists/${params.listId}/tasks`).post(body);
190
+ return formatResponse(created);
191
+ } catch (err) {
192
+ return formatError(parseGraphError(err));
193
+ }
194
+ }
195
+ async function handleUpdateTask(params) {
196
+ try {
197
+ const token = await acquireToken();
198
+ const client = getGraphClient(token);
199
+ const patch = {};
200
+ if (params.title !== void 0) patch.title = params.title;
201
+ if (params.body !== void 0) patch.body = { content: params.body, contentType: "text" };
202
+ if (params.dueDate !== void 0) {
203
+ patch.dueDateTime = { dateTime: `${params.dueDate}T00:00:00`, timeZone: "UTC" };
204
+ }
205
+ if (params.importance !== void 0) patch.importance = params.importance;
206
+ const updated = await client.api(`/me/todo/lists/${params.listId}/tasks/${params.taskId}`).patch(patch);
207
+ return formatResponse(updated);
208
+ } catch (err) {
209
+ return formatError(parseGraphError(err));
210
+ }
211
+ }
212
+ async function handleCompleteTask(listId, taskId) {
213
+ try {
214
+ const token = await acquireToken();
215
+ const client = getGraphClient(token);
216
+ const updated = await client.api(`/me/todo/lists/${listId}/tasks/${taskId}`).patch({ status: "completed" });
217
+ return formatResponse(updated);
218
+ } catch (err) {
219
+ return formatError(parseGraphError(err));
220
+ }
221
+ }
222
+ async function handleDeleteTask(listId, taskId) {
223
+ try {
224
+ const token = await acquireToken();
225
+ const client = getGraphClient(token);
226
+ await client.api(`/me/todo/lists/${listId}/tasks/${taskId}`).delete();
227
+ return formatResponse({ deleted: true, taskId });
228
+ } catch (err) {
229
+ return formatError(parseGraphError(err));
230
+ }
231
+ }
232
+
233
+ // src/tools/tasks/index.ts
234
+ function register(server) {
235
+ server.tool(
236
+ "list_task_lists",
237
+ "List all Microsoft To Do task lists for the signed-in user",
238
+ {},
239
+ async () => handleListTaskLists()
240
+ );
241
+ server.tool(
242
+ "list_tasks",
243
+ "List tasks in a specific To Do task list, with optional filter and limit",
244
+ ListTasksSchema,
245
+ async ({ listId, filter, top }) => handleListTasks(listId, filter, top)
246
+ );
247
+ server.tool(
248
+ "get_task",
249
+ "Get a single To Do task by list ID and task ID",
250
+ GetTaskSchema,
251
+ async ({ listId, taskId }) => handleGetTask(listId, taskId)
252
+ );
253
+ server.tool(
254
+ "create_task",
255
+ "Create a new task in a To Do task list",
256
+ CreateTaskSchema,
257
+ async (params) => handleCreateTask(params)
258
+ );
259
+ server.tool(
260
+ "update_task",
261
+ "Update an existing To Do task (title, body, due date, importance)",
262
+ UpdateTaskSchema,
263
+ async (params) => handleUpdateTask(params)
264
+ );
265
+ server.tool(
266
+ "complete_task",
267
+ "Mark a To Do task as completed",
268
+ CompleteTaskSchema,
269
+ async ({ listId, taskId }) => handleCompleteTask(listId, taskId)
270
+ );
271
+ server.tool(
272
+ "delete_task",
273
+ "Delete a To Do task permanently",
274
+ DeleteTaskSchema,
275
+ async ({ listId, taskId }) => handleDeleteTask(listId, taskId)
276
+ );
277
+ }
278
+
279
+ // src/tools/calendar/schemas.ts
280
+ import { z as z2 } from "zod";
281
+ var SearchEventsSchema = {
282
+ query: z2.string().optional().describe("Search query string for event subject/content"),
283
+ startDate: z2.string().optional().describe("Filter events starting on or after this date (ISO 8601, e.g. 2026-06-01)"),
284
+ endDate: z2.string().optional().describe("Filter events ending on or before this date (ISO 8601, e.g. 2026-06-30)"),
285
+ top: z2.number().int().min(1).max(100).optional().describe("Maximum number of events to return (1-100)")
286
+ };
287
+ var GetEventSchema = {
288
+ eventId: z2.string().describe("The ID of the calendar event")
289
+ };
290
+ var CreateEventSchema = {
291
+ subject: z2.string().describe("Event subject / title"),
292
+ start: z2.string().describe("Event start time in ISO 8601 format (e.g. 2026-06-01T10:00:00)"),
293
+ end: z2.string().describe("Event end time in ISO 8601 format (e.g. 2026-06-01T11:00:00)"),
294
+ timeZone: z2.string().optional().default("UTC").describe("IANA time zone for start/end (default: UTC)"),
295
+ attendees: z2.array(z2.string()).optional().describe("List of attendee email addresses"),
296
+ body: z2.string().optional().describe("Event body / description (HTML or plain text)"),
297
+ location: z2.string().optional().describe("Event location"),
298
+ isTeamsMeeting: z2.boolean().optional().describe("If true, create a Teams online meeting link")
299
+ };
300
+ var UpdateEventSchema = {
301
+ eventId: z2.string().describe("The ID of the calendar event to update"),
302
+ subject: z2.string().optional().describe("Updated event subject"),
303
+ start: z2.string().optional().describe("Updated start time in ISO 8601 format"),
304
+ end: z2.string().optional().describe("Updated end time in ISO 8601 format"),
305
+ timeZone: z2.string().optional().describe("IANA time zone for updated start/end"),
306
+ attendees: z2.array(z2.string()).optional().describe("Updated list of attendee email addresses"),
307
+ body: z2.string().optional().describe("Updated event body"),
308
+ location: z2.string().optional().describe("Updated event location")
309
+ };
310
+ var FindAvailabilitySchema = {
311
+ emails: z2.array(z2.string()).describe("List of email addresses to check availability for"),
312
+ startTime: z2.string().describe("Start of the availability window in ISO 8601 format"),
313
+ endTime: z2.string().describe("End of the availability window in ISO 8601 format"),
314
+ timeZone: z2.string().optional().default("UTC").describe("IANA time zone for the window (default: UTC)"),
315
+ intervalMinutes: z2.number().int().min(5).max(1440).optional().default(30).describe("Schedule slot interval in minutes (default: 30)")
316
+ };
317
+
318
+ // src/tools/calendar/handlers.ts
319
+ async function handleSearchEvents(query, startDate, endDate, top) {
320
+ try {
321
+ const token = await acquireToken();
322
+ const client = getGraphClient(token);
323
+ let request = client.api("/me/events");
324
+ if (query !== void 0) {
325
+ request = request.search(`"${query}"`);
326
+ }
327
+ const filters = [];
328
+ if (startDate !== void 0) {
329
+ filters.push(`start/dateTime ge '${startDate}T00:00:00Z'`);
330
+ }
331
+ if (endDate !== void 0) {
332
+ filters.push(`end/dateTime le '${endDate}T23:59:59Z'`);
333
+ }
334
+ if (filters.length > 0) {
335
+ request = request.filter(filters.join(" and "));
336
+ }
337
+ if (top !== void 0) {
338
+ request = request.top(top);
339
+ }
340
+ request = request.select("id,subject,start,end,location,attendees,bodyPreview,isOnlineMeeting,onlineMeetingUrl");
341
+ const response = await request.get();
342
+ return formatResponse(response.value ?? []);
343
+ } catch (err) {
344
+ return formatError(parseGraphError(err));
345
+ }
346
+ }
347
+ async function handleGetEvent(eventId) {
348
+ try {
349
+ const token = await acquireToken();
350
+ const client = getGraphClient(token);
351
+ const event = await client.api(`/me/events/${eventId}`).get();
352
+ return formatResponse(event);
353
+ } catch (err) {
354
+ return formatError(parseGraphError(err));
355
+ }
356
+ }
357
+ async function handleCreateEvent(params) {
358
+ try {
359
+ const token = await acquireToken();
360
+ const client = getGraphClient(token);
361
+ const tz = params.timeZone ?? "UTC";
362
+ const eventBody = {
363
+ subject: params.subject,
364
+ start: { dateTime: params.start, timeZone: tz },
365
+ end: { dateTime: params.end, timeZone: tz }
366
+ };
367
+ if (params.body !== void 0) {
368
+ eventBody.body = { contentType: "text", content: params.body };
369
+ }
370
+ if (params.location !== void 0) {
371
+ eventBody.location = { displayName: params.location };
372
+ }
373
+ if (params.attendees !== void 0 && params.attendees.length > 0) {
374
+ eventBody.attendees = params.attendees.map((email) => ({
375
+ emailAddress: { address: email },
376
+ type: "required"
377
+ }));
378
+ }
379
+ if (params.isTeamsMeeting === true) {
380
+ eventBody.isOnlineMeeting = true;
381
+ }
382
+ const created = await client.api("/me/events").post(eventBody);
383
+ return formatResponse(created);
384
+ } catch (err) {
385
+ return formatError(parseGraphError(err));
386
+ }
387
+ }
388
+ async function handleUpdateEvent(params) {
389
+ try {
390
+ const token = await acquireToken();
391
+ const client = getGraphClient(token);
392
+ const patch = {};
393
+ const tz = params.timeZone ?? "UTC";
394
+ if (params.subject !== void 0) patch.subject = params.subject;
395
+ if (params.start !== void 0) patch.start = { dateTime: params.start, timeZone: tz };
396
+ if (params.end !== void 0) patch.end = { dateTime: params.end, timeZone: tz };
397
+ if (params.body !== void 0) patch.body = { contentType: "text", content: params.body };
398
+ if (params.location !== void 0) patch.location = { displayName: params.location };
399
+ if (params.attendees !== void 0) {
400
+ patch.attendees = params.attendees.map((email) => ({
401
+ emailAddress: { address: email },
402
+ type: "required"
403
+ }));
404
+ }
405
+ const updated = await client.api(`/me/events/${params.eventId}`).patch(patch);
406
+ return formatResponse(updated);
407
+ } catch (err) {
408
+ return formatError(parseGraphError(err));
409
+ }
410
+ }
411
+ async function handleFindAvailability(params) {
412
+ try {
413
+ const token = await acquireToken();
414
+ const client = getGraphClient(token);
415
+ const tz = params.timeZone ?? "UTC";
416
+ const interval = params.intervalMinutes ?? 30;
417
+ const requestBody = {
418
+ schedules: params.emails,
419
+ startTime: { dateTime: params.startTime, timeZone: tz },
420
+ endTime: { dateTime: params.endTime, timeZone: tz },
421
+ availabilityViewInterval: interval
422
+ };
423
+ const response = await client.api("/me/calendar/getSchedule").post(requestBody);
424
+ return formatResponse(response.value ?? []);
425
+ } catch (err) {
426
+ return formatError(parseGraphError(err));
427
+ }
428
+ }
429
+
430
+ // src/tools/calendar/index.ts
431
+ function register2(server) {
432
+ server.tool(
433
+ "search_events",
434
+ "Search calendar events by query, date range, or both",
435
+ SearchEventsSchema,
436
+ async ({ query, startDate, endDate, top }) => handleSearchEvents(query, startDate, endDate, top)
437
+ );
438
+ server.tool(
439
+ "get_event",
440
+ "Get a single calendar event by ID",
441
+ GetEventSchema,
442
+ async ({ eventId }) => handleGetEvent(eventId)
443
+ );
444
+ server.tool(
445
+ "create_event",
446
+ "Create a new calendar event, optionally with attendees and a Teams meeting link",
447
+ CreateEventSchema,
448
+ async (params) => handleCreateEvent(params)
449
+ );
450
+ server.tool(
451
+ "update_event",
452
+ "Update an existing calendar event (subject, times, attendees, body, location)",
453
+ UpdateEventSchema,
454
+ async (params) => handleUpdateEvent(params)
455
+ );
456
+ server.tool(
457
+ "find_availability",
458
+ "Check free/busy availability for a list of people over a time window",
459
+ FindAvailabilitySchema,
460
+ async (params) => handleFindAvailability(params)
461
+ );
462
+ }
463
+
464
+ // src/tools/mail/schemas.ts
465
+ import { z as z3 } from "zod";
466
+ var SearchEmailsSchema = {
467
+ query: z3.string().optional().describe("Full-text search query across email subject and body"),
468
+ from: z3.string().optional().describe("Filter by sender email address"),
469
+ startDate: z3.string().optional().describe("Filter emails received on or after this date (ISO 8601)"),
470
+ endDate: z3.string().optional().describe("Filter emails received on or before this date (ISO 8601)"),
471
+ folder: z3.string().optional().describe('Mail folder name or well-known name (e.g. "inbox", "sentitems", "drafts")'),
472
+ top: z3.number().int().min(1).max(100).optional().describe("Maximum number of emails to return (1-100)")
473
+ };
474
+ var ReadEmailSchema = {
475
+ messageId: z3.string().describe("The ID of the email message")
476
+ };
477
+ var SendEmailSchema = {
478
+ to: z3.array(z3.string()).describe("List of recipient email addresses"),
479
+ subject: z3.string().describe("Email subject"),
480
+ body: z3.string().describe("Email body content"),
481
+ cc: z3.array(z3.string()).optional().describe("CC recipient email addresses"),
482
+ bcc: z3.array(z3.string()).optional().describe("BCC recipient email addresses"),
483
+ contentType: z3.enum(["text", "html"]).optional().default("text").describe("Body content type (default: text)")
484
+ };
485
+ var FlagEmailSchema = {
486
+ messageId: z3.string().describe("The ID of the email message"),
487
+ flagStatus: z3.enum(["flagged", "complete", "notFlagged"]).describe("Flag status to set on the message")
488
+ };
489
+ var MoveEmailSchema = {
490
+ messageId: z3.string().describe("The ID of the email message to move"),
491
+ destinationFolderId: z3.string().describe("ID or well-known folder name of the destination folder")
492
+ };
493
+
494
+ // src/tools/mail/handlers.ts
495
+ async function handleSearchEmails(params) {
496
+ try {
497
+ const token = await acquireToken();
498
+ const client = getGraphClient(token);
499
+ const folderSegment = params.folder !== void 0 ? `/me/mailFolders/${params.folder}/messages` : "/me/messages";
500
+ let request = client.api(folderSegment);
501
+ if (params.query !== void 0) {
502
+ request = request.search(`"${params.query}"`);
503
+ }
504
+ const filters = [];
505
+ if (params.from !== void 0) {
506
+ filters.push(`from/emailAddress/address eq '${params.from}'`);
507
+ }
508
+ if (params.startDate !== void 0) {
509
+ filters.push(`receivedDateTime ge ${params.startDate}T00:00:00Z`);
510
+ }
511
+ if (params.endDate !== void 0) {
512
+ filters.push(`receivedDateTime le ${params.endDate}T23:59:59Z`);
513
+ }
514
+ if (filters.length > 0) {
515
+ request = request.filter(filters.join(" and "));
516
+ }
517
+ if (params.top !== void 0) {
518
+ request = request.top(params.top);
519
+ }
520
+ request = request.select("id,subject,from,receivedDateTime,bodyPreview,isRead,flag,hasAttachments");
521
+ const response = await request.get();
522
+ return formatResponse(response.value ?? []);
523
+ } catch (err) {
524
+ return formatError(parseGraphError(err));
525
+ }
526
+ }
527
+ async function handleReadEmail(messageId) {
528
+ try {
529
+ const token = await acquireToken();
530
+ const client = getGraphClient(token);
531
+ const message = await client.api(`/me/messages/${messageId}`).select("id,subject,from,toRecipients,ccRecipients,receivedDateTime,body,isRead,flag,hasAttachments").get();
532
+ return formatResponse(message);
533
+ } catch (err) {
534
+ return formatError(parseGraphError(err));
535
+ }
536
+ }
537
+ function toRecipients(emails) {
538
+ return emails.map((address) => ({ emailAddress: { address } }));
539
+ }
540
+ async function handleSendEmail(params) {
541
+ try {
542
+ const token = await acquireToken();
543
+ const client = getGraphClient(token);
544
+ const contentType = params.contentType ?? "text";
545
+ const message = {
546
+ message: {
547
+ subject: params.subject,
548
+ body: { contentType, content: params.body },
549
+ toRecipients: toRecipients(params.to),
550
+ ...params.cc !== void 0 && { ccRecipients: toRecipients(params.cc) },
551
+ ...params.bcc !== void 0 && { bccRecipients: toRecipients(params.bcc) }
552
+ }
553
+ };
554
+ await client.api("/me/sendMail").post(message);
555
+ return formatResponse({ sent: true, subject: params.subject });
556
+ } catch (err) {
557
+ return formatError(parseGraphError(err));
558
+ }
559
+ }
560
+ async function handleFlagEmail(messageId, flagStatus) {
561
+ try {
562
+ const token = await acquireToken();
563
+ const client = getGraphClient(token);
564
+ const updated = await client.api(`/me/messages/${messageId}`).patch({ flag: { flagStatus } });
565
+ return formatResponse(updated);
566
+ } catch (err) {
567
+ return formatError(parseGraphError(err));
568
+ }
569
+ }
570
+ async function handleListMailFolders() {
571
+ try {
572
+ const token = await acquireToken();
573
+ const client = getGraphClient(token);
574
+ const folders = await collectAllPages(client, "/me/mailFolders");
575
+ return formatResponse(folders);
576
+ } catch (err) {
577
+ return formatError(parseGraphError(err));
578
+ }
579
+ }
580
+ async function handleMoveEmail(messageId, destinationFolderId) {
581
+ try {
582
+ const token = await acquireToken();
583
+ const client = getGraphClient(token);
584
+ const moved = await client.api(`/me/messages/${messageId}/move`).post({ destinationId: destinationFolderId });
585
+ return formatResponse(moved);
586
+ } catch (err) {
587
+ return formatError(parseGraphError(err));
588
+ }
589
+ }
590
+
591
+ // src/tools/mail/index.ts
592
+ function register3(server) {
593
+ server.tool(
594
+ "search_emails",
595
+ "Search emails by query, sender, date range, or folder",
596
+ SearchEmailsSchema,
597
+ async (params) => handleSearchEmails(params)
598
+ );
599
+ server.tool(
600
+ "read_email",
601
+ "Read the full content of an email message by ID",
602
+ ReadEmailSchema,
603
+ async ({ messageId }) => handleReadEmail(messageId)
604
+ );
605
+ server.tool(
606
+ "send_email",
607
+ "Send an email to one or more recipients",
608
+ SendEmailSchema,
609
+ async (params) => handleSendEmail(params)
610
+ );
611
+ server.tool(
612
+ "flag_email",
613
+ "Set the flag status on an email (flagged, complete, or notFlagged)",
614
+ FlagEmailSchema,
615
+ async ({ messageId, flagStatus }) => handleFlagEmail(messageId, flagStatus)
616
+ );
617
+ server.tool(
618
+ "list_mail_folders",
619
+ "List all mail folders for the signed-in user",
620
+ {},
621
+ async () => handleListMailFolders()
622
+ );
623
+ server.tool(
624
+ "move_email",
625
+ "Move an email to a different mail folder",
626
+ MoveEmailSchema,
627
+ async ({ messageId, destinationFolderId }) => handleMoveEmail(messageId, destinationFolderId)
628
+ );
629
+ }
630
+
631
+ // src/tools/contacts/schemas.ts
632
+ import { z as z4 } from "zod";
633
+ var EmailAddressSchema = z4.object({
634
+ address: z4.string().describe("Email address"),
635
+ name: z4.string().optional().describe("Display name for this email address")
636
+ });
637
+ var SearchContactsSchema = {
638
+ query: z4.string().describe("Search query (matches against name, email, company, etc.)"),
639
+ top: z4.number().int().min(1).max(100).optional().describe("Maximum number of contacts to return (1-100)")
640
+ };
641
+ var GetContactSchema = {
642
+ contactId: z4.string().describe("The ID of the contact")
643
+ };
644
+ var CreateContactSchema = {
645
+ givenName: z4.string().describe("Contact first name"),
646
+ surname: z4.string().optional().describe("Contact last name"),
647
+ emailAddresses: z4.array(EmailAddressSchema).optional().describe("Email addresses for the contact"),
648
+ businessPhones: z4.array(z4.string()).optional().describe("Business phone numbers"),
649
+ jobTitle: z4.string().optional().describe("Job title"),
650
+ companyName: z4.string().optional().describe("Company or organization name")
651
+ };
652
+ var UpdateContactSchema = {
653
+ contactId: z4.string().describe("The ID of the contact to update"),
654
+ givenName: z4.string().optional().describe("Updated first name"),
655
+ surname: z4.string().optional().describe("Updated last name"),
656
+ emailAddresses: z4.array(EmailAddressSchema).optional().describe("Updated email addresses"),
657
+ businessPhones: z4.array(z4.string()).optional().describe("Updated business phone numbers"),
658
+ jobTitle: z4.string().optional().describe("Updated job title"),
659
+ companyName: z4.string().optional().describe("Updated company name")
660
+ };
661
+
662
+ // src/tools/contacts/handlers.ts
663
+ async function handleSearchContacts(query, top) {
664
+ try {
665
+ const token = await acquireToken();
666
+ const client = getGraphClient(token);
667
+ let request = client.api("/me/contacts").search(`"${query}"`).select("id,displayName,givenName,surname,emailAddresses,businessPhones,jobTitle,companyName");
668
+ if (top !== void 0) {
669
+ request = request.top(top);
670
+ }
671
+ const response = await request.get();
672
+ return formatResponse(response.value ?? []);
673
+ } catch (err) {
674
+ return formatError(parseGraphError(err));
675
+ }
676
+ }
677
+ async function handleGetContact(contactId) {
678
+ try {
679
+ const token = await acquireToken();
680
+ const client = getGraphClient(token);
681
+ const contact = await client.api(`/me/contacts/${contactId}`).get();
682
+ return formatResponse(contact);
683
+ } catch (err) {
684
+ return formatError(parseGraphError(err));
685
+ }
686
+ }
687
+ async function handleCreateContact(params) {
688
+ try {
689
+ const token = await acquireToken();
690
+ const client = getGraphClient(token);
691
+ const body = {
692
+ givenName: params.givenName
693
+ };
694
+ if (params.surname !== void 0) body.surname = params.surname;
695
+ if (params.emailAddresses !== void 0) body.emailAddresses = params.emailAddresses;
696
+ if (params.businessPhones !== void 0) body.businessPhones = params.businessPhones;
697
+ if (params.jobTitle !== void 0) body.jobTitle = params.jobTitle;
698
+ if (params.companyName !== void 0) body.companyName = params.companyName;
699
+ const created = await client.api("/me/contacts").post(body);
700
+ return formatResponse(created);
701
+ } catch (err) {
702
+ return formatError(parseGraphError(err));
703
+ }
704
+ }
705
+ async function handleUpdateContact(params) {
706
+ try {
707
+ const token = await acquireToken();
708
+ const client = getGraphClient(token);
709
+ const patch = {};
710
+ if (params.givenName !== void 0) patch.givenName = params.givenName;
711
+ if (params.surname !== void 0) patch.surname = params.surname;
712
+ if (params.emailAddresses !== void 0) patch.emailAddresses = params.emailAddresses;
713
+ if (params.businessPhones !== void 0) patch.businessPhones = params.businessPhones;
714
+ if (params.jobTitle !== void 0) patch.jobTitle = params.jobTitle;
715
+ if (params.companyName !== void 0) patch.companyName = params.companyName;
716
+ const updated = await client.api(`/me/contacts/${params.contactId}`).patch(patch);
717
+ return formatResponse(updated);
718
+ } catch (err) {
719
+ return formatError(parseGraphError(err));
720
+ }
721
+ }
722
+
723
+ // src/tools/contacts/index.ts
724
+ function register4(server) {
725
+ server.tool(
726
+ "search_contacts",
727
+ "Search contacts by name, email, company, or other fields",
728
+ SearchContactsSchema,
729
+ async ({ query, top }) => handleSearchContacts(query, top)
730
+ );
731
+ server.tool(
732
+ "get_contact",
733
+ "Get a single contact by ID",
734
+ GetContactSchema,
735
+ async ({ contactId }) => handleGetContact(contactId)
736
+ );
737
+ server.tool(
738
+ "create_contact",
739
+ "Create a new contact in the signed-in user's contacts",
740
+ CreateContactSchema,
741
+ async (params) => handleCreateContact(params)
742
+ );
743
+ server.tool(
744
+ "update_contact",
745
+ "Update an existing contact (name, email, phone, title, company)",
746
+ UpdateContactSchema,
747
+ async (params) => handleUpdateContact(params)
748
+ );
749
+ }
750
+
751
+ // src/version.ts
752
+ var VERSION = true ? "0.1.0" : "0.0.0-dev";
753
+
754
+ // src/server.ts
755
+ function createServer() {
756
+ const server = new McpServer({
757
+ name: "m365-mcp",
758
+ version: VERSION
759
+ });
760
+ register(server);
761
+ register2(server);
762
+ register3(server);
763
+ register4(server);
764
+ return server;
765
+ }
766
+
767
+ // src/index.ts
768
+ var command = process.argv[2];
769
+ async function main() {
770
+ if (command === "setup") {
771
+ const { runSetupCommand } = await import("./setup-K6EYBEFH.js");
772
+ await runSetupCommand();
773
+ return;
774
+ }
775
+ if (command === "auth") {
776
+ await runAuthCommand();
777
+ return;
778
+ }
779
+ const server = createServer();
780
+ const transport = new StdioServerTransport();
781
+ process.on("uncaughtException", (err) => {
782
+ process.stderr.write(`[m365-mcp] Uncaught exception: ${String(err)}
783
+ `);
784
+ });
785
+ process.on("unhandledRejection", (reason) => {
786
+ process.stderr.write(`[m365-mcp] Unhandled rejection: ${String(reason)}
787
+ `);
788
+ });
789
+ await server.connect(transport);
790
+ }
791
+ main().catch((err) => {
792
+ process.stderr.write(`[m365-mcp] Fatal startup error: ${String(err)}
793
+ `);
794
+ process.exit(1);
795
+ });
796
+ //# sourceMappingURL=index.js.map