@littlebearapps/outlook-assistant 3.13.0 → 3.14.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.
- package/.env.example +30 -3
- package/README.md +67 -27
- package/advanced/index.js +44 -174
- package/auth/auth-errors.js +23 -1
- package/auth/oauth-server.js +7 -1
- package/auth/token-manager.js +7 -3
- package/auth/token-storage.js +28 -30
- package/auth/tools.js +61 -82
- package/calendar/attendees.js +36 -0
- package/calendar/cancel.js +9 -25
- package/calendar/create.js +42 -48
- package/calendar/decline.js +10 -25
- package/calendar/delete.js +10 -25
- package/calendar/index.js +20 -37
- package/calendar/list.js +4 -16
- package/calendar/preview.js +461 -0
- package/calendar/update.js +55 -83
- package/categories/index.js +68 -265
- package/config.js +29 -1
- package/contacts/index.js +72 -128
- package/email/attachments.js +43 -125
- package/email/conversations.js +44 -78
- package/email/delta.js +69 -46
- package/email/draft.js +170 -103
- package/email/export.js +145 -110
- package/email/folder-utils.js +3 -2
- package/email/headers.js +11 -49
- package/email/index.js +86 -110
- package/email/list.js +4 -17
- package/email/mail-tips.js +86 -57
- package/email/mark-as-read.js +13 -49
- package/email/mime.js +39 -51
- package/email/read.js +16 -50
- package/email/search.js +47 -87
- package/email/send.js +82 -48
- package/folder/create.js +6 -25
- package/folder/delete.js +117 -38
- package/folder/index.js +19 -17
- package/folder/list.js +5 -17
- package/folder/move.js +13 -42
- package/folder/resolve.js +11 -6
- package/folder/stats.js +18 -27
- package/index.js +39 -45
- package/llms-install.md +22 -4
- package/llms.txt +20 -11
- package/outlook-auth-server.js +10 -3
- package/package.json +4 -1
- package/request-handler.js +217 -116
- package/rules/create.js +28 -71
- package/rules/index.js +52 -93
- package/rules/list.js +7 -19
- package/rules/rule-builder.js +59 -22
- package/rules/update.js +27 -61
- package/server.js +41 -0
- package/settings/index.js +162 -145
- package/tools.js +30 -0
- package/utils/field-presets.js +4 -2
- package/utils/graph-api.js +65 -22
- package/utils/logger.js +251 -0
- package/utils/mock-data.js +91 -2
- package/utils/read-only.js +59 -0
- package/utils/response-formatter.js +54 -15
- package/utils/risk-classes.js +324 -0
- package/utils/safe-write.js +372 -6
- package/utils/safety.js +247 -42
- package/utils/server-instructions.js +73 -0
- package/utils/tool-error.js +33 -0
package/email/index.js
CHANGED
|
@@ -27,20 +27,16 @@ const handleDraft = require('./draft');
|
|
|
27
27
|
|
|
28
28
|
// Import flag handlers from advanced module
|
|
29
29
|
const { handleSetMessageFlag, handleClearMessageFlag } = require('../advanced');
|
|
30
|
+
const { toolMetadata } = require('../utils/risk-classes');
|
|
31
|
+
const { toolError } = require('../utils/tool-error');
|
|
30
32
|
|
|
31
33
|
// Consolidated email tool definitions (17 → 6)
|
|
32
34
|
const emailTools = [
|
|
33
35
|
{
|
|
34
36
|
name: 'search-emails',
|
|
35
37
|
description:
|
|
36
|
-
'Search, list, delta-sync
|
|
37
|
-
|
|
38
|
-
title: 'Search Emails',
|
|
39
|
-
readOnlyHint: true,
|
|
40
|
-
// openWorldHint: output includes email content authored by external
|
|
41
|
-
// senders (bodies/previews/threads) — may contain prompt-injection. (#92)
|
|
42
|
-
openWorldHint: true,
|
|
43
|
-
},
|
|
38
|
+
'Search, list, delta-sync or thread-group emails (read-only); parameters set the mode. No params: recent emails in `folder` (default `inbox`). `query`/`from`/`to`/`subject`/date filters: search, combined as an OData filter. `searchExpression` (deprecated alias `kqlQuery`): a raw Graph `$search` expression. `deltaMode: true`: current state plus a `deltaToken` to pass back next time for changes only. `groupByConversation: true`: conversation threads. `conversationId`: every message in one thread. `internetMessageId`: the message with that RFC Message-ID. `sharedMailbox` (alias `email`) searches a shared/delegated mailbox, custom folders and nested paths included. Personal Outlook.com accounts have limited `$search`, so the tool falls back to OData filters and a recent listing automatically; structured filters (`from`/`subject`/`receivedAfter`/`hasAttachments`/`unreadOnly`) give cleaner results there. Returns up to `count` messages (id/subject/from/receivedDateTime/preview); `outputVerbosity` expands them.',
|
|
39
|
+
...toolMetadata('search-emails', 'Search Emails'),
|
|
44
40
|
inputSchema: {
|
|
45
41
|
type: 'object',
|
|
46
42
|
properties: {
|
|
@@ -63,7 +59,7 @@ const emailTools = [
|
|
|
63
59
|
groupByConversation: {
|
|
64
60
|
type: 'boolean',
|
|
65
61
|
description:
|
|
66
|
-
'List conversations (threads) grouped by conversationId
|
|
62
|
+
'List conversations (threads) grouped by conversationId, not individual emails. Honors `sharedMailbox`/`email` (and custom `folder` paths) to group within a shared/delegated mailbox.',
|
|
67
63
|
},
|
|
68
64
|
// Search/list params
|
|
69
65
|
query: {
|
|
@@ -79,7 +75,7 @@ const emailTools = [
|
|
|
79
75
|
kqlQuery: {
|
|
80
76
|
type: 'string',
|
|
81
77
|
description:
|
|
82
|
-
'DEPRECATED alias for `searchExpression` (this was never full KQL — it is a Graph `$search` expression).
|
|
78
|
+
'DEPRECATED alias for `searchExpression` (this was never full KQL — it is a Graph `$search` expression).',
|
|
83
79
|
},
|
|
84
80
|
folder: {
|
|
85
81
|
type: 'string',
|
|
@@ -89,7 +85,7 @@ const emailTools = [
|
|
|
89
85
|
sharedMailbox: {
|
|
90
86
|
type: 'string',
|
|
91
87
|
description:
|
|
92
|
-
'Email address of a shared/delegated mailbox to search
|
|
88
|
+
'Email address of a shared/delegated mailbox to search (default: the signed-in account). Combine with `folder` (incl. custom subfolders/paths) or `searchAllFolders`. Work/school only; needs the server opt-in setting OUTLOOK_SHARED_MAILBOX (otherwise the call is refused with setup guidance).',
|
|
93
89
|
},
|
|
94
90
|
email: {
|
|
95
91
|
type: 'string',
|
|
@@ -131,7 +127,7 @@ const emailTools = [
|
|
|
131
127
|
count: {
|
|
132
128
|
type: 'number',
|
|
133
129
|
description:
|
|
134
|
-
'Number of results (list default: 25, search default: 10, max: 50)',
|
|
130
|
+
'Number of results (list default: 25, search default: 10, max: 50). There is no page cursor: when the result says more emails are available, raise `count` or narrow `receivedAfter`/`receivedBefore`.',
|
|
135
131
|
},
|
|
136
132
|
outputVerbosity: {
|
|
137
133
|
type: 'string',
|
|
@@ -200,13 +196,8 @@ const emailTools = [
|
|
|
200
196
|
{
|
|
201
197
|
name: 'read-email',
|
|
202
198
|
description:
|
|
203
|
-
'Read a single email by id (read-only).
|
|
204
|
-
|
|
205
|
-
title: 'Read Email',
|
|
206
|
-
readOnlyHint: true,
|
|
207
|
-
// openWorldHint: returns full message body from external senders. (#92)
|
|
208
|
-
openWorldHint: true,
|
|
209
|
-
},
|
|
199
|
+
'Read a single email by id (read-only). Returns subject, from/to/cc, date and the body as Markdown (HTML stripped to text): up to 2,000 characters by default, up to 40,000 with `outputVerbosity: full`; a cut body ends with a note on how to get the rest. With `headersMode: true`: returns RFC-822 forensic headers in place of the body (DKIM, SPF, DMARC, Received chain, Message-ID, Authentication-Results) — `importantOnly: true` for the security-relevant subset, `groupByType: true` for a category-bucketed view, `raw: true` for JSON. With `includeHeaders: true` (non-headers-mode): adds basic headers alongside the body. **If the id came from a shared/delegated mailbox (e.g. via `search-emails` or `access-shared-mailbox` with `sharedMailbox` set), you MUST pass the same `sharedMailbox` (or alias `email`) here** — message IDs are mailbox-scoped, and reading a shared-mailbox id without it fails with 404 ErrorInvalidMailboxItemId.',
|
|
200
|
+
...toolMetadata('read-email', 'Read Email'),
|
|
210
201
|
inputSchema: {
|
|
211
202
|
type: 'object',
|
|
212
203
|
properties: {
|
|
@@ -226,7 +217,7 @@ const emailTools = [
|
|
|
226
217
|
headersMode: {
|
|
227
218
|
type: 'boolean',
|
|
228
219
|
description:
|
|
229
|
-
'Return forensic headers
|
|
220
|
+
'Return forensic headers in place of the email content (default: false)',
|
|
230
221
|
},
|
|
231
222
|
includeHeaders: {
|
|
232
223
|
type: 'boolean',
|
|
@@ -236,7 +227,8 @@ const emailTools = [
|
|
|
236
227
|
outputVerbosity: {
|
|
237
228
|
type: 'string',
|
|
238
229
|
enum: ['minimal', 'standard', 'full'],
|
|
239
|
-
description:
|
|
230
|
+
description:
|
|
231
|
+
'Output detail level (default: standard). minimal: body preview only; standard: body up to 2,000 characters; full: adds IDs, body up to 40,000 characters. For a longer body, export it with `export` target=message.',
|
|
240
232
|
},
|
|
241
233
|
// Headers mode params
|
|
242
234
|
groupByType: {
|
|
@@ -252,7 +244,7 @@ const emailTools = [
|
|
|
252
244
|
raw: {
|
|
253
245
|
type: 'boolean',
|
|
254
246
|
description:
|
|
255
|
-
'Return raw JSON
|
|
247
|
+
'Return the headers as raw JSON, not Markdown (headersMode only, default: false)',
|
|
256
248
|
},
|
|
257
249
|
},
|
|
258
250
|
additionalProperties: false,
|
|
@@ -268,14 +260,8 @@ const emailTools = [
|
|
|
268
260
|
{
|
|
269
261
|
name: 'send-email',
|
|
270
262
|
description:
|
|
271
|
-
'Compose and send an email immediately (destructive: sends external comms). Returns a confirmation
|
|
272
|
-
|
|
273
|
-
title: 'Send Email',
|
|
274
|
-
readOnlyHint: false,
|
|
275
|
-
destructiveHint: true,
|
|
276
|
-
idempotentHint: false,
|
|
277
|
-
openWorldHint: true,
|
|
278
|
-
},
|
|
263
|
+
'Compose and send an email immediately (destructive: sends external comms). Returns a confirmation. Safety controls: `dryRun: true` returns the composed message for review without sending; `checkRecipients: true` runs `get-mail-tips` first and returns its warnings. If the tips show an out-of-office reply, a full mailbox, a delivery restriction or external recipients, the send is refused until repeated with `acknowledgeWarnings: true`. Personal Outlook.com accounts return no tips, and no warnings is not proof of delivery. Subject to the session limit (`OUTLOOK_MAX_SEND_EMAIL_PER_SESSION`, else `OUTLOOK_MAX_EMAILS_PER_SESSION`; 0 refuses every send) and recipient allowlist (`OUTLOOK_ALLOWED_RECIPIENTS`) when configured; both refuse before any Graph request. For a review-before-send workflow, use `draft` (action=`create` → `update` → `send`); a draft can be checked in Outlook before it goes. Comma-separated recipient strings or arrays both accepted.',
|
|
264
|
+
...toolMetadata('send-email', 'Send Email'),
|
|
279
265
|
inputSchema: {
|
|
280
266
|
type: 'object',
|
|
281
267
|
properties: {
|
|
@@ -316,7 +302,13 @@ const emailTools = [
|
|
|
316
302
|
checkRecipients: {
|
|
317
303
|
type: 'boolean',
|
|
318
304
|
description:
|
|
319
|
-
'Check recipients
|
|
305
|
+
'Check recipients with mail tips before sending (default: false). Out-of-office, mailbox full, delivery restrictions or external recipients refuse the send unless acknowledgeWarnings=true. Combine with dryRun=true for pre-send review.',
|
|
306
|
+
},
|
|
307
|
+
acknowledgeWarnings: {
|
|
308
|
+
type: 'boolean',
|
|
309
|
+
default: false,
|
|
310
|
+
description:
|
|
311
|
+
'Send even though checkRecipients flagged an out-of-office reply, a full mailbox, a delivery restriction or external recipients (default: false). Without it those warnings refuse the send. Pass only after the user has seen the warnings. No effect without checkRecipients.',
|
|
320
312
|
},
|
|
321
313
|
},
|
|
322
314
|
additionalProperties: false,
|
|
@@ -327,14 +319,8 @@ const emailTools = [
|
|
|
327
319
|
{
|
|
328
320
|
name: 'draft',
|
|
329
321
|
description:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
title: 'Draft Operations',
|
|
333
|
-
readOnlyHint: false,
|
|
334
|
-
destructiveHint: true,
|
|
335
|
-
idempotentHint: false,
|
|
336
|
-
openWorldHint: true,
|
|
337
|
-
},
|
|
322
|
+
"Draft lifecycle for review-before-send workflows (destructive: covers `send` and `delete`). action=`create` saves a new draft and returns its id (`dryRun: true` previews without saving; `checkRecipients: true` runs mail-tips first). action=`update` patches a draft by `id` (only fields passed change). action=`send` sends a draft and counts against the `send-email` session limit (0 refuses every send). action=`delete` moves a draft to Recoverable Items (restorable for a limited time). update/send/delete refuse any `id` that is not an unsent draft. action=`reply`/`reply-all` creates a reply draft from a message `id` (`comment` prepends text; not with `body`). action=`forward` creates a forward draft (needs `id` and `to`). The recipient allowlist (OUTLOOK_ALLOWED_RECIPIENTS) applies to create/update/forward, to the draft's current to/cc/bcc on send, and to reply/reply-all, whose draft is deleted if a recipient is not allowed. Returns the draft on create/update/reply/forward; a status on send/delete.",
|
|
323
|
+
...toolMetadata('draft', 'Draft Operations'),
|
|
338
324
|
inputSchema: {
|
|
339
325
|
type: 'object',
|
|
340
326
|
properties: {
|
|
@@ -390,7 +376,7 @@ const emailTools = [
|
|
|
390
376
|
dryRun: {
|
|
391
377
|
type: 'boolean',
|
|
392
378
|
description:
|
|
393
|
-
'Preview draft without saving
|
|
379
|
+
'Preview only (action=create): shows the draft without saving it. Other actions refuse dryRun and change nothing. Default false.',
|
|
394
380
|
},
|
|
395
381
|
checkRecipients: {
|
|
396
382
|
type: 'boolean',
|
|
@@ -406,14 +392,8 @@ const emailTools = [
|
|
|
406
392
|
{
|
|
407
393
|
name: 'update-email',
|
|
408
394
|
description:
|
|
409
|
-
'Update message state without modifying content (idempotent — safe to retry). action=`mark-read`/`mark-unread`
|
|
410
|
-
|
|
411
|
-
title: 'Update Email',
|
|
412
|
-
readOnlyHint: false,
|
|
413
|
-
destructiveHint: false,
|
|
414
|
-
idempotentHint: true,
|
|
415
|
-
openWorldHint: false,
|
|
416
|
-
},
|
|
395
|
+
'Update message state without modifying content (idempotent — safe to retry). action=`mark-read`/`mark-unread` sets `isRead` on a single message by `id`. action=`flag` sets a follow-up flag with optional `dueDateTime`/`startDateTime`: ISO 8601 with a time, kept as that exact instant when it has `Z` or a ±hh:mm offset and read in OUTLOOK_DEFAULT_TIMEZONE when it has none; date-only or unparseable values are refused before any change. With only `dueDateTime`, the start is 09:00 on the due date, or the due time if earlier. action=`unflag` clears the flag; action=`complete` marks it done. Flag/unflag/complete take `id` (single) or `ids` (batch, updated one at a time: one PATCH each, not Graph `$batch`). `sharedMailbox` (alias `email`) updates messages in a shared/delegated mailbox (default: the signed-in account; needs Mail.ReadWrite.Shared and delegate access). Returns a status per message.',
|
|
396
|
+
...toolMetadata('update-email', 'Update Email'),
|
|
417
397
|
inputSchema: {
|
|
418
398
|
type: 'object',
|
|
419
399
|
properties: {
|
|
@@ -425,7 +405,7 @@ const emailTools = [
|
|
|
425
405
|
id: {
|
|
426
406
|
type: 'string',
|
|
427
407
|
description:
|
|
428
|
-
'Single message ID (required for mark-read/mark-unread
|
|
408
|
+
'Single message ID (required for mark-read/mark-unread; flag actions take `id` or `ids`)',
|
|
429
409
|
},
|
|
430
410
|
ids: {
|
|
431
411
|
type: 'array',
|
|
@@ -447,7 +427,7 @@ const emailTools = [
|
|
|
447
427
|
sharedMailbox: {
|
|
448
428
|
type: 'string',
|
|
449
429
|
description:
|
|
450
|
-
'Email address of
|
|
430
|
+
'Email address of the shared/delegated mailbox whose message(s) to update (default: the signed-in account). Requires delegate access + Mail.ReadWrite.Shared. Work/school only; needs the server opt-in setting OUTLOOK_SHARED_MAILBOX (otherwise the call is refused with setup guidance).',
|
|
451
431
|
},
|
|
452
432
|
email: {
|
|
453
433
|
type: 'string',
|
|
@@ -491,29 +471,17 @@ const emailTools = [
|
|
|
491
471
|
sharedMailbox,
|
|
492
472
|
});
|
|
493
473
|
default:
|
|
494
|
-
return
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
type: 'text',
|
|
498
|
-
text: "Invalid action. Use 'mark-read', 'mark-unread', 'flag', 'unflag', or 'complete'.",
|
|
499
|
-
},
|
|
500
|
-
],
|
|
501
|
-
};
|
|
474
|
+
return toolError(
|
|
475
|
+
"Invalid action. Use 'mark-read', 'mark-unread', 'flag', 'unflag', or 'complete'."
|
|
476
|
+
);
|
|
502
477
|
}
|
|
503
478
|
},
|
|
504
479
|
},
|
|
505
480
|
{
|
|
506
481
|
name: 'attachments',
|
|
507
482
|
description:
|
|
508
|
-
'Inspect or retrieve email attachments. action=`list` (default) returns metadata for all attachments on `messageId` (id, name, contentType, size, isInline) — read-only. action=`view` returns inline content for text/JSON/XML attachments via `attachmentId`; binary types require download. action=`download` saves the attachment
|
|
509
|
-
|
|
510
|
-
title: 'Attachments',
|
|
511
|
-
readOnlyHint: false,
|
|
512
|
-
destructiveHint: false,
|
|
513
|
-
// openWorldHint: action=view returns attachment content supplied by
|
|
514
|
-
// external senders. (#92)
|
|
515
|
-
openWorldHint: true,
|
|
516
|
-
},
|
|
483
|
+
'Inspect or retrieve email attachments. action=`list` (default) returns metadata for all attachments on `messageId` (id, name, contentType, size, isInline) — read-only. action=`view` returns inline content for text/JSON/XML attachments via `attachmentId`; binary types require download. action=`download` saves the attachment under a new, unique name in `outputDir` (default system temp directory, auto-created; must be inside the temp directory, ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR) and returns the saved file path. `messageId` is required for all actions; `attachmentId` is required for view/download. If `messageId` came from a shared/delegated mailbox, pass the same `sharedMailbox` (or alias `email`) — attachment IDs are scoped to the message and fail under /me otherwise. Use `outputVerbosity` to control list field count.',
|
|
484
|
+
...toolMetadata('attachments', 'Attachments'),
|
|
517
485
|
inputSchema: {
|
|
518
486
|
type: 'object',
|
|
519
487
|
properties: {
|
|
@@ -542,7 +510,7 @@ const emailTools = [
|
|
|
542
510
|
outputDir: {
|
|
543
511
|
type: 'string',
|
|
544
512
|
description:
|
|
545
|
-
'
|
|
513
|
+
'Absolute directory (or ~/…) to save the file in (action=download, default: system temp directory). Auto-created if missing. Must be inside the system temp directory, ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR, with no dot-prefixed folder names.',
|
|
546
514
|
},
|
|
547
515
|
savePath: {
|
|
548
516
|
type: 'string',
|
|
@@ -563,29 +531,17 @@ const emailTools = [
|
|
|
563
531
|
case 'list':
|
|
564
532
|
return handleListAttachments(args);
|
|
565
533
|
default:
|
|
566
|
-
return
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
type: 'text',
|
|
570
|
-
text: `Unknown action '${action}'. Valid actions: list, view, download.`,
|
|
571
|
-
},
|
|
572
|
-
],
|
|
573
|
-
};
|
|
534
|
+
return toolError(
|
|
535
|
+
`Unknown action '${action}'. Valid actions: list, view, download.`
|
|
536
|
+
);
|
|
574
537
|
}
|
|
575
538
|
},
|
|
576
539
|
},
|
|
577
540
|
{
|
|
578
541
|
name: 'export',
|
|
579
542
|
description:
|
|
580
|
-
'Export emails to
|
|
581
|
-
|
|
582
|
-
title: 'Export Emails',
|
|
583
|
-
readOnlyHint: false,
|
|
584
|
-
destructiveHint: false,
|
|
585
|
-
// openWorldHint: exports full message/MIME/conversation content from
|
|
586
|
-
// external senders. (#92)
|
|
587
|
-
openWorldHint: true,
|
|
588
|
-
},
|
|
543
|
+
'Export emails to files. target=`message` (default) exports one email by `id` (mime/eml/markdown/json/csv) to `savePath`: a directory (or a path ending in `/`, created if missing) gets a new, unique file name; a file path is created new and an existing file is replaced only with `overwrite: true`. target=`messages` batch-exports `emailIds`, or matches for `searchQuery`/`query`, into `outputDir`, at most 100 messages per call. target=`conversation` exports a thread (up to 1000 messages) by `conversationId` into `outputDir` (eml/mbox/markdown/json/html/csv; `order: "reverse"` for newest first). target=`mime` returns raw RFC-822 MIME for `id` (`headersOnly`, `base64`, `maxSize`, default 1MB). Files are written only inside the system temp directory (the default), ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR, never to dot-prefixed names. Pass `sharedMailbox` (alias `email`) when the ids come from a shared mailbox. `includeAttachments` defaults to true for one message, false for batch.',
|
|
544
|
+
...toolMetadata('export', 'Export Emails'),
|
|
589
545
|
inputSchema: {
|
|
590
546
|
type: 'object',
|
|
591
547
|
properties: {
|
|
@@ -603,11 +559,17 @@ const emailTools = [
|
|
|
603
559
|
type: 'string',
|
|
604
560
|
enum: ['mime', 'eml', 'markdown', 'json', 'mbox', 'html', 'csv'],
|
|
605
561
|
description:
|
|
606
|
-
'Export format. Valid values vary by target: target=message accepts mime/eml/markdown/json/csv (mbox and html are conversation-only). target=conversation accepts eml/mbox/markdown/json/html/csv. target=messages (batch) accepts markdown/json
|
|
562
|
+
'Export format. Valid values vary by target: target=message accepts mime/eml/markdown/json/csv (mbox and html are conversation-only). target=conversation accepts eml/mbox/markdown/json/html/csv. target=messages (batch) accepts mime/eml/markdown/json (one file per message) or csv (one file). mime is an alias for eml (same RFC822 bytes, .eml extension on disk).',
|
|
607
563
|
},
|
|
608
564
|
savePath: {
|
|
609
565
|
type: 'string',
|
|
610
|
-
description:
|
|
566
|
+
description:
|
|
567
|
+
'Absolute file path or directory, or one starting with ~/ (target=message). Relative paths are refused. Must be inside the system temp directory, ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR. An existing file is not replaced unless overwrite is true.',
|
|
568
|
+
},
|
|
569
|
+
overwrite: {
|
|
570
|
+
type: 'boolean',
|
|
571
|
+
description:
|
|
572
|
+
'Replace an existing file at savePath (target=message, default: false). Never replaces a symlink, a hard-linked file, a dotfile, or a file in a dot-directory below the allowed folder.',
|
|
611
573
|
},
|
|
612
574
|
includeAttachments: {
|
|
613
575
|
type: 'boolean',
|
|
@@ -618,20 +580,43 @@ const emailTools = [
|
|
|
618
580
|
emailIds: {
|
|
619
581
|
type: 'array',
|
|
620
582
|
items: { type: 'string' },
|
|
621
|
-
description:
|
|
583
|
+
description:
|
|
584
|
+
'Email IDs to export (target=messages). At most 100 per call: any beyond the first 100 are left out, and the result says how many.',
|
|
622
585
|
},
|
|
623
586
|
searchQuery: {
|
|
624
587
|
type: 'object',
|
|
625
588
|
properties: {
|
|
626
|
-
folder: {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
589
|
+
folder: {
|
|
590
|
+
type: 'string',
|
|
591
|
+
description:
|
|
592
|
+
'Folder to search (default: inbox): a well-known name, display name, `Parent/Child` path or folder ID',
|
|
593
|
+
},
|
|
594
|
+
from: {
|
|
595
|
+
type: 'string',
|
|
596
|
+
description: 'Sender address or name to match (Graph `$search`)',
|
|
597
|
+
},
|
|
598
|
+
subject: {
|
|
599
|
+
type: 'string',
|
|
600
|
+
description: 'Subject text to match (Graph `$search`)',
|
|
601
|
+
},
|
|
602
|
+
receivedAfter: {
|
|
603
|
+
type: 'string',
|
|
604
|
+
description:
|
|
605
|
+
'Only messages received at or after this date/time (ISO 8601)',
|
|
606
|
+
},
|
|
607
|
+
receivedBefore: {
|
|
608
|
+
type: 'string',
|
|
609
|
+
description:
|
|
610
|
+
'Only messages received at or before this date/time (ISO 8601)',
|
|
611
|
+
},
|
|
612
|
+
maxResults: {
|
|
613
|
+
type: 'number',
|
|
614
|
+
description:
|
|
615
|
+
'Most messages to export (default: 25, max: 100 per call). Newest first, or by relevance when `from`/`subject` is set.',
|
|
616
|
+
},
|
|
632
617
|
},
|
|
633
618
|
description:
|
|
634
|
-
'Search
|
|
619
|
+
'Search to find emails (target=messages, alternative to emailIds)',
|
|
635
620
|
},
|
|
636
621
|
query: {
|
|
637
622
|
type: 'string',
|
|
@@ -641,7 +626,7 @@ const emailTools = [
|
|
|
641
626
|
outputDir: {
|
|
642
627
|
type: 'string',
|
|
643
628
|
description:
|
|
644
|
-
'
|
|
629
|
+
'Absolute output directory, or one starting with ~/ (target=messages, required; target=message/conversation, default: system temp directory). Must be inside the system temp directory, ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR.',
|
|
645
630
|
},
|
|
646
631
|
// Conversation export
|
|
647
632
|
conversationId: {
|
|
@@ -657,7 +642,7 @@ const emailTools = [
|
|
|
657
642
|
sharedMailbox: {
|
|
658
643
|
type: 'string',
|
|
659
644
|
description:
|
|
660
|
-
'Email address of a shared/delegated mailbox to export from
|
|
645
|
+
'Email address of a shared/delegated mailbox to export from (default: the signed-in account). Applies to all targets (message/messages/conversation/mime) — pass it whenever the id(s)/conversationId/searchQuery belong to a shared mailbox. Requires delegate access + Mail.Read.Shared. Work/school only; needs the server opt-in setting OUTLOOK_SHARED_MAILBOX (otherwise the call is refused with setup guidance).',
|
|
661
646
|
},
|
|
662
647
|
email: {
|
|
663
648
|
type: 'string',
|
|
@@ -692,14 +677,9 @@ const emailTools = [
|
|
|
692
677
|
case 'message':
|
|
693
678
|
return handleExportEmail(args);
|
|
694
679
|
default:
|
|
695
|
-
return
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
type: 'text',
|
|
699
|
-
text: `Unknown export target '${target}'. Valid targets: message, messages, conversation, mime.`,
|
|
700
|
-
},
|
|
701
|
-
],
|
|
702
|
-
};
|
|
680
|
+
return toolError(
|
|
681
|
+
`Unknown export target '${target}'. Valid targets: message, messages, conversation, mime.`
|
|
682
|
+
);
|
|
703
683
|
}
|
|
704
684
|
},
|
|
705
685
|
},
|
|
@@ -707,11 +687,7 @@ const emailTools = [
|
|
|
707
687
|
name: 'get-mail-tips',
|
|
708
688
|
description:
|
|
709
689
|
'Pre-send recipient validation via Graph `POST /me/getMailTips` (read-only; uses the existing `Mail.Read` scope — no extra permissions). Returns per-recipient tips covering automatic replies (out-of-office), mailbox full status, custom admin mail tips, delivery restrictions, moderation requirements, external-vs-internal scope, max message size, and group member counts (total + external). Use ahead of `send-email` or `draft` action=`create` to catch issues like OOO replies or external-recipient warnings before the message goes out; `send-email`/`draft` accept `checkRecipients: true` to invoke this automatically. Accepts either a comma-separated string or an array of addresses; `tipTypes` filters which tips are requested (defaults to all).',
|
|
710
|
-
|
|
711
|
-
title: 'Mail Tips',
|
|
712
|
-
readOnlyHint: true,
|
|
713
|
-
openWorldHint: false,
|
|
714
|
-
},
|
|
690
|
+
...toolMetadata('get-mail-tips', 'Mail Tips'),
|
|
715
691
|
inputSchema: {
|
|
716
692
|
type: 'object',
|
|
717
693
|
properties: {
|
package/email/list.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
DEFAULT_LIMITS,
|
|
17
17
|
} = require('../utils/response-formatter');
|
|
18
18
|
const { getEmailFields } = require('../utils/field-presets');
|
|
19
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Maps verbosity level to field preset
|
|
@@ -101,7 +102,7 @@ async function handleListEmails(args) {
|
|
|
101
102
|
const meta = {
|
|
102
103
|
returned: response.value.length,
|
|
103
104
|
totalAvailable: response['@odata.count'] || null,
|
|
104
|
-
hasMore: Boolean(response['@odata.nextLink']),
|
|
105
|
+
hasMore: Boolean(response.hasMore || response['@odata.nextLink']),
|
|
105
106
|
verbosity: verbosity,
|
|
106
107
|
};
|
|
107
108
|
|
|
@@ -124,24 +125,10 @@ async function handleListEmails(args) {
|
|
|
124
125
|
};
|
|
125
126
|
} catch (error) {
|
|
126
127
|
if (error.message === 'Authentication required') {
|
|
127
|
-
return
|
|
128
|
-
content: [
|
|
129
|
-
{
|
|
130
|
-
type: 'text',
|
|
131
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
};
|
|
128
|
+
return authRequiredError();
|
|
135
129
|
}
|
|
136
130
|
|
|
137
|
-
return {
|
|
138
|
-
content: [
|
|
139
|
-
{
|
|
140
|
-
type: 'text',
|
|
141
|
-
text: `Error listing emails: ${error.message}`,
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
};
|
|
131
|
+
return toolError(`Error listing emails: ${error.message}`);
|
|
145
132
|
}
|
|
146
133
|
}
|
|
147
134
|
|