@leonardocrdso/office365-mcp-server 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/build/index.js +245 -85
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -23555,6 +23555,64 @@ class StdioServerTransport {
23555
23555
  });
23556
23556
  }
23557
23557
  }
23558
+ // package.json
23559
+ var package_default = {
23560
+ name: "@leonardocrdso/office365-mcp-server",
23561
+ version: "1.1.0",
23562
+ description: "MCP server for Microsoft 365 — Outlook, Calendar, OneDrive, SharePoint & Teams via Graph API",
23563
+ type: "module",
23564
+ main: "build/index.js",
23565
+ bin: {
23566
+ "office365-mcp-server": "build/index.js"
23567
+ },
23568
+ files: [
23569
+ "build/",
23570
+ "README.md",
23571
+ "LICENSE"
23572
+ ],
23573
+ scripts: {
23574
+ dev: "bun run --watch src/index.ts",
23575
+ start: "bun run src/index.ts",
23576
+ build: "bun build src/index.ts --outdir build --target node",
23577
+ prepublishOnly: "bun run build"
23578
+ },
23579
+ keywords: [
23580
+ "office365",
23581
+ "microsoft-365",
23582
+ "outlook",
23583
+ "teams",
23584
+ "onedrive",
23585
+ "sharepoint",
23586
+ "calendar",
23587
+ "mcp",
23588
+ "model-context-protocol",
23589
+ "microsoft-graph",
23590
+ "claude",
23591
+ "ai"
23592
+ ],
23593
+ author: "leonardocrdso",
23594
+ license: "MIT",
23595
+ repository: {
23596
+ type: "git",
23597
+ url: "git+https://github.com/leonardocrdso/office365-mcp-server.git"
23598
+ },
23599
+ homepage: "https://github.com/leonardocrdso/office365-mcp-server#readme",
23600
+ bugs: {
23601
+ url: "https://github.com/leonardocrdso/office365-mcp-server/issues"
23602
+ },
23603
+ publishConfig: {
23604
+ access: "public"
23605
+ },
23606
+ dependencies: {
23607
+ "@azure/msal-node": "^3.8.8",
23608
+ "@modelcontextprotocol/sdk": "^1.26.0",
23609
+ zod: "^3.25.0"
23610
+ },
23611
+ devDependencies: {
23612
+ "@types/node": "^22.0.0",
23613
+ typescript: "^5.0.0"
23614
+ }
23615
+ };
23558
23616
 
23559
23617
  // node_modules/@azure/msal-node/dist/cache/serializer/Serializer.mjs
23560
23618
  /*! @azure/msal-node v3.8.8 2026-02-23 */
@@ -31708,16 +31766,12 @@ var SOURCES_THAT_SUPPORT_TOKEN_REVOCATION = [ManagedIdentitySourceNames.SERVICE_
31708
31766
  // node_modules/@azure/msal-node/dist/index.mjs
31709
31767
  /*! @azure/msal-node v3.8.8 2026-02-23 */
31710
31768
 
31711
- // src/auth/msal-client.ts
31712
- import { readFile, writeFile, unlink } from "node:fs/promises";
31713
- import { homedir } from "node:os";
31714
- import { join } from "node:path";
31715
-
31716
31769
  // src/constants.ts
31717
31770
  var DEFAULT_PAGE_SIZE_SMALL = 10;
31718
31771
  var DEFAULT_PAGE_SIZE_LARGE = 20;
31719
31772
  var DEFAULT_MEETING_DURATION_MINUTES = 30;
31720
31773
  var DEFAULT_TIMEZONE = "America/Sao_Paulo";
31774
+ var PREFER_TIMEZONE_HEADER = `outlook.timezone="${DEFAULT_TIMEZONE}"`;
31721
31775
  var BODY_PREVIEW_MAX_LENGTH = 100;
31722
31776
  var MESSAGE_CONTENT_MAX_LENGTH = 200;
31723
31777
  var SCOPES = {
@@ -31833,7 +31887,7 @@ var GRAPH_CODE_MAP = {
31833
31887
  ErrorSendAsDenied: ErrorCode2.PERMISSION_DENIED
31834
31888
  };
31835
31889
  var ERROR_MESSAGES = {
31836
- [ErrorCode2.AUTH_NOT_CONFIGURED]: "AZURE_CLIENT_ID não configurado. Defina a variável de ambiente AZURE_CLIENT_ID com o ID do seu app registration no Azure AD.",
31890
+ [ErrorCode2.AUTH_NOT_CONFIGURED]: "AZURE_CLIENT_ID não configurado. Use a tool 'configure' para informar suas credenciais Azure, ou defina a variável de ambiente AZURE_CLIENT_ID.",
31837
31891
  [ErrorCode2.AUTH_NOT_AUTHENTICATED]: "Usuário não autenticado. Use a tool 'login' para iniciar a autenticação.",
31838
31892
  [ErrorCode2.AUTH_TOKEN_EXPIRED]: "Token expirado ou inválido. Use a tool 'login' para re-autenticar.",
31839
31893
  [ErrorCode2.AUTH_TOKEN_REFRESH_FAILED]: "Falha ao renovar token silenciosamente. Use a tool 'login' para re-autenticar.",
@@ -31877,22 +31931,65 @@ Detalhe: ${error2.message}`;
31877
31931
  return `[${ErrorCode2.UNKNOWN_ERROR}] ${String(error2)}`;
31878
31932
  }
31879
31933
 
31934
+ // src/auth/config-manager.ts
31935
+ import { readFile, writeFile } from "node:fs/promises";
31936
+ import { homedir } from "node:os";
31937
+ import { join } from "node:path";
31938
+ var CONFIG_PATH = join(homedir(), ".office365-mcp-config.json");
31939
+ async function loadConfig() {
31940
+ try {
31941
+ const data = await readFile(CONFIG_PATH, "utf-8");
31942
+ return JSON.parse(data);
31943
+ } catch {
31944
+ return {};
31945
+ }
31946
+ }
31947
+ async function saveConfig(clientId, tenantId) {
31948
+ await writeFile(CONFIG_PATH, JSON.stringify({ clientId, tenantId }, null, 2), { mode: 384 });
31949
+ }
31950
+
31951
+ // src/auth/token-cache-manager.ts
31952
+ import { readFile as readFile2, writeFile as writeFile2, unlink } from "node:fs/promises";
31953
+ import { homedir as homedir2 } from "node:os";
31954
+ import { join as join2 } from "node:path";
31955
+ var TOKEN_CACHE_PATH = join2(homedir2(), ".office365-mcp-tokens.json");
31956
+ async function loadTokenCache() {
31957
+ try {
31958
+ return await readFile2(TOKEN_CACHE_PATH, "utf-8");
31959
+ } catch {
31960
+ return null;
31961
+ }
31962
+ }
31963
+ async function saveTokenCache(data) {
31964
+ await writeFile2(TOKEN_CACHE_PATH, data, { mode: 384 });
31965
+ }
31966
+ async function removeTokenCache() {
31967
+ try {
31968
+ await unlink(TOKEN_CACHE_PATH);
31969
+ } catch {}
31970
+ }
31971
+
31880
31972
  // src/auth/msal-client.ts
31881
- var TOKEN_CACHE_PATH = join(homedir(), ".office365-mcp-tokens.json");
31882
31973
  var ALL_SCOPES = ["User.Read", ...Object.values(SCOPES).flat()];
31883
31974
 
31884
31975
  class MsalClient {
31885
31976
  pca = null;
31977
+ fileConfig = null;
31886
31978
  deviceCodeCallback = null;
31979
+ pendingLogin = null;
31887
31980
  setDeviceCodeCallback(callback) {
31888
31981
  this.deviceCodeCallback = callback;
31889
31982
  }
31983
+ resetPca() {
31984
+ this.pca = null;
31985
+ this.fileConfig = null;
31986
+ }
31890
31987
  getConfig() {
31891
- const clientId = process.env.AZURE_CLIENT_ID;
31988
+ const clientId = process.env.AZURE_CLIENT_ID ?? this.fileConfig?.clientId;
31892
31989
  if (!clientId) {
31893
31990
  throw new AuthError2("AZURE_CLIENT_ID não configurado.", ErrorCode2.AUTH_NOT_CONFIGURED);
31894
31991
  }
31895
- const tenantId = process.env.AZURE_TENANT_ID || "common";
31992
+ const tenantId = process.env.AZURE_TENANT_ID ?? this.fileConfig?.tenantId ?? "common";
31896
31993
  return {
31897
31994
  auth: {
31898
31995
  clientId,
@@ -31902,10 +31999,15 @@ class MsalClient {
31902
31999
  }
31903
32000
  async getPca() {
31904
32001
  if (!this.pca) {
32002
+ if (!this.fileConfig) {
32003
+ this.fileConfig = await loadConfig();
32004
+ }
31905
32005
  this.pca = new PublicClientApplication(this.getConfig());
31906
32006
  try {
31907
- const cacheData = await readFile(TOKEN_CACHE_PATH, "utf-8");
31908
- this.pca.getTokenCache().deserialize(cacheData);
32007
+ const cacheData = await loadTokenCache();
32008
+ if (cacheData) {
32009
+ this.pca.getTokenCache().deserialize(cacheData);
32010
+ }
31909
32011
  } catch (error2) {
31910
32012
  console.error("Failed to load token cache:", error2);
31911
32013
  }
@@ -31916,7 +32018,7 @@ class MsalClient {
31916
32018
  if (!this.pca)
31917
32019
  return;
31918
32020
  const cacheData = this.pca.getTokenCache().serialize();
31919
- await writeFile(TOKEN_CACHE_PATH, cacheData, { mode: 384 });
32021
+ await saveTokenCache(cacheData);
31920
32022
  }
31921
32023
  async login() {
31922
32024
  const pca = await this.getPca();
@@ -31934,12 +32036,18 @@ class MsalClient {
31934
32036
  });
31935
32037
  }
31936
32038
  };
31937
- pca.acquireTokenByDeviceCode(request).then(async (result) => {
32039
+ this.pendingLogin = pca.acquireTokenByDeviceCode(request).then(async (result) => {
31938
32040
  if (result) {
32041
+ console.error("[msal] Token acquired, saving cache...");
31939
32042
  await this.saveCache();
32043
+ console.error("[msal] Cache saved successfully");
32044
+ } else {
32045
+ console.error("[msal] acquireTokenByDeviceCode returned null");
31940
32046
  }
31941
32047
  }).catch((error2) => {
31942
- console.error("Device code flow error:", error2.message);
32048
+ console.error("[msal] Device code flow error:", error2.message);
32049
+ }).finally(() => {
32050
+ this.pendingLogin = null;
31943
32051
  });
31944
32052
  });
31945
32053
  }
@@ -31983,16 +32091,29 @@ class MsalClient {
31983
32091
  await this.pca.getTokenCache().removeAccount(account);
31984
32092
  }
31985
32093
  }
31986
- try {
31987
- await unlink(TOKEN_CACHE_PATH);
31988
- } catch (error2) {
31989
- console.error("Failed to remove token cache file:", error2);
31990
- }
32094
+ await removeTokenCache();
31991
32095
  this.pca = null;
31992
32096
  }
31993
32097
  }
31994
32098
  var msalClient = new MsalClient;
31995
32099
 
32100
+ // src/types/graph.ts
32101
+ function recipientAddress(recipient) {
32102
+ return recipient?.emailAddress?.address ?? "unknown";
32103
+ }
32104
+ function recipientAddresses(recipients) {
32105
+ return recipients?.map((r) => r.emailAddress?.address).join(", ") ?? "";
32106
+ }
32107
+ function messageSenderName(message) {
32108
+ return message.from?.user?.displayName ?? "Desconhecido";
32109
+ }
32110
+ function toRecipient(email2) {
32111
+ return { emailAddress: { address: email2 } };
32112
+ }
32113
+ function toAttendee(email2, type = "required") {
32114
+ return { emailAddress: { address: email2 }, type };
32115
+ }
32116
+
31996
32117
  // src/utils/graph-client.ts
31997
32118
  async function executeRequest(accessToken, url, options = {}) {
31998
32119
  const baseUrl = url.startsWith("https://") ? url : `https://graph.microsoft.com/v1.0${url}`;
@@ -32035,11 +32156,14 @@ async function graphFetchVoid(accessToken, url, options = {}) {
32035
32156
  await executeRequest(accessToken, url, options);
32036
32157
  }
32037
32158
 
32159
+ // src/utils/auth-helper.ts
32160
+ function createGetToken(auth, scopes) {
32161
+ return () => auth.getAccessToken([...scopes]);
32162
+ }
32163
+
32038
32164
  // src/services/mail.ts
32039
32165
  function createMailService(auth) {
32040
- async function getToken() {
32041
- return auth.getAccessToken([...SCOPES.MAIL]);
32042
- }
32166
+ const getToken = createGetToken(auth, SCOPES.MAIL);
32043
32167
  async function listEmails(params = {}) {
32044
32168
  const token = await getToken();
32045
32169
  const { folder, top = DEFAULT_PAGE_SIZE_SMALL, skip = 0, filter } = params;
@@ -32072,15 +32196,9 @@ function createMailService(auth) {
32072
32196
  async function sendEmail(params) {
32073
32197
  const token = await getToken();
32074
32198
  const { to, subject, body, cc, bcc, contentType = "Text" } = params;
32075
- const toRecipients = to.map((email2) => ({
32076
- emailAddress: { address: email2 }
32077
- }));
32078
- const ccRecipients = cc?.map((email2) => ({
32079
- emailAddress: { address: email2 }
32080
- }));
32081
- const bccRecipients = bcc?.map((email2) => ({
32082
- emailAddress: { address: email2 }
32083
- }));
32199
+ const toRecipients = to.map(toRecipient);
32200
+ const ccRecipients = cc?.map(toRecipient);
32201
+ const bccRecipients = bcc?.map(toRecipient);
32084
32202
  await graphFetchVoid(token, "/me/sendMail", {
32085
32203
  method: "POST",
32086
32204
  body: JSON.stringify({
@@ -32116,9 +32234,7 @@ function createMailService(auth) {
32116
32234
 
32117
32235
  // src/services/calendar.ts
32118
32236
  function createCalendarService(auth) {
32119
- async function getToken() {
32120
- return auth.getAccessToken([...SCOPES.CALENDAR]);
32121
- }
32237
+ const getToken = createGetToken(auth, SCOPES.CALENDAR);
32122
32238
  async function listEvents(params) {
32123
32239
  const token = await getToken();
32124
32240
  const { startDateTime, endDateTime, top = DEFAULT_PAGE_SIZE_LARGE } = params;
@@ -32129,7 +32245,11 @@ function createCalendarService(auth) {
32129
32245
  $select: "id,subject,start,end,location,organizer,attendees,isOnlineMeeting,onlineMeetingUrl,bodyPreview",
32130
32246
  $orderby: "start/dateTime"
32131
32247
  });
32132
- const result = await graphFetch(token, `/me/calendarView?${queryParams}`);
32248
+ const result = await graphFetch(token, `/me/calendarView?${queryParams}`, {
32249
+ headers: {
32250
+ Prefer: PREFER_TIMEZONE_HEADER
32251
+ }
32252
+ });
32133
32253
  return result.value;
32134
32254
  }
32135
32255
  async function createEvent(params) {
@@ -32157,10 +32277,7 @@ function createCalendarService(auth) {
32157
32277
  eventBody.location = { displayName: location };
32158
32278
  }
32159
32279
  if (attendees?.length) {
32160
- eventBody.attendees = attendees.map((email2) => ({
32161
- emailAddress: { address: email2 },
32162
- type: "required"
32163
- }));
32280
+ eventBody.attendees = attendees.map((email2) => toAttendee(email2));
32164
32281
  }
32165
32282
  return graphFetch(token, "/me/events", {
32166
32283
  method: "POST",
@@ -32201,11 +32318,11 @@ function createCalendarService(auth) {
32201
32318
  } = params;
32202
32319
  return graphFetch(token, "/me/findMeetingTimes", {
32203
32320
  method: "POST",
32321
+ headers: {
32322
+ Prefer: PREFER_TIMEZONE_HEADER
32323
+ },
32204
32324
  body: JSON.stringify({
32205
- attendees: attendees.map((email2) => ({
32206
- emailAddress: { address: email2 },
32207
- type: "required"
32208
- })),
32325
+ attendees: attendees.map((email2) => toAttendee(email2)),
32209
32326
  timeConstraint: {
32210
32327
  timeslots: [
32211
32328
  {
@@ -32223,9 +32340,7 @@ function createCalendarService(auth) {
32223
32340
 
32224
32341
  // src/services/onedrive.ts
32225
32342
  function createOneDriveService(auth) {
32226
- async function getToken() {
32227
- return auth.getAccessToken([...SCOPES.DRIVE]);
32228
- }
32343
+ const getToken = createGetToken(auth, SCOPES.DRIVE);
32229
32344
  async function listFiles(params = {}) {
32230
32345
  const token = await getToken();
32231
32346
  const { itemId, path, top = DEFAULT_PAGE_SIZE_LARGE } = params;
@@ -32248,6 +32363,19 @@ function createOneDriveService(auth) {
32248
32363
  const token = await getToken();
32249
32364
  return graphFetch(token, `/me/drive/items/${itemId}/content`);
32250
32365
  }
32366
+ async function readSharedFileContent(params) {
32367
+ const token = await getToken();
32368
+ const { driveId, itemId, path } = params;
32369
+ let endpoint;
32370
+ if (itemId) {
32371
+ endpoint = `/drives/${driveId}/items/${itemId}/content`;
32372
+ } else if (path) {
32373
+ endpoint = `/drives/${driveId}/root:/${path.replace(/^\//, "")}:/content`;
32374
+ } else {
32375
+ throw new Error("É necessário informar itemId ou path do arquivo.");
32376
+ }
32377
+ return graphFetch(token, endpoint);
32378
+ }
32251
32379
  async function uploadFile(params) {
32252
32380
  const token = await getToken();
32253
32381
  const { path, content } = params;
@@ -32275,14 +32403,12 @@ function createOneDriveService(auth) {
32275
32403
  body: JSON.stringify({ type, scope })
32276
32404
  });
32277
32405
  }
32278
- return { listFiles, readFileContent, uploadFile, searchFiles, shareFile };
32406
+ return { listFiles, readFileContent, readSharedFileContent, uploadFile, searchFiles, shareFile };
32279
32407
  }
32280
32408
 
32281
32409
  // src/services/sharepoint.ts
32282
32410
  function createSharePointService(auth) {
32283
- async function getToken() {
32284
- return auth.getAccessToken([...SCOPES.SHAREPOINT]);
32285
- }
32411
+ const getToken = createGetToken(auth, SCOPES.SHAREPOINT);
32286
32412
  async function listSites(query) {
32287
32413
  const token = await getToken();
32288
32414
  const searchQuery = query ?? "*";
@@ -32331,9 +32457,7 @@ function createSharePointService(auth) {
32331
32457
 
32332
32458
  // src/services/teams.ts
32333
32459
  function createTeamsService(auth) {
32334
- async function getToken() {
32335
- return auth.getAccessToken([...SCOPES.TEAMS]);
32336
- }
32460
+ const getToken = createGetToken(auth, SCOPES.TEAMS);
32337
32461
  async function listTeams() {
32338
32462
  const token = await getToken();
32339
32463
  const result = await graphFetch(token, "/me/joinedTeams?$select=id,displayName,description");
@@ -32401,9 +32525,33 @@ function safeTool(handler) {
32401
32525
  }
32402
32526
 
32403
32527
  // src/tools/auth-tools.ts
32404
- function registerAuthTools(server) {
32528
+ function registerAuthTools(server, auth) {
32529
+ server.tool("configure", "Configura as credenciais do Azure (Client ID e Tenant ID) para autenticação com Microsoft 365. Salva em arquivo persistente (~/.office365-mcp-config.json).", {
32530
+ clientId: exports_external.string().describe("Azure App Registration Client ID"),
32531
+ tenantId: exports_external.string().optional().default("common").describe("Azure Tenant ID (padrão: 'common')")
32532
+ }, safeTool(async (params) => {
32533
+ await saveConfig(params.clientId, params.tenantId);
32534
+ auth.resetPca();
32535
+ return {
32536
+ content: [
32537
+ {
32538
+ type: "text",
32539
+ text: [
32540
+ "## Configuração salva com sucesso",
32541
+ "",
32542
+ `**Client ID:** \`${params.clientId}\``,
32543
+ `**Tenant ID:** \`${params.tenantId}\``,
32544
+ "",
32545
+ "As credenciais foram salvas em `~/.office365-mcp-config.json`.",
32546
+ "Agora use a tool **'login'** para autenticar com sua conta Microsoft."
32547
+ ].join(`
32548
+ `)
32549
+ }
32550
+ ]
32551
+ };
32552
+ }));
32405
32553
  server.tool("login", "Inicia autenticação com Microsoft 365 via Device Code Flow. Retorna um código e URL para o usuário autenticar no navegador.", {}, safeTool(async () => {
32406
- const result = await msalClient.login();
32554
+ const result = await auth.login();
32407
32555
  return {
32408
32556
  content: [
32409
32557
  {
@@ -32425,7 +32573,7 @@ function registerAuthTools(server) {
32425
32573
  };
32426
32574
  }));
32427
32575
  server.tool("auth-status", "Verifica o status da autenticação e mostra informações do usuário logado.", {}, safeTool(async () => {
32428
- const info = await msalClient.getAccountInfo();
32576
+ const info = await auth.getAccountInfo();
32429
32577
  if (!info.isLoggedIn) {
32430
32578
  return {
32431
32579
  content: [
@@ -32454,7 +32602,7 @@ function registerAuthTools(server) {
32454
32602
  };
32455
32603
  }));
32456
32604
  server.tool("logout", "Faz logout e remove tokens salvos.", {}, safeTool(async () => {
32457
- await msalClient.logout();
32605
+ await auth.logout();
32458
32606
  return {
32459
32607
  content: [
32460
32608
  {
@@ -32466,15 +32614,17 @@ function registerAuthTools(server) {
32466
32614
  }));
32467
32615
  }
32468
32616
 
32469
- // src/types/graph.ts
32470
- function recipientAddress(recipient) {
32471
- return recipient?.emailAddress?.address ?? "unknown";
32472
- }
32473
- function recipientAddresses(recipients) {
32474
- return recipients?.map((r) => r.emailAddress?.address).join(", ") ?? "";
32617
+ // src/utils/date.ts
32618
+ function formatDateBR(isoDate) {
32619
+ return new Date(isoDate).toLocaleString("pt-BR", { timeZone: DEFAULT_TIMEZONE });
32475
32620
  }
32476
- function messageSenderName(message) {
32477
- return message.from?.user?.displayName ?? "Desconhecido";
32621
+ function formatCalendarDate(dateTime) {
32622
+ const match = dateTime.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/);
32623
+ if (match) {
32624
+ const [, year, month, day, hour, minute] = match;
32625
+ return `${day}/${month}/${year}, ${hour}:${minute}`;
32626
+ }
32627
+ return new Date(dateTime).toLocaleString("pt-BR", { timeZone: DEFAULT_TIMEZONE });
32478
32628
  }
32479
32629
 
32480
32630
  // src/formatters/mail.ts
@@ -32483,7 +32633,7 @@ function formatEmailList(emails) {
32483
32633
  return "Nenhum email encontrado.";
32484
32634
  const formatted = emails.map((e) => {
32485
32635
  const from = recipientAddress(e.from);
32486
- const date4 = new Date(e.receivedDateTime).toLocaleString("pt-BR");
32636
+ const date4 = formatDateBR(e.receivedDateTime);
32487
32637
  const read = e.isRead ? "" : " [NÃO LIDO]";
32488
32638
  const attach = e.hasAttachments ? " [ANEXO]" : "";
32489
32639
  return `- **${e.subject}**${read}${attach}
@@ -32502,7 +32652,7 @@ function formatEmailSearchResults(query, emails) {
32502
32652
  return `Nenhum email encontrado para "${query}".`;
32503
32653
  const formatted = emails.map((e) => {
32504
32654
  const from = recipientAddress(e.from);
32505
- const date4 = new Date(e.receivedDateTime).toLocaleString("pt-BR");
32655
+ const date4 = formatDateBR(e.receivedDateTime);
32506
32656
  return `- **${e.subject}**
32507
32657
  De: ${from} | ${date4}
32508
32658
  ID: ${e.id}`;
@@ -32517,7 +32667,7 @@ function formatEmailDetail(email2) {
32517
32667
  const from = recipientAddress(email2.from);
32518
32668
  const to = recipientAddresses(email2.toRecipients);
32519
32669
  const cc = recipientAddresses(email2.ccRecipients);
32520
- const date4 = new Date(email2.receivedDateTime).toLocaleString("pt-BR");
32670
+ const date4 = formatDateBR(email2.receivedDateTime);
32521
32671
  const bodyContent = email2.body?.content ?? "(sem conteúdo)";
32522
32672
  const text = [
32523
32673
  `## ${email2.subject}`,
@@ -32622,8 +32772,8 @@ function formatEventList(events) {
32622
32772
  if (events.length === 0)
32623
32773
  return "Nenhum evento encontrado no período.";
32624
32774
  const formatted = events.map((e) => {
32625
- const start = new Date(e.start.dateTime).toLocaleString("pt-BR");
32626
- const end = new Date(e.end.dateTime).toLocaleString("pt-BR");
32775
+ const start = formatCalendarDate(e.start.dateTime);
32776
+ const end = formatCalendarDate(e.end.dateTime);
32627
32777
  const location = e.location?.displayName ? ` @ ${e.location.displayName}` : "";
32628
32778
  const online = e.isOnlineMeeting ? " [Online]" : "";
32629
32779
  const attendees = e.attendees?.length ? `
@@ -32655,8 +32805,8 @@ function formatFreeSlotsResult(result) {
32655
32805
  return "Nenhum horário disponível encontrado para todos os participantes no período.";
32656
32806
  }
32657
32807
  const formatted = suggestions.map((s, i) => {
32658
- const start = new Date(s.meetingTimeSlot.start.dateTime).toLocaleString("pt-BR");
32659
- const end = new Date(s.meetingTimeSlot.end.dateTime).toLocaleString("pt-BR");
32808
+ const start = formatCalendarDate(s.meetingTimeSlot.start.dateTime);
32809
+ const end = formatCalendarDate(s.meetingTimeSlot.end.dateTime);
32660
32810
  return `${i + 1}. ${start} → ${end} (confiança: ${s.confidence}%)`;
32661
32811
  });
32662
32812
  return `## Horários Sugeridos
@@ -32734,7 +32884,7 @@ function registerCalendarTools(server, calendar) {
32734
32884
  }));
32735
32885
  }
32736
32886
 
32737
- // src/formatters/onedrive.ts
32887
+ // src/utils/format.ts
32738
32888
  function formatSize(bytes) {
32739
32889
  if (bytes < 1024)
32740
32890
  return `${bytes} B`;
@@ -32744,6 +32894,8 @@ function formatSize(bytes) {
32744
32894
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
32745
32895
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
32746
32896
  }
32897
+
32898
+ // src/formatters/onedrive.ts
32747
32899
  function formatDriveItems(items) {
32748
32900
  if (items.length === 0)
32749
32901
  return "Pasta vazia.";
@@ -32751,7 +32903,7 @@ function formatDriveItems(items) {
32751
32903
  const isFolder = !!item.folder;
32752
32904
  const icon = isFolder ? "[pasta]" : "[arquivo]";
32753
32905
  const size = item.size ? ` (${formatSize(item.size)})` : "";
32754
- const modified = new Date(item.lastModifiedDateTime).toLocaleString("pt-BR");
32906
+ const modified = formatDateBR(item.lastModifiedDateTime);
32755
32907
  const childCount = item.folder?.childCount != null ? ` — ${item.folder.childCount} itens` : "";
32756
32908
  return `- ${icon} **${item.name}**${size}${childCount}
32757
32909
  Modificado: ${modified}
@@ -32797,6 +32949,9 @@ function formatShareLink(result) {
32797
32949
  }
32798
32950
 
32799
32951
  // src/tools/onedrive-tools.ts
32952
+ function formatFileContent(content) {
32953
+ return typeof content === "string" ? content : JSON.stringify(content, null, 2);
32954
+ }
32800
32955
  function registerOneDriveTools(server, onedrive) {
32801
32956
  server.tool("list-drive-files", "Lista arquivos e pastas do OneDrive. Por padrão lista a raiz.", {
32802
32957
  itemId: exports_external.string().optional().describe("ID do item/pasta para listar conteúdo"),
@@ -32813,12 +32968,17 @@ function registerOneDriveTools(server, onedrive) {
32813
32968
  }, safeTool(async (params) => {
32814
32969
  const content = await onedrive.readFileContent(params.itemId);
32815
32970
  return {
32816
- content: [
32817
- {
32818
- type: "text",
32819
- text: typeof content === "string" ? content : JSON.stringify(content, null, 2)
32820
- }
32821
- ]
32971
+ content: [{ type: "text", text: formatFileContent(content) }]
32972
+ };
32973
+ }));
32974
+ server.tool("read-shared-file-content", "Lê o conteúdo de um arquivo de um drive compartilhado (SharePoint ou OneDrive de outro usuário). Use o driveId e itemId retornados por search-sharepoint ou list-library-items.", {
32975
+ driveId: exports_external.string().describe("ID do drive compartilhado"),
32976
+ itemId: exports_external.string().optional().describe("ID do arquivo no drive compartilhado"),
32977
+ path: exports_external.string().optional().describe("Caminho do arquivo no drive (alternativa ao itemId, ex: 'Documents/pasta/arquivo.docx')")
32978
+ }, safeTool(async (params) => {
32979
+ const content = await onedrive.readSharedFileContent(params);
32980
+ return {
32981
+ content: [{ type: "text", text: formatFileContent(content) }]
32822
32982
  };
32823
32983
  }));
32824
32984
  server.tool("upload-file", "Faz upload de um arquivo para o OneDrive (até 4MB de conteúdo texto).", {
@@ -32895,7 +33055,7 @@ function formatLibraryItems(items) {
32895
33055
  const formatted = items.map((item) => {
32896
33056
  const isFolder = !!item.folder;
32897
33057
  const icon = isFolder ? "[pasta]" : "[arquivo]";
32898
- const size = item.size ? ` (${item.size} bytes)` : "";
33058
+ const size = item.size ? ` (${formatSize(item.size)})` : "";
32899
33059
  return `- ${icon} **${item.name}**${size}
32900
33060
  URL: ${item.webUrl}
32901
33061
  ID: ${item.id}`;
@@ -33000,7 +33160,7 @@ function formatChannelMessages(messages) {
33000
33160
  return "Nenhuma mensagem encontrada.";
33001
33161
  const formatted = messages.map((m) => {
33002
33162
  const from = messageSenderName(m);
33003
- const date4 = new Date(m.createdDateTime).toLocaleString("pt-BR");
33163
+ const date4 = formatDateBR(m.createdDateTime);
33004
33164
  const content = m.body?.content?.substring(0, MESSAGE_CONTENT_MAX_LENGTH) ?? "";
33005
33165
  return `- **${from}** (${date4})
33006
33166
  ${content}`;
@@ -33017,7 +33177,7 @@ function formatChatList(chats) {
33017
33177
  const formatted = chats.map((c) => {
33018
33178
  const topic = c.topic ?? "Chat sem título";
33019
33179
  const type = c.chatType ?? "unknown";
33020
- const updated = c.lastUpdatedDateTime ? new Date(c.lastUpdatedDateTime).toLocaleString("pt-BR") : "N/A";
33180
+ const updated = c.lastUpdatedDateTime ? formatDateBR(c.lastUpdatedDateTime) : "N/A";
33021
33181
  const members = c.members?.map((m) => m.displayName).join(", ") ?? "";
33022
33182
  return `- **${topic}** (${type})
33023
33183
  Membros: ${members}
@@ -33095,14 +33255,14 @@ function registerTeamsTools(server, teams) {
33095
33255
  // src/index.ts
33096
33256
  var server = new McpServer({
33097
33257
  name: "office365-mcp-server",
33098
- version: "1.0.0"
33258
+ version: package_default.version
33099
33259
  });
33100
33260
  var mailService = createMailService(msalClient);
33101
33261
  var calendarService = createCalendarService(msalClient);
33102
33262
  var oneDriveService = createOneDriveService(msalClient);
33103
33263
  var sharePointService = createSharePointService(msalClient);
33104
33264
  var teamsService = createTeamsService(msalClient);
33105
- registerAuthTools(server);
33265
+ registerAuthTools(server, msalClient);
33106
33266
  registerMailTools(server, mailService);
33107
33267
  registerCalendarTools(server, calendarService);
33108
33268
  registerOneDriveTools(server, oneDriveService);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leonardocrdso/office365-mcp-server",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for Microsoft 365 — Outlook, Calendar, OneDrive, SharePoint & Teams via Graph API",
5
5
  "type": "module",
6
6
  "main": "build/index.js",