@softeria/ms-365-mcp-server 0.92.0 → 0.93.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/dist/cli.js +6 -0
- package/dist/obo-client.js +42 -0
- package/dist/server.js +25 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -35,6 +35,9 @@ program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").versio
|
|
|
35
35
|
).option(
|
|
36
36
|
"--public-url <url>",
|
|
37
37
|
"Public base URL (e.g. https://mcp.example.com) used in browser-facing OAuth redirects when running behind a reverse proxy. Server-to-server endpoints (token, register) stay on the request host."
|
|
38
|
+
).option(
|
|
39
|
+
"--obo",
|
|
40
|
+
"Enable On-Behalf-Of token exchange in HTTP mode. Exchanges the incoming bearer token for a Graph API token using the OBO flow. Requires MS365_MCP_CLIENT_SECRET."
|
|
38
41
|
).addOption(
|
|
39
42
|
// DEPRECATED: kept only so existing deployments that set --base-url or
|
|
40
43
|
// MS365_MCP_BASE_URL do not crash at startup. Use --public-url /
|
|
@@ -99,6 +102,9 @@ function parseArgs() {
|
|
|
99
102
|
options.enableDynamicRegistration = true;
|
|
100
103
|
}
|
|
101
104
|
}
|
|
105
|
+
if (process.env.MS365_MCP_OBO === "true" || process.env.MS365_MCP_OBO === "1") {
|
|
106
|
+
options.obo = true;
|
|
107
|
+
}
|
|
102
108
|
if (options.cloud) {
|
|
103
109
|
process.env.MS365_MCP_CLOUD_TYPE = options.cloud;
|
|
104
110
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ConfidentialClientApplication } from "@azure/msal-node";
|
|
2
|
+
import logger from "./logger.js";
|
|
3
|
+
import { getCloudEndpoints } from "./cloud-config.js";
|
|
4
|
+
class OboClient {
|
|
5
|
+
constructor(secrets) {
|
|
6
|
+
if (!secrets.clientSecret) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"On-Behalf-Of flow requires MS365_MCP_CLIENT_SECRET to be set (confidential client)."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
const cloudEndpoints = getCloudEndpoints(secrets.cloudType);
|
|
12
|
+
this.cca = new ConfidentialClientApplication({
|
|
13
|
+
auth: {
|
|
14
|
+
clientId: secrets.clientId,
|
|
15
|
+
clientSecret: secrets.clientSecret,
|
|
16
|
+
authority: `${cloudEndpoints.authority}/${secrets.tenantId || "common"}`
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const graphBase = cloudEndpoints.graphApi.replace(/\/$/, "");
|
|
20
|
+
this.graphScopes = [`${graphBase}/.default`];
|
|
21
|
+
}
|
|
22
|
+
async exchangeToken(userAssertion) {
|
|
23
|
+
try {
|
|
24
|
+
const result = await this.cca.acquireTokenOnBehalfOf({
|
|
25
|
+
oboAssertion: userAssertion,
|
|
26
|
+
scopes: this.graphScopes
|
|
27
|
+
});
|
|
28
|
+
if (!result?.accessToken) {
|
|
29
|
+
throw new Error("OBO token exchange returned no access token");
|
|
30
|
+
}
|
|
31
|
+
logger.info("OBO token exchange successful");
|
|
32
|
+
return result.accessToken;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error(`OBO token exchange failed: ${error.message}`);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
var obo_client_default = OboClient;
|
|
40
|
+
export {
|
|
41
|
+
obo_client_default as default
|
|
42
|
+
};
|
package/dist/server.js
CHANGED
|
@@ -19,6 +19,7 @@ import { getSecrets } from "./secrets.js";
|
|
|
19
19
|
import { getCloudEndpoints } from "./cloud-config.js";
|
|
20
20
|
import { requestContext } from "./request-context.js";
|
|
21
21
|
import crypto from "node:crypto";
|
|
22
|
+
import OboClient from "./obo-client.js";
|
|
22
23
|
function parseHttpOption(httpOption) {
|
|
23
24
|
if (typeof httpOption === "boolean") {
|
|
24
25
|
return { host: void 0, port: 3e3 };
|
|
@@ -45,6 +46,7 @@ class MicrosoftGraphServer {
|
|
|
45
46
|
this.graphClient = null;
|
|
46
47
|
this.server = null;
|
|
47
48
|
this.secrets = null;
|
|
49
|
+
this.oboClient = null;
|
|
48
50
|
}
|
|
49
51
|
createMcpServer() {
|
|
50
52
|
const server = new McpServer(
|
|
@@ -103,6 +105,18 @@ class MicrosoftGraphServer {
|
|
|
103
105
|
} catch (err) {
|
|
104
106
|
logger.warn(`Failed to detect multi-account mode: ${err.message}`);
|
|
105
107
|
}
|
|
108
|
+
if (this.options.obo) {
|
|
109
|
+
if (!this.options.http) {
|
|
110
|
+
throw new Error("--obo requires --http (On-Behalf-Of flow only works in HTTP mode).");
|
|
111
|
+
}
|
|
112
|
+
if (!this.secrets.clientSecret) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"--obo requires MS365_MCP_CLIENT_SECRET to be set (confidential client required for On-Behalf-Of flow)."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
this.oboClient = new OboClient(this.secrets);
|
|
118
|
+
logger.info("On-Behalf-Of (OBO) flow enabled");
|
|
119
|
+
}
|
|
106
120
|
const outputFormat = this.options.toon ? "toon" : "json";
|
|
107
121
|
this.graphClient = new GraphClient(this.authManager, this.secrets, outputFormat);
|
|
108
122
|
if (!this.options.http) {
|
|
@@ -174,7 +188,7 @@ class MicrosoftGraphServer {
|
|
|
174
188
|
const protocol = req.secure ? "https" : "http";
|
|
175
189
|
const requestOrigin = `${protocol}://${req.get("host")}`;
|
|
176
190
|
const browserBase = publicBase ?? requestOrigin;
|
|
177
|
-
const scopes = buildScopesFromEndpoints(this.options.orgMode, this.options.enabledTools);
|
|
191
|
+
const scopes = this.options.obo ? [`api://${this.secrets.clientId}/access_as_user`] : buildScopesFromEndpoints(this.options.orgMode, this.options.enabledTools);
|
|
178
192
|
res.json({
|
|
179
193
|
resource: `${requestOrigin}/mcp`,
|
|
180
194
|
authorization_servers: [browserBase],
|
|
@@ -398,7 +412,11 @@ class MicrosoftGraphServer {
|
|
|
398
412
|
};
|
|
399
413
|
try {
|
|
400
414
|
if (req.microsoftAuth) {
|
|
401
|
-
|
|
415
|
+
let accessToken = req.microsoftAuth.accessToken;
|
|
416
|
+
if (this.oboClient) {
|
|
417
|
+
accessToken = await this.oboClient.exchangeToken(accessToken);
|
|
418
|
+
}
|
|
419
|
+
await requestContext.run({ accessToken }, handler);
|
|
402
420
|
} else {
|
|
403
421
|
await handler();
|
|
404
422
|
}
|
|
@@ -436,7 +454,11 @@ class MicrosoftGraphServer {
|
|
|
436
454
|
};
|
|
437
455
|
try {
|
|
438
456
|
if (req.microsoftAuth) {
|
|
439
|
-
|
|
457
|
+
let accessToken = req.microsoftAuth.accessToken;
|
|
458
|
+
if (this.oboClient) {
|
|
459
|
+
accessToken = await this.oboClient.exchangeToken(accessToken);
|
|
460
|
+
}
|
|
461
|
+
await requestContext.run({ accessToken }, handler);
|
|
440
462
|
} else {
|
|
441
463
|
await handler();
|
|
442
464
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.93.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",
|