@softeria/ms-365-mcp-server 0.36.1 → 0.38.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/README.md CHANGED
@@ -490,6 +490,25 @@ Environment variables:
490
490
  - `MS365_MCP_TENANT_ID`: Custom tenant ID (defaults to 'common' for multi-tenant)
491
491
  - `MS365_MCP_OAUTH_TOKEN`: Pre-existing OAuth token for Microsoft Graph API (BYOT method)
492
492
  - `MS365_MCP_KEYVAULT_URL`: Azure Key Vault URL for secrets management (see Azure Key Vault section)
493
+ - `MS365_MCP_TOKEN_CACHE_PATH`: Custom file path for MSAL token cache (see Token Storage below)
494
+ - `MS365_MCP_SELECTED_ACCOUNT_PATH`: Custom file path for selected account metadata (see Token Storage below)
495
+
496
+ ## Token Storage
497
+
498
+ Authentication tokens are stored using the OS credential store (via keytar) when available. If keytar is not installed or fails (common on headless Linux), the server falls back to file-based storage.
499
+
500
+ **Default fallback paths** are relative to the installed package directory. This means tokens can be lost when the package is reinstalled or updated via npm.
501
+
502
+ To persist tokens across updates, set custom paths outside the package directory:
503
+
504
+ ```bash
505
+ export MS365_MCP_TOKEN_CACHE_PATH="$HOME/.config/ms365-mcp/.token-cache.json"
506
+ export MS365_MCP_SELECTED_ACCOUNT_PATH="$HOME/.config/ms365-mcp/.selected-account.json"
507
+ ```
508
+
509
+ Parent directories are created automatically. Files are written with `0600` permissions.
510
+
511
+ > **Security note**: File-based token storage writes sensitive credentials to disk. Ensure the chosen directory has appropriate access controls. The OS credential store (keytar) is preferred when available.
493
512
 
494
513
  ## Azure Key Vault Integration
495
514
 
@@ -253,6 +253,8 @@ function reduceProperties(schema, schemaName) {
253
253
  'path',
254
254
  'method',
255
255
  'enabled',
256
+ 'singleValueExtendedProperties',
257
+ 'multiValueExtendedProperties',
256
258
  ];
257
259
 
258
260
  const keptProperties = {};
@@ -264,12 +266,14 @@ function reduceProperties(schema, schemaName) {
264
266
  }
265
267
  });
266
268
 
267
- const remainingSlots = 25 - Object.keys(keptProperties).length;
269
+ const remainingSlots = Math.max(0, 25 - Object.keys(keptProperties).length);
268
270
  const otherKeys = propertyKeys.filter((key) => !keptProperties[key]);
269
271
 
270
- otherKeys.slice(0, remainingSlots).forEach((key) => {
271
- keptProperties[key] = properties[key];
272
- });
272
+ if (remainingSlots > 0) {
273
+ otherKeys.slice(0, remainingSlots).forEach((key) => {
274
+ keptProperties[key] = properties[key];
275
+ });
276
+ }
273
277
 
274
278
  schema.properties = keptProperties;
275
279
  schema.additionalProperties = true;
package/dist/auth.js CHANGED
@@ -34,8 +34,20 @@ const SERVICE_NAME = "ms-365-mcp-server";
34
34
  const TOKEN_CACHE_ACCOUNT = "msal-token-cache";
35
35
  const SELECTED_ACCOUNT_KEY = "selected-account";
36
36
  const FALLBACK_DIR = path.dirname(fileURLToPath(import.meta.url));
37
- const FALLBACK_PATH = path.join(FALLBACK_DIR, "..", ".token-cache.json");
38
- const SELECTED_ACCOUNT_PATH = path.join(FALLBACK_DIR, "..", ".selected-account.json");
37
+ const DEFAULT_TOKEN_CACHE_PATH = path.join(FALLBACK_DIR, "..", ".token-cache.json");
38
+ const DEFAULT_SELECTED_ACCOUNT_PATH = path.join(FALLBACK_DIR, "..", ".selected-account.json");
39
+ function getTokenCachePath() {
40
+ const envPath = process.env.MS365_MCP_TOKEN_CACHE_PATH?.trim();
41
+ return envPath || DEFAULT_TOKEN_CACHE_PATH;
42
+ }
43
+ function getSelectedAccountPath() {
44
+ const envPath = process.env.MS365_MCP_SELECTED_ACCOUNT_PATH?.trim();
45
+ return envPath || DEFAULT_SELECTED_ACCOUNT_PATH;
46
+ }
47
+ function ensureParentDir(filePath) {
48
+ const dir = path.dirname(filePath);
49
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
50
+ }
39
51
  function createMsalConfig(secrets) {
40
52
  const cloudEndpoints = getCloudEndpoints(secrets.cloudType);
41
53
  return {
@@ -128,8 +140,9 @@ class AuthManager {
128
140
  `Keychain access failed, falling back to file storage: ${keytarError.message}`
129
141
  );
130
142
  }
131
- if (!cacheData && existsSync(FALLBACK_PATH)) {
132
- cacheData = readFileSync(FALLBACK_PATH, "utf8");
143
+ const cachePath = getTokenCachePath();
144
+ if (!cacheData && existsSync(cachePath)) {
145
+ cacheData = readFileSync(cachePath, "utf8");
133
146
  }
134
147
  if (cacheData) {
135
148
  this.msalApp.getTokenCache().deserialize(cacheData);
@@ -155,8 +168,9 @@ class AuthManager {
155
168
  `Keychain access failed for selected account, falling back to file storage: ${keytarError.message}`
156
169
  );
157
170
  }
158
- if (!selectedAccountData && existsSync(SELECTED_ACCOUNT_PATH)) {
159
- selectedAccountData = readFileSync(SELECTED_ACCOUNT_PATH, "utf8");
171
+ const accountPath = getSelectedAccountPath();
172
+ if (!selectedAccountData && existsSync(accountPath)) {
173
+ selectedAccountData = readFileSync(accountPath, "utf8");
160
174
  }
161
175
  if (selectedAccountData) {
162
176
  const parsed = JSON.parse(selectedAccountData);
@@ -175,13 +189,17 @@ class AuthManager {
175
189
  if (kt) {
176
190
  await kt.setPassword(SERVICE_NAME, TOKEN_CACHE_ACCOUNT, cacheData);
177
191
  } else {
178
- fs.writeFileSync(FALLBACK_PATH, cacheData, { mode: 384 });
192
+ const cachePath = getTokenCachePath();
193
+ ensureParentDir(cachePath);
194
+ fs.writeFileSync(cachePath, cacheData, { mode: 384 });
179
195
  }
180
196
  } catch (keytarError) {
181
197
  logger.warn(
182
198
  `Keychain save failed, falling back to file storage: ${keytarError.message}`
183
199
  );
184
- fs.writeFileSync(FALLBACK_PATH, cacheData, { mode: 384 });
200
+ const cachePath = getTokenCachePath();
201
+ ensureParentDir(cachePath);
202
+ fs.writeFileSync(cachePath, cacheData, { mode: 384 });
185
203
  }
186
204
  } catch (error) {
187
205
  logger.error(`Error saving token cache: ${error.message}`);
@@ -195,13 +213,17 @@ class AuthManager {
195
213
  if (kt) {
196
214
  await kt.setPassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY, selectedAccountData);
197
215
  } else {
198
- fs.writeFileSync(SELECTED_ACCOUNT_PATH, selectedAccountData, { mode: 384 });
216
+ const accountPath = getSelectedAccountPath();
217
+ ensureParentDir(accountPath);
218
+ fs.writeFileSync(accountPath, selectedAccountData, { mode: 384 });
199
219
  }
200
220
  } catch (keytarError) {
201
221
  logger.warn(
202
222
  `Keychain save failed for selected account, falling back to file storage: ${keytarError.message}`
203
223
  );
204
- fs.writeFileSync(SELECTED_ACCOUNT_PATH, selectedAccountData, { mode: 384 });
224
+ const accountPath = getSelectedAccountPath();
225
+ ensureParentDir(accountPath);
226
+ fs.writeFileSync(accountPath, selectedAccountData, { mode: 384 });
205
227
  }
206
228
  } catch (error) {
207
229
  logger.error(`Error saving selected account: ${error.message}`);
@@ -359,11 +381,13 @@ class AuthManager {
359
381
  } catch (keytarError) {
360
382
  logger.warn(`Keychain deletion failed: ${keytarError.message}`);
361
383
  }
362
- if (fs.existsSync(FALLBACK_PATH)) {
363
- fs.unlinkSync(FALLBACK_PATH);
384
+ const cachePath = getTokenCachePath();
385
+ if (fs.existsSync(cachePath)) {
386
+ fs.unlinkSync(cachePath);
364
387
  }
365
- if (fs.existsSync(SELECTED_ACCOUNT_PATH)) {
366
- fs.unlinkSync(SELECTED_ACCOUNT_PATH);
388
+ const accountPath = getSelectedAccountPath();
389
+ if (fs.existsSync(accountPath)) {
390
+ fs.unlinkSync(accountPath);
367
391
  }
368
392
  return true;
369
393
  } catch (error) {
@@ -418,5 +442,7 @@ class AuthManager {
418
442
  var auth_default = AuthManager;
419
443
  export {
420
444
  buildScopesFromEndpoints,
421
- auth_default as default
445
+ auth_default as default,
446
+ getSelectedAccountPath,
447
+ getTokenCachePath
422
448
  };
@@ -153,14 +153,16 @@
153
153
  "method": "get",
154
154
  "toolName": "list-calendar-events",
155
155
  "scopes": ["Calendars.Read"],
156
- "supportsTimezone": true
156
+ "supportsTimezone": true,
157
+ "supportsExpandExtendedProperties": true
157
158
  },
158
159
  {
159
160
  "pathPattern": "/me/events/{event-id}",
160
161
  "method": "get",
161
162
  "toolName": "get-calendar-event",
162
163
  "scopes": ["Calendars.Read"],
163
- "supportsTimezone": true
164
+ "supportsTimezone": true,
165
+ "supportsExpandExtendedProperties": true
164
166
  },
165
167
  {
166
168
  "pathPattern": "/me/events",
@@ -187,14 +189,16 @@
187
189
  "method": "get",
188
190
  "toolName": "list-specific-calendar-events",
189
191
  "scopes": ["Calendars.Read"],
190
- "supportsTimezone": true
192
+ "supportsTimezone": true,
193
+ "supportsExpandExtendedProperties": true
191
194
  },
192
195
  {
193
196
  "pathPattern": "/me/calendars/{calendar-id}/events/{event-id}",
194
197
  "method": "get",
195
198
  "toolName": "get-specific-calendar-event",
196
199
  "scopes": ["Calendars.Read"],
197
- "supportsTimezone": true
200
+ "supportsTimezone": true,
201
+ "supportsExpandExtendedProperties": true
198
202
  },
199
203
  {
200
204
  "pathPattern": "/me/calendars/{calendar-id}/events",
@@ -221,7 +225,27 @@
221
225
  "method": "get",
222
226
  "toolName": "get-calendar-view",
223
227
  "scopes": ["Calendars.Read"],
224
- "supportsTimezone": true
228
+ "supportsTimezone": true,
229
+ "supportsExpandExtendedProperties": true,
230
+ "llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar."
231
+ },
232
+ {
233
+ "pathPattern": "/me/calendars/{calendar-id}/calendarView",
234
+ "method": "get",
235
+ "toolName": "get-specific-calendar-view",
236
+ "scopes": ["Calendars.Read"],
237
+ "supportsTimezone": true,
238
+ "supportsExpandExtendedProperties": true,
239
+ "llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events."
240
+ },
241
+ {
242
+ "pathPattern": "/me/calendars/{calendar-id}/events/{event-id}/instances",
243
+ "method": "get",
244
+ "toolName": "list-calendar-event-instances",
245
+ "scopes": ["Calendars.Read"],
246
+ "supportsTimezone": true,
247
+ "supportsExpandExtendedProperties": true,
248
+ "llmTip": "Expand a recurring event into individual instances within a date range. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use this to see all occurrences of a recurring event."
225
249
  },
226
250
  {
227
251
  "pathPattern": "/me/calendars",
@@ -1345,53 +1345,6 @@ const microsoft_graph_attendee = z.object({
1345
1345
  status: microsoft_graph_responseStatus.optional()
1346
1346
  }).strict();
1347
1347
  const microsoft_graph_importance = z.enum(["low", "normal", "high"]);
1348
- const microsoft_graph_physicalAddress = z.object({
1349
- city: z.string().describe("The city.").nullish(),
1350
- countryOrRegion: z.string().describe(
1351
- "The country or region. It's a free-format string value, for example, 'United States'."
1352
- ).nullish(),
1353
- postalCode: z.string().describe("The postal code.").nullish(),
1354
- state: z.string().describe("The state.").nullish(),
1355
- street: z.string().describe("The street.").nullish()
1356
- }).strict();
1357
- const microsoft_graph_outlookGeoCoordinates = z.object({
1358
- accuracy: z.number().describe(
1359
- "The accuracy of the latitude and longitude. As an example, the accuracy can be measured in meters, such as the latitude and longitude are accurate to within 50 meters. [Simplified from 3 options]"
1360
- ).nullish(),
1361
- altitude: z.number().describe("The altitude of the location. [Simplified from 3 options]").nullish(),
1362
- altitudeAccuracy: z.number().describe("The accuracy of the altitude. [Simplified from 3 options]").nullish(),
1363
- latitude: z.number().describe("The latitude of the location. [Simplified from 3 options]").nullish(),
1364
- longitude: z.number().describe("The longitude of the location. [Simplified from 3 options]").nullish()
1365
- }).strict();
1366
- const microsoft_graph_locationType = z.enum([
1367
- "default",
1368
- "conferenceRoom",
1369
- "homeAddress",
1370
- "businessAddress",
1371
- "geoCoordinates",
1372
- "streetAddress",
1373
- "hotel",
1374
- "restaurant",
1375
- "localBusiness",
1376
- "postalAddress"
1377
- ]);
1378
- const microsoft_graph_locationUniqueIdType = z.enum([
1379
- "unknown",
1380
- "locationStore",
1381
- "directory",
1382
- "private",
1383
- "bing"
1384
- ]);
1385
- const microsoft_graph_location = z.object({
1386
- address: microsoft_graph_physicalAddress.optional(),
1387
- coordinates: microsoft_graph_outlookGeoCoordinates.optional(),
1388
- displayName: z.string().describe("The name associated with the location.").nullish(),
1389
- locationEmailAddress: z.string().describe("Optional email address of the location.").nullish(),
1390
- locationType: microsoft_graph_locationType.optional(),
1391
- locationUri: z.string().describe("Optional URI representing the location.").nullish(),
1392
- uniqueId: z.string().describe("For internal use only.").nullish(),
1393
- uniqueIdType: microsoft_graph_locationUniqueIdType.optional()
1394
- }).strict();
1395
1348
  const microsoft_graph_event = z.object({
1396
1349
  id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
1397
1350
  createdDateTime: z.string().regex(
@@ -1410,6 +1363,12 @@ const microsoft_graph_event = z.object({
1410
1363
  attachments: z.array(microsoft_graph_attachment).describe(
1411
1364
  "The collection of FileAttachment, ItemAttachment, and referenceAttachment attachments for the event. Navigation property. Read-only. Nullable."
1412
1365
  ).optional(),
1366
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
1367
+ "The collection of single-value extended properties defined for the event. Read-only. Nullable."
1368
+ ).optional(),
1369
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
1370
+ "The collection of multi-value extended properties defined for the event. Read-only. Nullable."
1371
+ ).optional(),
1413
1372
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
1414
1373
  changeKey: z.string().describe(
1415
1374
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -1443,9 +1402,7 @@ const microsoft_graph_event = z.object({
1443
1402
  ).nullish(),
1444
1403
  isOrganizer: z.boolean().describe(
1445
1404
  "Set to true if the calendar owner (specified by the owner property of the calendar) is the organizer of the event (specified by the organizer property of the event). It also applies if a delegate organized the event on behalf of the owner."
1446
- ).nullish(),
1447
- isReminderOn: z.boolean().describe("Set to true if an alert is set to remind the user of the event.").nullish(),
1448
- location: microsoft_graph_location.optional()
1405
+ ).nullish()
1449
1406
  }).strict().passthrough();
1450
1407
  const microsoft_graph_calendar = z.object({
1451
1408
  id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
@@ -1503,6 +1460,15 @@ const microsoft_graph_chatCollectionResponse = z.object({
1503
1460
  "@odata.nextLink": z.string().nullable(),
1504
1461
  value: z.array(microsoft_graph_chat)
1505
1462
  }).partial().strict();
1463
+ const microsoft_graph_physicalAddress = z.object({
1464
+ city: z.string().describe("The city.").nullish(),
1465
+ countryOrRegion: z.string().describe(
1466
+ "The country or region. It's a free-format string value, for example, 'United States'."
1467
+ ).nullish(),
1468
+ postalCode: z.string().describe("The postal code.").nullish(),
1469
+ state: z.string().describe("The state.").nullish(),
1470
+ street: z.string().describe("The street.").nullish()
1471
+ }).strict();
1506
1472
  const microsoft_graph_contact = z.object({
1507
1473
  id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
1508
1474
  displayName: z.string().describe(
@@ -1519,6 +1485,12 @@ const microsoft_graph_contact = z.object({
1519
1485
  "The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z"
1520
1486
  ).nullish(),
1521
1487
  title: z.string().describe("The contact's title.").nullish(),
1488
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
1489
+ "The collection of single-value extended properties defined for the contact. Read-only. Nullable."
1490
+ ).optional(),
1491
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
1492
+ "The collection of multi-value extended properties defined for the contact. Read-only. Nullable."
1493
+ ).optional(),
1522
1494
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
1523
1495
  changeKey: z.string().describe(
1524
1496
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -1542,9 +1514,7 @@ const microsoft_graph_contact = z.object({
1542
1514
  homeAddress: microsoft_graph_physicalAddress.optional(),
1543
1515
  homePhones: z.array(z.string().nullable()).describe("The contact's home phone numbers.").optional(),
1544
1516
  imAddresses: z.array(z.string().nullable()).describe("The contact's instant messaging (IM) addresses.").optional(),
1545
- initials: z.string().describe("The contact's initials.").nullish(),
1546
- jobTitle: z.string().describe("The contact\u2019s job title.").nullish(),
1547
- manager: z.string().describe("The name of the contact's manager.").nullish()
1517
+ initials: z.string().describe("The contact's initials.").nullish()
1548
1518
  }).strict().passthrough();
1549
1519
  const microsoft_graph_contactCollectionResponse = z.object({
1550
1520
  "@odata.count": z.number().int().nullable(),
@@ -2453,6 +2423,12 @@ const microsoft_graph_message = z.object({
2453
2423
  body: microsoft_graph_itemBody.optional(),
2454
2424
  subject: z.string().describe("The subject of the message.").nullish(),
2455
2425
  attachments: z.array(microsoft_graph_attachment).describe("The fileAttachment and itemAttachment attachments for the message.").optional(),
2426
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
2427
+ "The collection of single-value extended properties defined for the message. Nullable."
2428
+ ).optional(),
2429
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
2430
+ "The collection of multi-value extended properties defined for the message. Nullable."
2431
+ ).optional(),
2456
2432
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
2457
2433
  changeKey: z.string().describe(
2458
2434
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -2477,9 +2453,7 @@ const microsoft_graph_message = z.object({
2477
2453
  isDraft: z.boolean().describe(
2478
2454
  "Indicates whether the message is a draft. A message is a draft if it hasn't been sent yet."
2479
2455
  ).nullish(),
2480
- isRead: z.boolean().describe("Indicates whether the message has been read.").nullish(),
2481
- isReadReceiptRequested: z.boolean().describe("Indicates whether a read receipt is requested for the message.").nullish(),
2482
- parentFolderId: z.string().describe("The unique identifier for the message's parent mailFolder.").nullish()
2456
+ isRead: z.boolean().describe("Indicates whether the message has been read.").nullish()
2483
2457
  }).strict().passthrough();
2484
2458
  const microsoft_graph_mailFolder = z.lazy(
2485
2459
  () => z.object({
@@ -2531,6 +2505,34 @@ const microsoft_graph_attendeeBase = z.object({
2531
2505
  emailAddress: microsoft_graph_emailAddress.optional(),
2532
2506
  type: microsoft_graph_attendeeType.optional()
2533
2507
  }).strict();
2508
+ const microsoft_graph_outlookGeoCoordinates = z.object({
2509
+ accuracy: z.number().describe(
2510
+ "The accuracy of the latitude and longitude. As an example, the accuracy can be measured in meters, such as the latitude and longitude are accurate to within 50 meters. [Simplified from 3 options]"
2511
+ ).nullish(),
2512
+ altitude: z.number().describe("The altitude of the location. [Simplified from 3 options]").nullish(),
2513
+ altitudeAccuracy: z.number().describe("The accuracy of the altitude. [Simplified from 3 options]").nullish(),
2514
+ latitude: z.number().describe("The latitude of the location. [Simplified from 3 options]").nullish(),
2515
+ longitude: z.number().describe("The longitude of the location. [Simplified from 3 options]").nullish()
2516
+ }).strict();
2517
+ const microsoft_graph_locationType = z.enum([
2518
+ "default",
2519
+ "conferenceRoom",
2520
+ "homeAddress",
2521
+ "businessAddress",
2522
+ "geoCoordinates",
2523
+ "streetAddress",
2524
+ "hotel",
2525
+ "restaurant",
2526
+ "localBusiness",
2527
+ "postalAddress"
2528
+ ]);
2529
+ const microsoft_graph_locationUniqueIdType = z.enum([
2530
+ "unknown",
2531
+ "locationStore",
2532
+ "directory",
2533
+ "private",
2534
+ "bing"
2535
+ ]);
2534
2536
  const microsoft_graph_locationConstraintItem = z.object({
2535
2537
  address: microsoft_graph_physicalAddress.optional(),
2536
2538
  coordinates: microsoft_graph_outlookGeoCoordinates.optional(),
@@ -3493,16 +3495,12 @@ const schemas = {
3493
3495
  microsoft_graph_responseStatus,
3494
3496
  microsoft_graph_attendee,
3495
3497
  microsoft_graph_importance,
3496
- microsoft_graph_physicalAddress,
3497
- microsoft_graph_outlookGeoCoordinates,
3498
- microsoft_graph_locationType,
3499
- microsoft_graph_locationUniqueIdType,
3500
- microsoft_graph_location,
3501
3498
  microsoft_graph_event,
3502
3499
  microsoft_graph_calendar,
3503
3500
  microsoft_graph_calendarCollectionResponse,
3504
3501
  microsoft_graph_eventCollectionResponse,
3505
3502
  microsoft_graph_chatCollectionResponse,
3503
+ microsoft_graph_physicalAddress,
3506
3504
  microsoft_graph_contact,
3507
3505
  microsoft_graph_contactCollectionResponse,
3508
3506
  microsoft_graph_storagePlanInformation,
@@ -3588,6 +3586,9 @@ const schemas = {
3588
3586
  create_forward_draft_Body,
3589
3587
  reply_mail_message_Body,
3590
3588
  microsoft_graph_attendeeBase,
3589
+ microsoft_graph_outlookGeoCoordinates,
3590
+ microsoft_graph_locationType,
3591
+ microsoft_graph_locationUniqueIdType,
3591
3592
  microsoft_graph_locationConstraintItem,
3592
3593
  microsoft_graph_locationConstraint,
3593
3594
  microsoft_graph_activityDomain,
@@ -4273,6 +4274,70 @@ const endpoints = makeApi([
4273
4274
  ],
4274
4275
  response: z.void()
4275
4276
  },
4277
+ {
4278
+ method: "get",
4279
+ path: "/me/calendars/:calendarId/calendarView",
4280
+ alias: "get-specific-calendar-view",
4281
+ description: `The calendar view for the calendar. Navigation property. Read-only.`,
4282
+ requestFormat: "json",
4283
+ parameters: [
4284
+ {
4285
+ name: "startDateTime",
4286
+ type: "Query",
4287
+ schema: z.string().describe(
4288
+ "The start date and time of the time range, represented in ISO 8601 format. For example, 2019-11-08T19:00:00-08:00"
4289
+ )
4290
+ },
4291
+ {
4292
+ name: "endDateTime",
4293
+ type: "Query",
4294
+ schema: z.string().describe(
4295
+ "The end date and time of the time range, represented in ISO 8601 format. For example, 2019-11-08T20:00:00-08:00"
4296
+ )
4297
+ },
4298
+ {
4299
+ name: "$top",
4300
+ type: "Query",
4301
+ schema: z.number().int().gte(0).describe("Show only the first n items").optional()
4302
+ },
4303
+ {
4304
+ name: "$skip",
4305
+ type: "Query",
4306
+ schema: z.number().int().gte(0).describe("Skip the first n items").optional()
4307
+ },
4308
+ {
4309
+ name: "$search",
4310
+ type: "Query",
4311
+ schema: z.string().describe("Search items by search phrases").optional()
4312
+ },
4313
+ {
4314
+ name: "$filter",
4315
+ type: "Query",
4316
+ schema: z.string().describe("Filter items by property values").optional()
4317
+ },
4318
+ {
4319
+ name: "$count",
4320
+ type: "Query",
4321
+ schema: z.boolean().describe("Include count of items").optional()
4322
+ },
4323
+ {
4324
+ name: "$orderby",
4325
+ type: "Query",
4326
+ schema: z.array(z.string()).describe("Order items by property values").optional()
4327
+ },
4328
+ {
4329
+ name: "$select",
4330
+ type: "Query",
4331
+ schema: z.array(z.string()).describe("Select properties to be returned").optional()
4332
+ },
4333
+ {
4334
+ name: "$expand",
4335
+ type: "Query",
4336
+ schema: z.array(z.string()).describe("Expand related entities").optional()
4337
+ }
4338
+ ],
4339
+ response: z.void()
4340
+ },
4276
4341
  {
4277
4342
  method: "get",
4278
4343
  path: "/me/calendars/:calendarId/events",
@@ -4352,6 +4417,12 @@ const endpoints = makeApi([
4352
4417
  attachments: z.array(microsoft_graph_attachment).describe(
4353
4418
  "The collection of FileAttachment, ItemAttachment, and referenceAttachment attachments for the event. Navigation property. Read-only. Nullable."
4354
4419
  ).optional(),
4420
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
4421
+ "The collection of single-value extended properties defined for the event. Read-only. Nullable."
4422
+ ).optional(),
4423
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
4424
+ "The collection of multi-value extended properties defined for the event. Read-only. Nullable."
4425
+ ).optional(),
4355
4426
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
4356
4427
  changeKey: z.string().describe(
4357
4428
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -4387,9 +4458,7 @@ const endpoints = makeApi([
4387
4458
  ).nullish(),
4388
4459
  isOrganizer: z.boolean().describe(
4389
4460
  "Set to true if the calendar owner (specified by the owner property of the calendar) is the organizer of the event (specified by the organizer property of the event). It also applies if a delegate organized the event on behalf of the owner."
4390
- ).nullish(),
4391
- isReminderOn: z.boolean().describe("Set to true if an alert is set to remind the user of the event.").nullish(),
4392
- location: microsoft_graph_location.optional()
4461
+ ).nullish()
4393
4462
  }).strict().passthrough()
4394
4463
  }
4395
4464
  ],
@@ -4444,6 +4513,12 @@ const endpoints = makeApi([
4444
4513
  attachments: z.array(microsoft_graph_attachment).describe(
4445
4514
  "The collection of FileAttachment, ItemAttachment, and referenceAttachment attachments for the event. Navigation property. Read-only. Nullable."
4446
4515
  ).optional(),
4516
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
4517
+ "The collection of single-value extended properties defined for the event. Read-only. Nullable."
4518
+ ).optional(),
4519
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
4520
+ "The collection of multi-value extended properties defined for the event. Read-only. Nullable."
4521
+ ).optional(),
4447
4522
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
4448
4523
  changeKey: z.string().describe(
4449
4524
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -4479,9 +4554,7 @@ const endpoints = makeApi([
4479
4554
  ).nullish(),
4480
4555
  isOrganizer: z.boolean().describe(
4481
4556
  "Set to true if the calendar owner (specified by the owner property of the calendar) is the organizer of the event (specified by the organizer property of the event). It also applies if a delegate organized the event on behalf of the owner."
4482
- ).nullish(),
4483
- isReminderOn: z.boolean().describe("Set to true if an alert is set to remind the user of the event.").nullish(),
4484
- location: microsoft_graph_location.optional()
4557
+ ).nullish()
4485
4558
  }).strict().passthrough()
4486
4559
  }
4487
4560
  ],
@@ -4502,6 +4575,70 @@ const endpoints = makeApi([
4502
4575
  ],
4503
4576
  response: z.void()
4504
4577
  },
4578
+ {
4579
+ method: "get",
4580
+ path: "/me/calendars/:calendarId/events/:eventId/instances",
4581
+ alias: "list-calendar-event-instances",
4582
+ description: `The occurrences of a recurring series, if the event is a series master. This property includes occurrences that are part of the recurrence pattern, and exceptions modified, but doesn't include occurrences canceled from the series. Navigation property. Read-only. Nullable.`,
4583
+ requestFormat: "json",
4584
+ parameters: [
4585
+ {
4586
+ name: "startDateTime",
4587
+ type: "Query",
4588
+ schema: z.string().describe(
4589
+ "The start date and time of the time range, represented in ISO 8601 format. For example, 2019-11-08T19:00:00-08:00"
4590
+ )
4591
+ },
4592
+ {
4593
+ name: "endDateTime",
4594
+ type: "Query",
4595
+ schema: z.string().describe(
4596
+ "The end date and time of the time range, represented in ISO 8601 format. For example, 2019-11-08T20:00:00-08:00"
4597
+ )
4598
+ },
4599
+ {
4600
+ name: "$top",
4601
+ type: "Query",
4602
+ schema: z.number().int().gte(0).describe("Show only the first n items").optional()
4603
+ },
4604
+ {
4605
+ name: "$skip",
4606
+ type: "Query",
4607
+ schema: z.number().int().gte(0).describe("Skip the first n items").optional()
4608
+ },
4609
+ {
4610
+ name: "$search",
4611
+ type: "Query",
4612
+ schema: z.string().describe("Search items by search phrases").optional()
4613
+ },
4614
+ {
4615
+ name: "$filter",
4616
+ type: "Query",
4617
+ schema: z.string().describe("Filter items by property values").optional()
4618
+ },
4619
+ {
4620
+ name: "$count",
4621
+ type: "Query",
4622
+ schema: z.boolean().describe("Include count of items").optional()
4623
+ },
4624
+ {
4625
+ name: "$orderby",
4626
+ type: "Query",
4627
+ schema: z.array(z.string()).describe("Order items by property values").optional()
4628
+ },
4629
+ {
4630
+ name: "$select",
4631
+ type: "Query",
4632
+ schema: z.array(z.string()).describe("Select properties to be returned").optional()
4633
+ },
4634
+ {
4635
+ name: "$expand",
4636
+ type: "Query",
4637
+ schema: z.array(z.string()).describe("Expand related entities").optional()
4638
+ }
4639
+ ],
4640
+ response: z.void()
4641
+ },
4505
4642
  {
4506
4643
  method: "get",
4507
4644
  path: "/me/calendarView",
@@ -4694,6 +4831,12 @@ or from some other calendar of the user.`,
4694
4831
  "The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z"
4695
4832
  ).nullish(),
4696
4833
  title: z.string().describe("The contact's title.").nullish(),
4834
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
4835
+ "The collection of single-value extended properties defined for the contact. Read-only. Nullable."
4836
+ ).optional(),
4837
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
4838
+ "The collection of multi-value extended properties defined for the contact. Read-only. Nullable."
4839
+ ).optional(),
4697
4840
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
4698
4841
  changeKey: z.string().describe(
4699
4842
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -4717,9 +4860,7 @@ or from some other calendar of the user.`,
4717
4860
  homeAddress: microsoft_graph_physicalAddress.optional(),
4718
4861
  homePhones: z.array(z.string().nullable()).describe("The contact's home phone numbers.").optional(),
4719
4862
  imAddresses: z.array(z.string().nullable()).describe("The contact's instant messaging (IM) addresses.").optional(),
4720
- initials: z.string().describe("The contact's initials.").nullish(),
4721
- jobTitle: z.string().describe("The contact\u2019s job title.").nullish(),
4722
- manager: z.string().describe("The name of the contact's manager.").nullish()
4863
+ initials: z.string().describe("The contact's initials.").nullish()
4723
4864
  }).strict().passthrough()
4724
4865
  }
4725
4866
  ],
@@ -4772,6 +4913,12 @@ or from some other calendar of the user.`,
4772
4913
  "The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z"
4773
4914
  ).nullish(),
4774
4915
  title: z.string().describe("The contact's title.").nullish(),
4916
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
4917
+ "The collection of single-value extended properties defined for the contact. Read-only. Nullable."
4918
+ ).optional(),
4919
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
4920
+ "The collection of multi-value extended properties defined for the contact. Read-only. Nullable."
4921
+ ).optional(),
4775
4922
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
4776
4923
  changeKey: z.string().describe(
4777
4924
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -4795,9 +4942,7 @@ or from some other calendar of the user.`,
4795
4942
  homeAddress: microsoft_graph_physicalAddress.optional(),
4796
4943
  homePhones: z.array(z.string().nullable()).describe("The contact's home phone numbers.").optional(),
4797
4944
  imAddresses: z.array(z.string().nullable()).describe("The contact's instant messaging (IM) addresses.").optional(),
4798
- initials: z.string().describe("The contact's initials.").nullish(),
4799
- jobTitle: z.string().describe("The contact\u2019s job title.").nullish(),
4800
- manager: z.string().describe("The name of the contact's manager.").nullish()
4945
+ initials: z.string().describe("The contact's initials.").nullish()
4801
4946
  }).strict().passthrough()
4802
4947
  }
4803
4948
  ],
@@ -4950,6 +5095,12 @@ open extensions or extended properties, and how to specify extended properties.`
4950
5095
  attachments: z.array(microsoft_graph_attachment).describe(
4951
5096
  "The collection of FileAttachment, ItemAttachment, and referenceAttachment attachments for the event. Navigation property. Read-only. Nullable."
4952
5097
  ).optional(),
5098
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
5099
+ "The collection of single-value extended properties defined for the event. Read-only. Nullable."
5100
+ ).optional(),
5101
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
5102
+ "The collection of multi-value extended properties defined for the event. Read-only. Nullable."
5103
+ ).optional(),
4953
5104
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
4954
5105
  changeKey: z.string().describe(
4955
5106
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -4985,9 +5136,7 @@ open extensions or extended properties, and how to specify extended properties.`
4985
5136
  ).nullish(),
4986
5137
  isOrganizer: z.boolean().describe(
4987
5138
  "Set to true if the calendar owner (specified by the owner property of the calendar) is the organizer of the event (specified by the organizer property of the event). It also applies if a delegate organized the event on behalf of the owner."
4988
- ).nullish(),
4989
- isReminderOn: z.boolean().describe("Set to true if an alert is set to remind the user of the event.").nullish(),
4990
- location: microsoft_graph_location.optional()
5139
+ ).nullish()
4991
5140
  }).strict().passthrough()
4992
5141
  }
4993
5142
  ],
@@ -5042,6 +5191,12 @@ open extensions or extended properties, and how to specify extended properties.`
5042
5191
  attachments: z.array(microsoft_graph_attachment).describe(
5043
5192
  "The collection of FileAttachment, ItemAttachment, and referenceAttachment attachments for the event. Navigation property. Read-only. Nullable."
5044
5193
  ).optional(),
5194
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
5195
+ "The collection of single-value extended properties defined for the event. Read-only. Nullable."
5196
+ ).optional(),
5197
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
5198
+ "The collection of multi-value extended properties defined for the event. Read-only. Nullable."
5199
+ ).optional(),
5045
5200
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
5046
5201
  changeKey: z.string().describe(
5047
5202
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -5077,9 +5232,7 @@ open extensions or extended properties, and how to specify extended properties.`
5077
5232
  ).nullish(),
5078
5233
  isOrganizer: z.boolean().describe(
5079
5234
  "Set to true if the calendar owner (specified by the owner property of the calendar) is the organizer of the event (specified by the organizer property of the event). It also applies if a delegate organized the event on behalf of the owner."
5080
- ).nullish(),
5081
- isReminderOn: z.boolean().describe("Set to true if an alert is set to remind the user of the event.").nullish(),
5082
- location: microsoft_graph_location.optional()
5235
+ ).nullish()
5083
5236
  }).strict().passthrough()
5084
5237
  }
5085
5238
  ],
@@ -5409,6 +5562,12 @@ folder collection and navigate to another folder. By default, this operation doe
5409
5562
  body: microsoft_graph_itemBody.optional(),
5410
5563
  subject: z.string().describe("The subject of the message.").nullish(),
5411
5564
  attachments: z.array(microsoft_graph_attachment).describe("The fileAttachment and itemAttachment attachments for the message.").optional(),
5565
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
5566
+ "The collection of single-value extended properties defined for the message. Nullable."
5567
+ ).optional(),
5568
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
5569
+ "The collection of multi-value extended properties defined for the message. Nullable."
5570
+ ).optional(),
5412
5571
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
5413
5572
  changeKey: z.string().describe(
5414
5573
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -5433,9 +5592,7 @@ folder collection and navigate to another folder. By default, this operation doe
5433
5592
  isDraft: z.boolean().describe(
5434
5593
  "Indicates whether the message is a draft. A message is a draft if it hasn't been sent yet."
5435
5594
  ).nullish(),
5436
- isRead: z.boolean().describe("Indicates whether the message has been read.").nullish(),
5437
- isReadReceiptRequested: z.boolean().describe("Indicates whether a read receipt is requested for the message.").nullish(),
5438
- parentFolderId: z.string().describe("The unique identifier for the message's parent mailFolder.").nullish()
5595
+ isRead: z.boolean().describe("Indicates whether the message has been read.").nullish()
5439
5596
  }).strict().passthrough()
5440
5597
  }
5441
5598
  ],
@@ -5487,6 +5644,12 @@ folder collection and navigate to another folder. By default, this operation doe
5487
5644
  body: microsoft_graph_itemBody.optional(),
5488
5645
  subject: z.string().describe("The subject of the message.").nullish(),
5489
5646
  attachments: z.array(microsoft_graph_attachment).describe("The fileAttachment and itemAttachment attachments for the message.").optional(),
5647
+ singleValueExtendedProperties: z.array(microsoft_graph_singleValueLegacyExtendedProperty).describe(
5648
+ "The collection of single-value extended properties defined for the message. Nullable."
5649
+ ).optional(),
5650
+ multiValueExtendedProperties: z.array(microsoft_graph_multiValueLegacyExtendedProperty).describe(
5651
+ "The collection of multi-value extended properties defined for the message. Nullable."
5652
+ ).optional(),
5490
5653
  categories: z.array(z.string().nullable()).describe("The categories associated with the item").optional(),
5491
5654
  changeKey: z.string().describe(
5492
5655
  "Identifies the version of the item. Every time the item is changed, changeKey changes as well. This allows Exchange to apply changes to the correct version of the object. Read-only."
@@ -5511,9 +5674,7 @@ folder collection and navigate to another folder. By default, this operation doe
5511
5674
  isDraft: z.boolean().describe(
5512
5675
  "Indicates whether the message is a draft. A message is a draft if it hasn't been sent yet."
5513
5676
  ).nullish(),
5514
- isRead: z.boolean().describe("Indicates whether the message has been read.").nullish(),
5515
- isReadReceiptRequested: z.boolean().describe("Indicates whether a read receipt is requested for the message.").nullish(),
5516
- parentFolderId: z.string().describe("The unique identifier for the message's parent mailFolder.").nullish()
5677
+ isRead: z.boolean().describe("Indicates whether the message has been read.").nullish()
5517
5678
  }).strict().passthrough()
5518
5679
  }
5519
5680
  ],
@@ -19,7 +19,13 @@ async function executeGraphTool(tool, config, graphClient, params) {
19
19
  const headers = {};
20
20
  let body = null;
21
21
  for (const [paramName, paramValue] of Object.entries(params)) {
22
- if (["fetchAllPages", "includeHeaders", "excludeResponse", "timezone"].includes(paramName)) {
22
+ if ([
23
+ "fetchAllPages",
24
+ "includeHeaders",
25
+ "excludeResponse",
26
+ "timezone",
27
+ "expandExtendedProperties"
28
+ ].includes(paramName)) {
23
29
  continue;
24
30
  }
25
31
  const odataParams = [
@@ -86,6 +92,15 @@ async function executeGraphTool(tool, config, graphClient, params) {
86
92
  headers["Prefer"] = `outlook.timezone="${params.timezone}"`;
87
93
  logger.info(`Setting timezone header: Prefer: outlook.timezone="${params.timezone}"`);
88
94
  }
95
+ if (config?.supportsExpandExtendedProperties && params.expandExtendedProperties === true) {
96
+ const expandValue = "singleValueExtendedProperties";
97
+ if (queryParams["$expand"]) {
98
+ queryParams["$expand"] += `,${expandValue}`;
99
+ } else {
100
+ queryParams["$expand"] = expandValue;
101
+ }
102
+ logger.info(`Adding $expand=${expandValue} for extended properties`);
103
+ }
89
104
  if (config?.contentType) {
90
105
  headers["Content-Type"] = config.contentType;
91
106
  logger.info(`Setting custom Content-Type: ${config.contentType}`);
@@ -257,6 +272,11 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
257
272
  'IANA timezone name (e.g., "America/New_York", "Europe/London", "Asia/Tokyo") for calendar event times. If not specified, times are returned in UTC.'
258
273
  ).optional();
259
274
  }
275
+ if (endpointConfig?.supportsExpandExtendedProperties) {
276
+ paramSchema["expandExtendedProperties"] = z.boolean().describe(
277
+ "When true, expands singleValueExtendedProperties on each event. Use this to retrieve custom extended properties (e.g., sync metadata) stored on calendar events."
278
+ ).optional();
279
+ }
260
280
  let toolDescription = tool.description || `Execute ${tool.method.toUpperCase()} request to ${tool.path}`;
261
281
  if (endpointConfig?.llmTip) {
262
282
  toolDescription += `
@@ -1,10 +1,10 @@
1
- 2026-02-09 08:24:59 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
2
- 2026-02-09 08:24:59 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
3
- 2026-02-09 08:24:59 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
4
- 2026-02-09 08:24:59 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/messages
5
- 2026-02-09 08:24:59 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/calendar
6
- 2026-02-09 08:24:59 INFO: Using environment variables for secrets
7
- 2026-02-09 08:24:59 INFO: Using environment variables for secrets
8
- 2026-02-09 08:24:59 INFO: Using environment variables for secrets
9
- 2026-02-09 08:24:59 INFO: Using environment variables for secrets
10
- 2026-02-09 08:24:59 INFO: Using environment variables for secrets
1
+ 2026-02-09 22:59:33 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
2
+ 2026-02-09 22:59:33 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
3
+ 2026-02-09 22:59:33 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
4
+ 2026-02-09 22:59:33 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/messages
5
+ 2026-02-09 22:59:33 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/calendar
6
+ 2026-02-09 22:59:34 INFO: Using environment variables for secrets
7
+ 2026-02-09 22:59:34 INFO: Using environment variables for secrets
8
+ 2026-02-09 22:59:34 INFO: Using environment variables for secrets
9
+ 2026-02-09 22:59:34 INFO: Using environment variables for secrets
10
+ 2026-02-09 22:59:34 INFO: Using environment variables for secrets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.36.1",
3
+ "version": "0.38.0",
4
4
  "description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -153,14 +153,16 @@
153
153
  "method": "get",
154
154
  "toolName": "list-calendar-events",
155
155
  "scopes": ["Calendars.Read"],
156
- "supportsTimezone": true
156
+ "supportsTimezone": true,
157
+ "supportsExpandExtendedProperties": true
157
158
  },
158
159
  {
159
160
  "pathPattern": "/me/events/{event-id}",
160
161
  "method": "get",
161
162
  "toolName": "get-calendar-event",
162
163
  "scopes": ["Calendars.Read"],
163
- "supportsTimezone": true
164
+ "supportsTimezone": true,
165
+ "supportsExpandExtendedProperties": true
164
166
  },
165
167
  {
166
168
  "pathPattern": "/me/events",
@@ -187,14 +189,16 @@
187
189
  "method": "get",
188
190
  "toolName": "list-specific-calendar-events",
189
191
  "scopes": ["Calendars.Read"],
190
- "supportsTimezone": true
192
+ "supportsTimezone": true,
193
+ "supportsExpandExtendedProperties": true
191
194
  },
192
195
  {
193
196
  "pathPattern": "/me/calendars/{calendar-id}/events/{event-id}",
194
197
  "method": "get",
195
198
  "toolName": "get-specific-calendar-event",
196
199
  "scopes": ["Calendars.Read"],
197
- "supportsTimezone": true
200
+ "supportsTimezone": true,
201
+ "supportsExpandExtendedProperties": true
198
202
  },
199
203
  {
200
204
  "pathPattern": "/me/calendars/{calendar-id}/events",
@@ -221,7 +225,27 @@
221
225
  "method": "get",
222
226
  "toolName": "get-calendar-view",
223
227
  "scopes": ["Calendars.Read"],
224
- "supportsTimezone": true
228
+ "supportsTimezone": true,
229
+ "supportsExpandExtendedProperties": true,
230
+ "llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar."
231
+ },
232
+ {
233
+ "pathPattern": "/me/calendars/{calendar-id}/calendarView",
234
+ "method": "get",
235
+ "toolName": "get-specific-calendar-view",
236
+ "scopes": ["Calendars.Read"],
237
+ "supportsTimezone": true,
238
+ "supportsExpandExtendedProperties": true,
239
+ "llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events."
240
+ },
241
+ {
242
+ "pathPattern": "/me/calendars/{calendar-id}/events/{event-id}/instances",
243
+ "method": "get",
244
+ "toolName": "list-calendar-event-instances",
245
+ "scopes": ["Calendars.Read"],
246
+ "supportsTimezone": true,
247
+ "supportsExpandExtendedProperties": true,
248
+ "llmTip": "Expand a recurring event into individual instances within a date range. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use this to see all occurrences of a recurring event."
225
249
  },
226
250
  {
227
251
  "pathPattern": "/me/calendars",