@littlebearapps/outlook-assistant 3.12.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +27 -3
- package/README.md +108 -33
- package/advanced/index.js +44 -174
- package/auth/auth-errors.js +23 -1
- package/auth/client-config.js +142 -0
- package/auth/index.js +4 -2
- package/auth/oauth-server.js +12 -2
- package/auth/token-manager.js +7 -3
- package/auth/token-storage.js +46 -33
- package/auth/tools.js +223 -93
- 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 +335 -0
- package/calendar/update.js +42 -86
- package/categories/index.js +59 -264
- package/config.js +36 -2
- package/contacts/index.js +72 -128
- package/email/attachments.js +42 -124
- package/email/conversations.js +44 -78
- package/email/delta.js +10 -34
- package/email/draft.js +140 -96
- package/email/export.js +141 -110
- package/email/folder-utils.js +3 -2
- package/email/headers.js +11 -49
- package/email/index.js +85 -109
- 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 +14 -49
- package/email/read.js +16 -50
- package/email/search.js +46 -86
- package/email/send.js +82 -48
- package/folder/create.js +6 -25
- package/folder/delete.js +117 -38
- package/folder/index.js +17 -16
- package/folder/list.js +5 -17
- package/folder/move.js +13 -42
- package/folder/resolve.js +11 -6
- package/folder/stats.js +6 -20
- package/index.js +23 -45
- package/llms-install.md +31 -7
- package/llms.txt +19 -10
- package/outlook-auth-server.js +10 -3
- package/package.json +6 -2
- package/request-handler.js +217 -116
- package/rules/create.js +27 -70
- package/rules/index.js +30 -92
- package/rules/list.js +5 -17
- package/rules/rule-builder.js +57 -20
- package/rules/update.js +26 -60
- package/server.js +37 -0
- package/settings/index.js +142 -143
- 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 +109 -25
- package/utils/server-instructions.js +62 -0
- package/utils/tool-error.js +33 -0
package/calendar/create.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Create event functionality
|
|
3
3
|
*/
|
|
4
|
+
const { randomUUID } = require('crypto');
|
|
4
5
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
6
|
const { ensureAuthenticated } = require('../auth');
|
|
6
7
|
const { DEFAULT_TIMEZONE } = require('../config');
|
|
7
|
-
const { buildAttendees } = require('./attendees');
|
|
8
|
+
const { buildAttendees, checkAttendeeAllowlist } = require('./attendees');
|
|
9
|
+
const { checkRateLimit } = require('../utils/safety');
|
|
10
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
11
|
+
const { previewCreateEvent } = require('./preview');
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Create event handler
|
|
@@ -12,17 +16,12 @@ const { buildAttendees } = require('./attendees');
|
|
|
12
16
|
* @returns {object} - MCP response
|
|
13
17
|
*/
|
|
14
18
|
async function handleCreateEvent(args) {
|
|
15
|
-
const { subject, start, end, attendees, body } = args;
|
|
19
|
+
const { subject, start, end, attendees, body, dryRun = false } = args;
|
|
16
20
|
|
|
17
21
|
if (!subject || !start || !end) {
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
type: 'text',
|
|
22
|
-
text: 'Subject, start, and end times are required to create an event.',
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
};
|
|
22
|
+
return toolError(
|
|
23
|
+
'Subject, start, and end times are required to create an event.'
|
|
24
|
+
);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
// Plain strings are required attendees; {email, type} sets the type (#249).
|
|
@@ -36,37 +35,46 @@ async function handleCreateEvent(args) {
|
|
|
36
35
|
isError: true,
|
|
37
36
|
};
|
|
38
37
|
}
|
|
38
|
+
const allowlistError = checkAttendeeAllowlist(graphAttendees, { dryRun });
|
|
39
|
+
if (allowlistError) return allowlistError;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
// Request body
|
|
43
|
+
const bodyContent = {
|
|
44
|
+
subject,
|
|
45
|
+
start: {
|
|
46
|
+
dateTime: start.dateTime || start,
|
|
47
|
+
timeZone: start.timeZone || DEFAULT_TIMEZONE,
|
|
48
|
+
},
|
|
49
|
+
end: {
|
|
50
|
+
dateTime: end.dateTime || end,
|
|
51
|
+
timeZone: end.timeZone || DEFAULT_TIMEZONE,
|
|
52
|
+
},
|
|
53
|
+
attendees: graphAttendees,
|
|
54
|
+
body: { contentType: 'HTML', content: body || '' },
|
|
55
|
+
};
|
|
56
|
+
|
|
41
57
|
try {
|
|
58
|
+
// dryRun: say who would be invited; create nothing (#274).
|
|
59
|
+
if (dryRun) return await previewCreateEvent(bodyContent);
|
|
60
|
+
|
|
61
|
+
// Counts real creates only; unlimited unless a limit is configured.
|
|
62
|
+
const rateLimitError = checkRateLimit('create-event');
|
|
63
|
+
if (rateLimitError) return rateLimitError;
|
|
64
|
+
|
|
42
65
|
// Get access token
|
|
43
66
|
const accessToken = await ensureAuthenticated();
|
|
44
67
|
|
|
45
68
|
// Build API endpoint
|
|
46
69
|
const endpoint = `me/events`;
|
|
47
70
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end: {
|
|
56
|
-
dateTime: end.dateTime || end,
|
|
57
|
-
timeZone: end.timeZone || DEFAULT_TIMEZONE,
|
|
58
|
-
},
|
|
59
|
-
attendees: graphAttendees,
|
|
60
|
-
body: { contentType: 'HTML', content: body || '' },
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Make API call
|
|
64
|
-
const response = await callGraphAPI(
|
|
65
|
-
accessToken,
|
|
66
|
-
'POST',
|
|
67
|
-
endpoint,
|
|
68
|
-
bodyContent
|
|
69
|
-
);
|
|
71
|
+
// A fresh transactionId per call (#280): Graph treats POSTs that share
|
|
72
|
+
// one as the same event, so a 429 retry in utils/graph-api.js (which
|
|
73
|
+
// re-sends this exact body) can't book the meeting twice.
|
|
74
|
+
const response = await callGraphAPI(accessToken, 'POST', endpoint, {
|
|
75
|
+
...bodyContent,
|
|
76
|
+
transactionId: randomUUID(),
|
|
77
|
+
});
|
|
70
78
|
|
|
71
79
|
const output = [`Event '${subject}' has been successfully created.`];
|
|
72
80
|
if (response.id) {
|
|
@@ -102,24 +110,10 @@ async function handleCreateEvent(args) {
|
|
|
102
110
|
};
|
|
103
111
|
} catch (error) {
|
|
104
112
|
if (error.message === 'Authentication required') {
|
|
105
|
-
return
|
|
106
|
-
content: [
|
|
107
|
-
{
|
|
108
|
-
type: 'text',
|
|
109
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
};
|
|
113
|
+
return authRequiredError();
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
return {
|
|
116
|
-
content: [
|
|
117
|
-
{
|
|
118
|
-
type: 'text',
|
|
119
|
-
text: `Error creating event: ${error.message}`,
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
};
|
|
116
|
+
return toolError(`Error creating event: ${error.message}`);
|
|
123
117
|
}
|
|
124
118
|
}
|
|
125
119
|
|
package/calendar/decline.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
5
|
const { ensureAuthenticated } = require('../auth');
|
|
6
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
7
|
+
const { previewDeclineEvent } = require('./preview');
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Decline event handler
|
|
@@ -10,23 +12,20 @@ const { ensureAuthenticated } = require('../auth');
|
|
|
10
12
|
* @returns {object} - MCP response
|
|
11
13
|
*/
|
|
12
14
|
async function handleDeclineEvent(args) {
|
|
13
|
-
const { eventId, comment, sendResponse } = args;
|
|
15
|
+
const { eventId, comment, sendResponse, dryRun = false } = args;
|
|
14
16
|
|
|
15
17
|
if (!eventId) {
|
|
16
|
-
return
|
|
17
|
-
content: [
|
|
18
|
-
{
|
|
19
|
-
type: 'text',
|
|
20
|
-
text: 'Event ID is required to decline an event.',
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
};
|
|
18
|
+
return toolError('Event ID is required to decline an event.');
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
try {
|
|
27
22
|
// Get access token
|
|
28
23
|
const accessToken = await ensureAuthenticated();
|
|
29
24
|
|
|
25
|
+
// dryRun: read the event and say whether the organiser would be
|
|
26
|
+
// emailed; send nothing.
|
|
27
|
+
if (dryRun) return await previewDeclineEvent(accessToken, args);
|
|
28
|
+
|
|
30
29
|
// Build API endpoint
|
|
31
30
|
const endpoint = `me/events/${eventId}/decline`;
|
|
32
31
|
|
|
@@ -53,24 +52,10 @@ async function handleDeclineEvent(args) {
|
|
|
53
52
|
};
|
|
54
53
|
} catch (error) {
|
|
55
54
|
if (error.message === 'Authentication required') {
|
|
56
|
-
return
|
|
57
|
-
content: [
|
|
58
|
-
{
|
|
59
|
-
type: 'text',
|
|
60
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
};
|
|
55
|
+
return authRequiredError();
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
return {
|
|
67
|
-
content: [
|
|
68
|
-
{
|
|
69
|
-
type: 'text',
|
|
70
|
-
text: `Error declining event: ${error.message}`,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
};
|
|
58
|
+
return toolError(`Error declining event: ${error.message}`);
|
|
74
59
|
}
|
|
75
60
|
}
|
|
76
61
|
|
package/calendar/delete.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
5
|
const { ensureAuthenticated } = require('../auth');
|
|
6
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
7
|
+
const { previewDeleteEvent } = require('./preview');
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Delete event handler
|
|
@@ -10,23 +12,20 @@ const { ensureAuthenticated } = require('../auth');
|
|
|
10
12
|
* @returns {object} - MCP response
|
|
11
13
|
*/
|
|
12
14
|
async function handleDeleteEvent(args) {
|
|
13
|
-
const { eventId } = args;
|
|
15
|
+
const { eventId, dryRun = false } = args;
|
|
14
16
|
|
|
15
17
|
if (!eventId) {
|
|
16
|
-
return
|
|
17
|
-
content: [
|
|
18
|
-
{
|
|
19
|
-
type: 'text',
|
|
20
|
-
text: 'Event ID is required to delete an event.',
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
};
|
|
18
|
+
return toolError('Event ID is required to delete an event.');
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
try {
|
|
27
22
|
// Get access token
|
|
28
23
|
const accessToken = await ensureAuthenticated();
|
|
29
24
|
|
|
25
|
+
// dryRun: read the event and say who (if anyone) would get a
|
|
26
|
+
// cancellation; delete nothing.
|
|
27
|
+
if (dryRun) return await previewDeleteEvent(accessToken, args);
|
|
28
|
+
|
|
30
29
|
// Build API endpoint
|
|
31
30
|
const endpoint = `me/events/${eventId}`;
|
|
32
31
|
|
|
@@ -43,24 +42,10 @@ async function handleDeleteEvent(args) {
|
|
|
43
42
|
};
|
|
44
43
|
} catch (error) {
|
|
45
44
|
if (error.message === 'Authentication required') {
|
|
46
|
-
return
|
|
47
|
-
content: [
|
|
48
|
-
{
|
|
49
|
-
type: 'text',
|
|
50
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
};
|
|
45
|
+
return authRequiredError();
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
return {
|
|
57
|
-
content: [
|
|
58
|
-
{
|
|
59
|
-
type: 'text',
|
|
60
|
-
text: `Error deleting event: ${error.message}`,
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
};
|
|
48
|
+
return toolError(`Error deleting event: ${error.message}`);
|
|
64
49
|
}
|
|
65
50
|
}
|
|
66
51
|
|
package/calendar/index.js
CHANGED
|
@@ -8,6 +8,8 @@ const handleCancelEvent = require('./cancel');
|
|
|
8
8
|
const handleDeleteEvent = require('./delete');
|
|
9
9
|
const handleUpdateEvent = require('./update');
|
|
10
10
|
const { ATTENDEE_TYPES } = require('./attendees');
|
|
11
|
+
const { toolMetadata } = require('../utils/risk-classes');
|
|
12
|
+
const { toolError } = require('../utils/tool-error');
|
|
11
13
|
|
|
12
14
|
// One attendee: an email string, or {email, type} (#249). schema-coerce
|
|
13
15
|
// doesn't validate inside array items, so calendar/attendees.js re-checks.
|
|
@@ -32,11 +34,7 @@ const calendarTools = [
|
|
|
32
34
|
name: 'list-events',
|
|
33
35
|
description:
|
|
34
36
|
'List calendar events for the signed-in user (read-only). By default returns upcoming events (start ≥ now). Optional `startAfter`, `startBefore` and `subject` filters find past, current or specifically-named events; supplying any of them replaces the default "now" lower bound and the filters are AND-ed together. Results are oldest first, except when the search only looks backwards (`startBefore` without `startAfter`, or `subject` alone), where they are newest first. Each event shows its subject, location, start/end, a body preview and its id. Use `count` (default 10, max 100) to control page size. Each start/end is returned as a canonical UTC ISO-8601 instant (e.g. `2026-04-02T22:00:00.000Z`) followed by a labelled local rendering in the configured display timezone (default Australia/Melbourne; override with `OUTLOOK_DEFAULT_TIMEZONE`) — the UTC value is authoritative, so consumers never have to guess the zone.',
|
|
35
|
-
|
|
36
|
-
title: 'List Calendar Events',
|
|
37
|
-
readOnlyHint: true,
|
|
38
|
-
openWorldHint: false,
|
|
39
|
-
},
|
|
37
|
+
...toolMetadata('list-events', 'List Calendar Events'),
|
|
40
38
|
inputSchema: {
|
|
41
39
|
type: 'object',
|
|
42
40
|
properties: {
|
|
@@ -71,13 +69,8 @@ const calendarTools = [
|
|
|
71
69
|
{
|
|
72
70
|
name: 'create-event',
|
|
73
71
|
description:
|
|
74
|
-
"Create a new calendar event on the signed-in user's default calendar. Returns the created event with its `id`, `webLink`, and (if attendees are present) an auto-generated online-meeting URL — attendees receive invitations on save. Times use the configured timezone (default Australia/Melbourne; override with `OUTLOOK_DEFAULT_TIMEZONE`); omit the `Z` suffix to send local time. Use `manage-event` action=`update` to modify an event after creation, or `manage-event` action=`cancel`/`delete` to remove it.",
|
|
75
|
-
|
|
76
|
-
title: 'Create Calendar Event',
|
|
77
|
-
readOnlyHint: false,
|
|
78
|
-
destructiveHint: false,
|
|
79
|
-
openWorldHint: false,
|
|
80
|
-
},
|
|
72
|
+
"Create a new calendar event on the signed-in user's default calendar. Returns the created event with its `id`, `webLink`, and (if attendees are present) an auto-generated online-meeting URL — attendees receive invitations on save, so pass `dryRun: true` first to preview who would be invited (and how many are external) without creating anything. When `OUTLOOK_ALLOWED_RECIPIENTS` is set, every attendee must be allowed or nothing is created. Times use the configured timezone (default Australia/Melbourne; override with `OUTLOOK_DEFAULT_TIMEZONE`); omit the `Z` suffix to send local time. Use `manage-event` action=`update` to modify an event after creation, or `manage-event` action=`cancel`/`delete` to remove it.",
|
|
73
|
+
...toolMetadata('create-event', 'Create Calendar Event'),
|
|
81
74
|
inputSchema: {
|
|
82
75
|
type: 'object',
|
|
83
76
|
properties: {
|
|
@@ -103,6 +96,11 @@ const calendarTools = [
|
|
|
103
96
|
type: 'string',
|
|
104
97
|
description: 'Optional body content for the event',
|
|
105
98
|
},
|
|
99
|
+
dryRun: {
|
|
100
|
+
type: 'boolean',
|
|
101
|
+
description:
|
|
102
|
+
'Preview only: nothing is created and no invitations are sent. Shows who would be invited and how many are external (default false).',
|
|
103
|
+
},
|
|
106
104
|
},
|
|
107
105
|
additionalProperties: false,
|
|
108
106
|
required: ['subject', 'start', 'end'],
|
|
@@ -112,13 +110,8 @@ const calendarTools = [
|
|
|
112
110
|
{
|
|
113
111
|
name: 'manage-event',
|
|
114
112
|
description:
|
|
115
|
-
"Manage an existing calendar event
|
|
116
|
-
|
|
117
|
-
title: 'Manage Calendar Event',
|
|
118
|
-
readOnlyHint: false,
|
|
119
|
-
destructiveHint: true,
|
|
120
|
-
openWorldHint: false,
|
|
121
|
-
},
|
|
113
|
+
"Manage an existing calendar event. `dryRun: true` previews any action without changing or sending anything: who would be emailed, with an external count (decline/cancel/delete), or the PATCH body (update). action=`update` edits fields via PATCH (subject, start, end, attendees, body, location, isOnlineMeeting, sensitivity, showAs, importance, categories, reminderMinutesBeforeStart); only fields you pass change. action=`decline` declines an invitation (optional `comment`; `sendResponse: false` declines without notifying the organiser). action=`cancel` cancels an event you organised and emails attendees. action=`delete` removes the event from your calendar (Graph documents no guaranteed recovery); deleting a meeting you organised that has attendees still emails them a cancellation, so use `cancel` with a `comment` to control that message. Returns the updated event on update; a confirmation otherwise. There is no `accept` action: accept invitations in the Outlook UI, as Graph's accept verb is unreliable.",
|
|
114
|
+
...toolMetadata('manage-event', 'Manage Calendar Event'),
|
|
122
115
|
inputSchema: {
|
|
123
116
|
type: 'object',
|
|
124
117
|
properties: {
|
|
@@ -186,7 +179,7 @@ const calendarTools = [
|
|
|
186
179
|
type: 'array',
|
|
187
180
|
items: ATTENDEE_ITEM_SCHEMA,
|
|
188
181
|
description:
|
|
189
|
-
"Full replacement attendee list — pass the complete desired list, or [] to clear (action=update only). Each entry is an email address string or an {email, type} object (type 'required', 'optional' or 'resource'). A string, or an object without a type, keeps the type that address already has on the event (new addresses are required); an explicit type always wins.",
|
|
182
|
+
"Full replacement attendee list — pass the complete desired list, or [] to clear (action=update only). Each entry is an email address string or an {email, type} object (type 'required', 'optional' or 'resource'). A string, or an object without a type, keeps the type that address already has on the event (new addresses are required); an explicit type always wins. When OUTLOOK_ALLOWED_RECIPIENTS is set, every address on the list must be allowed or the update is refused.",
|
|
190
183
|
},
|
|
191
184
|
body: {
|
|
192
185
|
type: 'string',
|
|
@@ -236,7 +229,7 @@ const calendarTools = [
|
|
|
236
229
|
dryRun: {
|
|
237
230
|
type: 'boolean',
|
|
238
231
|
description:
|
|
239
|
-
'Preview
|
|
232
|
+
'Preview only: nothing is changed or sent. Shows who would be emailed (decline/cancel/delete) or the PATCH body (update). Default false.',
|
|
240
233
|
},
|
|
241
234
|
},
|
|
242
235
|
additionalProperties: false,
|
|
@@ -251,14 +244,9 @@ const calendarTools = [
|
|
|
251
244
|
normalised.eventId = normalised.id;
|
|
252
245
|
}
|
|
253
246
|
if (!normalised.eventId) {
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
type: 'text',
|
|
258
|
-
text: 'Required parameter `eventId` (or alias `id`) is missing.',
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
};
|
|
247
|
+
return toolError(
|
|
248
|
+
'Required parameter `eventId` (or alias `id`) is missing.'
|
|
249
|
+
);
|
|
262
250
|
}
|
|
263
251
|
args = normalised;
|
|
264
252
|
switch (args.action) {
|
|
@@ -271,14 +259,9 @@ const calendarTools = [
|
|
|
271
259
|
case 'delete':
|
|
272
260
|
return handleDeleteEvent(args);
|
|
273
261
|
default:
|
|
274
|
-
return
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
type: 'text',
|
|
278
|
-
text: "Invalid action. Use 'update', 'decline', 'cancel', or 'delete'.",
|
|
279
|
-
},
|
|
280
|
-
],
|
|
281
|
-
};
|
|
262
|
+
return toolError(
|
|
263
|
+
"Invalid action. Use 'update', 'decline', 'cancel', or 'delete'."
|
|
264
|
+
);
|
|
282
265
|
}
|
|
283
266
|
},
|
|
284
267
|
},
|
package/calendar/list.js
CHANGED
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
buildODataFilter,
|
|
10
10
|
} = require('../utils/odata-helpers');
|
|
11
11
|
const { parseIsoInstant } = require('../utils/datetime');
|
|
12
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
12
13
|
|
|
13
14
|
const MAX_SUBJECT_LENGTH = 255;
|
|
14
15
|
|
|
@@ -312,24 +313,10 @@ async function handleListEvents(args) {
|
|
|
312
313
|
};
|
|
313
314
|
} catch (error) {
|
|
314
315
|
if (error.message === 'Authentication required') {
|
|
315
|
-
return
|
|
316
|
-
content: [
|
|
317
|
-
{
|
|
318
|
-
type: 'text',
|
|
319
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
};
|
|
316
|
+
return authRequiredError();
|
|
323
317
|
}
|
|
324
318
|
|
|
325
|
-
return {
|
|
326
|
-
content: [
|
|
327
|
-
{
|
|
328
|
-
type: 'text',
|
|
329
|
-
text: `Error listing events: ${error.message}`,
|
|
330
|
-
},
|
|
331
|
-
],
|
|
332
|
-
};
|
|
319
|
+
return toolError(`Error listing events: ${error.message}`);
|
|
333
320
|
}
|
|
334
321
|
}
|
|
335
322
|
|
|
@@ -337,6 +324,7 @@ async function handleListEvents(args) {
|
|
|
337
324
|
// and tests). Helpers are attached for unit testing without changing callers.
|
|
338
325
|
handleListEvents.toUtcIso = toUtcIso;
|
|
339
326
|
handleListEvents.formatLocal = formatLocal;
|
|
327
|
+
handleListEvents.toUtcIso = toUtcIso;
|
|
340
328
|
|
|
341
329
|
module.exports = handleListEvents;
|
|
342
330
|
module.exports.buildListEventsFilter = buildListEventsFilter;
|