@softeria/ms-365-mcp-server 0.11.4 → 0.11.5
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/.releaserc.json +12 -0
- package/README.md +2 -1
- package/dist/auth-tools.js +181 -173
- package/dist/auth.js +402 -415
- package/dist/cli.js +35 -36
- package/dist/generated/client.js +10355 -13943
- package/dist/generated/endpoint-types.js +0 -1
- package/dist/generated/hack.js +39 -33
- package/dist/graph-client.js +426 -473
- package/dist/graph-tools.js +217 -228
- package/dist/index.js +76 -79
- package/dist/lib/microsoft-auth.js +62 -72
- package/dist/logger.js +36 -27
- package/dist/oauth-provider.js +48 -47
- package/dist/server.js +277 -264
- package/dist/version.js +9 -6
- package/package.json +11 -3
- package/tsup.config.ts +30 -0
- package/bin/release.mjs +0 -69
|
@@ -1,75 +1,65 @@
|
|
|
1
|
-
import logger from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Extract refresh token from a custom header (if provided)
|
|
16
|
-
const refreshToken = req.headers['x-microsoft-refresh-token'] || '';
|
|
17
|
-
// Store tokens in request for later use
|
|
18
|
-
req.microsoftAuth = {
|
|
19
|
-
accessToken,
|
|
20
|
-
refreshToken,
|
|
21
|
-
};
|
|
22
|
-
next();
|
|
1
|
+
import logger from "../logger.js";
|
|
2
|
+
const microsoftBearerTokenAuthMiddleware = (req, res, next) => {
|
|
3
|
+
const authHeader = req.headers.authorization;
|
|
4
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
5
|
+
res.status(401).json({ error: "Missing or invalid access token" });
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const accessToken = authHeader.substring(7);
|
|
9
|
+
const refreshToken = req.headers["x-microsoft-refresh-token"] || "";
|
|
10
|
+
req.microsoftAuth = {
|
|
11
|
+
accessToken,
|
|
12
|
+
refreshToken
|
|
13
|
+
};
|
|
14
|
+
next();
|
|
23
15
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
logger.error(`Failed to exchange code for token: ${error}`);
|
|
49
|
-
throw new Error(`Failed to exchange code for token: ${error}`);
|
|
50
|
-
}
|
|
51
|
-
return response.json();
|
|
16
|
+
async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, tenantId = "common", codeVerifier) {
|
|
17
|
+
const params = new URLSearchParams({
|
|
18
|
+
grant_type: "authorization_code",
|
|
19
|
+
code,
|
|
20
|
+
redirect_uri: redirectUri,
|
|
21
|
+
client_id: clientId,
|
|
22
|
+
client_secret: clientSecret
|
|
23
|
+
});
|
|
24
|
+
if (codeVerifier) {
|
|
25
|
+
params.append("code_verifier", codeVerifier);
|
|
26
|
+
}
|
|
27
|
+
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
31
|
+
},
|
|
32
|
+
body: params
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const error = await response.text();
|
|
36
|
+
logger.error(`Failed to exchange code for token: ${error}`);
|
|
37
|
+
throw new Error(`Failed to exchange code for token: ${error}`);
|
|
38
|
+
}
|
|
39
|
+
return response.json();
|
|
52
40
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
throw new Error(`Failed to refresh token: ${error}`);
|
|
73
|
-
}
|
|
74
|
-
return response.json();
|
|
41
|
+
async function refreshAccessToken(refreshToken, clientId, clientSecret, tenantId = "common") {
|
|
42
|
+
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
46
|
+
},
|
|
47
|
+
body: new URLSearchParams({
|
|
48
|
+
grant_type: "refresh_token",
|
|
49
|
+
refresh_token: refreshToken,
|
|
50
|
+
client_id: clientId,
|
|
51
|
+
client_secret: clientSecret
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const error = await response.text();
|
|
56
|
+
logger.error(`Failed to refresh token: ${error}`);
|
|
57
|
+
throw new Error(`Failed to refresh token: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
return response.json();
|
|
75
60
|
}
|
|
61
|
+
export {
|
|
62
|
+
exchangeCodeForToken,
|
|
63
|
+
microsoftBearerTokenAuthMiddleware,
|
|
64
|
+
refreshAccessToken
|
|
65
|
+
};
|
package/dist/logger.js
CHANGED
|
@@ -1,33 +1,42 @@
|
|
|
1
|
-
import winston from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import fs from
|
|
1
|
+
import winston from "winston";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import fs from "fs";
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const logsDir = path.join(__dirname,
|
|
6
|
+
const logsDir = path.join(__dirname, "..", "logs");
|
|
7
7
|
if (!fs.existsSync(logsDir)) {
|
|
8
|
-
|
|
8
|
+
fs.mkdirSync(logsDir);
|
|
9
9
|
}
|
|
10
10
|
const logger = winston.createLogger({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
level: process.env.LOG_LEVEL || "info",
|
|
12
|
+
format: winston.format.combine(
|
|
13
|
+
winston.format.timestamp({
|
|
14
|
+
format: "YYYY-MM-DD HH:mm:ss"
|
|
15
|
+
}),
|
|
16
|
+
winston.format.printf(({ level, message, timestamp }) => {
|
|
17
|
+
return `${timestamp} ${level.toUpperCase()}: ${message}`;
|
|
18
|
+
})
|
|
19
|
+
),
|
|
20
|
+
transports: [
|
|
21
|
+
new winston.transports.File({
|
|
22
|
+
filename: path.join(logsDir, "error.log"),
|
|
23
|
+
level: "error"
|
|
24
|
+
}),
|
|
25
|
+
new winston.transports.File({
|
|
26
|
+
filename: path.join(logsDir, "mcp-server.log")
|
|
27
|
+
})
|
|
28
|
+
]
|
|
26
29
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const enableConsoleLogging = () => {
|
|
31
|
+
logger.add(
|
|
32
|
+
new winston.transports.Console({
|
|
33
|
+
format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
|
|
34
|
+
silent: process.env.SILENT === "true" || process.env.SILENT === "1"
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
var logger_default = logger;
|
|
39
|
+
export {
|
|
40
|
+
logger_default as default,
|
|
41
|
+
enableConsoleLogging
|
|
32
42
|
};
|
|
33
|
-
export default logger;
|
package/dist/oauth-provider.js
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
import { ProxyOAuthServerProvider } from
|
|
2
|
-
import logger from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.authManager = authManager;
|
|
47
|
-
}
|
|
1
|
+
import { ProxyOAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js";
|
|
2
|
+
import logger from "./logger.js";
|
|
3
|
+
class MicrosoftOAuthProvider extends ProxyOAuthServerProvider {
|
|
4
|
+
constructor(authManager) {
|
|
5
|
+
const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
|
|
6
|
+
const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
|
|
7
|
+
super({
|
|
8
|
+
endpoints: {
|
|
9
|
+
authorizationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
|
|
10
|
+
tokenUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
|
|
11
|
+
revocationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/logout`
|
|
12
|
+
},
|
|
13
|
+
verifyAccessToken: async (token) => {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch("https://graph.microsoft.com/v1.0/me", {
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${token}`
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (response.ok) {
|
|
21
|
+
const userData = await response.json();
|
|
22
|
+
logger.info(`OAuth token verified for user: ${userData.userPrincipalName}`);
|
|
23
|
+
await authManager.setOAuthToken(token);
|
|
24
|
+
return {
|
|
25
|
+
token,
|
|
26
|
+
clientId,
|
|
27
|
+
scopes: []
|
|
28
|
+
};
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(`Token verification failed: ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
logger.error(`OAuth token verification error: ${error}`);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
getClient: async (client_id) => {
|
|
38
|
+
return {
|
|
39
|
+
client_id,
|
|
40
|
+
redirect_uris: ["http://localhost:3000/callback"]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
this.authManager = authManager;
|
|
45
|
+
}
|
|
48
46
|
}
|
|
47
|
+
export {
|
|
48
|
+
MicrosoftOAuthProvider
|
|
49
|
+
};
|