@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 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
- await requestContext.run({ accessToken: req.microsoftAuth.accessToken }, handler);
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
- await requestContext.run({ accessToken: req.microsoftAuth.accessToken }, handler);
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.92.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",