@chrischall/office-outlook-mcp 0.8.0 → 0.8.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.8.0"
10
+ "version": "0.8.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.8.0",
18
+ "version": "0.8.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.8.0",
4
+ "version": "0.8.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/dist/bundle.js CHANGED
@@ -39086,7 +39086,7 @@ var init_version = __esm({
39086
39086
  "src/version.ts"() {
39087
39087
  "use strict";
39088
39088
  PACKAGE_NAME = "office-outlook-mcp";
39089
- VERSION = "0.8.0";
39089
+ VERSION = "0.8.1";
39090
39090
  }
39091
39091
  });
39092
39092
 
@@ -50429,7 +50429,7 @@ function workMeta(intent, extra = {}) {
50429
50429
  return { mint: { domain: "work", intent, ...extra } };
50430
50430
  }
50431
50431
  var BRIEF_MAX = 120;
50432
- var BRIEF_NOTE = '"brief": one line per item, `title \u2014 <ISO date or "no date"> \u2014 <status>`, at most 120 chars each, as `data` in the untrusted-content envelope; a last line `-- more` means another page exists (re-ask without "brief" for its nextLink).';
50432
+ var BRIEF_NOTE = '"brief": one line per item, `title \u2014 <ISO date or "no date"> \u2014 <status>`, at most 120 chars each, as `data` in the untrusted-content envelope; a line starting `-- ` is a note (`-- none` for an empty page; a title starting `-- ` or `\\` gets one leading `\\`); a last line `-- more` means another page exists (re-ask without "brief" for its nextLink).';
50433
50433
  function briefViewParam(rungs, note) {
50434
50434
  const base = viewParam(rungs);
50435
50435
  return external_exports.enum([...rungs, "brief"]).optional().describe(`${base.description ?? ""} ${BRIEF_NOTE} ${note}`.trim());
@@ -50441,15 +50441,23 @@ function flat(s) {
50441
50441
  function briefDate(dt) {
50442
50442
  return flat(dt?.replace(/\.0+(?=Z?$)/, "")) ?? "no date";
50443
50443
  }
50444
+ var TITLE_FLOOR = 24;
50445
+ var cut = (s, n) => s.length > n ? `${s.slice(0, Math.max(n - 1, 0))}\u2026` : s;
50444
50446
  function briefLine(title, date5, status) {
50445
- const tail = ` \u2014 ${date5} \u2014 ${status}`;
50446
- const shown = flat(title) ?? "(no subject)";
50447
- const room = BRIEF_MAX - tail.length;
50448
- return `${shown.length > room ? `${shown.slice(0, Math.max(room - 1, 0))}\u2026` : shown}${tail}`;
50447
+ const raw = flat(title) ?? "(no subject)";
50448
+ const shown = raw.startsWith("-- ") || raw.startsWith("\\") ? `\\${raw}` : raw;
50449
+ const sep2 = ` \u2014 ${date5} \u2014 `;
50450
+ const room = BRIEF_MAX - sep2.length;
50451
+ if (shown.length + status.length <= room) return `${shown}${sep2}${status}`;
50452
+ const titleRoom = Math.max(room - status.length, Math.min(shown.length, TITLE_FLOOR));
50453
+ const t = cut(shown, titleRoom);
50454
+ return cut(`${t}${sep2}${cut(status, room - t.length)}`, BRIEF_MAX);
50449
50455
  }
50450
50456
  var BRIEF_MORE = "-- more";
50457
+ var BRIEF_NONE = "-- none";
50451
50458
  function briefResult(lines, hasMore = false) {
50452
- return mailboxUntrusted(hasMore ? [...lines, BRIEF_MORE] : lines);
50459
+ const items = lines.length > 0 ? lines : [BRIEF_NONE];
50460
+ return mailboxUntrusted(hasMore ? [...items, BRIEF_MORE] : items);
50453
50461
  }
50454
50462
  function spaced(value) {
50455
50463
  return value.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
@@ -50914,7 +50922,7 @@ function registerMailTools(server, client2) {
50914
50922
  _meta: workMeta("whats-new", { canonical: true, aliases: ["email", "mail", "inbox", "unread", "new mail", "work email"] }),
50915
50923
  inputSchema: external_exports.object({
50916
50924
  view: external_exports.enum(["brief"]).optional().describe(
50917
- 'Omit for the triage batch above. "brief": one line per message, `<sender>: <subject> \u2014 <received ISO time or "no date"> \u2014 <unread | meeting request | meeting cancelled | meeting response>`, at most 120 chars each, as `data` in the untrusted-content envelope; fetches no body and no invite; a last line `-- more` means another page exists (re-ask without "brief" for its nextLink / next_from).'
50925
+ 'Omit for the triage batch above. "brief": one line per message, `<sender>: <subject> \u2014 <received ISO time or "no date"> \u2014 <unread | meeting request | meeting cancelled | meeting response>`, at most 120 chars each, as `data` in the untrusted-content envelope; fetches no body and no invite; a line starting `-- ` is a note (`-- none` for an empty page; a title starting `-- ` or `\\` gets one leading `\\`); a last line `-- more` means another page exists (re-ask without "brief" for its nextLink / next_from).'
50918
50926
  ),
50919
50927
  folder: external_exports.string().min(1).optional().describe('Folder id or well-known name (default "inbox")'),
50920
50928
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max messages (default 25, max 50)"),
@@ -20,7 +20,7 @@ export function workMeta(intent, extra = {}) {
20
20
  }
21
21
  /** The longest a brief line may be. */
22
22
  export const BRIEF_MAX = 120;
23
- const BRIEF_NOTE = '"brief": one line per item, `title — <ISO date or "no date"> — <status>`, at most 120 chars each, as `data` in the untrusted-content envelope; a last line `-- more` means another page exists (re-ask without "brief" for its nextLink).';
23
+ const BRIEF_NOTE = '"brief": one line per item, `title — <ISO date or "no date"> — <status>`, at most 120 chars each, as `data` in the untrusted-content envelope; a line starting `-- ` is a note (`-- none` for an empty page; a title starting `-- ` or `\\` gets one leading `\\`); a last line `-- more` means another page exists (re-ask without "brief" for its nextLink).';
24
24
  /**
25
25
  * The `view` parameter of a canonical read: the tool's own rungs plus
26
26
  * `brief`. Absent still means the tool's default.
@@ -41,26 +41,45 @@ function flat(s) {
41
41
  export function briefDate(dt) {
42
42
  return flat(dt?.replace(/\.0+(?=Z?$)/, '')) ?? 'no date';
43
43
  }
44
- /** `title — date — status`, the title cut with an ellipsis to keep the line ≤ 120. */
44
+ /** The shortest a title is cut to before the status gives way too. */
45
+ const TITLE_FLOOR = 24;
46
+ const cut = (s, n) => (s.length > n ? `${s.slice(0, Math.max(n - 1, 0))}…` : s);
47
+ /**
48
+ * `title — date — status`, cut to ≤ 120 chars as a WHOLE, title first: the
49
+ * title shortens until the line fits; once it is at its floor the status is
50
+ * cut too (and, past that, the line itself). A title that starts with `-- `
51
+ * or `\` is escaped with one leading `\`, so a subject or sender name can
52
+ * never read as a note — mcp-host strips that one backslash from every item
53
+ * line after its note check.
54
+ */
45
55
  export function briefLine(title, date, status) {
46
- const tail = ` — ${date} — ${status}`;
47
- const shown = flat(title) ?? '(no subject)';
48
- const room = BRIEF_MAX - tail.length;
49
- return `${shown.length > room ? `${shown.slice(0, Math.max(room - 1, 0))}…` : shown}${tail}`;
56
+ const raw = flat(title) ?? '(no subject)';
57
+ const shown = raw.startsWith('-- ') || raw.startsWith('\\') ? `\\${raw}` : raw;
58
+ const sep = ` — ${date} — `;
59
+ const room = BRIEF_MAX - sep.length;
60
+ if (shown.length + status.length <= room)
61
+ return `${shown}${sep}${status}`;
62
+ const titleRoom = Math.max(room - status.length, Math.min(shown.length, TITLE_FLOOR));
63
+ const t = cut(shown, titleRoom);
64
+ return cut(`${t}${sep}${cut(status, room - t.length)}`, BRIEF_MAX);
50
65
  }
51
66
  /** The note line a brief answer ends with when Outlook holds another page. */
52
67
  export const BRIEF_MORE = '-- more';
68
+ /** The single note line a brief answer with no items carries. */
69
+ export const BRIEF_NONE = '-- none';
53
70
  /**
54
71
  * One line per item, inside the untrusted-data envelope as `data: string[]`
55
72
  * (mcp-host's brief-view contract): sender names, subjects and event text are
56
73
  * written by other people, so a brief line is fenced like every other rung of
57
- * these tools. A line starting with `-- ` is a NOTE, not an item: `-- more`
74
+ * these tools. A line starting with `-- ` is a NOTE, not an item: `-- none`
75
+ * answers an empty page, and `-- more`
58
76
  * says Outlook has another page (it gives no total, so no `N of M`) — the
59
77
  * brief view never silently drops one. Re-ask without `view: "brief"` for the
60
78
  * `nextLink` / `next_from` to follow it.
61
79
  */
62
80
  export function briefResult(lines, hasMore = false) {
63
- return mailboxUntrusted(hasMore ? [...lines, BRIEF_MORE] : lines);
81
+ const items = lines.length > 0 ? lines : [BRIEF_NONE];
82
+ return mailboxUntrusted(hasMore ? [...items, BRIEF_MORE] : items);
64
83
  }
65
84
  /** `NotStarted` → `not started`, `TentativelyAccepted` → `tentatively accepted`. */
66
85
  export function spaced(value) {
@@ -306,7 +306,7 @@ export function registerMailTools(server, client) {
306
306
  view: z
307
307
  .enum(['brief'])
308
308
  .optional()
309
- .describe('Omit for the triage batch above. "brief": one line per message, `<sender>: <subject> — <received ISO time or "no date"> — <unread | meeting request | meeting cancelled | meeting response>`, at most 120 chars each, as `data` in the untrusted-content envelope; fetches no body and no invite; a last line `-- more` means another page exists (re-ask without "brief" for its nextLink / next_from).'),
309
+ .describe('Omit for the triage batch above. "brief": one line per message, `<sender>: <subject> — <received ISO time or "no date"> — <unread | meeting request | meeting cancelled | meeting response>`, at most 120 chars each, as `data` in the untrusted-content envelope; fetches no body and no invite; a line starting `-- ` is a note (`-- none` for an empty page; a title starting `-- ` or `\\` gets one leading `\\`); a last line `-- more` means another page exists (re-ask without "brief" for its nextLink / next_from).'),
310
310
  folder: z.string().min(1).optional().describe('Folder id or well-known name (default "inbox")'),
311
311
  limit: z.number().int().min(1).max(50).optional().describe('Max messages (default 25, max 50)'),
312
312
  sinceHours: z
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.8.0'; // x-release-please-version
13
+ export const VERSION = '0.8.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.8.0",
3
+ "version": "0.8.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.8.0",
9
+ "version": "0.8.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/office-outlook-mcp",
14
- "version": "0.8.0",
14
+ "version": "0.8.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -86,8 +86,10 @@ When the office-outlook-mcp server is hosted on mcp-host, its tools join the
86
86
  each answered with `view: "brief"` (one line per item,
87
87
  `title — <ISO date | no date> — <status>`, returned inside the
88
88
  untrusted-content envelope as `data: string[]` because the text is other
89
- people's; a final `-- more` line is a note that Outlook has another page —
90
- re-ask without `brief` for the `nextLink`). `brief` is additive: on
89
+ people's; a line starting `-- ` is a note — `-- none` answers an empty page,
90
+ and a final `-- more` says Outlook has another page, so re-ask without
91
+ `brief` for the `nextLink`; a title starting `-- ` or `\` is escaped with one
92
+ leading `\`). `brief` is additive: on
91
93
  `outlook_list_events` and `outlook_list_tasks` it joins the existing
92
94
  `compact | full | raw` values, and `outlook_list_unread_messages` takes a
93
95
  new optional `view` whose only value is `brief`: