@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.
- package/build/index.js +245 -85
- 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.
|
|
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
|
|
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
|
|
31908
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
32076
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
32470
|
-
function
|
|
32471
|
-
return
|
|
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
|
|
32477
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
32626
|
-
const end =
|
|
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 =
|
|
32659
|
-
const end =
|
|
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/
|
|
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 =
|
|
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
|
-
|
|
32819
|
-
|
|
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}
|
|
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 =
|
|
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 ?
|
|
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:
|
|
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