outlook-cli 1.2.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/auth/tools.js ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Authentication-related tools for the Outlook MCP server
3
+ */
4
+ const config = require('../config');
5
+ const tokenManager = require('./token-manager');
6
+
7
+ /**
8
+ * About tool handler
9
+ * @returns {object} - MCP response
10
+ */
11
+ async function handleAbout() {
12
+ return {
13
+ content: [{
14
+ type: "text",
15
+ text: `outlook-cli v${config.SERVER_VERSION}\n\nProduction-ready Outlook CLI and MCP server powered by Microsoft Graph API.`
16
+ }]
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Authentication tool handler
22
+ * @param {object} args - Tool arguments
23
+ * @returns {object} - MCP response
24
+ */
25
+ async function handleAuthenticate(args) {
26
+ const force = args && args.force === true;
27
+
28
+ // For test mode, create a test token
29
+ if (config.USE_TEST_MODE) {
30
+ // Create a test token with a 1-hour expiry
31
+ tokenManager.createTestTokens();
32
+
33
+ return {
34
+ content: [{
35
+ type: "text",
36
+ text: 'Successfully authenticated with Microsoft Graph API (test mode)'
37
+ }]
38
+ };
39
+ }
40
+
41
+ if (force) {
42
+ tokenManager.clearTokenCache();
43
+ }
44
+
45
+ if (!config.AUTH_CONFIG.clientId) {
46
+ return {
47
+ content: [{
48
+ type: "text",
49
+ text: "Client ID is not configured. Set OUTLOOK_CLIENT_ID or MS_CLIENT_ID before authenticating."
50
+ }]
51
+ };
52
+ }
53
+
54
+ // For real authentication, generate an auth URL and instruct the user to visit it
55
+ const authUrl = `${config.AUTH_CONFIG.authServerUrl}/auth?client_id=${config.AUTH_CONFIG.clientId}`;
56
+
57
+ return {
58
+ content: [{
59
+ type: "text",
60
+ text: `Authentication required. Please visit the following URL to authenticate with Microsoft: ${authUrl}\n\nAfter authentication, you will be redirected back to this application.`
61
+ }]
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Check authentication status tool handler
67
+ * @returns {object} - MCP response
68
+ */
69
+ async function handleCheckAuthStatus() {
70
+ const accessToken = await tokenManager.getValidAccessToken();
71
+
72
+ if (!accessToken) {
73
+ return {
74
+ content: [{ type: "text", text: "Not authenticated" }]
75
+ };
76
+ }
77
+
78
+ return {
79
+ content: [{ type: "text", text: "Authenticated and ready" }]
80
+ };
81
+ }
82
+
83
+ // Tool definitions
84
+ const authTools = [
85
+ {
86
+ name: "about",
87
+ description: "Returns information about this Outlook Assistant server",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {},
91
+ required: []
92
+ },
93
+ handler: handleAbout
94
+ },
95
+ {
96
+ name: "authenticate",
97
+ description: "Authenticate with Microsoft Graph API to access Outlook data",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {
101
+ force: {
102
+ type: "boolean",
103
+ description: "Force re-authentication even if already authenticated"
104
+ }
105
+ },
106
+ required: []
107
+ },
108
+ handler: handleAuthenticate
109
+ },
110
+ {
111
+ name: "check-auth-status",
112
+ description: "Check the current authentication status with Microsoft Graph API",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {},
116
+ required: []
117
+ },
118
+ handler: handleCheckAuthStatus
119
+ }
120
+ ];
121
+
122
+ module.exports = {
123
+ authTools,
124
+ handleAbout,
125
+ handleAuthenticate,
126
+ handleCheckAuthStatus
127
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Accept event functionality
3
+ */
4
+ const { callGraphAPI } = require('../utils/graph-api');
5
+ const { ensureAuthenticated } = require('../auth');
6
+
7
+ /**
8
+ * Accept event handler
9
+ * @param {object} args - Tool arguments
10
+ * @returns {object} - MCP response
11
+ */
12
+ async function handleAcceptEvent(args) {
13
+ const { eventId, comment } = args;
14
+
15
+ if (!eventId) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: "Event ID is required to accept an event."
20
+ }]
21
+ };
22
+ }
23
+
24
+ try {
25
+ // Get access token
26
+ const accessToken = await ensureAuthenticated();
27
+
28
+ // Build API endpoint
29
+ const endpoint = `me/events/${eventId}/accept`;
30
+
31
+ // Request body
32
+ const body = {
33
+ comment: comment || "Accepted via API"
34
+ };
35
+
36
+ // Make API call
37
+ await callGraphAPI(accessToken, 'POST', endpoint, body);
38
+
39
+ return {
40
+ content: [{
41
+ type: "text",
42
+ text: `Event with ID ${eventId} has been successfully accepted.`
43
+ }]
44
+ };
45
+ } catch (error) {
46
+ if (error.message === 'Authentication required') {
47
+ return {
48
+ content: [{
49
+ type: "text",
50
+ text: "Authentication required. Please use the 'authenticate' tool first."
51
+ }]
52
+ };
53
+ }
54
+
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: `Error accepting event: ${error.message}`
59
+ }]
60
+ };
61
+ }
62
+ }
63
+
64
+ module.exports = handleAcceptEvent;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Cancel event functionality
3
+ */
4
+ const { callGraphAPI } = require('../utils/graph-api');
5
+ const { ensureAuthenticated } = require('../auth');
6
+
7
+ /**
8
+ * Cancel event handler
9
+ * @param {object} args - Tool arguments
10
+ * @returns {object} - MCP response
11
+ */
12
+ async function handleCancelEvent(args) {
13
+ const { eventId, comment } = args;
14
+
15
+ if (!eventId) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: "Event ID is required to cancel an event."
20
+ }]
21
+ };
22
+ }
23
+
24
+ try {
25
+ // Get access token
26
+ const accessToken = await ensureAuthenticated();
27
+
28
+ // Build API endpoint
29
+ const endpoint = `me/events/${eventId}/cancel`;
30
+
31
+ // Request body
32
+ const body = {
33
+ comment: comment || "Cancelled via API"
34
+ };
35
+
36
+ // Make API call
37
+ await callGraphAPI(accessToken, 'POST', endpoint, body);
38
+
39
+ return {
40
+ content: [{
41
+ type: "text",
42
+ text: `Event with ID ${eventId} has been successfully cancelled.`
43
+ }]
44
+ };
45
+ } catch (error) {
46
+ if (error.message === 'Authentication required') {
47
+ return {
48
+ content: [{
49
+ type: "text",
50
+ text: "Authentication required. Please use the 'authenticate' tool first."
51
+ }]
52
+ };
53
+ }
54
+
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: `Error cancelling event: ${error.message}`
59
+ }]
60
+ };
61
+ }
62
+ }
63
+
64
+ module.exports = handleCancelEvent;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Create event functionality
3
+ */
4
+ const { callGraphAPI } = require('../utils/graph-api');
5
+ const { ensureAuthenticated } = require('../auth');
6
+ const { DEFAULT_TIMEZONE } = require('../config');
7
+
8
+ /**
9
+ * Create event handler
10
+ * @param {object} args - Tool arguments
11
+ * @returns {object} - MCP response
12
+ */
13
+ async function handleCreateEvent(args) {
14
+ const { subject, start, end, attendees, body } = args;
15
+
16
+ if (!subject || !start || !end) {
17
+ return {
18
+ content: [{
19
+ type: "text",
20
+ text: "Subject, start, and end times are required to create an event."
21
+ }]
22
+ };
23
+ }
24
+
25
+ try {
26
+ // Get access token
27
+ const accessToken = await ensureAuthenticated();
28
+
29
+ // Build API endpoint
30
+ const endpoint = `me/events`;
31
+
32
+ // Request body
33
+ const bodyContent = {
34
+ subject,
35
+ start: { dateTime: start.dateTime || start, timeZone: start.timeZone || DEFAULT_TIMEZONE },
36
+ end: { dateTime: end.dateTime || end, timeZone: end.timeZone || DEFAULT_TIMEZONE },
37
+ attendees: attendees?.map(email => ({ emailAddress: { address: email }, type: "required" })),
38
+ body: { contentType: "HTML", content: body || "" }
39
+ };
40
+
41
+ // Make API call
42
+ const response = await callGraphAPI(accessToken, 'POST', endpoint, bodyContent);
43
+
44
+ return {
45
+ content: [{
46
+ type: "text",
47
+ text: `Event '${subject}' has been successfully created.`
48
+ }]
49
+ };
50
+ } catch (error) {
51
+ if (error.message === 'Authentication required') {
52
+ return {
53
+ content: [{
54
+ type: "text",
55
+ text: "Authentication required. Please use the 'authenticate' tool first."
56
+ }]
57
+ };
58
+ }
59
+
60
+ return {
61
+ content: [{
62
+ type: "text",
63
+ text: `Error creating event: ${error.message}`
64
+ }]
65
+ };
66
+ }
67
+ }
68
+
69
+ module.exports = handleCreateEvent;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Decline event functionality
3
+ */
4
+ const { callGraphAPI } = require('../utils/graph-api');
5
+ const { ensureAuthenticated } = require('../auth');
6
+
7
+ /**
8
+ * Decline event handler
9
+ * @param {object} args - Tool arguments
10
+ * @returns {object} - MCP response
11
+ */
12
+ async function handleDeclineEvent(args) {
13
+ const { eventId, comment } = args;
14
+
15
+ if (!eventId) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: "Event ID is required to decline an event."
20
+ }]
21
+ };
22
+ }
23
+
24
+ try {
25
+ // Get access token
26
+ const accessToken = await ensureAuthenticated();
27
+
28
+ // Build API endpoint
29
+ const endpoint = `me/events/${eventId}/decline`;
30
+
31
+ // Request body
32
+ const body = {
33
+ comment: comment || "Declined via API"
34
+ };
35
+
36
+ // Make API call
37
+ await callGraphAPI(accessToken, 'POST', endpoint, body);
38
+
39
+ return {
40
+ content: [{
41
+ type: "text",
42
+ text: `Event with ID ${eventId} has been successfully declined.`
43
+ }]
44
+ };
45
+ } catch (error) {
46
+ if (error.message === 'Authentication required') {
47
+ return {
48
+ content: [{
49
+ type: "text",
50
+ text: "Authentication required. Please use the 'authenticate' tool first."
51
+ }]
52
+ };
53
+ }
54
+
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: `Error declining event: ${error.message}`
59
+ }]
60
+ };
61
+ }
62
+ }
63
+
64
+ module.exports = handleDeclineEvent;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Delete event functionality
3
+ */
4
+ const { callGraphAPI } = require('../utils/graph-api');
5
+ const { ensureAuthenticated } = require('../auth');
6
+
7
+ /**
8
+ * Delete event handler
9
+ * @param {object} args - Tool arguments
10
+ * @returns {object} - MCP response
11
+ */
12
+ async function handleDeleteEvent(args) {
13
+ const { eventId } = args;
14
+
15
+ if (!eventId) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: "Event ID is required to delete an event."
20
+ }]
21
+ };
22
+ }
23
+
24
+ try {
25
+ // Get access token
26
+ const accessToken = await ensureAuthenticated();
27
+
28
+ // Build API endpoint
29
+ const endpoint = `me/events/${eventId}`;
30
+
31
+ // Make API call
32
+ await callGraphAPI(accessToken, 'DELETE', endpoint);
33
+
34
+ return {
35
+ content: [{
36
+ type: "text",
37
+ text: `Event with ID ${eventId} has been successfully deleted.`
38
+ }]
39
+ };
40
+ } catch (error) {
41
+ if (error.message === 'Authentication required') {
42
+ return {
43
+ content: [{
44
+ type: "text",
45
+ text: "Authentication required. Please use the 'authenticate' tool first."
46
+ }]
47
+ };
48
+ }
49
+
50
+ return {
51
+ content: [{
52
+ type: "text",
53
+ text: `Error deleting event: ${error.message}`
54
+ }]
55
+ };
56
+ }
57
+ }
58
+
59
+ module.exports = handleDeleteEvent;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Calendar module for Outlook MCP server
3
+ */
4
+ const handleListEvents = require('./list');
5
+ const handleDeclineEvent = require('./decline');
6
+ const handleCreateEvent = require('./create');
7
+ const handleCancelEvent = require('./cancel');
8
+ const handleDeleteEvent = require('./delete');
9
+
10
+ // Calendar tool definitions
11
+ const calendarTools = [
12
+ {
13
+ name: "list-events",
14
+ description: "Lists upcoming events from your calendar",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ count: {
19
+ type: "number",
20
+ description: "Number of events to retrieve (default: 10, max: 50)"
21
+ }
22
+ },
23
+ required: []
24
+ },
25
+ handler: handleListEvents
26
+ },
27
+ {
28
+ name: "decline-event",
29
+ description: "Declines a calendar event",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ eventId: {
34
+ type: "string",
35
+ description: "The ID of the event to decline"
36
+ },
37
+ comment: {
38
+ type: "string",
39
+ description: "Optional comment for declining the event"
40
+ }
41
+ },
42
+ required: ["eventId"]
43
+ },
44
+ handler: handleDeclineEvent
45
+ },
46
+ {
47
+ name: "create-event",
48
+ description: "Creates a new calendar event",
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ subject: {
53
+ type: "string",
54
+ description: "The subject of the event"
55
+ },
56
+ start: {
57
+ type: "string",
58
+ description: "The start time of the event in ISO 8601 format"
59
+ },
60
+ end: {
61
+ type: "string",
62
+ description: "The end time of the event in ISO 8601 format"
63
+ },
64
+ attendees: {
65
+ type: "array",
66
+ items: {
67
+ type: "string"
68
+ },
69
+ description: "List of attendee email addresses"
70
+ },
71
+ body: {
72
+ type: "string",
73
+ description: "Optional body content for the event"
74
+ }
75
+ },
76
+ required: ["subject", "start", "end"]
77
+ },
78
+ handler: handleCreateEvent
79
+ },
80
+ {
81
+ name: "cancel-event",
82
+ description: "Cancels a calendar event",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: {
86
+ eventId: {
87
+ type: "string",
88
+ description: "The ID of the event to cancel"
89
+ },
90
+ comment: {
91
+ type: "string",
92
+ description: "Optional comment for cancelling the event"
93
+ }
94
+ },
95
+ required: ["eventId"]
96
+ },
97
+ handler: handleCancelEvent
98
+ },
99
+ {
100
+ name: "delete-event",
101
+ description: "Deletes a calendar event",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ eventId: {
106
+ type: "string",
107
+ description: "The ID of the event to delete"
108
+ }
109
+ },
110
+ required: ["eventId"]
111
+ },
112
+ handler: handleDeleteEvent
113
+ }
114
+ ];
115
+
116
+ module.exports = {
117
+ calendarTools,
118
+ handleListEvents,
119
+ handleDeclineEvent,
120
+ handleCreateEvent,
121
+ handleCancelEvent,
122
+ handleDeleteEvent
123
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * List events functionality
3
+ */
4
+ const config = require('../config');
5
+ const { callGraphAPI } = require('../utils/graph-api');
6
+ const { ensureAuthenticated } = require('../auth');
7
+
8
+ /**
9
+ * List events handler
10
+ * @param {object} args - Tool arguments
11
+ * @returns {object} - MCP response
12
+ */
13
+ async function handleListEvents(args) {
14
+ const count = Math.min(args.count || 10, config.MAX_RESULT_COUNT);
15
+
16
+ try {
17
+ // Get access token
18
+ const accessToken = await ensureAuthenticated();
19
+
20
+ // Build API endpoint
21
+ let endpoint = 'me/events';
22
+
23
+ // Add query parameters
24
+ const queryParams = {
25
+ $top: count,
26
+ $orderby: 'start/dateTime',
27
+ $filter: `start/dateTime ge '${new Date().toISOString()}'`,
28
+ $select: config.CALENDAR_SELECT_FIELDS
29
+ };
30
+
31
+ // Make API call
32
+ const response = await callGraphAPI(accessToken, 'GET', endpoint, null, queryParams);
33
+
34
+ if (!response.value || response.value.length === 0) {
35
+ return {
36
+ content: [{
37
+ type: "text",
38
+ text: "No calendar events found."
39
+ }]
40
+ };
41
+ }
42
+
43
+ // Format results
44
+ const eventList = response.value.map((event, index) => {
45
+ const startDate = new Date(event.start.dateTime).toLocaleString(event.start.timeZone);
46
+ const endDate = new Date(event.end.dateTime).toLocaleString(event.end.timeZone);
47
+ const location = event.location.displayName || 'No location';
48
+
49
+ return `${index + 1}. ${event.subject} - Location: ${location}\nStart: ${startDate}\nEnd: ${endDate}\nSubject: ${event.subject}\nSummary: ${event.bodyPreview}\nID: ${event.id}\n`;
50
+ }).join("\n");
51
+
52
+ return {
53
+ content: [{
54
+ type: "text",
55
+ text: `Found ${response.value.length} events:\n\n${eventList}`
56
+ }]
57
+ };
58
+ } catch (error) {
59
+ if (error.message === 'Authentication required') {
60
+ return {
61
+ content: [{
62
+ type: "text",
63
+ text: "Authentication required. Please use the 'authenticate' tool first."
64
+ }]
65
+ };
66
+ }
67
+
68
+ return {
69
+ content: [{
70
+ type: "text",
71
+ text: `Error listing events: ${error.message}`
72
+ }]
73
+ };
74
+ }
75
+ }
76
+
77
+ module.exports = handleListEvents;