@chrischall/office-outlook-mcp 0.5.0 → 0.5.1

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Outlook / Microsoft 365 — mail, calendar, contacts and tasks via natural language",
10
- "version": "0.5.0"
10
+ "version": "0.5.1"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Outlook (Microsoft 365)",
16
16
  "source": "./",
17
17
  "description": "MCP server for Outlook / Microsoft 365 — triage unread mail, reply, answer meeting invites and schedule meetings, plus folders, calendar, contacts and tasks, with confirmation before anything is sent",
18
- "version": "0.5.0",
18
+ "version": "0.5.1",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "office-outlook-mcp",
3
3
  "displayName": "Outlook (Microsoft 365)",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "description": "Outlook / Microsoft 365 for Claude — triage unread mail, reply, answer meeting invites and schedule meetings, plus folders, calendar, contacts and tasks, with confirmation before anything is sent, using your signed-in browser session",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/README.md CHANGED
@@ -180,7 +180,10 @@ An agent can work through new mail end to end without opening Outlook:
180
180
  there are more.
181
181
  2. **Act on each item** —
182
182
  - an invite: `outlook_respond_to_invite` with the message id and
183
- `accept`, `tentative` or `decline` (and an optional comment);
183
+ `accept`, `tentative` or `decline` (and an optional comment). Once you
184
+ respond, Outlook moves the invite message to Deleted Items under a new
185
+ id, so an answered invite is already filed — skip steps 3 and 4 for it
186
+ (its old id would 404);
184
187
  - a message that needs an answer: `outlook_reply` (`reply`, `replyAll` or
185
188
  `forward`), or `draftOnly: true` to leave it in Drafts for you to review;
186
189
  - a request for a meeting: `outlook_find_meeting_times` for the attendees,
@@ -190,8 +193,9 @@ An agent can work through new mail end to end without opening Outlook:
190
193
  `outlook_list_categories`) and marks read up to 50 messages in one call;
191
194
  `outlook_move_message` moves one to another folder.
192
195
  4. **Mark it handled** — `outlook_mark_read` (or `isRead: true` on
193
- `outlook_update_message`), so the next `outlook_get_unread` returns only
194
- what is still new.
196
+ `outlook_update_message`) on the plain mail you handled, so the next
197
+ `outlook_get_unread` returns only what is still new. Answered invites are
198
+ already out of the inbox (see step 2).
195
199
 
196
200
  Replies, forwards, invite responses and new meetings reach other people, so
197
201
  each asks you to confirm first — except a reply saved with `draftOnly: true`
package/dist/bundle.js CHANGED
@@ -37588,7 +37588,7 @@ var init_version = __esm({
37588
37588
  "src/version.ts"() {
37589
37589
  "use strict";
37590
37590
  PACKAGE_NAME = "office-outlook-mcp";
37591
- VERSION = "0.5.0";
37591
+ VERSION = "0.5.1";
37592
37592
  }
37593
37593
  });
37594
37594
 
@@ -49246,7 +49246,7 @@ function registerMailTools(server, client2) {
49246
49246
  server.registerTool(
49247
49247
  "outlook_get_unread",
49248
49248
  {
49249
- description: 'Triage batch: the unread messages in a folder (default inbox), newest first, each WITH its plain-text body (truncated to `maxBodyChars`) in one call \u2014 no follow-up outlook_get_message needed. Every item has a `kind`: "mail", "meetingRequest", "meetingCancelled" or "meetingResponse" (a reply to an invite you sent). Requests and cancellations also carry `event` (id, time, organizer, your current `responseStatus`) so the invite can be answered by its event id (when the meeting is no longer on your calendar \u2014 already declined, cancelled or deleted \u2014 the item has a `hint` instead and there is nothing to respond to). `to`/`cc` list at most 20 addresses; a longer list adds `toCount`/`ccCount` and `toTruncated`/`ccTruncated`. This does NOT mark anything read. Processing loop: read the batch \u2192 act on each item (reply, respond to the invite, file, flag \u2014 or leave it) \u2192 call outlook_mark_read on the ids you handled, so the next call returns only what is still new. Follow `nextLink` for more. ' + UNTRUSTED_DESCRIPTION_SUFFIX,
49249
+ description: 'Triage batch: the unread messages in a folder (default inbox), newest first, each WITH its plain-text body (truncated to `maxBodyChars`) in one call \u2014 no follow-up outlook_get_message needed. Every item has a `kind`: "mail", "meetingRequest", "meetingCancelled" or "meetingResponse" (a reply to an invite you sent). Requests and cancellations also carry `event` (id, time, organizer, your current `responseStatus`) so the invite can be answered by its event id (when the meeting is no longer on your calendar \u2014 already declined, cancelled or deleted \u2014 the item has a `hint` instead and there is nothing to respond to). `to`/`cc` list at most 20 addresses; a longer list adds `toCount`/`ccCount` and `toTruncated`/`ccTruncated`. This does NOT mark anything read. Processing loop: read the batch \u2192 act on each item (reply, respond to the invite, file, flag \u2014 or leave it) \u2192 call outlook_mark_read on the plain mail you handled, so the next call returns only what is still new. An invite you answer with outlook_respond_to_invite needs no mark-read: Outlook moves it to Deleted Items under a new id, so its old id would 404. Follow `nextLink` for more. ' + UNTRUSTED_DESCRIPTION_SUFFIX,
49250
49250
  annotations: { readOnlyHint: true, openWorldHint: true },
49251
49251
  inputSchema: external_exports.object({
49252
49252
  folder: external_exports.string().min(1).optional().describe('Folder id or well-known name (default "inbox")'),
@@ -49465,7 +49465,7 @@ function registerCalendarTools(server, client2) {
49465
49465
  server.registerTool(
49466
49466
  "outlook_list_events",
49467
49467
  {
49468
- description: 'List calendar events in a date window. This uses the calendar VIEW, which expands recurring series into their individual occurrences \u2014 the right tool for "what is on my schedule". Times are returned in `timeZone` when given. ' + UNTRUSTED_DESCRIPTION_SUFFIX,
49468
+ description: 'List calendar events in a date window. This uses the calendar VIEW, which expands recurring series into their individual occurrences \u2014 the right tool for "what is on my schedule". Times are returned in `timeZone` when given. view:"raw" returns Outlook\'s records as-is and uncapped; the list carries no Attendees in any view \u2014 outlook_get_event returns them. ' + UNTRUSTED_DESCRIPTION_SUFFIX,
49469
49469
  annotations: { readOnlyHint: true, openWorldHint: true },
49470
49470
  inputSchema: external_exports.object({
49471
49471
  view: viewParam(VIEWS2),
@@ -49505,7 +49505,7 @@ function registerCalendarTools(server, client2) {
49505
49505
  server.registerTool(
49506
49506
  "outlook_get_event",
49507
49507
  {
49508
- description: "Get one calendar event in full, including attendees and their responses. " + UNTRUSTED_DESCRIPTION_SUFFIX,
49508
+ description: `Get one calendar event in full, including attendees and their responses. Attendees appear in view:"full", every one of them \u2014 there is no 20-address cap as on a message's To/Cc; view:"raw" returns Outlook's untouched record, uncapped, Attendees included. ` + UNTRUSTED_DESCRIPTION_SUFFIX,
49509
49509
  annotations: { readOnlyHint: true, openWorldHint: true },
49510
49510
  inputSchema: external_exports.object({
49511
49511
  view: viewParam(VIEWS2),
@@ -49851,6 +49851,7 @@ async function writeOrUnknown(run, checkWhere) {
49851
49851
  }
49852
49852
  var INVITE_EVENT_SELECT = "Id,Subject,IsOrganizer,IsCancelled,Start,End,Organizer,ResponseStatus";
49853
49853
  var DELETE_EVENT_SELECT = "Id,Subject,IsOrganizer,IsCancelled,ResponseStatus,Type,SeriesMasterId,Start,End,Organizer,Attendees";
49854
+ var INVITE_FILED_NOTE = "Outlook moves the invite message out of your inbox (to Deleted Items, under a new id) once you respond, so it is already filed: do not call outlook_mark_read or outlook_move_message on its old id.";
49854
49855
  var INVITE_RESPONSES = {
49855
49856
  accept: { verb: "accept", status: "Accepted" },
49856
49857
  tentative: { verb: "tentativelyaccept", status: "TentativelyAccepted" },
@@ -50327,7 +50328,7 @@ function registerWriteTools(server, client2) {
50327
50328
  server.registerTool(
50328
50329
  "outlook_respond_to_invite",
50329
50330
  {
50330
- description: "Accept, tentatively accept or decline a meeting invitation. Pass the invite's `messageId` (from outlook_get_unread, which marks it kind \"meetingRequest\") or the event's `eventId` \u2014 exactly one. The organizer is sent your response unless `sendResponse: false`, in which case only your calendar changes and no confirmation is asked. Cancelled meetings and meetings you organize are refused. The result is verified by re-reading the event's response status; a declined meeting may leave your calendar entirely. " + CONFIRM_FLOW_SENTENCE + " " + TIMEOUT_SENTENCE + " " + CONFIRM_INJECTION_RULE,
50331
+ description: "Accept, tentatively accept or decline a meeting invitation. Pass the invite's `messageId` (from outlook_get_unread, which marks it kind \"meetingRequest\") or the event's `eventId` \u2014 exactly one. The organizer is sent your response unless `sendResponse: false`, in which case only your calendar changes and no confirmation is asked. Cancelled meetings and meetings you organize are refused. The result is verified by re-reading the event's response status; a declined meeting may leave your calendar entirely. Outlook then moves the invite message itself to Deleted Items (under a new id), so an answered invite needs no outlook_mark_read. " + CONFIRM_FLOW_SENTENCE + " " + TIMEOUT_SENTENCE + " " + CONFIRM_INJECTION_RULE,
50331
50332
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
50332
50333
  inputSchema: external_exports.object({
50333
50334
  messageId: external_exports.string().min(1).optional().describe("Id of the invite message (resolved to its event). Give this or eventId."),
@@ -50420,14 +50421,16 @@ function registerWriteTools(server, client2) {
50420
50421
  eventId: id,
50421
50422
  response: status,
50422
50423
  sentResponse: notify,
50423
- note: "The declined meeting is no longer on your calendar."
50424
+ note: "The declined meeting is no longer on your calendar.",
50425
+ inviteMessage: INVITE_FILED_NOTE
50424
50426
  });
50425
50427
  }
50426
50428
  return minifiedResult({
50427
50429
  responded: null,
50428
50430
  eventId: id,
50429
50431
  sentResponse: notify,
50430
- warning: `Outlook accepted the response but re-reading the event failed (${e instanceof Error ? e.message : String(e)}), so it is unverified.`
50432
+ warning: `Outlook accepted the response but re-reading the event failed (${e instanceof Error ? e.message : String(e)}), so it is unverified.`,
50433
+ inviteMessage: INVITE_FILED_NOTE
50431
50434
  });
50432
50435
  }
50433
50436
  const got = after?.ResponseStatus?.Response;
@@ -50436,7 +50439,8 @@ function registerWriteTools(server, client2) {
50436
50439
  eventId: id,
50437
50440
  response: got,
50438
50441
  sentResponse: notify,
50439
- ...got === status ? {} : { warning: `Outlook accepted the response but the status did not change (still ${got ?? "unknown"}).` }
50442
+ ...got === status ? {} : { warning: `Outlook accepted the response but the status did not change (still ${got ?? "unknown"}).` },
50443
+ inviteMessage: INVITE_FILED_NOTE
50440
50444
  });
50441
50445
  }
50442
50446
  );
@@ -50557,12 +50561,26 @@ function registerWriteTools(server, client2) {
50557
50561
  ...forwardTo
50558
50562
  });
50559
50563
  } catch (e) {
50564
+ const what = `your comment${mode === "forward" ? " and recipients" : ""}`;
50565
+ const reason = e instanceof Error ? e.message : String(e);
50566
+ if (isCredentialFailure(e)) {
50567
+ throw new McpToolError(
50568
+ `The reply draft was created (draftId ${created.Id}) but adding ${what} failed: ${reason}`,
50569
+ {
50570
+ hint: `${e instanceof McpToolError && e.hint ? `${e.hint} ` : ""}Then edit draft ${created.Id} in Drafts, or delete it before trying again.`,
50571
+ cause: e,
50572
+ kind: errorKindOf(e),
50573
+ status: errorStatusOf(e)
50574
+ }
50575
+ );
50576
+ }
50577
+ const warning = e instanceof WriteOutcomeUnknownError ? `The draft was created, and ${what} may have been applied: Outlook did not confirm the edit (${e.timedOut ? "it did not answer in time" : "the connection dropped"}). Check the draft in Drafts before editing it again or trying again.` : `The draft was created but adding ${what} failed (${reason}). Edit it in Drafts, or delete it before trying again.`;
50560
50578
  return minifiedResult({
50561
50579
  drafted: true,
50562
50580
  mode,
50563
50581
  draftId: created.Id,
50564
50582
  WebLink: created.WebLink,
50565
- warning: `The draft was created but adding your comment${mode === "forward" ? " and recipients" : ""} failed (${e instanceof Error ? e.message : String(e)}). Edit it in Drafts, or delete it before trying again.`
50583
+ warning
50566
50584
  });
50567
50585
  }
50568
50586
  return minifiedResult({ drafted: true, mode, draftId: created.Id, WebLink: created.WebLink });
@@ -40,7 +40,7 @@ function trimTime(dt) {
40
40
  const attendeeList = (what) => z.array(z.string().min(3)).describe(`Email addresses of ${what}`);
41
41
  export function registerCalendarTools(server, client) {
42
42
  server.registerTool('outlook_list_events', {
43
- description: 'List calendar events in a date window. This uses the calendar VIEW, which expands recurring series into their individual occurrences — the right tool for "what is on my schedule". Times are returned in `timeZone` when given.' +
43
+ description: 'List calendar events in a date window. This uses the calendar VIEW, which expands recurring series into their individual occurrences — the right tool for "what is on my schedule". Times are returned in `timeZone` when given. view:"raw" returns Outlook\'s records as-is and uncapped; the list carries no Attendees in any view — outlook_get_event returns them.' +
44
44
  ' ' + UNTRUSTED_DESCRIPTION_SUFFIX,
45
45
  annotations: { readOnlyHint: true, openWorldHint: true },
46
46
  inputSchema: z.object({
@@ -75,7 +75,7 @@ export function registerCalendarTools(server, client) {
75
75
  return mailboxUntrusted(projectCollection(data, v === 'full' ? fullEvent : compactEvent, 'event'));
76
76
  });
77
77
  server.registerTool('outlook_get_event', {
78
- description: 'Get one calendar event in full, including attendees and their responses.' +
78
+ description: 'Get one calendar event in full, including attendees and their responses. Attendees appear in view:"full", every one of them — there is no 20-address cap as on a message\'s To/Cc; view:"raw" returns Outlook\'s untouched record, uncapped, Attendees included.' +
79
79
  ' ' + UNTRUSTED_DESCRIPTION_SUFFIX,
80
80
  annotations: { readOnlyHint: true, openWorldHint: true },
81
81
  inputSchema: z.object({
@@ -240,7 +240,7 @@ export function registerMailTools(server, client) {
240
240
  return mailboxUntrusted(v === 'full' ? fullMessage(data) : compactMessage(data));
241
241
  });
242
242
  server.registerTool('outlook_get_unread', {
243
- description: 'Triage batch: the unread messages in a folder (default inbox), newest first, each WITH its plain-text body (truncated to `maxBodyChars`) in one call — no follow-up outlook_get_message needed. Every item has a `kind`: "mail", "meetingRequest", "meetingCancelled" or "meetingResponse" (a reply to an invite you sent). Requests and cancellations also carry `event` (id, time, organizer, your current `responseStatus`) so the invite can be answered by its event id (when the meeting is no longer on your calendar — already declined, cancelled or deleted — the item has a `hint` instead and there is nothing to respond to). `to`/`cc` list at most 20 addresses; a longer list adds `toCount`/`ccCount` and `toTruncated`/`ccTruncated`. This does NOT mark anything read. Processing loop: read the batch → act on each item (reply, respond to the invite, file, flag — or leave it) → call outlook_mark_read on the ids you handled, so the next call returns only what is still new. Follow `nextLink` for more.' +
243
+ description: 'Triage batch: the unread messages in a folder (default inbox), newest first, each WITH its plain-text body (truncated to `maxBodyChars`) in one call — no follow-up outlook_get_message needed. Every item has a `kind`: "mail", "meetingRequest", "meetingCancelled" or "meetingResponse" (a reply to an invite you sent). Requests and cancellations also carry `event` (id, time, organizer, your current `responseStatus`) so the invite can be answered by its event id (when the meeting is no longer on your calendar — already declined, cancelled or deleted — the item has a `hint` instead and there is nothing to respond to). `to`/`cc` list at most 20 addresses; a longer list adds `toCount`/`ccCount` and `toTruncated`/`ccTruncated`. This does NOT mark anything read. Processing loop: read the batch → act on each item (reply, respond to the invite, file, flag — or leave it) → call outlook_mark_read on the plain mail you handled, so the next call returns only what is still new. An invite you answer with outlook_respond_to_invite needs no mark-read: Outlook moves it to Deleted Items under a new id, so its old id would 404. Follow `nextLink` for more.' +
244
244
  ' ' + UNTRUSTED_DESCRIPTION_SUFFIX,
245
245
  annotations: { readOnlyHint: true, openWorldHint: true },
246
246
  inputSchema: z.object({
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { CONFIRM_FLOW_SENTENCE, CONFIRM_INJECTION_RULE, confirmTokenParam, confirmWrite, errorStatusOf, mapWithConcurrency, McpToolError, minifiedResult, WriteOutcomeUnknownError, } from '@chrischall/mcp-utils';
2
+ import { CONFIRM_FLOW_SENTENCE, CONFIRM_INJECTION_RULE, confirmTokenParam, confirmWrite, errorKindOf, errorStatusOf, mapWithConcurrency, McpToolError, minifiedResult, WriteOutcomeUnknownError, } from '@chrischall/mcp-utils';
3
3
  import { isCredentialFailure } from '../client.js';
4
4
  import { mailboxTimeZone } from '../timezone.js';
5
5
  import { cappedRecipients, MAX_RECIPIENTS } from '../view.js';
@@ -90,6 +90,12 @@ async function writeOrUnknown(run, checkWhere) {
90
90
  }
91
91
  const INVITE_EVENT_SELECT = 'Id,Subject,IsOrganizer,IsCancelled,Start,End,Organizer,ResponseStatus';
92
92
  const DELETE_EVENT_SELECT = 'Id,Subject,IsOrganizer,IsCancelled,ResponseStatus,Type,SeriesMasterId,Start,End,Organizer,Attendees';
93
+ /**
94
+ * What happens to the invite MESSAGE once you respond. Live 2026-10-10:
95
+ * Outlook moves it to Deleted Items under a new id, so the triage loop's
96
+ * "respond, then outlook_mark_read the ids you handled" 404'd on every invite.
97
+ */
98
+ const INVITE_FILED_NOTE = 'Outlook moves the invite message out of your inbox (to Deleted Items, under a new id) once you respond, so it is already filed: do not call outlook_mark_read or outlook_move_message on its old id.';
93
99
  /** Tool input → the action segment Outlook takes and the ResponseStatus it should leave. */
94
100
  const INVITE_RESPONSES = {
95
101
  accept: { verb: 'accept', status: 'Accepted' },
@@ -655,7 +661,7 @@ export function registerWriteTools(server, client) {
655
661
  });
656
662
  });
657
663
  server.registerTool('outlook_respond_to_invite', {
658
- description: 'Accept, tentatively accept or decline a meeting invitation. Pass the invite\'s `messageId` (from outlook_get_unread, which marks it kind "meetingRequest") or the event\'s `eventId` — exactly one. The organizer is sent your response unless `sendResponse: false`, in which case only your calendar changes and no confirmation is asked. Cancelled meetings and meetings you organize are refused. The result is verified by re-reading the event\'s response status; a declined meeting may leave your calendar entirely.' +
664
+ description: 'Accept, tentatively accept or decline a meeting invitation. Pass the invite\'s `messageId` (from outlook_get_unread, which marks it kind "meetingRequest") or the event\'s `eventId` — exactly one. The organizer is sent your response unless `sendResponse: false`, in which case only your calendar changes and no confirmation is asked. Cancelled meetings and meetings you organize are refused. The result is verified by re-reading the event\'s response status; a declined meeting may leave your calendar entirely. Outlook then moves the invite message itself to Deleted Items (under a new id), so an answered invite needs no outlook_mark_read.' +
659
665
  ' ' +
660
666
  CONFIRM_FLOW_SENTENCE +
661
667
  ' ' +
@@ -765,6 +771,7 @@ export function registerWriteTools(server, client) {
765
771
  response: status,
766
772
  sentResponse: notify,
767
773
  note: 'The declined meeting is no longer on your calendar.',
774
+ inviteMessage: INVITE_FILED_NOTE,
768
775
  });
769
776
  }
770
777
  return minifiedResult({
@@ -772,6 +779,7 @@ export function registerWriteTools(server, client) {
772
779
  eventId: id,
773
780
  sentResponse: notify,
774
781
  warning: `Outlook accepted the response but re-reading the event failed (${e instanceof Error ? e.message : String(e)}), so it is unverified.`,
782
+ inviteMessage: INVITE_FILED_NOTE,
775
783
  });
776
784
  }
777
785
  const got = after?.ResponseStatus?.Response;
@@ -783,6 +791,7 @@ export function registerWriteTools(server, client) {
783
791
  ...(got === status
784
792
  ? {}
785
793
  : { warning: `Outlook accepted the response but the status did not change (still ${got ?? 'unknown'}).` }),
794
+ inviteMessage: INVITE_FILED_NOTE,
786
795
  });
787
796
  });
788
797
  server.registerTool('outlook_delete_event', {
@@ -925,14 +934,31 @@ export function registerWriteTools(server, client) {
925
934
  });
926
935
  }
927
936
  catch (e) {
937
+ const what = `your comment${mode === 'forward' ? ' and recipients' : ''}`;
938
+ const reason = e instanceof Error ? e.message : String(e);
939
+ // A rejected or missing credential is not about this draft: every
940
+ // next call fails the same way, so it stays the tool error — with
941
+ // the draft id in it, so a retry does not leave a second draft.
942
+ if (isCredentialFailure(e)) {
943
+ throw new McpToolError(`The reply draft was created (draftId ${created.Id}) but adding ${what} failed: ${reason}`, {
944
+ hint: `${e instanceof McpToolError && e.hint ? `${e.hint} ` : ''}Then edit draft ${created.Id} in Drafts, or delete it before trying again.`,
945
+ cause: e,
946
+ kind: errorKindOf(e),
947
+ status: errorStatusOf(e),
948
+ });
949
+ }
928
950
  // The draft exists either way; an error here would hide its id and
929
- // invite a retry that leaves a second one.
951
+ // invite a retry that leaves a second one. A PATCH that was sent
952
+ // but never answered may well have landed, so it is not "failed".
953
+ const warning = e instanceof WriteOutcomeUnknownError
954
+ ? `The draft was created, and ${what} may have been applied: Outlook did not confirm the edit (${e.timedOut ? 'it did not answer in time' : 'the connection dropped'}). Check the draft in Drafts before editing it again or trying again.`
955
+ : `The draft was created but adding ${what} failed (${reason}). Edit it in Drafts, or delete it before trying again.`;
930
956
  return minifiedResult({
931
957
  drafted: true,
932
958
  mode,
933
959
  draftId: created.Id,
934
960
  WebLink: created.WebLink,
935
- warning: `The draft was created but adding your comment${mode === 'forward' ? ' and recipients' : ''} failed (${e instanceof Error ? e.message : String(e)}). Edit it in Drafts, or delete it before trying again.`,
961
+ warning,
936
962
  });
937
963
  }
938
964
  return minifiedResult({ drafted: true, mode, draftId: created.Id, WebLink: created.WebLink });
package/dist/version.js CHANGED
@@ -10,4 +10,4 @@
10
10
  * line below and nowhere else — not even in prose.
11
11
  */
12
12
  export const PACKAGE_NAME = 'office-outlook-mcp';
13
- export const VERSION = '0.5.0'; // x-release-please-version
13
+ export const VERSION = '0.5.1'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/office-outlook-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "mcpName": "io.github.chrischall/office-outlook-mcp",
5
5
  "description": "Outlook / Microsoft 365 MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/office-outlook-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.5.0",
9
+ "version": "0.5.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/office-outlook-mcp",
14
- "version": "0.5.0",
14
+ "version": "0.5.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -70,6 +70,34 @@
70
70
  "description": "Absolute path where the fetchproxy long-term identity keypair is stored (default ~/.fetchproxy/identity/).",
71
71
  "isRequired": false,
72
72
  "format": "string"
73
+ },
74
+ {
75
+ "name": "MCP_CONFIRM_MODE",
76
+ "description": "What outlook_send_mail, outlook_reply, outlook_respond_to_invite and the other write tools do on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop): \"ask-user\" (default: the first call returns a preview and a confirmToken, and the model must get your approval in chat before using it), \"auto\" (the model may use the token after reviewing the preview itself) or \"refuse\". An unrecognised value is treated as refuse.",
77
+ "isRequired": false,
78
+ "format": "string",
79
+ "isSecret": false
80
+ },
81
+ {
82
+ "name": "MCP_CONFIRM_ELICITATION",
83
+ "description": "\"off\" never shows a confirmation prompt, so every client gets the MCP_CONFIRM_MODE flow. Set it for a client that says it can show prompts but never does. Defaults to on.",
84
+ "isRequired": false,
85
+ "format": "string",
86
+ "isSecret": false
87
+ },
88
+ {
89
+ "name": "MCP_CONFIRM_TTL_SECONDS",
90
+ "description": "How long a confirmToken stays valid, in seconds. Defaults to 600.",
91
+ "isRequired": false,
92
+ "format": "string",
93
+ "isSecret": false
94
+ },
95
+ {
96
+ "name": "MCP_CONFIRM_SECRET",
97
+ "description": "Signing key for confirmTokens. Random per process by default; set it only if tokens must survive a server restart.",
98
+ "isRequired": false,
99
+ "format": "string",
100
+ "isSecret": true
73
101
  }
74
102
  ]
75
103
  }