@zapier/microsoft-outlook-connector 0.0.0 → 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.
Files changed (45) hide show
  1. package/LICENSE +93 -0
  2. package/NOTICE +8 -0
  3. package/NOTICE.md +8 -0
  4. package/README.md +145 -2
  5. package/SKILL.md +159 -0
  6. package/cli.js +71 -0
  7. package/cli.ts +5 -0
  8. package/connections.ts +69 -0
  9. package/dist/cli.js +4 -0
  10. package/dist/index.js +1823 -0
  11. package/index.ts +103 -0
  12. package/package.json +65 -4
  13. package/preflight.sh +157 -0
  14. package/references/microsoft-outlook-api-gotchas.md +210 -0
  15. package/scripts/copyMessage.ts +68 -0
  16. package/scripts/createContact.ts +39 -0
  17. package/scripts/createDraft.ts +71 -0
  18. package/scripts/createEvent.ts +61 -0
  19. package/scripts/createMailFolder.ts +68 -0
  20. package/scripts/createReplyDraft.ts +81 -0
  21. package/scripts/deleteContact.ts +47 -0
  22. package/scripts/deleteEvent.ts +61 -0
  23. package/scripts/deleteMessage.ts +57 -0
  24. package/scripts/forwardMessage.ts +75 -0
  25. package/scripts/getAttachment.ts +60 -0
  26. package/scripts/getContact.ts +44 -0
  27. package/scripts/getEvent.ts +63 -0
  28. package/scripts/getMe.ts +42 -0
  29. package/scripts/getMessage.ts +68 -0
  30. package/scripts/listAttachments.ts +94 -0
  31. package/scripts/listCalendarView.ts +99 -0
  32. package/scripts/listCalendars.ts +85 -0
  33. package/scripts/listCategories.ts +49 -0
  34. package/scripts/listContacts.ts +81 -0
  35. package/scripts/listEvents.ts +94 -0
  36. package/scripts/listMailFolders.ts +98 -0
  37. package/scripts/listMessages.ts +106 -0
  38. package/scripts/moveMessage.ts +68 -0
  39. package/scripts/replyToMessage.ts +73 -0
  40. package/scripts/sendDraft.ts +55 -0
  41. package/scripts/sendMail.ts +68 -0
  42. package/scripts/updateContact.ts +49 -0
  43. package/scripts/updateEvent.ts +69 -0
  44. package/scripts/updateMessage.ts +99 -0
  45. package/tsup.config.ts +63 -0
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import {
7
+ buildListQuery,
8
+ GRAPH_BASE,
9
+ mailboxRoot,
10
+ outlookFetch,
11
+ toListResult,
12
+ } from "../lib/graph.ts";
13
+
14
+ const inputSchema = z
15
+ .object({
16
+ parentFolderId: z
17
+ .string()
18
+ .describe(
19
+ "Omit to list top-level folders. Set a folder id (from a previous call) or a well-known name (inbox, drafts, sentitems, deleteditems, archive, junkemail) to list that folder's subfolders.",
20
+ )
21
+ .optional(),
22
+ mailbox: z
23
+ .string()
24
+ .describe(
25
+ "Shared-mailbox address (UPN/email) to read instead of your own, e.g. team@contoso.com. Requires shared-mailbox delegation. Omit for your own mailbox.",
26
+ )
27
+ .optional(),
28
+ limit: z
29
+ .number()
30
+ .int()
31
+ .gte(1)
32
+ .describe(
33
+ "Folders per page. Defaults to 20 when omitted; pass a value when you need a specific number.",
34
+ )
35
+ .optional(),
36
+ cursor: z
37
+ .string()
38
+ .describe(
39
+ "Pagination cursor from a previous response's next_cursor. Omit for the first page.",
40
+ )
41
+ .optional(),
42
+ })
43
+ .strict();
44
+
45
+ const outputSchema = z.object({
46
+ items: z.array(
47
+ z.object({
48
+ id: z.string(),
49
+ displayName: z.string(),
50
+ parentFolderId: z.string().optional(),
51
+ childFolderCount: z.number().optional(),
52
+ unreadItemCount: z.number().optional(),
53
+ totalItemCount: z.number().optional(),
54
+ }),
55
+ ),
56
+ next_cursor: z
57
+ .string()
58
+ .describe("Pass as cursor to fetch the next page. Absent on the last page.")
59
+ .optional(),
60
+ });
61
+
62
+ const definition = defineTool({
63
+ name: "listMailFolders",
64
+ title: "List Mail Folders",
65
+ description:
66
+ "List mail folders. Returns top-level folders by default; pass parentFolderId to list a folder's subfolders. The entry point for resolving a folder id to pass as folderId to listMessages or destinationId to moveMessage.",
67
+ inputSchema,
68
+ outputSchema,
69
+ annotations: {
70
+ readOnlyHint: true,
71
+ destructiveHint: false,
72
+ idempotentHint: true,
73
+ openWorldHint: true,
74
+ },
75
+ connection: "microsoft-outlook",
76
+ run: async (input, ctx) => {
77
+ // `@odata.nextLink` is an opaque full URL — when paging, fetch it verbatim
78
+ // and skip rebuilding the path/query.
79
+ let url: string;
80
+ if (input.cursor !== undefined) {
81
+ url = input.cursor;
82
+ } else {
83
+ const root = mailboxRoot(input.mailbox);
84
+ const path =
85
+ input.parentFolderId !== undefined
86
+ ? `${root}/mailFolders/${encodeURIComponent(input.parentFolderId)}/childFolders`
87
+ : `${root}/mailFolders`;
88
+ const query = buildListQuery({ limit: input.limit ?? 20 });
89
+ url = `${GRAPH_BASE}${path}${query}`;
90
+ }
91
+ const res = await outlookFetch(ctx.fetch, "listMailFolders", url);
92
+ return toListResult(await res.json());
93
+ },
94
+ });
95
+
96
+ export default definition;
97
+
98
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import {
7
+ buildListQuery,
8
+ GRAPH_BASE,
9
+ mailboxRoot,
10
+ outlookFetch,
11
+ toListResult,
12
+ } from "../lib/graph.ts";
13
+ import { messageListItemSchema } from "../lib/schemas.ts";
14
+
15
+ const inputSchema = z
16
+ .object({
17
+ folderId: z
18
+ .string()
19
+ .describe(
20
+ "Restrict to one folder: a folder id from listMailFolders, or a well-known name (inbox, drafts, sentitems, deleteditems, archive, junkemail). Omit to list across all folders.",
21
+ )
22
+ .optional(),
23
+ search: z
24
+ .string()
25
+ .describe(
26
+ 'Full-text KQL search over the default from/subject/body properties, e.g. "subject:invoice from:acme". Results are sorted by the date and time sent. Omit to list newest-first.',
27
+ )
28
+ .optional(),
29
+ filter: z
30
+ .string()
31
+ .describe(
32
+ 'OData $filter predicate for exact matches, e.g. "isRead eq false" or "importance eq \'high\'". Use search for free-text instead.',
33
+ )
34
+ .optional(),
35
+ mailbox: z
36
+ .string()
37
+ .describe(
38
+ "Shared-mailbox address (UPN/email) to read instead of your own, e.g. team@contoso.com. Requires shared-mailbox delegation. Omit for your own mailbox.",
39
+ )
40
+ .optional(),
41
+ limit: z
42
+ .number()
43
+ .int()
44
+ .gte(1)
45
+ .describe(
46
+ "Messages per page. Defaults to 10 when omitted; pass a value when you need a specific number of results.",
47
+ )
48
+ .optional(),
49
+ cursor: z
50
+ .string()
51
+ .describe(
52
+ "Pagination cursor from a previous response's next_cursor. Omit for the first page.",
53
+ )
54
+ .optional(),
55
+ })
56
+ .strict();
57
+
58
+ const outputSchema = z.object({
59
+ items: z.array(messageListItemSchema),
60
+ next_cursor: z
61
+ .string()
62
+ .describe("Pass as cursor to fetch the next page. Absent on the last page.")
63
+ .optional(),
64
+ });
65
+
66
+ const definition = defineTool({
67
+ name: "listMessages",
68
+ title: "List Messages",
69
+ description:
70
+ 'Search or list mailbox messages. Listing is newest-first; search results (KQL, e.g. "subject:invoice from:acme") are ordered by sent date, not recency. Scope with folderId; exact-match with filter. The entry point for resolving a message id before getMessage/updateMessage/moveMessage.',
71
+ inputSchema,
72
+ outputSchema,
73
+ annotations: {
74
+ readOnlyHint: true,
75
+ destructiveHint: false,
76
+ idempotentHint: true,
77
+ openWorldHint: true,
78
+ },
79
+ connection: "microsoft-outlook",
80
+ run: async (input, ctx) => {
81
+ // `@odata.nextLink` is an opaque full URL — when paging, fetch it verbatim
82
+ // and skip rebuilding the path/query.
83
+ let url: string;
84
+ if (input.cursor !== undefined) {
85
+ url = input.cursor;
86
+ } else {
87
+ const root = mailboxRoot(input.mailbox);
88
+ const path =
89
+ input.folderId !== undefined
90
+ ? `${root}/mailFolders/${encodeURIComponent(input.folderId)}/messages`
91
+ : `${root}/messages`;
92
+ const query = buildListQuery({
93
+ limit: input.limit ?? 10,
94
+ search: input.search,
95
+ filter: input.filter,
96
+ });
97
+ url = `${GRAPH_BASE}${path}${query}`;
98
+ }
99
+ const res = await outlookFetch(ctx.fetch, "listMessages", url);
100
+ return toListResult(await res.json());
101
+ },
102
+ });
103
+
104
+ export default definition;
105
+
106
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import {
7
+ GRAPH_BASE,
8
+ mailboxRoot,
9
+ outlookFetch,
10
+ parseGraphResponse,
11
+ } from "../lib/graph.ts";
12
+
13
+ const inputSchema = z
14
+ .object({
15
+ messageId: z
16
+ .string()
17
+ .describe(
18
+ "Message id from listMessages or another message tool. Opaque and case-sensitive; changes when the message is moved between folders.",
19
+ ),
20
+ destinationId: z
21
+ .string()
22
+ .describe(
23
+ "Target folder: a folder id from listMailFolders, or a well-known name (inbox, archive, deleteditems, junkemail, drafts, sentitems).",
24
+ ),
25
+ mailbox: z
26
+ .string()
27
+ .describe(
28
+ "Shared-mailbox address (UPN/email) to move within instead of your own. Requires shared-mailbox delegation. Omit for your own mailbox.",
29
+ )
30
+ .optional(),
31
+ })
32
+ .strict();
33
+
34
+ const outputSchema = z.object({
35
+ id: z.string(),
36
+ parentFolderId: z.string(),
37
+ subject: z.string(),
38
+ });
39
+
40
+ const definition = defineTool({
41
+ name: "moveMessage",
42
+ title: "Move Message",
43
+ description:
44
+ "Move a message into another folder. Resolve the message id via listMessages first. IMPORTANT: the message id CHANGES after a move — use the id from this response for any follow-up call, not the id you passed in.",
45
+ inputSchema,
46
+ outputSchema,
47
+ annotations: {
48
+ readOnlyHint: false,
49
+ destructiveHint: false,
50
+ idempotentHint: false,
51
+ openWorldHint: true,
52
+ },
53
+ connection: "microsoft-outlook",
54
+ run: async (input, ctx) => {
55
+ const url = `${GRAPH_BASE}${mailboxRoot(input.mailbox)}/messages/${encodeURIComponent(
56
+ input.messageId,
57
+ )}/move`;
58
+ const res = await outlookFetch(ctx.fetch, "moveMessage", url, {
59
+ method: "POST",
60
+ body: JSON.stringify({ destinationId: input.destinationId }),
61
+ });
62
+ return parseGraphResponse(res);
63
+ },
64
+ });
65
+
66
+ export default definition;
67
+
68
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import { GRAPH_BASE, mailboxRoot, outlookFetch } from "../lib/graph.ts";
7
+
8
+ const inputSchema = z
9
+ .object({
10
+ messageId: z
11
+ .string()
12
+ .describe(
13
+ "Id of the message to reply to, from listMessages or getMessage. Opaque and case-sensitive; changes when the message is moved between folders.",
14
+ ),
15
+ comment: z
16
+ .string()
17
+ .describe(
18
+ "Reply text prepended above the quoted original. Plain text or HTML. Omit to send an empty-bodied reply.",
19
+ )
20
+ .optional(),
21
+ replyAll: z
22
+ .boolean()
23
+ .describe(
24
+ "Reply to all original recipients (To + Cc) instead of just the sender. Defaults to false.",
25
+ )
26
+ .optional(),
27
+ mailbox: z
28
+ .string()
29
+ .describe(
30
+ "Shared-mailbox address (UPN/email) to reply from instead of your own, e.g. team@contoso.com. Requires Send-as/Send-on-behalf delegation. Omit to reply as yourself.",
31
+ )
32
+ .optional(),
33
+ })
34
+ .strict();
35
+
36
+ // Graph's POST /reply and /replyAll return 202 with no body, so there is
37
+ // nothing to echo back — run() synthesizes a success result.
38
+ const outputSchema = z.object({
39
+ success: z.literal(true),
40
+ });
41
+
42
+ const definition = defineTool({
43
+ name: "replyToMessage",
44
+ title: "Reply To Message",
45
+ description:
46
+ "Reply to a message and send immediately, quoting the original. Set replyAll to reply to all original recipients (To + Cc). Returns only a success flag (202 accepted, no id). Resolve the message id via listMessages first; to draft a reply for review instead, use createReplyDraft.",
47
+ inputSchema,
48
+ outputSchema,
49
+ annotations: {
50
+ readOnlyHint: false,
51
+ destructiveHint: false,
52
+ idempotentHint: false,
53
+ openWorldHint: true,
54
+ },
55
+ connection: "microsoft-outlook",
56
+ run: async (input, ctx) => {
57
+ const action = input.replyAll ? "replyAll" : "reply";
58
+ const url = `${GRAPH_BASE}${mailboxRoot(input.mailbox)}/messages/${encodeURIComponent(
59
+ input.messageId,
60
+ )}/${action}`;
61
+ await outlookFetch(ctx.fetch, "replyToMessage", url, {
62
+ method: "POST",
63
+ body: JSON.stringify(
64
+ input.comment !== undefined ? { comment: input.comment } : {},
65
+ ),
66
+ });
67
+ return { success: true as const };
68
+ },
69
+ });
70
+
71
+ export default definition;
72
+
73
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import { GRAPH_BASE, mailboxRoot, outlookFetch } from "../lib/graph.ts";
7
+
8
+ const inputSchema = z
9
+ .object({
10
+ messageId: z
11
+ .string()
12
+ .describe(
13
+ "Draft id from createDraft. Opaque and case-sensitive; the id is dead once the draft is sent.",
14
+ ),
15
+ mailbox: z
16
+ .string()
17
+ .describe(
18
+ "Shared-mailbox address (UPN/email) the draft lives in instead of your own, e.g. team@contoso.com. Requires Send-as/Send-on-behalf delegation. Omit for your own mailbox.",
19
+ )
20
+ .optional(),
21
+ })
22
+ .strict();
23
+
24
+ // Graph's POST /messages/{id}/send returns 202 with no body, so there is
25
+ // nothing to echo back — run() synthesizes a success result.
26
+ const outputSchema = z.object({
27
+ success: z.literal(true),
28
+ });
29
+
30
+ const definition = defineTool({
31
+ name: "sendDraft",
32
+ title: "Send Draft",
33
+ description:
34
+ "Send a draft email created by createDraft. Returns only a success flag (the send is accepted asynchronously, and 202 ≠ delivered); the draft id is dead after sending. Resolve the draft id from createDraft first.",
35
+ inputSchema,
36
+ outputSchema,
37
+ annotations: {
38
+ readOnlyHint: false,
39
+ destructiveHint: false,
40
+ idempotentHint: false,
41
+ openWorldHint: true,
42
+ },
43
+ connection: "microsoft-outlook",
44
+ run: async (input, ctx) => {
45
+ const url = `${GRAPH_BASE}${mailboxRoot(input.mailbox)}/messages/${encodeURIComponent(
46
+ input.messageId,
47
+ )}/send`;
48
+ await outlookFetch(ctx.fetch, "sendDraft", url, { method: "POST" });
49
+ return { success: true as const };
50
+ },
51
+ });
52
+
53
+ export default definition;
54
+
55
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import { GRAPH_BASE, mailboxRoot, outlookFetch } from "../lib/graph.ts";
7
+ import {
8
+ outgoingMessageSchema,
9
+ toGraphOutgoingMessage,
10
+ } from "../lib/schemas.ts";
11
+
12
+ const inputSchema = z
13
+ .object({
14
+ message: outgoingMessageSchema.describe(
15
+ "The email to send. subject and toRecipients are required.",
16
+ ),
17
+ saveToSentItems: z
18
+ .boolean()
19
+ .describe("Keep a copy in Sent Items. Defaults to true.")
20
+ .optional(),
21
+ mailbox: z
22
+ .string()
23
+ .describe(
24
+ "Shared-mailbox address (UPN/email) to send as instead of your own, e.g. team@contoso.com. Requires Send-as/Send-on-behalf delegation. Omit to send as yourself.",
25
+ )
26
+ .optional(),
27
+ })
28
+ .strict();
29
+
30
+ // Graph's POST /sendMail returns 202 with no body and no message id, so there
31
+ // is nothing to echo back — run() synthesizes a success result.
32
+ const outputSchema = z.object({
33
+ success: z.literal(true),
34
+ });
35
+
36
+ const definition = defineTool({
37
+ name: "sendMail",
38
+ title: "Send Mail",
39
+ description:
40
+ "Compose and send an email in one step. Returns only a success flag — the send is accepted asynchronously with no message id (and 202 ≠ delivered). When you need the sent message's id, use createDraft then sendDraft instead.",
41
+ inputSchema,
42
+ outputSchema,
43
+ annotations: {
44
+ readOnlyHint: false,
45
+ destructiveHint: false,
46
+ idempotentHint: false,
47
+ openWorldHint: true,
48
+ },
49
+ connection: "microsoft-outlook",
50
+ run: async (input, ctx) => {
51
+ const url = `${GRAPH_BASE}${mailboxRoot(input.mailbox)}/sendMail`;
52
+ const body: Record<string, unknown> = {
53
+ message: toGraphOutgoingMessage(input.message),
54
+ };
55
+ if (input.saveToSentItems !== undefined) {
56
+ body.saveToSentItems = input.saveToSentItems;
57
+ }
58
+ await outlookFetch(ctx.fetch, "sendMail", url, {
59
+ method: "POST",
60
+ body: JSON.stringify(body),
61
+ });
62
+ return { success: true as const };
63
+ },
64
+ });
65
+
66
+ export default definition;
67
+
68
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import { GRAPH_BASE, outlookFetch, parseGraphResponse } from "../lib/graph.ts";
7
+ import { contactSchema, outgoingContactSchema } from "../lib/schemas.ts";
8
+
9
+ const inputSchema = z
10
+ .object({
11
+ contactId: z
12
+ .string()
13
+ .describe("Contact id from listContacts. Opaque and case-sensitive."),
14
+ ...outgoingContactSchema.shape,
15
+ })
16
+ .strict();
17
+
18
+ const outputSchema = contactSchema;
19
+
20
+ const definition = defineTool({
21
+ name: "updateContact",
22
+ title: "Update Contact",
23
+ description:
24
+ "Update fields on a personal contact. Set only the fields you want to change. Array fields (emailAddresses, businessPhones) REPLACE the existing values — read current via getContact, merge, then update.",
25
+ inputSchema,
26
+ outputSchema,
27
+ annotations: {
28
+ readOnlyHint: false,
29
+ destructiveHint: false,
30
+ idempotentHint: true,
31
+ openWorldHint: true,
32
+ },
33
+ connection: "microsoft-outlook",
34
+ run: async (input, ctx) => {
35
+ const { contactId, ...patch } = input;
36
+ const url = `${GRAPH_BASE}/me/contacts/${encodeURIComponent(contactId)}`;
37
+ // undefined keys are dropped by JSON.stringify, so only supplied fields
38
+ // are sent in the PATCH body.
39
+ const res = await outlookFetch(ctx.fetch, "updateContact", url, {
40
+ method: "PATCH",
41
+ body: JSON.stringify(patch),
42
+ });
43
+ return parseGraphResponse(res);
44
+ },
45
+ });
46
+
47
+ export default definition;
48
+
49
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import {
7
+ calendarRoot,
8
+ GRAPH_BASE,
9
+ outlookFetch,
10
+ parseGraphResponse,
11
+ } from "../lib/graph.ts";
12
+ import { eventSchema, outgoingEventSchema } from "../lib/schemas.ts";
13
+
14
+ const inputSchema = z
15
+ .object({
16
+ eventId: z
17
+ .string()
18
+ .describe(
19
+ "Opaque event id from listEvents, listCalendarView, or getEvent.",
20
+ ),
21
+ ...outgoingEventSchema.partial().shape,
22
+ mailbox: z
23
+ .string()
24
+ .describe(
25
+ "Shared-mailbox address (UPN/email) holding the event instead of your own. Requires shared-mailbox delegation. Omit for your own mailbox.",
26
+ )
27
+ .optional(),
28
+ calendarId: z
29
+ .string()
30
+ .describe(
31
+ "Target a specific calendar (id from listCalendars). Omit to use the default calendar.",
32
+ )
33
+ .optional(),
34
+ })
35
+ .strict();
36
+
37
+ const outputSchema = eventSchema;
38
+
39
+ const definition = defineTool({
40
+ name: "updateEvent",
41
+ title: "Update Event",
42
+ description:
43
+ "Update a calendar event — change subject, time, location, body, attendees, or categories. Set only the fields you want to change. The attendees array REPLACES the existing list, so read the current attendees via getEvent, append, then update.",
44
+ inputSchema,
45
+ outputSchema,
46
+ annotations: {
47
+ readOnlyHint: false,
48
+ destructiveHint: false,
49
+ idempotentHint: true,
50
+ openWorldHint: true,
51
+ },
52
+ connection: "microsoft-outlook",
53
+ run: async (input, ctx) => {
54
+ const { eventId, mailbox, calendarId, ...patch } = input;
55
+ const url = `${GRAPH_BASE}${calendarRoot(mailbox, calendarId)}/events/${encodeURIComponent(
56
+ eventId,
57
+ )}`;
58
+ // JSON.stringify drops undefined keys, so only the provided fields are sent.
59
+ const res = await outlookFetch(ctx.fetch, "updateEvent", url, {
60
+ method: "PATCH",
61
+ body: JSON.stringify(patch),
62
+ });
63
+ return parseGraphResponse(res);
64
+ },
65
+ });
66
+
67
+ export default definition;
68
+
69
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
3
+ import { z } from "zod";
4
+
5
+ import { connectionResolvers } from "../connections.ts";
6
+ import {
7
+ GRAPH_BASE,
8
+ mailboxRoot,
9
+ outlookFetch,
10
+ parseGraphResponse,
11
+ } from "../lib/graph.ts";
12
+ import {
13
+ dateTimeTimeZoneInputSchema,
14
+ followupFlagSchema,
15
+ } from "../lib/schemas.ts";
16
+
17
+ const inputSchema = z
18
+ .object({
19
+ messageId: z
20
+ .string()
21
+ .describe(
22
+ "Message id from listMessages or another message tool. Opaque and case-sensitive; changes when the message is moved between folders.",
23
+ ),
24
+ isRead: z
25
+ .boolean()
26
+ .describe("Mark the message read (true) or unread (false).")
27
+ .optional(),
28
+ importance: z
29
+ .enum(["low", "normal", "high"])
30
+ .describe("Importance level.")
31
+ .optional(),
32
+ categories: z
33
+ .array(z.string())
34
+ .describe(
35
+ "Category names to assign. REPLACES the existing set — read the current categories via getMessage and include them if you want to append rather than overwrite.",
36
+ )
37
+ .optional(),
38
+ flag: z
39
+ .strictObject({
40
+ flagStatus: z
41
+ .enum(["notFlagged", "flagged", "complete"])
42
+ .describe("Follow-up flag state."),
43
+ startDateTime: dateTimeTimeZoneInputSchema.optional(),
44
+ dueDateTime: dateTimeTimeZoneInputSchema.optional(),
45
+ })
46
+ .describe("Follow-up flag to set on the message.")
47
+ .optional(),
48
+ mailbox: z
49
+ .string()
50
+ .describe(
51
+ "Shared-mailbox address (UPN/email) to update instead of your own. Requires shared-mailbox delegation. Omit for your own mailbox.",
52
+ )
53
+ .optional(),
54
+ })
55
+ .strict();
56
+
57
+ const outputSchema = z.object({
58
+ id: z.string(),
59
+ subject: z.string(),
60
+ isRead: z.boolean(),
61
+ importance: z.string().optional(),
62
+ categories: z.array(z.string()).optional(),
63
+ flag: followupFlagSchema.optional(),
64
+ });
65
+
66
+ const definition = defineTool({
67
+ name: "updateMessage",
68
+ title: "Update Message",
69
+ description:
70
+ "Update a message's state — mark read/unread, change importance, replace its categories, or set a follow-up flag. Set only the fields you want to change; everything else is left untouched. Resolve the message id via listMessages first. Subject/body are editable only on drafts.",
71
+ inputSchema,
72
+ outputSchema,
73
+ annotations: {
74
+ readOnlyHint: false,
75
+ destructiveHint: false,
76
+ idempotentHint: true,
77
+ openWorldHint: true,
78
+ },
79
+ connection: "microsoft-outlook",
80
+ run: async (input, ctx) => {
81
+ const url = `${GRAPH_BASE}${mailboxRoot(input.mailbox)}/messages/${encodeURIComponent(
82
+ input.messageId,
83
+ )}`;
84
+ const body: Record<string, unknown> = {};
85
+ if (input.isRead !== undefined) body.isRead = input.isRead;
86
+ if (input.importance !== undefined) body.importance = input.importance;
87
+ if (input.categories !== undefined) body.categories = input.categories;
88
+ if (input.flag !== undefined) body.flag = input.flag;
89
+ const res = await outlookFetch(ctx.fetch, "updateMessage", url, {
90
+ method: "PATCH",
91
+ body: JSON.stringify(body),
92
+ });
93
+ return parseGraphResponse(res);
94
+ },
95
+ });
96
+
97
+ export default definition;
98
+
99
+ await handleIfScriptMain(import.meta, definition, { connectionResolvers });