@softeria/ms-365-mcp-server 0.3.5 → 0.4.1

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.
Files changed (49) hide show
  1. package/.github/workflows/build.yml +3 -0
  2. package/.github/workflows/npm-publish.yml +2 -0
  3. package/README.md +1 -1
  4. package/bin/generate-graph-client.mjs +59 -0
  5. package/bin/{download-openapi.mjs → modules/download-openapi.mjs} +10 -20
  6. package/bin/modules/extract-descriptions.mjs +48 -0
  7. package/bin/modules/generate-mcp-tools.mjs +36 -0
  8. package/bin/modules/simplified-openapi.mjs +78 -0
  9. package/dist/auth-tools.js +80 -0
  10. package/dist/auth.js +219 -0
  11. package/dist/cli.js +21 -0
  12. package/dist/endpoints.json +375 -0
  13. package/dist/generated/client.js +14683 -0
  14. package/dist/generated/endpoint-types.js +1 -0
  15. package/dist/generated/hack.js +37 -0
  16. package/dist/graph-client.js +254 -0
  17. package/dist/graph-tools.js +98 -0
  18. package/dist/index.js +39 -0
  19. package/dist/logger.js +33 -0
  20. package/dist/server.js +32 -0
  21. package/{src/version.mjs → dist/version.js} +0 -2
  22. package/package.json +12 -9
  23. package/src/{auth-tools.mjs → auth-tools.ts} +7 -5
  24. package/src/{auth.mjs → auth.ts} +60 -30
  25. package/src/{cli.mjs → cli.ts} +9 -1
  26. package/src/endpoints.json +375 -0
  27. package/src/generated/README.md +51 -0
  28. package/src/generated/client.ts +24916 -0
  29. package/src/generated/endpoint-types.ts +27 -0
  30. package/src/generated/hack.ts +50 -0
  31. package/src/{graph-client.mjs → graph-client.ts} +53 -18
  32. package/src/graph-tools.ts +174 -0
  33. package/{index.mjs → src/index.ts} +6 -6
  34. package/src/{logger.mjs → logger.ts} +1 -1
  35. package/src/{server.mjs → server.ts} +16 -9
  36. package/src/version.ts +9 -0
  37. package/test/{auth-tools.test.js → auth-tools.test.ts} +41 -38
  38. package/test/{cli.test.js → cli.test.ts} +3 -3
  39. package/test/{graph-api.test.js → graph-api.test.ts} +5 -5
  40. package/test/test-hack.ts +17 -0
  41. package/tsconfig.json +16 -0
  42. package/src/dynamic-tools.mjs +0 -442
  43. package/src/openapi-helpers.mjs +0 -187
  44. package/src/param-mapper.mjs +0 -30
  45. package/test/dynamic-tools.test.js +0 -852
  46. package/test/mappings.test.js +0 -29
  47. package/test/mcp-server.test.js +0 -36
  48. package/test/openapi-helpers.test.js +0 -210
  49. package/test/param-mapper.test.js +0 -56
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ export function makeApi(endpoints) {
3
+ return endpoints;
4
+ }
5
+ export class Zodios {
6
+ constructor(baseUrlOrEndpoints, endpoints, options) {
7
+ if (typeof baseUrlOrEndpoints === 'string') {
8
+ throw new Error('No such hack');
9
+ }
10
+ this.endpoints = baseUrlOrEndpoints.map((endpoint) => {
11
+ endpoint.parameters = endpoint.parameters || [];
12
+ for (const parameter of endpoint.parameters) {
13
+ // We need a hack since MCP won't support $ in parameter names
14
+ parameter.name = parameter.name.replace(/\$/g, '__');
15
+ }
16
+ const pathParamRegex = /:([a-zA-Z0-9]+)/g;
17
+ const pathParams = [];
18
+ let match;
19
+ while ((match = pathParamRegex.exec(endpoint.path)) !== null) {
20
+ pathParams.push(match[1]);
21
+ }
22
+ for (const pathParam of pathParams) {
23
+ const paramExists = endpoint.parameters.some((param) => param.name === pathParam || param.name === pathParam.replace(/\$/g, '__'));
24
+ if (!paramExists) {
25
+ const newParam = {
26
+ name: pathParam,
27
+ type: 'Path',
28
+ schema: z.string().describe(`Path parameter: ${pathParam}`),
29
+ description: `Path parameter: ${pathParam}`,
30
+ };
31
+ endpoint.parameters.push(newParam);
32
+ }
33
+ }
34
+ return endpoint;
35
+ });
36
+ }
37
+ }
@@ -0,0 +1,254 @@
1
+ import logger from './logger.js';
2
+ class GraphClient {
3
+ constructor(authManager) {
4
+ this.authManager = authManager;
5
+ this.sessions = new Map();
6
+ }
7
+ async createSession(filePath) {
8
+ try {
9
+ if (!filePath) {
10
+ logger.error('No file path provided for Excel session');
11
+ return null;
12
+ }
13
+ if (this.sessions.has(filePath)) {
14
+ return this.sessions.get(filePath) || null;
15
+ }
16
+ logger.info(`Creating new Excel session for file: ${filePath}`);
17
+ const accessToken = await this.authManager.getToken();
18
+ const response = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:${filePath}:/workbook/createSession`, {
19
+ method: 'POST',
20
+ headers: {
21
+ Authorization: `Bearer ${accessToken}`,
22
+ 'Content-Type': 'application/json',
23
+ },
24
+ body: JSON.stringify({ persistChanges: true }),
25
+ });
26
+ if (!response.ok) {
27
+ const errorText = await response.text();
28
+ logger.error(`Failed to create session: ${response.status} - ${errorText}`);
29
+ return null;
30
+ }
31
+ const result = await response.json();
32
+ logger.info(`Session created successfully for file: ${filePath}`);
33
+ this.sessions.set(filePath, result.id);
34
+ return result.id;
35
+ }
36
+ catch (error) {
37
+ logger.error(`Error creating Excel session: ${error}`);
38
+ return null;
39
+ }
40
+ }
41
+ async graphRequest(endpoint, options = {}) {
42
+ try {
43
+ logger.info(`Calling ${endpoint} with options: ${JSON.stringify(options)}`);
44
+ let accessToken = await this.authManager.getToken();
45
+ let url;
46
+ let sessionId = null;
47
+ if (options.excelFile &&
48
+ !endpoint.startsWith('/drive') &&
49
+ !endpoint.startsWith('/users') &&
50
+ !endpoint.startsWith('/me')) {
51
+ sessionId = this.sessions.get(options.excelFile) || null;
52
+ if (!sessionId) {
53
+ sessionId = await this.createSession(options.excelFile);
54
+ }
55
+ url = `https://graph.microsoft.com/v1.0/me/drive/root:${options.excelFile}:${endpoint}`;
56
+ }
57
+ else if (endpoint.startsWith('/drive') ||
58
+ endpoint.startsWith('/users') ||
59
+ endpoint.startsWith('/me')) {
60
+ url = `https://graph.microsoft.com/v1.0${endpoint}`;
61
+ }
62
+ else {
63
+ logger.error('Excel operation requested without specifying a file');
64
+ return {
65
+ content: [
66
+ {
67
+ type: 'text',
68
+ text: JSON.stringify({ error: 'No Excel file specified for this operation' }),
69
+ },
70
+ ],
71
+ };
72
+ }
73
+ const headers = {
74
+ ...options.headers,
75
+ Authorization: `Bearer ${accessToken}`,
76
+ 'Content-Type': 'application/json',
77
+ ...(sessionId && { 'workbook-session-id': sessionId }),
78
+ };
79
+ delete options.headers;
80
+ logger.info(` ** Making request to ${url} with options: ${JSON.stringify(options)}`);
81
+ const response = await fetch(url, {
82
+ headers,
83
+ ...options,
84
+ });
85
+ if (response.status === 401) {
86
+ logger.info('Access token expired, refreshing...');
87
+ const newToken = await this.authManager.getToken(true);
88
+ if (options.excelFile &&
89
+ !endpoint.startsWith('/drive') &&
90
+ !endpoint.startsWith('/users') &&
91
+ !endpoint.startsWith('/me')) {
92
+ sessionId = await this.createSession(options.excelFile);
93
+ }
94
+ headers.Authorization = `Bearer ${newToken}`;
95
+ if (sessionId &&
96
+ !endpoint.startsWith('/drive') &&
97
+ !endpoint.startsWith('/users') &&
98
+ !endpoint.startsWith('/me')) {
99
+ headers['workbook-session-id'] = sessionId;
100
+ }
101
+ const retryResponse = await fetch(url, {
102
+ headers,
103
+ ...options,
104
+ });
105
+ if (!retryResponse.ok) {
106
+ throw new Error(`Graph API error: ${retryResponse.status} ${await retryResponse.text()}`);
107
+ }
108
+ return this.formatResponse(retryResponse, options.rawResponse);
109
+ }
110
+ if (!response.ok) {
111
+ throw new Error(`Graph API error: ${response.status} ${await response.text()}`);
112
+ }
113
+ return this.formatResponse(response, options.rawResponse);
114
+ }
115
+ catch (error) {
116
+ logger.error(`Error in Graph API request: ${error}`);
117
+ return {
118
+ content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }],
119
+ isError: true,
120
+ };
121
+ }
122
+ }
123
+ async formatResponse(response, rawResponse = false) {
124
+ try {
125
+ if (response.status === 204) {
126
+ return {
127
+ content: [
128
+ {
129
+ type: 'text',
130
+ text: JSON.stringify({
131
+ message: 'Operation completed successfully',
132
+ }),
133
+ },
134
+ ],
135
+ };
136
+ }
137
+ if (rawResponse) {
138
+ const contentType = response.headers.get('content-type');
139
+ if (contentType && contentType.startsWith('text/')) {
140
+ const text = await response.text();
141
+ return {
142
+ content: [{ type: 'text', text }],
143
+ };
144
+ }
145
+ return {
146
+ content: [
147
+ {
148
+ type: 'text',
149
+ text: JSON.stringify({
150
+ message: 'Binary file content received',
151
+ contentType: contentType,
152
+ contentLength: response.headers.get('content-length'),
153
+ }),
154
+ },
155
+ ],
156
+ };
157
+ }
158
+ const result = await response.json();
159
+ const removeODataProps = (obj) => {
160
+ if (!obj || typeof obj !== 'object')
161
+ return;
162
+ if (Array.isArray(obj)) {
163
+ obj.forEach((item) => removeODataProps(item));
164
+ }
165
+ else {
166
+ Object.keys(obj).forEach((key) => {
167
+ if (key.startsWith('@odata')) {
168
+ delete obj[key];
169
+ }
170
+ else if (typeof obj[key] === 'object') {
171
+ removeODataProps(obj[key]);
172
+ }
173
+ });
174
+ }
175
+ };
176
+ removeODataProps(result);
177
+ return {
178
+ content: [{ type: 'text', text: JSON.stringify(result) }],
179
+ };
180
+ }
181
+ catch (error) {
182
+ logger.error(`Error formatting response: ${error}`);
183
+ return {
184
+ content: [{ type: 'text', text: JSON.stringify({ message: 'Success' }) }],
185
+ };
186
+ }
187
+ }
188
+ async closeSession(filePath) {
189
+ if (!filePath || !this.sessions.has(filePath)) {
190
+ return {
191
+ content: [
192
+ {
193
+ type: 'text',
194
+ text: JSON.stringify({ message: 'No active session for the specified file' }),
195
+ },
196
+ ],
197
+ };
198
+ }
199
+ const sessionId = this.sessions.get(filePath);
200
+ try {
201
+ const accessToken = await this.authManager.getToken();
202
+ const response = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:${filePath}:/workbook/closeSession`, {
203
+ method: 'POST',
204
+ headers: {
205
+ Authorization: `Bearer ${accessToken}`,
206
+ 'Content-Type': 'application/json',
207
+ 'workbook-session-id': sessionId,
208
+ },
209
+ });
210
+ if (response.ok) {
211
+ this.sessions.delete(filePath);
212
+ return {
213
+ content: [
214
+ {
215
+ type: 'text',
216
+ text: JSON.stringify({ message: `Session for ${filePath} closed successfully` }),
217
+ },
218
+ ],
219
+ };
220
+ }
221
+ else {
222
+ throw new Error(`Failed to close session: ${response.status}`);
223
+ }
224
+ }
225
+ catch (error) {
226
+ logger.error(`Error closing session: ${error}`);
227
+ return {
228
+ content: [
229
+ {
230
+ type: 'text',
231
+ text: JSON.stringify({ error: `Failed to close session for ${filePath}` }),
232
+ },
233
+ ],
234
+ isError: true,
235
+ };
236
+ }
237
+ }
238
+ async closeAllSessions() {
239
+ const results = [];
240
+ for (const [filePath] of this.sessions) {
241
+ const result = await this.closeSession(filePath);
242
+ results.push(result);
243
+ }
244
+ return {
245
+ content: [
246
+ {
247
+ type: 'text',
248
+ text: JSON.stringify({ message: 'All sessions closed', results }),
249
+ },
250
+ ],
251
+ };
252
+ }
253
+ }
254
+ export default GraphClient;
@@ -0,0 +1,98 @@
1
+ import logger from './logger.js';
2
+ import { api } from './generated/client.js';
3
+ import { z } from 'zod';
4
+ export function registerGraphTools(server, graphClient) {
5
+ for (const tool of api.endpoints) {
6
+ // Create a zod schema for the parameters
7
+ const paramSchema = {};
8
+ if (tool.parameters && tool.parameters.length > 0) {
9
+ for (const param of tool.parameters) {
10
+ // Use z.any() as a fallback schema if the parameter schema is not specified
11
+ paramSchema[param.name] = param.schema || z.any();
12
+ }
13
+ }
14
+ server.tool(tool.alias, tool.description ?? '', paramSchema, {
15
+ title: tool.alias,
16
+ readOnlyHint: tool.method.toUpperCase() === 'GET',
17
+ }, async (params, extra) => {
18
+ logger.info(`Tool ${tool.alias} called with params: ${JSON.stringify(params)}`);
19
+ try {
20
+ logger.info(`params: ${JSON.stringify(params)}`);
21
+ const parameterDefinitions = tool.parameters || [];
22
+ let path = tool.path;
23
+ const queryParams = {};
24
+ const headers = {};
25
+ let body = null;
26
+ for (let [paramName, paramValue] of Object.entries(params)) {
27
+ const fixedParamName = paramName.replace(/__/g, '$');
28
+ const paramDef = parameterDefinitions.find((p) => p.name === paramName);
29
+ if (paramDef) {
30
+ switch (paramDef.type) {
31
+ case 'Path':
32
+ path = path
33
+ .replace(`{${paramName}}`, encodeURIComponent(paramValue))
34
+ .replace(`:${paramName}`, encodeURIComponent(paramValue));
35
+ break;
36
+ case 'Query':
37
+ queryParams[fixedParamName] = `${paramValue}`;
38
+ break;
39
+ case 'Body':
40
+ body = paramValue;
41
+ break;
42
+ case 'Header':
43
+ headers[fixedParamName] = `${paramValue}`;
44
+ break;
45
+ }
46
+ }
47
+ else if (paramName === 'body') {
48
+ body = paramValue;
49
+ logger.info(`Set legacy body param: ${JSON.stringify(body)}`);
50
+ }
51
+ }
52
+ if (Object.keys(queryParams).length > 0) {
53
+ const queryString = Object.entries(queryParams)
54
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
55
+ .join('&');
56
+ path = `${path}${path.includes('?') ? '&' : '?'}${queryString}`;
57
+ }
58
+ const options = {
59
+ method: tool.method.toUpperCase(),
60
+ headers,
61
+ };
62
+ if (options.method !== 'GET' && body) {
63
+ options.body = JSON.stringify(body);
64
+ }
65
+ logger.info(`Making graph request to ${path} with options: ${JSON.stringify(options)}`);
66
+ const response = await graphClient.graphRequest(path, options);
67
+ // Convert McpResponse to CallToolResult with the correct structure
68
+ const content = response.content.map((item) => {
69
+ // GraphClient only returns text content items, so create proper TextContent items
70
+ const textContent = {
71
+ type: 'text',
72
+ text: item.text,
73
+ };
74
+ return textContent;
75
+ });
76
+ const result = {
77
+ content,
78
+ _meta: response._meta,
79
+ isError: response.isError,
80
+ };
81
+ return result;
82
+ }
83
+ catch (error) {
84
+ logger.error(`Error in tool ${tool.alias}: ${error.message}`);
85
+ const errorContent = {
86
+ type: 'text',
87
+ text: JSON.stringify({
88
+ error: `Error in tool ${tool.alias}: ${error.message}`,
89
+ }),
90
+ };
91
+ return {
92
+ content: [errorContent],
93
+ isError: true,
94
+ };
95
+ }
96
+ });
97
+ }
98
+ }
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from './cli.js';
3
+ import logger from './logger.js';
4
+ import AuthManager from './auth.js';
5
+ import MicrosoftGraphServer from './server.js';
6
+ import { version } from './version.js';
7
+ async function main() {
8
+ try {
9
+ const args = parseArgs();
10
+ const authManager = new AuthManager();
11
+ await authManager.loadTokenCache();
12
+ if (args.login) {
13
+ await authManager.acquireTokenByDeviceCode();
14
+ logger.info('Login completed, testing connection with Graph API...');
15
+ const result = await authManager.testLogin();
16
+ console.log(JSON.stringify(result));
17
+ process.exit(0);
18
+ }
19
+ if (args.verifyLogin) {
20
+ logger.info('Verifying login...');
21
+ const result = await authManager.testLogin();
22
+ console.log(JSON.stringify(result));
23
+ process.exit(0);
24
+ }
25
+ if (args.logout) {
26
+ await authManager.logout();
27
+ console.log(JSON.stringify({ message: 'Logged out successfully' }));
28
+ process.exit(0);
29
+ }
30
+ const server = new MicrosoftGraphServer(authManager, args);
31
+ await server.initialize(version);
32
+ await server.start();
33
+ }
34
+ catch (error) {
35
+ logger.error(`Startup error: ${error}`);
36
+ process.exit(1);
37
+ }
38
+ }
39
+ main();
package/dist/logger.js ADDED
@@ -0,0 +1,33 @@
1
+ import winston from 'winston';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import fs from 'fs';
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const logsDir = path.join(__dirname, '..', 'logs');
7
+ if (!fs.existsSync(logsDir)) {
8
+ fs.mkdirSync(logsDir);
9
+ }
10
+ const logger = winston.createLogger({
11
+ level: process.env.LOG_LEVEL || 'info',
12
+ format: winston.format.combine(winston.format.timestamp({
13
+ format: 'YYYY-MM-DD HH:mm:ss',
14
+ }), winston.format.printf(({ level, message, timestamp }) => {
15
+ return `${timestamp} ${level.toUpperCase()}: ${message}`;
16
+ })),
17
+ transports: [
18
+ new winston.transports.File({
19
+ filename: path.join(logsDir, 'error.log'),
20
+ level: 'error',
21
+ }),
22
+ new winston.transports.File({
23
+ filename: path.join(logsDir, 'mcp-server.log'),
24
+ }),
25
+ ],
26
+ });
27
+ export const enableConsoleLogging = () => {
28
+ logger.add(new winston.transports.Console({
29
+ format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
30
+ silent: process.env.SILENT === 'true',
31
+ }));
32
+ };
33
+ export default logger;
package/dist/server.js ADDED
@@ -0,0 +1,32 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import logger, { enableConsoleLogging } from './logger.js';
4
+ import { registerAuthTools } from './auth-tools.js';
5
+ import { registerGraphTools } from './graph-tools.js';
6
+ import GraphClient from './graph-client.js';
7
+ class MicrosoftGraphServer {
8
+ constructor(authManager, options = {}) {
9
+ this.authManager = authManager;
10
+ this.options = options;
11
+ this.graphClient = new GraphClient(authManager);
12
+ this.server = null;
13
+ }
14
+ async initialize(version) {
15
+ this.server = new McpServer({
16
+ name: 'Microsoft365MCP',
17
+ version,
18
+ });
19
+ registerAuthTools(this.server, this.authManager);
20
+ registerGraphTools(this.server, this.graphClient);
21
+ }
22
+ async start() {
23
+ if (this.options.v) {
24
+ enableConsoleLogging();
25
+ }
26
+ logger.info('Microsoft 365 MCP Server starting...');
27
+ const transport = new StdioServerTransport();
28
+ await this.server.connect(transport);
29
+ logger.info('Server connected to transport');
30
+ }
31
+ }
32
+ export default MicrosoftGraphServer;
@@ -1,9 +1,7 @@
1
1
  import { readFileSync } from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
-
5
4
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
5
  const packageJsonPath = path.join(__dirname, '..', 'package.json');
7
6
  const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
8
-
9
7
  export const version = packageJson.version;
package/package.json CHANGED
@@ -1,21 +1,20 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "description": "Microsoft 365 MCP Server",
5
5
  "type": "module",
6
- "main": "index.mjs",
6
+ "main": "dist/index.js",
7
7
  "bin": {
8
- "ms-365-mcp-server": "index.mjs"
8
+ "ms-365-mcp-server": "dist/index.js"
9
9
  },
10
10
  "scripts": {
11
+ "build": "tsc",
11
12
  "test": "vitest run",
12
13
  "test:watch": "vitest",
13
- "start": "node index.mjs",
14
- "format": "prettier --write \"**/*.{js,mjs,json,md}\"",
15
- "release": "node bin/release.mjs",
16
- "download-openapi": "node bin/download-openapi.mjs",
17
- "inspect": "npx @modelcontextprotocol/inspector node index.mjs",
18
- "postinstall": "npm run download-openapi"
14
+ "dev": "tsx src/index.ts",
15
+ "format": "prettier --write \"**/*.{ts,mts,js,mjs,json,md}\"",
16
+ "release": "ts-node --esm bin/release.mts",
17
+ "inspect": "npx @modelcontextprotocol/inspector tsx src/index.ts"
19
18
  },
20
19
  "keywords": [
21
20
  "microsoft",
@@ -38,7 +37,11 @@
38
37
  "zod": "^3.24.2"
39
38
  },
40
39
  "devDependencies": {
40
+ "@redocly/cli": "^1.34.3",
41
+ "@types/node": "^22.15.15",
41
42
  "prettier": "^3.5.3",
43
+ "tsx": "^4.19.4",
44
+ "typescript": "^5.8.3",
42
45
  "vitest": "^3.1.1"
43
46
  },
44
47
  "repository": {
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import AuthManager from './auth.js';
2
4
 
3
- export function registerAuthTools(server, authManager) {
5
+ export function registerAuthTools(server: McpServer, authManager: AuthManager): void {
4
6
  server.tool(
5
7
  'login',
6
8
  {
@@ -16,7 +18,7 @@ export function registerAuthTools(server, authManager) {
16
18
  {
17
19
  type: 'text',
18
20
  text: JSON.stringify({
19
- message: 'Already logged in',
21
+ status: 'Already logged in',
20
22
  ...loginStatus,
21
23
  }),
22
24
  },
@@ -25,7 +27,7 @@ export function registerAuthTools(server, authManager) {
25
27
  }
26
28
  }
27
29
 
28
- const text = await new Promise((r) => {
30
+ const text = await new Promise<string>((r) => {
29
31
  authManager.acquireTokenByDeviceCode(r);
30
32
  });
31
33
  return {
@@ -41,7 +43,7 @@ export function registerAuthTools(server, authManager) {
41
43
  content: [
42
44
  {
43
45
  type: 'text',
44
- text: JSON.stringify({ error: `Authentication failed: ${error.message}` }),
46
+ text: JSON.stringify({ error: `Authentication failed: ${(error as Error).message}` }),
45
47
  },
46
48
  ],
47
49
  };
@@ -72,7 +74,7 @@ export function registerAuthTools(server, authManager) {
72
74
  }
73
75
  });
74
76
 
75
- server.tool('verify-login', {}, async () => {
77
+ server.tool('verify-login', async () => {
76
78
  const testResult = await authManager.testLogin();
77
79
 
78
80
  return {