@softeria/ms-365-mcp-server 0.24.0 → 0.24.2
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/Dockerfile +13 -0
- package/README.md +18 -7
- package/dist/auth.js +66 -13
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +4 -2
package/Dockerfile
ADDED
package/README.md
CHANGED
|
@@ -287,19 +287,30 @@ registration:
|
|
|
287
287
|
- Navigate to Azure Active Directory → App registrations → New registration
|
|
288
288
|
- Set name: "MS365 MCP Server"
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
Add these redirect URIs for testing with MCP Inspector (`npm run inspector`):
|
|
290
|
+
2. **Configure Redirect URIs**:
|
|
292
291
|
|
|
293
|
-
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
292
|
+
- **Configure the OAuth callback URI**: Go to your app registration and on the left side, go to Authentication.
|
|
293
|
+
- Under Platform configurations:
|
|
294
|
+
- Click Add a platform (if you don’t already see one for "Mobile and desktop applications" / "Public client").
|
|
295
|
+
- Choose Mobile and desktop applications or Public client/native (mobile & desktop) (label depends on portal version).
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
3. **Testing with MCP Inspector (`npm run inspector`)**:
|
|
298
|
+
|
|
299
|
+
- Go to your app registration and on the left side, go to Authentication.
|
|
300
|
+
- Under Platform configurations:
|
|
301
|
+
- Click Add a platform (if you don’t already see one for "Web").
|
|
302
|
+
- Choose Web.
|
|
303
|
+
- Configure the following redirect URIs
|
|
304
|
+
- `http://localhost:6274/oauth/callback`
|
|
305
|
+
- `http://localhost:6274/oauth/callback/debug`
|
|
306
|
+
- `http://localhost:3000/callback` (optional, for server callback)
|
|
307
|
+
|
|
308
|
+
4. **Get Credentials**:
|
|
298
309
|
|
|
299
310
|
- Copy the **Application (client) ID** from Overview page
|
|
300
311
|
- Go to Certificates & secrets → New client secret → Copy the secret value
|
|
301
312
|
|
|
302
|
-
|
|
313
|
+
5. **Configure Environment Variables**:
|
|
303
314
|
Create a `.env` file in your project root:
|
|
304
315
|
```env
|
|
305
316
|
MS365_MCP_CLIENT_ID=your-azure-ad-app-client-id-here
|
package/dist/auth.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { PublicClientApplication } from "@azure/msal-node";
|
|
2
|
-
import keytar from "keytar";
|
|
3
2
|
import logger from "./logger.js";
|
|
4
3
|
import fs, { existsSync, readFileSync } from "fs";
|
|
5
4
|
import { fileURLToPath } from "url";
|
|
6
5
|
import path from "path";
|
|
6
|
+
let keytar = null;
|
|
7
|
+
async function getKeytar() {
|
|
8
|
+
if (keytar === void 0) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (keytar === null) {
|
|
12
|
+
try {
|
|
13
|
+
keytar = await import("keytar");
|
|
14
|
+
return keytar;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
logger.info("keytar not available, using file-based credential storage");
|
|
17
|
+
keytar = void 0;
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return keytar;
|
|
22
|
+
}
|
|
7
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
24
|
const __dirname = path.dirname(__filename);
|
|
9
25
|
const endpointsData = JSON.parse(
|
|
@@ -31,9 +47,23 @@ const SCOPE_HIERARCHY = {
|
|
|
31
47
|
"Tasks.ReadWrite": ["Tasks.Read"],
|
|
32
48
|
"Contacts.ReadWrite": ["Contacts.Read"]
|
|
33
49
|
};
|
|
34
|
-
function buildScopesFromEndpoints(includeWorkAccountScopes = false) {
|
|
50
|
+
function buildScopesFromEndpoints(includeWorkAccountScopes = false, enabledToolsPattern) {
|
|
35
51
|
const scopesSet = /* @__PURE__ */ new Set();
|
|
52
|
+
let enabledToolsRegex;
|
|
53
|
+
if (enabledToolsPattern) {
|
|
54
|
+
try {
|
|
55
|
+
enabledToolsRegex = new RegExp(enabledToolsPattern, "i");
|
|
56
|
+
logger.info(`Building scopes with tool filter pattern: ${enabledToolsPattern}`);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error(
|
|
59
|
+
`Invalid tool filter regex pattern: ${enabledToolsPattern}. Building scopes without filter.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
36
63
|
endpoints.default.forEach((endpoint) => {
|
|
64
|
+
if (enabledToolsRegex && !enabledToolsRegex.test(endpoint.toolName)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
37
67
|
if (!includeWorkAccountScopes && !endpoint.scopes && endpoint.workScopes) {
|
|
38
68
|
return;
|
|
39
69
|
}
|
|
@@ -50,7 +80,11 @@ function buildScopesFromEndpoints(includeWorkAccountScopes = false) {
|
|
|
50
80
|
scopesSet.add(higherScope);
|
|
51
81
|
}
|
|
52
82
|
});
|
|
53
|
-
|
|
83
|
+
const scopes = Array.from(scopesSet);
|
|
84
|
+
if (enabledToolsPattern) {
|
|
85
|
+
logger.info(`Built ${scopes.length} scopes for filtered tools: ${scopes.join(", ")}`);
|
|
86
|
+
}
|
|
87
|
+
return scopes;
|
|
54
88
|
}
|
|
55
89
|
class AuthManager {
|
|
56
90
|
constructor(config = DEFAULT_CONFIG, scopes = buildScopesFromEndpoints()) {
|
|
@@ -69,9 +103,12 @@ class AuthManager {
|
|
|
69
103
|
try {
|
|
70
104
|
let cacheData;
|
|
71
105
|
try {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
106
|
+
const kt = await getKeytar();
|
|
107
|
+
if (kt) {
|
|
108
|
+
const cachedData = await kt.getPassword(SERVICE_NAME, TOKEN_CACHE_ACCOUNT);
|
|
109
|
+
if (cachedData) {
|
|
110
|
+
cacheData = cachedData;
|
|
111
|
+
}
|
|
75
112
|
}
|
|
76
113
|
} catch (keytarError) {
|
|
77
114
|
logger.warn(
|
|
@@ -93,9 +130,12 @@ class AuthManager {
|
|
|
93
130
|
try {
|
|
94
131
|
let selectedAccountData;
|
|
95
132
|
try {
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
|
|
133
|
+
const kt = await getKeytar();
|
|
134
|
+
if (kt) {
|
|
135
|
+
const cachedData = await kt.getPassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY);
|
|
136
|
+
if (cachedData) {
|
|
137
|
+
selectedAccountData = cachedData;
|
|
138
|
+
}
|
|
99
139
|
}
|
|
100
140
|
} catch (keytarError) {
|
|
101
141
|
logger.warn(
|
|
@@ -118,7 +158,12 @@ class AuthManager {
|
|
|
118
158
|
try {
|
|
119
159
|
const cacheData = this.msalApp.getTokenCache().serialize();
|
|
120
160
|
try {
|
|
121
|
-
await
|
|
161
|
+
const kt = await getKeytar();
|
|
162
|
+
if (kt) {
|
|
163
|
+
await kt.setPassword(SERVICE_NAME, TOKEN_CACHE_ACCOUNT, cacheData);
|
|
164
|
+
} else {
|
|
165
|
+
fs.writeFileSync(FALLBACK_PATH, cacheData);
|
|
166
|
+
}
|
|
122
167
|
} catch (keytarError) {
|
|
123
168
|
logger.warn(
|
|
124
169
|
`Keychain save failed, falling back to file storage: ${keytarError.message}`
|
|
@@ -133,7 +178,12 @@ class AuthManager {
|
|
|
133
178
|
try {
|
|
134
179
|
const selectedAccountData = JSON.stringify({ accountId: this.selectedAccountId });
|
|
135
180
|
try {
|
|
136
|
-
await
|
|
181
|
+
const kt = await getKeytar();
|
|
182
|
+
if (kt) {
|
|
183
|
+
await kt.setPassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY, selectedAccountData);
|
|
184
|
+
} else {
|
|
185
|
+
fs.writeFileSync(SELECTED_ACCOUNT_PATH, selectedAccountData);
|
|
186
|
+
}
|
|
137
187
|
} catch (keytarError) {
|
|
138
188
|
logger.warn(
|
|
139
189
|
`Keychain save failed for selected account, falling back to file storage: ${keytarError.message}`
|
|
@@ -286,8 +336,11 @@ class AuthManager {
|
|
|
286
336
|
this.tokenExpiry = null;
|
|
287
337
|
this.selectedAccountId = null;
|
|
288
338
|
try {
|
|
289
|
-
await
|
|
290
|
-
|
|
339
|
+
const kt = await getKeytar();
|
|
340
|
+
if (kt) {
|
|
341
|
+
await kt.deletePassword(SERVICE_NAME, TOKEN_CACHE_ACCOUNT);
|
|
342
|
+
await kt.deletePassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY);
|
|
343
|
+
}
|
|
291
344
|
} catch (keytarError) {
|
|
292
345
|
logger.warn(`Keychain deletion failed: ${keytarError.message}`);
|
|
293
346
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ async function main() {
|
|
|
12
12
|
if (includeWorkScopes) {
|
|
13
13
|
logger.info("Organization mode enabled - including work account scopes");
|
|
14
14
|
}
|
|
15
|
-
const scopes = buildScopesFromEndpoints(includeWorkScopes);
|
|
15
|
+
const scopes = buildScopesFromEndpoints(includeWorkScopes, args.enabledTools);
|
|
16
16
|
const authManager = new AuthManager(void 0, scopes);
|
|
17
17
|
await authManager.loadTokenCache();
|
|
18
18
|
if (args.login) {
|
package/dist/server.js
CHANGED
|
@@ -78,7 +78,7 @@ class MicrosoftGraphServer {
|
|
|
78
78
|
app.get("/.well-known/oauth-authorization-server", async (req, res) => {
|
|
79
79
|
const protocol = req.secure ? "https" : "http";
|
|
80
80
|
const url = new URL(`${protocol}://${req.get("host")}`);
|
|
81
|
-
const scopes = buildScopesFromEndpoints(this.options.orgMode);
|
|
81
|
+
const scopes = buildScopesFromEndpoints(this.options.orgMode, this.options.enabledTools);
|
|
82
82
|
res.json({
|
|
83
83
|
issuer: url.origin,
|
|
84
84
|
authorization_endpoint: `${url.origin}/authorize`,
|
|
@@ -95,7 +95,7 @@ class MicrosoftGraphServer {
|
|
|
95
95
|
app.get("/.well-known/oauth-protected-resource", async (req, res) => {
|
|
96
96
|
const protocol = req.secure ? "https" : "http";
|
|
97
97
|
const url = new URL(`${protocol}://${req.get("host")}`);
|
|
98
|
-
const scopes = buildScopesFromEndpoints(this.options.orgMode);
|
|
98
|
+
const scopes = buildScopesFromEndpoints(this.options.orgMode, this.options.enabledTools);
|
|
99
99
|
res.json({
|
|
100
100
|
resource: `${url.origin}/mcp`,
|
|
101
101
|
authorization_servers: [url.origin],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.2",
|
|
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",
|
|
@@ -40,10 +40,12 @@
|
|
|
40
40
|
"dotenv": "^17.0.1",
|
|
41
41
|
"express": "^5.1.0",
|
|
42
42
|
"js-yaml": "^4.1.0",
|
|
43
|
-
"keytar": "^7.9.0",
|
|
44
43
|
"winston": "^3.17.0",
|
|
45
44
|
"zod": "^3.24.2"
|
|
46
45
|
},
|
|
46
|
+
"optionalDependencies": {
|
|
47
|
+
"keytar": "^7.9.0"
|
|
48
|
+
},
|
|
47
49
|
"devDependencies": {
|
|
48
50
|
"@redocly/cli": "^1.34.3",
|
|
49
51
|
"@semantic-release/exec": "^7.1.0",
|