@softeria/ms-365-mcp-server 0.27.0 → 0.28.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 +2 -2
- package/dist/cli.js +2 -2
- package/dist/generated/client.js +1 -1
- package/dist/graph-client.js +4 -2
- package/dist/lib/microsoft-auth.js +13 -8
- package/dist/server.js +42 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -308,13 +308,13 @@ registration:
|
|
|
308
308
|
4. **Get Credentials**:
|
|
309
309
|
|
|
310
310
|
- Copy the **Application (client) ID** from Overview page
|
|
311
|
-
- Go to Certificates & secrets → New client secret → Copy the secret value
|
|
311
|
+
- Go to Certificates & secrets → New client secret → Copy the secret value (optional for public apps)
|
|
312
312
|
|
|
313
313
|
5. **Configure Environment Variables**:
|
|
314
314
|
Create a `.env` file in your project root:
|
|
315
315
|
```env
|
|
316
316
|
MS365_MCP_CLIENT_ID=your-azure-ad-app-client-id-here
|
|
317
|
-
MS365_MCP_CLIENT_SECRET=your-
|
|
317
|
+
MS365_MCP_CLIENT_SECRET=your-secret-here # Optional for public apps
|
|
318
318
|
MS365_MCP_TENANT_ID=common
|
|
319
319
|
```
|
|
320
320
|
|
package/dist/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
|
9
9
|
const version = packageJson.version;
|
|
10
10
|
const program = new Command();
|
|
11
11
|
program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").version(version).option("-v", "Enable verbose logging").option("--login", "Login using device code flow").option("--logout", "Log out and clear saved credentials").option("--verify-login", "Verify login without starting the server").option("--list-accounts", "List all cached accounts").option("--select-account <accountId>", "Select a specific account by ID").option("--remove-account <accountId>", "Remove a specific account by ID").option("--read-only", "Start server in read-only mode, disabling write operations").option(
|
|
12
|
-
"--http [
|
|
13
|
-
|
|
12
|
+
"--http [address]",
|
|
13
|
+
'Use Streamable HTTP transport instead of stdio. Format: [host:]port (e.g., "localhost:3000", ":3000", "3000"). Default: all interfaces on port 3000'
|
|
14
14
|
).option(
|
|
15
15
|
"--enable-auth-tools",
|
|
16
16
|
"Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)"
|
package/dist/generated/client.js
CHANGED
|
@@ -3041,7 +3041,7 @@ const microsoft_graph_searchRequest = z.object({
|
|
|
3041
3041
|
"This triggers hybrid sort for messages : the first 3 messages are the most relevant. This property is only applicable to entityType=message. Optional."
|
|
3042
3042
|
).nullish(),
|
|
3043
3043
|
entityTypes: z.array(z.union([microsoft_graph_entityType, z.object({}).partial().strict()])).describe(
|
|
3044
|
-
"One or more types of resources expected in the response. Possible values are: event, message, driveItem, externalItem, site, list, listItem, drive, chatMessage, person, acronym, bookmark. Use the Prefer: include-unknown-enum-members request header to get the following
|
|
3044
|
+
"One or more types of resources expected in the response. Possible values are: event, message, driveItem, externalItem, site, list, listItem, drive, chatMessage, person, acronym, bookmark. Use the Prefer: include-unknown-enum-members request header to get the following members in this evolvable enum: chatMessage, person, acronym, bookmark. See known limitations for those combinations of two or more entity types that are supported in the same search request. Required."
|
|
3045
3045
|
).optional(),
|
|
3046
3046
|
fields: z.array(z.string().nullable()).describe(
|
|
3047
3047
|
"Contains the fields to be returned for each resource object specified in entityTypes, allowing customization of the fields returned by default; otherwise, including additional fields such as custom managed properties from SharePoint and OneDrive, or custom fields in externalItem from the content that Microsoft 365 Copilot connectors bring in. The fields property can use the semantic labels applied to properties. For example, if a property is labeled as title, you can retrieve it using the following syntax: label_title. Optional."
|
package/dist/graph-client.js
CHANGED
|
@@ -75,8 +75,10 @@ class GraphClient {
|
|
|
75
75
|
const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
|
|
76
76
|
const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
|
|
77
77
|
const clientSecret = process.env.MS365_MCP_CLIENT_SECRET;
|
|
78
|
-
if (
|
|
79
|
-
|
|
78
|
+
if (clientSecret) {
|
|
79
|
+
logger.info("GraphClient: Refreshing token with confidential client");
|
|
80
|
+
} else {
|
|
81
|
+
logger.info("GraphClient: Refreshing token with public client");
|
|
80
82
|
}
|
|
81
83
|
const response = await refreshAccessToken(refreshToken, clientId, clientSecret, tenantId);
|
|
82
84
|
this.accessToken = response.access_token;
|
|
@@ -18,9 +18,11 @@ async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, t
|
|
|
18
18
|
grant_type: "authorization_code",
|
|
19
19
|
code,
|
|
20
20
|
redirect_uri: redirectUri,
|
|
21
|
-
client_id: clientId
|
|
22
|
-
client_secret: clientSecret
|
|
21
|
+
client_id: clientId
|
|
23
22
|
});
|
|
23
|
+
if (clientSecret) {
|
|
24
|
+
params.append("client_secret", clientSecret);
|
|
25
|
+
}
|
|
24
26
|
if (codeVerifier) {
|
|
25
27
|
params.append("code_verifier", codeVerifier);
|
|
26
28
|
}
|
|
@@ -39,17 +41,20 @@ async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, t
|
|
|
39
41
|
return response.json();
|
|
40
42
|
}
|
|
41
43
|
async function refreshAccessToken(refreshToken, clientId, clientSecret, tenantId = "common") {
|
|
44
|
+
const params = new URLSearchParams({
|
|
45
|
+
grant_type: "refresh_token",
|
|
46
|
+
refresh_token: refreshToken,
|
|
47
|
+
client_id: clientId
|
|
48
|
+
});
|
|
49
|
+
if (clientSecret) {
|
|
50
|
+
params.append("client_secret", clientSecret);
|
|
51
|
+
}
|
|
42
52
|
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
|
|
43
53
|
method: "POST",
|
|
44
54
|
headers: {
|
|
45
55
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
46
56
|
},
|
|
47
|
-
body:
|
|
48
|
-
grant_type: "refresh_token",
|
|
49
|
-
refresh_token: refreshToken,
|
|
50
|
-
client_id: clientId,
|
|
51
|
-
client_secret: clientSecret
|
|
52
|
-
})
|
|
57
|
+
body: params
|
|
53
58
|
});
|
|
54
59
|
if (!response.ok) {
|
|
55
60
|
const error = await response.text();
|
package/dist/server.js
CHANGED
|
@@ -16,6 +16,20 @@ import {
|
|
|
16
16
|
refreshAccessToken
|
|
17
17
|
} from "./lib/microsoft-auth.js";
|
|
18
18
|
const registeredClients = /* @__PURE__ */ new Map();
|
|
19
|
+
function parseHttpOption(httpOption) {
|
|
20
|
+
if (typeof httpOption === "boolean") {
|
|
21
|
+
return { host: void 0, port: 3e3 };
|
|
22
|
+
}
|
|
23
|
+
const httpString = httpOption.trim();
|
|
24
|
+
if (httpString.includes(":")) {
|
|
25
|
+
const [hostPart, portPart] = httpString.split(":");
|
|
26
|
+
const host = hostPart || void 0;
|
|
27
|
+
const port2 = parseInt(portPart) || 3e3;
|
|
28
|
+
return { host, port: port2 };
|
|
29
|
+
}
|
|
30
|
+
const port = parseInt(httpString) || 3e3;
|
|
31
|
+
return { host: void 0, port };
|
|
32
|
+
}
|
|
19
33
|
class MicrosoftGraphServer {
|
|
20
34
|
constructor(authManager, options = {}) {
|
|
21
35
|
this.authManager = authManager;
|
|
@@ -66,7 +80,7 @@ class MicrosoftGraphServer {
|
|
|
66
80
|
logger.info("Server running in READ-ONLY mode. Write operations are disabled.");
|
|
67
81
|
}
|
|
68
82
|
if (this.options.http) {
|
|
69
|
-
const
|
|
83
|
+
const { host, port } = parseHttpOption(this.options.http);
|
|
70
84
|
const app = express();
|
|
71
85
|
app.set("trust proxy", true);
|
|
72
86
|
app.use(express.json());
|
|
@@ -200,13 +214,10 @@ class MicrosoftGraphServer {
|
|
|
200
214
|
const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
|
|
201
215
|
const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
|
|
202
216
|
const clientSecret = process.env.MS365_MCP_CLIENT_SECRET;
|
|
203
|
-
if (
|
|
204
|
-
logger.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
error_description: "Server configuration error"
|
|
208
|
-
});
|
|
209
|
-
return;
|
|
217
|
+
if (clientSecret) {
|
|
218
|
+
logger.info("Token endpoint: Using confidential client with client_secret");
|
|
219
|
+
} else {
|
|
220
|
+
logger.info("Token endpoint: Using public client without client_secret");
|
|
210
221
|
}
|
|
211
222
|
const result = await exchangeCodeForToken(
|
|
212
223
|
body.code,
|
|
@@ -221,13 +232,10 @@ class MicrosoftGraphServer {
|
|
|
221
232
|
const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
|
|
222
233
|
const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
|
|
223
234
|
const clientSecret = process.env.MS365_MCP_CLIENT_SECRET;
|
|
224
|
-
if (
|
|
225
|
-
logger.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
error_description: "Server configuration error"
|
|
229
|
-
});
|
|
230
|
-
return;
|
|
235
|
+
if (clientSecret) {
|
|
236
|
+
logger.info("Refresh endpoint: Using confidential client with client_secret");
|
|
237
|
+
} else {
|
|
238
|
+
logger.info("Refresh endpoint: Using public client without client_secret");
|
|
231
239
|
}
|
|
232
240
|
const result = await refreshAccessToken(
|
|
233
241
|
body.refresh_token,
|
|
@@ -329,14 +337,25 @@ class MicrosoftGraphServer {
|
|
|
329
337
|
app.get("/", (req, res) => {
|
|
330
338
|
res.send("Microsoft 365 MCP Server is running");
|
|
331
339
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
+
if (host) {
|
|
341
|
+
app.listen(port, host, () => {
|
|
342
|
+
logger.info(`Server listening on ${host}:${port}`);
|
|
343
|
+
logger.info(` - MCP endpoint: http://${host}:${port}/mcp`);
|
|
344
|
+
logger.info(` - OAuth endpoints: http://${host}:${port}/auth/*`);
|
|
345
|
+
logger.info(
|
|
346
|
+
` - OAuth discovery: http://${host}:${port}/.well-known/oauth-authorization-server`
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
} else {
|
|
350
|
+
app.listen(port, () => {
|
|
351
|
+
logger.info(`Server listening on all interfaces (0.0.0.0:${port})`);
|
|
352
|
+
logger.info(` - MCP endpoint: http://localhost:${port}/mcp`);
|
|
353
|
+
logger.info(` - OAuth endpoints: http://localhost:${port}/auth/*`);
|
|
354
|
+
logger.info(
|
|
355
|
+
` - OAuth discovery: http://localhost:${port}/.well-known/oauth-authorization-server`
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
340
359
|
} else {
|
|
341
360
|
const transport = new StdioServerTransport();
|
|
342
361
|
await this.server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.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",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"test": "vitest run",
|
|
14
14
|
"test:watch": "vitest",
|
|
15
15
|
"dev": "tsx src/index.ts",
|
|
16
|
-
"dev:http": "tsx --watch src/index.ts --http 3000 -v",
|
|
16
|
+
"dev:http": "tsx --watch src/index.ts --http 127.0.0.1:3000 -v",
|
|
17
17
|
"format": "prettier --write \"**/*.{ts,mts,js,mjs,json,md}\"",
|
|
18
18
|
"format:check": "prettier --check \"**/*.{ts,mts,js,mjs,json,md}\"",
|
|
19
19
|
"lint": "eslint .",
|