@littlebearapps/outlook-assistant 3.3.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 +22 -0
- package/LICENSE +21 -0
- package/README.md +422 -0
- package/advanced/index.js +652 -0
- package/auth/index.js +32 -0
- package/auth/oauth-server.js +233 -0
- package/auth/token-manager.js +105 -0
- package/auth/token-storage.js +359 -0
- package/auth/tools.js +159 -0
- package/calendar/accept.js +72 -0
- package/calendar/cancel.js +72 -0
- package/calendar/create.js +115 -0
- package/calendar/decline.js +72 -0
- package/calendar/delete.js +67 -0
- package/calendar/index.js +130 -0
- package/calendar/list.js +108 -0
- package/categories/index.js +955 -0
- package/config.js +95 -0
- package/contacts/index.js +754 -0
- package/email/attachments.js +365 -0
- package/email/conversations.js +666 -0
- package/email/delta.js +210 -0
- package/email/export.js +572 -0
- package/email/folder-utils.js +192 -0
- package/email/headers.js +344 -0
- package/email/index.js +537 -0
- package/email/list.js +136 -0
- package/email/mark-as-read.js +114 -0
- package/email/mime.js +286 -0
- package/email/read.js +161 -0
- package/email/search.js +628 -0
- package/email/send.js +169 -0
- package/folder/create.js +137 -0
- package/folder/delete.js +108 -0
- package/folder/index.js +112 -0
- package/folder/list.js +289 -0
- package/folder/move.js +186 -0
- package/folder/stats.js +322 -0
- package/index.js +162 -0
- package/llms.txt +76 -0
- package/outlook-auth-server.js +384 -0
- package/package.json +97 -0
- package/rules/create.js +273 -0
- package/rules/index.js +276 -0
- package/rules/list.js +216 -0
- package/settings/index.js +678 -0
- package/utils/field-presets.js +311 -0
- package/utils/graph-api.js +268 -0
- package/utils/mock-data.js +154 -0
- package/utils/odata-helpers.js +33 -0
- package/utils/response-formatter.js +457 -0
- package/utils/safety.js +123 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const url = require('url');
|
|
5
|
+
const querystring = require('querystring');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Escapes HTML special characters to prevent XSS in rendered responses.
|
|
11
|
+
*/
|
|
12
|
+
function escapeHtml(str) {
|
|
13
|
+
if (typeof str !== 'string') return '';
|
|
14
|
+
return str
|
|
15
|
+
.replace(/&/g, '&')
|
|
16
|
+
.replace(/</g, '<')
|
|
17
|
+
.replace(/>/g, '>')
|
|
18
|
+
.replace(/"/g, '"')
|
|
19
|
+
.replace(/'/g, ''');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Security headers for all HTML responses
|
|
23
|
+
const SECURITY_HEADERS = {
|
|
24
|
+
'Content-Type': 'text/html',
|
|
25
|
+
'X-Content-Type-Options': 'nosniff',
|
|
26
|
+
'X-Frame-Options': 'DENY',
|
|
27
|
+
'Content-Security-Policy': "default-src 'none'; style-src 'unsafe-inline'",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Pending OAuth state tokens for CSRF validation
|
|
31
|
+
const pendingStates = new Map();
|
|
32
|
+
|
|
33
|
+
// Load environment variables from .env file
|
|
34
|
+
require('dotenv').config();
|
|
35
|
+
|
|
36
|
+
// Import scopes and token path from central config to stay in sync
|
|
37
|
+
const { AUTH_CONFIG: centralAuth } = require('./config');
|
|
38
|
+
|
|
39
|
+
// Log to console
|
|
40
|
+
console.log('Starting Outlook Authentication Server');
|
|
41
|
+
|
|
42
|
+
// Authentication configuration — scopes and tokenStorePath from config.js (single source of truth)
|
|
43
|
+
const AUTH_CONFIG = {
|
|
44
|
+
clientId: process.env.OUTLOOK_CLIENT_ID || process.env.MS_CLIENT_ID || '',
|
|
45
|
+
clientSecret:
|
|
46
|
+
process.env.OUTLOOK_CLIENT_SECRET || process.env.MS_CLIENT_SECRET || '',
|
|
47
|
+
redirectUri: centralAuth.redirectUri,
|
|
48
|
+
scopes: centralAuth.scopes,
|
|
49
|
+
tokenStorePath: centralAuth.tokenStorePath,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Create HTTP server
|
|
53
|
+
const server = http.createServer((req, res) => {
|
|
54
|
+
const parsedUrl = url.parse(req.url, true);
|
|
55
|
+
const pathname = parsedUrl.pathname;
|
|
56
|
+
|
|
57
|
+
console.log(`Request received: ${pathname}`);
|
|
58
|
+
|
|
59
|
+
if (pathname === '/auth/callback') {
|
|
60
|
+
const query = parsedUrl.query;
|
|
61
|
+
|
|
62
|
+
if (query.error) {
|
|
63
|
+
console.error(
|
|
64
|
+
`Authentication error: ${query.error} - ${query.error_description}`
|
|
65
|
+
);
|
|
66
|
+
res.writeHead(400, SECURITY_HEADERS);
|
|
67
|
+
res.end(`
|
|
68
|
+
<html>
|
|
69
|
+
<head>
|
|
70
|
+
<title>Authentication Error</title>
|
|
71
|
+
<style>
|
|
72
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
73
|
+
h1 { color: #d9534f; }
|
|
74
|
+
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 4px; }
|
|
75
|
+
</style>
|
|
76
|
+
</head>
|
|
77
|
+
<body>
|
|
78
|
+
<h1>Authentication Error</h1>
|
|
79
|
+
<div class="error-box">
|
|
80
|
+
<p><strong>Error:</strong> ${escapeHtml(query.error)}</p>
|
|
81
|
+
<p><strong>Description:</strong> ${escapeHtml(query.error_description || 'No description provided')}</p>
|
|
82
|
+
</div>
|
|
83
|
+
<p>Please close this window and try again.</p>
|
|
84
|
+
</body>
|
|
85
|
+
</html>
|
|
86
|
+
`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (query.code) {
|
|
91
|
+
// Validate OAuth state parameter for CSRF protection
|
|
92
|
+
if (!query.state || !pendingStates.has(query.state)) {
|
|
93
|
+
console.error('Invalid or missing OAuth state parameter');
|
|
94
|
+
res.writeHead(403, SECURITY_HEADERS);
|
|
95
|
+
res.end(`
|
|
96
|
+
<html>
|
|
97
|
+
<head>
|
|
98
|
+
<title>Invalid State</title>
|
|
99
|
+
<style>
|
|
100
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
101
|
+
h1 { color: #d9534f; }
|
|
102
|
+
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 4px; }
|
|
103
|
+
</style>
|
|
104
|
+
</head>
|
|
105
|
+
<body>
|
|
106
|
+
<h1>Invalid Request</h1>
|
|
107
|
+
<div class="error-box">
|
|
108
|
+
<p>The OAuth state parameter is invalid or expired. This may indicate a CSRF attempt.</p>
|
|
109
|
+
</div>
|
|
110
|
+
<p>Please close this window and try authenticating again.</p>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
113
|
+
`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
pendingStates.delete(query.state);
|
|
117
|
+
|
|
118
|
+
console.log('Authorization code received, exchanging for tokens...');
|
|
119
|
+
|
|
120
|
+
// Exchange code for tokens
|
|
121
|
+
exchangeCodeForTokens(query.code)
|
|
122
|
+
.then((_tokens) => {
|
|
123
|
+
console.log('Token exchange successful');
|
|
124
|
+
res.writeHead(200, SECURITY_HEADERS);
|
|
125
|
+
res.end(`
|
|
126
|
+
<html>
|
|
127
|
+
<head>
|
|
128
|
+
<title>Authentication Successful</title>
|
|
129
|
+
<style>
|
|
130
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
131
|
+
h1 { color: #5cb85c; }
|
|
132
|
+
.success-box { background-color: #d4edda; border: 1px solid #c3e6cb; padding: 15px; border-radius: 4px; }
|
|
133
|
+
</style>
|
|
134
|
+
</head>
|
|
135
|
+
<body>
|
|
136
|
+
<h1>Authentication Successful!</h1>
|
|
137
|
+
<div class="success-box">
|
|
138
|
+
<p>You have successfully authenticated with Microsoft Graph API.</p>
|
|
139
|
+
<p>The access token has been saved securely.</p>
|
|
140
|
+
</div>
|
|
141
|
+
<p>You can now close this window and return to your AI assistant.</p>
|
|
142
|
+
</body>
|
|
143
|
+
</html>
|
|
144
|
+
`);
|
|
145
|
+
})
|
|
146
|
+
.catch((error) => {
|
|
147
|
+
console.error(`Token exchange error: ${error.message}`);
|
|
148
|
+
res.writeHead(500, SECURITY_HEADERS);
|
|
149
|
+
res.end(`
|
|
150
|
+
<html>
|
|
151
|
+
<head>
|
|
152
|
+
<title>Token Exchange Error</title>
|
|
153
|
+
<style>
|
|
154
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
155
|
+
h1 { color: #d9534f; }
|
|
156
|
+
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 4px; }
|
|
157
|
+
</style>
|
|
158
|
+
</head>
|
|
159
|
+
<body>
|
|
160
|
+
<h1>Token Exchange Error</h1>
|
|
161
|
+
<div class="error-box">
|
|
162
|
+
<p>Token exchange failed. Please try authenticating again.</p>
|
|
163
|
+
</div>
|
|
164
|
+
<p>Please close this window and try again.</p>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
167
|
+
`);
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
console.error('No authorization code provided');
|
|
171
|
+
res.writeHead(400, SECURITY_HEADERS);
|
|
172
|
+
res.end(`
|
|
173
|
+
<html>
|
|
174
|
+
<head>
|
|
175
|
+
<title>Missing Authorization Code</title>
|
|
176
|
+
<style>
|
|
177
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
178
|
+
h1 { color: #d9534f; }
|
|
179
|
+
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 4px; }
|
|
180
|
+
</style>
|
|
181
|
+
</head>
|
|
182
|
+
<body>
|
|
183
|
+
<h1>Missing Authorization Code</h1>
|
|
184
|
+
<div class="error-box">
|
|
185
|
+
<p>No authorization code was provided in the callback.</p>
|
|
186
|
+
</div>
|
|
187
|
+
<p>Please close this window and try again.</p>
|
|
188
|
+
</body>
|
|
189
|
+
</html>
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
} else if (pathname === '/auth') {
|
|
193
|
+
// Handle the /auth route - redirect to Microsoft's OAuth authorization endpoint
|
|
194
|
+
console.log('Auth request received, redirecting to Microsoft login...');
|
|
195
|
+
|
|
196
|
+
// Verify credentials are set
|
|
197
|
+
if (!AUTH_CONFIG.clientId || !AUTH_CONFIG.clientSecret) {
|
|
198
|
+
res.writeHead(500, SECURITY_HEADERS);
|
|
199
|
+
res.end(`
|
|
200
|
+
<html>
|
|
201
|
+
<head>
|
|
202
|
+
<title>Configuration Error</title>
|
|
203
|
+
<style>
|
|
204
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
205
|
+
h1 { color: #d9534f; }
|
|
206
|
+
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 4px; }
|
|
207
|
+
code { background: #f4f4f4; padding: 2px 4px; border-radius: 4px; }
|
|
208
|
+
</style>
|
|
209
|
+
</head>
|
|
210
|
+
<body>
|
|
211
|
+
<h1>Configuration Error</h1>
|
|
212
|
+
<div class="error-box">
|
|
213
|
+
<p>Microsoft Graph API credentials are not set. Please set the following environment variables:</p>
|
|
214
|
+
<ul>
|
|
215
|
+
<li><code>OUTLOOK_CLIENT_ID</code></li>
|
|
216
|
+
<li><code>OUTLOOK_CLIENT_SECRET</code></li>
|
|
217
|
+
</ul>
|
|
218
|
+
<p style="margin-top: 10px; font-size: 0.9em; color: #666;">Legacy names <code>MS_CLIENT_ID</code> / <code>MS_CLIENT_SECRET</code> are also accepted.</p>
|
|
219
|
+
</div>
|
|
220
|
+
</body>
|
|
221
|
+
</html>
|
|
222
|
+
`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Get client_id from query parameters or use the default
|
|
227
|
+
const query = parsedUrl.query;
|
|
228
|
+
const clientId = query.client_id || AUTH_CONFIG.clientId;
|
|
229
|
+
|
|
230
|
+
// Generate cryptographically secure state for CSRF protection
|
|
231
|
+
const state = crypto.randomUUID();
|
|
232
|
+
pendingStates.set(state, Date.now());
|
|
233
|
+
|
|
234
|
+
// Clean up expired states (older than 10 minutes)
|
|
235
|
+
const TEN_MINUTES = 10 * 60 * 1000;
|
|
236
|
+
for (const [key, timestamp] of pendingStates) {
|
|
237
|
+
if (Date.now() - timestamp > TEN_MINUTES) pendingStates.delete(key);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Build the authorization URL
|
|
241
|
+
const authParams = {
|
|
242
|
+
client_id: clientId,
|
|
243
|
+
response_type: 'code',
|
|
244
|
+
redirect_uri: AUTH_CONFIG.redirectUri,
|
|
245
|
+
scope: AUTH_CONFIG.scopes.join(' '),
|
|
246
|
+
response_mode: 'query',
|
|
247
|
+
state,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${querystring.stringify(authParams)}`;
|
|
251
|
+
console.log(`Redirecting to: ${authUrl}`);
|
|
252
|
+
|
|
253
|
+
// Redirect to Microsoft's login page
|
|
254
|
+
res.writeHead(302, { Location: authUrl });
|
|
255
|
+
res.end();
|
|
256
|
+
} else if (pathname === '/') {
|
|
257
|
+
// Root path - provide instructions
|
|
258
|
+
res.writeHead(200, SECURITY_HEADERS);
|
|
259
|
+
res.end(`
|
|
260
|
+
<html>
|
|
261
|
+
<head>
|
|
262
|
+
<title>Outlook Authentication Server</title>
|
|
263
|
+
<style>
|
|
264
|
+
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
|
265
|
+
h1 { color: #0078d4; }
|
|
266
|
+
.info-box { background-color: #e7f6fd; border: 1px solid #b3e0ff; padding: 15px; border-radius: 4px; }
|
|
267
|
+
code { background: #f4f4f4; padding: 2px 4px; border-radius: 4px; }
|
|
268
|
+
</style>
|
|
269
|
+
</head>
|
|
270
|
+
<body>
|
|
271
|
+
<h1>Outlook Authentication Server</h1>
|
|
272
|
+
<div class="info-box">
|
|
273
|
+
<p>This server is running to handle Microsoft Graph API authentication callbacks.</p>
|
|
274
|
+
<p>Don't navigate here directly. Instead, use the <code>auth</code> tool (with <code>action=authenticate</code>) in your AI assistant to start the authentication process.</p>
|
|
275
|
+
<p>Make sure you've set the <code>OUTLOOK_CLIENT_ID</code> and <code>OUTLOOK_CLIENT_SECRET</code> environment variables.</p>
|
|
276
|
+
</div>
|
|
277
|
+
<p>Server is running at http://localhost:3333</p>
|
|
278
|
+
</body>
|
|
279
|
+
</html>
|
|
280
|
+
`);
|
|
281
|
+
} else {
|
|
282
|
+
// Not found
|
|
283
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
284
|
+
res.end('Not Found');
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
function exchangeCodeForTokens(code) {
|
|
289
|
+
return new Promise((resolve, reject) => {
|
|
290
|
+
const postData = querystring.stringify({
|
|
291
|
+
client_id: AUTH_CONFIG.clientId,
|
|
292
|
+
client_secret: AUTH_CONFIG.clientSecret,
|
|
293
|
+
code: code,
|
|
294
|
+
redirect_uri: AUTH_CONFIG.redirectUri,
|
|
295
|
+
grant_type: 'authorization_code',
|
|
296
|
+
scope: AUTH_CONFIG.scopes.join(' '),
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const options = {
|
|
300
|
+
hostname: 'login.microsoftonline.com',
|
|
301
|
+
path: '/common/oauth2/v2.0/token',
|
|
302
|
+
method: 'POST',
|
|
303
|
+
headers: {
|
|
304
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
305
|
+
'Content-Length': Buffer.byteLength(postData),
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const req = https.request(options, (res) => {
|
|
310
|
+
let data = '';
|
|
311
|
+
|
|
312
|
+
res.on('data', (chunk) => {
|
|
313
|
+
data += chunk;
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
res.on('end', () => {
|
|
317
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
318
|
+
try {
|
|
319
|
+
const tokenResponse = JSON.parse(data);
|
|
320
|
+
|
|
321
|
+
// Calculate expiration time (current time + expires_in seconds)
|
|
322
|
+
const expiresAt = Date.now() + tokenResponse.expires_in * 1000;
|
|
323
|
+
|
|
324
|
+
// Add expires_at for easier expiration checking
|
|
325
|
+
tokenResponse.expires_at = expiresAt;
|
|
326
|
+
|
|
327
|
+
// Save tokens to file
|
|
328
|
+
fs.writeFileSync(
|
|
329
|
+
AUTH_CONFIG.tokenStorePath,
|
|
330
|
+
JSON.stringify(tokenResponse, null, 2),
|
|
331
|
+
{ encoding: 'utf8', mode: 0o600 }
|
|
332
|
+
);
|
|
333
|
+
console.log(`Tokens saved to ${AUTH_CONFIG.tokenStorePath}`);
|
|
334
|
+
|
|
335
|
+
resolve(tokenResponse);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
reject(new Error(`Error parsing token response: ${error.message}`));
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
reject(
|
|
341
|
+
new Error(
|
|
342
|
+
`Token exchange failed with status ${res.statusCode}: ${data}`
|
|
343
|
+
)
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
req.on('error', (error) => {
|
|
350
|
+
reject(error);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
req.write(postData);
|
|
354
|
+
req.end();
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Start server
|
|
359
|
+
const PORT = 3333;
|
|
360
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
361
|
+
console.log(`Authentication server running at http://localhost:${PORT}`);
|
|
362
|
+
console.log(
|
|
363
|
+
`Waiting for authentication callback at ${AUTH_CONFIG.redirectUri}`
|
|
364
|
+
);
|
|
365
|
+
console.log(`Token will be stored at: ${AUTH_CONFIG.tokenStorePath}`);
|
|
366
|
+
|
|
367
|
+
if (!AUTH_CONFIG.clientId || !AUTH_CONFIG.clientSecret) {
|
|
368
|
+
console.log('\n⚠️ WARNING: Microsoft Graph API credentials are not set.');
|
|
369
|
+
console.log(
|
|
370
|
+
' Please set the OUTLOOK_CLIENT_ID and OUTLOOK_CLIENT_SECRET environment variables.'
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Handle termination
|
|
376
|
+
process.on('SIGINT', () => {
|
|
377
|
+
console.log('Authentication server shutting down');
|
|
378
|
+
process.exit(0);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
process.on('SIGTERM', () => {
|
|
382
|
+
console.log('Authentication server shutting down');
|
|
383
|
+
process.exit(0);
|
|
384
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@littlebearapps/outlook-assistant",
|
|
3
|
+
"version": "3.3.0",
|
|
4
|
+
"mcpName": "io.github.littlebearapps/outlook-assistant",
|
|
5
|
+
"description": "Outlook Assistant — MCP server with 20 tools for email, calendar, contacts, and settings via Microsoft Graph API",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"outlook-assistant": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node index.js",
|
|
12
|
+
"auth-server": "node outlook-auth-server.js",
|
|
13
|
+
"test-mode": "USE_TEST_MODE=true node index.js",
|
|
14
|
+
"inspect": "npx @modelcontextprotocol/inspector node index.js",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"lint": "eslint .",
|
|
17
|
+
"lint:fix": "eslint . --fix",
|
|
18
|
+
"format": "prettier --write .",
|
|
19
|
+
"format:check": "prettier --check .",
|
|
20
|
+
"prepare": "husky"
|
|
21
|
+
},
|
|
22
|
+
"lint-staged": {
|
|
23
|
+
"*.js": [
|
|
24
|
+
"eslint --fix",
|
|
25
|
+
"prettier --write"
|
|
26
|
+
],
|
|
27
|
+
"*.{md,json,yml,yaml}": [
|
|
28
|
+
"prettier --write"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"claude",
|
|
33
|
+
"outlook",
|
|
34
|
+
"mcp",
|
|
35
|
+
"microsoft-graph",
|
|
36
|
+
"email",
|
|
37
|
+
"calendar",
|
|
38
|
+
"contacts",
|
|
39
|
+
"model-context-protocol",
|
|
40
|
+
"anthropic"
|
|
41
|
+
],
|
|
42
|
+
"author": "Little Bear Apps <hello@littlebearapps.com>",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/littlebearapps/outlook-assistant.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://littlebearapps.com/builds/outlook-assistant",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/littlebearapps/outlook-assistant/issues"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"index.js",
|
|
57
|
+
"config.js",
|
|
58
|
+
"outlook-auth-server.js",
|
|
59
|
+
"auth/",
|
|
60
|
+
"calendar/",
|
|
61
|
+
"categories/",
|
|
62
|
+
"contacts/",
|
|
63
|
+
"email/",
|
|
64
|
+
"folder/",
|
|
65
|
+
"rules/",
|
|
66
|
+
"settings/",
|
|
67
|
+
"advanced/",
|
|
68
|
+
"utils/",
|
|
69
|
+
".env.example",
|
|
70
|
+
"README.md",
|
|
71
|
+
"LICENSE",
|
|
72
|
+
"llms.txt"
|
|
73
|
+
],
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
76
|
+
"dotenv": "^17.3.1"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@commitlint/cli": "^20.4.3",
|
|
80
|
+
"@commitlint/config-conventional": "^20.4.3",
|
|
81
|
+
"@eslint/js": "^10.0.1",
|
|
82
|
+
"@modelcontextprotocol/inspector": "^0.21.1",
|
|
83
|
+
"eslint": "^10.0.2",
|
|
84
|
+
"globals": "^17.4.0",
|
|
85
|
+
"husky": "^9.1.7",
|
|
86
|
+
"jest": "^30.2.0",
|
|
87
|
+
"lint-staged": "^16.3.2",
|
|
88
|
+
"prettier": "^3.8.1",
|
|
89
|
+
"supertest": "^7.2.2"
|
|
90
|
+
},
|
|
91
|
+
"engines": {
|
|
92
|
+
"node": ">=18.18.0"
|
|
93
|
+
},
|
|
94
|
+
"overrides": {
|
|
95
|
+
"minimatch": ">=3.1.3"
|
|
96
|
+
}
|
|
97
|
+
}
|