@softeria/ms-365-mcp-server 0.169.2 → 0.170.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/.env.example +6 -1
- package/README.md +9 -6
- package/dist/cli.js +4 -1
- package/dist/cloud-config.js +12 -1
- package/dist/lib/user-field-policy.js +24 -5
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -15,7 +15,7 @@ MS365_MCP_CLIENT_SECRET=your-azure-ad-app-client-secret-here
|
|
|
15
15
|
# Tenant ID - use "common" for multi-tenant or your specific tenant ID
|
|
16
16
|
MS365_MCP_TENANT_ID=common
|
|
17
17
|
|
|
18
|
-
# Cloud environment: global (default)
|
|
18
|
+
# Cloud environment: global (default), china (21Vianet) or gcc-high (US Government)
|
|
19
19
|
# MS365_MCP_CLOUD_TYPE=global
|
|
20
20
|
|
|
21
21
|
# Instructions for Global Cloud:
|
|
@@ -37,6 +37,11 @@ MS365_MCP_TENANT_ID=common
|
|
|
37
37
|
# 3. Follow the same steps as above
|
|
38
38
|
# 4. Set MS365_MCP_CLOUD_TYPE=china
|
|
39
39
|
|
|
40
|
+
# Instructions for GCC High:
|
|
41
|
+
# 1. Go to https://portal.azure.us
|
|
42
|
+
# 2. Register an app in your government tenant (no default client ID exists)
|
|
43
|
+
# 3. Set MS365_MCP_CLIENT_ID, MS365_MCP_TENANT_ID and MS365_MCP_CLOUD_TYPE=gcc-high
|
|
44
|
+
|
|
40
45
|
# -------------------------------------------------------------------
|
|
41
46
|
# Azure Key Vault Integration (Optional)
|
|
42
47
|
# -------------------------------------------------------------------
|
package/README.md
CHANGED
|
@@ -11,10 +11,13 @@ API.
|
|
|
11
11
|
|
|
12
12
|
This server supports multiple Microsoft cloud environments:
|
|
13
13
|
|
|
14
|
-
| Cloud | Description
|
|
15
|
-
| -------------------- |
|
|
16
|
-
| **Global** (default) | International Microsoft 365
|
|
17
|
-
| **China** (21Vianet) | Microsoft 365 operated by 21Vianet
|
|
14
|
+
| Cloud | Description | Auth Endpoint | Graph API Endpoint |
|
|
15
|
+
| -------------------- | ------------------------------------ | ------------------------- | ------------------------------- |
|
|
16
|
+
| **Global** (default) | International Microsoft 365 | login.microsoftonline.com | graph.microsoft.com |
|
|
17
|
+
| **China** (21Vianet) | Microsoft 365 operated by 21Vianet | login.chinacloudapi.cn | microsoftgraph.chinacloudapi.cn |
|
|
18
|
+
| **GCC High** | Microsoft 365 US Government GCC High | login.microsoftonline.us | graph.microsoft.us |
|
|
19
|
+
|
|
20
|
+
GCC High has no pre-registered client: register an app in your government tenant and set `MS365_MCP_CLIENT_ID` (and `MS365_MCP_TENANT_ID`), then use `--cloud gcc-high`.
|
|
18
21
|
|
|
19
22
|
To route Graph traffic through a proxy of your own (one that terminates the client's plain HTTP and originates TLS to Microsoft), set `MS365_MCP_GRAPH_BASE_URL` to its base URL; see the environment variable list under CLI Options, including what still bypasses it.
|
|
20
23
|
|
|
@@ -673,7 +676,7 @@ The following options can be used when running ms-365-mcp-server directly from t
|
|
|
673
676
|
--org-mode Enable organization/work mode from start (includes Teams, SharePoint, etc.)
|
|
674
677
|
--work-mode Alias for --org-mode
|
|
675
678
|
--force-work-scopes Backwards compatibility alias for --org-mode (deprecated)
|
|
676
|
-
--cloud <type> Microsoft cloud environment: global (default)
|
|
679
|
+
--cloud <type> Microsoft cloud environment: global (default), china (21Vianet) or gcc-high
|
|
677
680
|
--allowed-scopes <scopes> Limit exposed tools to Graph scopes covered by this allowlist
|
|
678
681
|
--extra-scopes <scopes> Append additional Graph scopes to the token request (for use with your own app registration + graph-batch)
|
|
679
682
|
--expected-username <username> Require local MSAL auth to use this account username
|
|
@@ -745,7 +748,7 @@ Environment variables:
|
|
|
745
748
|
- `MS365_MCP_ATTACHMENT_HOST=<host>`: Interface the `MS365_MCP_ATTACHMENT_PORT` listener binds (alternative to --attachment-host; requires `--attachment-port`). Defaults to the host `--http` bound — which for a wildcard `--http` means both ports answer everywhere and the port split isolates nothing. See "Splitting the attachment listener"
|
|
746
749
|
- `MS365_MCP_HTTP_LOCAL_FILE_TOOLS=true|1`: Register download-bytes-to-file, and upload-attachment-from-file when `MS365_MCP_UPLOAD_DIRS` is set, over HTTP (alternative to --http-local-file-tools; same restrictions)
|
|
747
750
|
- `MS365_MCP_UPLOAD_DIRS=<dirs>`: Directories `upload-attachment-from-file` may read from, as absolute paths separated like `PATH`. Unset (the default) leaves the tool unregistered. See "Attaching a local file in stdio mode"
|
|
748
|
-
- `MS365_MCP_CLOUD_TYPE=global|china`: Microsoft cloud environment (alternative to --cloud flag)
|
|
751
|
+
- `MS365_MCP_CLOUD_TYPE=global|china|gcc-high`: Microsoft cloud environment (alternative to --cloud flag)
|
|
749
752
|
- `MS365_MCP_GRAPH_BASE_URL=<url>`: Send Graph API requests to this base URL instead of the cloud's `graph.microsoft.com` (e.g. `http://127.0.0.1:10255/tenant-a/graph` for an egress proxy that originates TLS itself). Absolute http(s) URL without query or fragment; a path prefix is kept, so requests go to `<url>/v1.0/...`. The login authority and the On-Behalf-Of token resource are unchanged. Two things bypass it: `get-download-url` returns Graph's pre-authenticated SharePoint URL, and the `302` from `/content` points straight at SharePoint, so whoever follows either connects to SharePoint directly; if you control egress, allow those SharePoint hosts or disable those tools. Not read from `.env`
|
|
750
753
|
- `LOG_LEVEL`: Set logging level (default: 'info')
|
|
751
754
|
- `SILENT=true|1`: Disable console output
|
package/dist/cli.js
CHANGED
|
@@ -55,7 +55,10 @@ program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").versio
|
|
|
55
55
|
).option("--list-presets", "List all available presets and exit").option("--list-permissions", "List all required Graph API permissions and exit").option(
|
|
56
56
|
"--org-mode",
|
|
57
57
|
"Enable organization/work mode from start (includes Teams, SharePoint, etc.)"
|
|
58
|
-
).option("--work-mode", "Alias for --org-mode").option("--force-work-scopes", "Backwards compatibility alias for --org-mode (deprecated)").option("--toon", "(experimental) Enable TOON output format for 30-60% token reduction").option("--discovery", "Enable runtime tool discovery and loading (experimental feature)").option(
|
|
58
|
+
).option("--work-mode", "Alias for --org-mode").option("--force-work-scopes", "Backwards compatibility alias for --org-mode (deprecated)").option("--toon", "(experimental) Enable TOON output format for 30-60% token reduction").option("--discovery", "Enable runtime tool discovery and loading (experimental feature)").option(
|
|
59
|
+
"--cloud <type>",
|
|
60
|
+
"Microsoft cloud environment: global (default), china (21Vianet) or gcc-high (US Government GCC High)"
|
|
61
|
+
).option(
|
|
59
62
|
"--enable-dynamic-registration",
|
|
60
63
|
"Enable OAuth Dynamic Client Registration endpoint (kept for backwards compatibility, now enabled by default in HTTP mode)"
|
|
61
64
|
).option(
|
package/dist/cloud-config.js
CHANGED
|
@@ -8,6 +8,11 @@ const CLOUD_ENDPOINTS = {
|
|
|
8
8
|
authority: "https://login.chinacloudapi.cn",
|
|
9
9
|
graphApi: "https://microsoftgraph.chinacloudapi.cn",
|
|
10
10
|
portal: "https://portal.azure.cn"
|
|
11
|
+
},
|
|
12
|
+
"gcc-high": {
|
|
13
|
+
authority: "https://login.microsoftonline.us",
|
|
14
|
+
graphApi: "https://graph.microsoft.us",
|
|
15
|
+
portal: "https://portal.azure.us"
|
|
11
16
|
}
|
|
12
17
|
};
|
|
13
18
|
const DEFAULT_CLIENT_IDS = {
|
|
@@ -15,7 +20,13 @@ const DEFAULT_CLIENT_IDS = {
|
|
|
15
20
|
china: "f3e61a6e-bc26-4281-8588-2c7359a02141"
|
|
16
21
|
};
|
|
17
22
|
function getDefaultClientId(cloudType = "global") {
|
|
18
|
-
|
|
23
|
+
const clientId = DEFAULT_CLIENT_IDS[cloudType];
|
|
24
|
+
if (!clientId) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`No default client ID exists for the ${cloudType} cloud. Register an app in the ${cloudType} tenant and set MS365_MCP_CLIENT_ID.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return clientId;
|
|
19
30
|
}
|
|
20
31
|
const GRAPH_BASE_URL_ENV = "MS365_MCP_GRAPH_BASE_URL";
|
|
21
32
|
function getGraphBaseUrlOverride() {
|
|
@@ -3,10 +3,29 @@ const USER_ENTITY_PATH = /^\/users(?:\/[^/]+)?\/?$/i;
|
|
|
3
3
|
const DIRECTORY_NAVIGATION_PATH = /^(?:\/me|\/users\/[^/]+)\/(?:manager|directReports)\/?$|^\/groups\/[^/]+\/(?:members|owners)\/?$/i;
|
|
4
4
|
const EXPAND_USER_PATH = /^\/me\/?$|^\/groups(?:\/[^/]+)?\/?$/i;
|
|
5
5
|
function normalizePath(path) {
|
|
6
|
-
|
|
6
|
+
let withoutQuery = path.split("?")[0];
|
|
7
|
+
for (let i = 0; i < 4; i++) {
|
|
8
|
+
const next = withoutQuery.replace(
|
|
9
|
+
/%([0-9a-f]{2})/gi,
|
|
10
|
+
(_, hex) => String.fromCharCode(parseInt(hex, 16))
|
|
11
|
+
);
|
|
12
|
+
if (next === withoutQuery) break;
|
|
13
|
+
withoutQuery = next;
|
|
14
|
+
}
|
|
7
15
|
return withoutQuery.startsWith("/") ? withoutQuery : `/${withoutQuery}`;
|
|
8
16
|
}
|
|
17
|
+
const ENCODED_SEPARATOR = /%(?:25)*(?:2f|5c)/i;
|
|
18
|
+
function deleteOdataOption(params, name) {
|
|
19
|
+
const values = [];
|
|
20
|
+
for (const key of [...new Set(params.keys())]) {
|
|
21
|
+
if (key.replace(/^\$/, "").toLowerCase() !== name) continue;
|
|
22
|
+
values.push(...params.getAll(key));
|
|
23
|
+
params.delete(key);
|
|
24
|
+
}
|
|
25
|
+
return values;
|
|
26
|
+
}
|
|
9
27
|
function userFieldEnforcement(path) {
|
|
28
|
+
if (ENCODED_SEPARATOR.test(path.split("?")[0])) return "request-and-response";
|
|
10
29
|
const normalized = normalizePath(path);
|
|
11
30
|
if (USER_ENTITY_PATH.test(normalized)) return "request-and-response";
|
|
12
31
|
if (DIRECTORY_NAVIGATION_PATH.test(normalized)) return "response-only";
|
|
@@ -23,8 +42,7 @@ function stripUserFieldExpandFromUrl(url) {
|
|
|
23
42
|
const [path, query = ""] = url.split("?");
|
|
24
43
|
if (!shouldStripUserFieldExpand(path) || query === "") return url;
|
|
25
44
|
const params = new URLSearchParams(query);
|
|
26
|
-
if (
|
|
27
|
-
params.delete("$expand");
|
|
45
|
+
if (deleteOdataOption(params, "expand").length === 0) return url;
|
|
28
46
|
const rebuilt = params.toString();
|
|
29
47
|
if (rebuilt === "") return path;
|
|
30
48
|
return `${path}?${rebuilt.replace(/%24/gi, "$").replace(/%2C/gi, ",")}`;
|
|
@@ -46,14 +64,15 @@ function restrictUserFieldQuery(queryParams, allowlist, enforcement) {
|
|
|
46
64
|
function restrictUserFieldUrl(url, allowlist, enforcement) {
|
|
47
65
|
const [path, query = ""] = url.split("?");
|
|
48
66
|
const params = new URLSearchParams(query);
|
|
67
|
+
const requested = enforcement === "request-and-response" ? deleteOdataOption(params, "select") : params.getAll("$select");
|
|
49
68
|
const fields = effectiveUserFields(
|
|
50
|
-
parseSelectFields(
|
|
69
|
+
parseSelectFields(requested.join(",") || void 0),
|
|
51
70
|
allowlist
|
|
52
71
|
);
|
|
53
72
|
if (enforcement === "request-and-response") {
|
|
54
73
|
params.set("$select", fields.join(","));
|
|
55
74
|
}
|
|
56
|
-
params
|
|
75
|
+
deleteOdataOption(params, "expand");
|
|
57
76
|
const rebuilt = params.toString();
|
|
58
77
|
if (rebuilt === "") return { url: path, fields };
|
|
59
78
|
return { url: `${path}?${rebuilt.replace(/%24/gi, "$").replace(/%2C/gi, ",")}`, fields };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
3
|
"mcpName": "io.github.Softeria/ms-365-mcp-server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.170.0",
|
|
5
5
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|