@softeria/ms-365-mcp-server 0.1.9 → 0.1.11

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.
@@ -0,0 +1,31 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ node-version: [ 18.x, 20.x ]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Use Node.js ${{ matrix.node-version }}
22
+ uses: actions/setup-node@v4
23
+ with:
24
+ node-version: ${{ matrix.node-version }}
25
+ cache: 'npm'
26
+
27
+ - name: Install dependencies
28
+ run: npm ci
29
+
30
+ - name: Run tests
31
+ run: npm test
@@ -1,6 +1,3 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3
-
4
1
  name: Node.js Package
5
2
 
6
3
  on:
package/README.md CHANGED
@@ -4,51 +4,157 @@ Microsoft 365 MCP Server
4
4
 
5
5
  A Model Context Protocol (MCP) server for interacting with Microsoft 365 services through the Graph API.
6
6
 
7
- [![Test Status](https://img.shields.io/badge/tests-passing-brightgreen)]()
7
+ [![Build Status](https://github.com/softeria-eu/ms-365-mcp-server/actions/workflows/build.yml/badge.svg)](https://github.com/softeria-eu/ms-365-mcp-server/actions/workflows/build.yml)
8
+ [![npm version](https://img.shields.io/npm/v/@softeria/ms-365-mcp-server.svg)](https://www.npmjs.com/package/@softeria/ms-365-mcp-server)
8
9
 
9
10
  ## Features
10
11
 
11
12
  - Authentication using Microsoft Authentication Library (MSAL)
12
13
  - Excel file operations:
13
- - Update cell values
14
- - Create and manage charts
15
- - Format cells
16
- - Sort data
17
- - Create tables
18
- - Read cell values
19
- - List worksheets
14
+ - Update cell values
15
+ - Create and manage charts
16
+ - Format cells
17
+ - Sort data
18
+ - Create tables
19
+ - Read cell values
20
+ - List worksheets
20
21
  - Built on the Model Context Protocol
21
22
 
22
23
  ## Installation
23
24
 
24
25
  ```bash
25
- npm install -g ms-365-mcp-server
26
+ npx @softeria/ms-365-mcp-server
26
27
  ```
27
28
 
28
- Or use directly with npx:
29
+ ## Integration with Claude
30
+
31
+ ### Claude Code CLI
32
+
33
+ To add this MCP server to Claude Code CLI:
34
+
35
+ ```bash
36
+ claude mcp add ms -- npx @softeria/ms-365-mcp-server
37
+ ```
38
+
39
+ ### Claude Desktop
40
+
41
+ To add this MCP server to Claude Desktop:
42
+
43
+ 1. Launch Claude Desktop
44
+ 2. Go to Settings > MCPs
45
+ 3. Click "Add MCP"
46
+ 4. Set the following configuration:
47
+ - Name: `ms` (or any name you prefer)
48
+ - Command: `npx @softeria/ms-365-mcp-server`
49
+ - Click "Add"
50
+
51
+ ### Direct Configuration
52
+
53
+ You can also use this configuration JSON in compatible Claude interfaces:
54
+
55
+ ```json
56
+ {
57
+ "name": "ms",
58
+ "command": "npx @softeria/ms-365-mcp-server"
59
+ }
60
+ ```
61
+
62
+ ## Development
63
+
64
+ ### Setup
65
+
66
+ ```bash
67
+ # Clone the repository
68
+ git clone https://github.com/softeria-eu/ms-365-mcp-server.git
69
+ cd ms-365-mcp-server
70
+
71
+ # Install dependencies
72
+ npm install
73
+
74
+ # Run tests
75
+ npm test
76
+ ```
77
+
78
+ ### GitHub Actions
79
+
80
+ This repository uses GitHub Actions for continuous integration and deployment:
81
+
82
+ - **Build Workflow**: Runs on all pushes to main and pull requests. Verifies the project builds successfully and passes
83
+ all tests.
84
+ - **Publish Workflow**: Automatically publishes to npm when a new GitHub release is created.
85
+
86
+ ### Release Process
87
+
88
+ To create a new release:
29
89
 
30
90
  ```bash
31
- npx ms-365-mcp-server
91
+ npm run release
32
92
  ```
33
93
 
94
+ This script will:
95
+
96
+ 1. Run tests to verify everything works
97
+ 2. Bump the version number
98
+ 3. Commit the version changes
99
+ 4. Push to GitHub
100
+ 5. Create a GitHub release
101
+ 6. Trigger the publish workflow to publish to npm
102
+
34
103
  ## Usage
35
104
 
36
105
  ### Command Line Options
37
106
 
38
107
  ```bash
39
- npx ms-365-mcp-server [options]
108
+ npx @softeria/ms-365-mcp-server [options]
40
109
  ```
41
110
 
42
111
  Options:
43
112
 
44
- - `--login`: Force login using device code flow
113
+ - `--login`: Force login using device code flow and verify Graph API access
45
114
  - `--logout`: Log out and clear saved credentials
46
- - `--file <path>`: Excel file path to use (default: "/Livet.xlsx")
47
- - `--silent`: Run without informational messages to stderr
115
+ - `--test-login`: Test current authentication and verify Graph API access without starting the server
116
+ - `-v`: Enable verbose logging
48
117
 
49
118
  ### Authentication
50
119
 
51
- The first time you run the server, it will automatically initiate the device code flow authentication. You'll see instructions in the terminal about how to complete the authentication in your browser.
120
+ **Important:** You must authenticate before using the MCP server. There are two ways to authenticate:
121
+
122
+ 1. Running the server with the `--login` flag:
123
+ ```bash
124
+ npx @softeria/ms-365-mcp-server --login
125
+ ```
126
+ This will display the login URL and code in the terminal.
127
+
128
+ 2. When using Claude Code or other MCP clients, use the login tools:
129
+ - First use the `login` tool, which will return the login URL and code
130
+ - Visit the URL and enter the code in your browser
131
+ - Then use the `verify-login` tool to check if the login was successful
132
+
133
+ Both methods trigger the device code flow authentication, but they handle the UI interaction differently:
134
+
135
+ - CLI version displays the instructions directly in the terminal
136
+ - MCP tool version returns the instructions as data that can be shown in the client UI
137
+
138
+ You can verify your authentication status with the `--test-login` flag, which will check if your token can successfully
139
+ fetch user data from Microsoft Graph API:
140
+
141
+ ```bash
142
+ npx @softeria/ms-365-mcp-server --test-login
143
+ ```
144
+
145
+ Both `--login` and `--test-login` will return a JSON response that includes your basic user information from Microsoft
146
+ Graph API if authentication is successful:
147
+
148
+ ```json
149
+ {
150
+ "success": true,
151
+ "message": "Login successful",
152
+ "userData": {
153
+ "displayName": "Your Name",
154
+ "userPrincipalName": "your.email@example.com"
155
+ }
156
+ }
157
+ ```
52
158
 
53
159
  Authentication tokens are cached securely in your system's credential store with fallback to file storage if needed.
54
160
 
@@ -56,8 +162,10 @@ Authentication tokens are cached securely in your system's credential store with
56
162
 
57
163
  This server provides several MCP tools for interacting with Excel files:
58
164
 
59
- - `login`: Force a new login with Microsoft
165
+ - `login`: Start a new login process with Microsoft (returns login URL and code)
166
+ - `verify-login`: Check if login was completed successfully and verify Graph API access
60
167
  - `logout`: Log out of Microsoft and clear credentials
168
+ - `test-login`: Test current authentication status and verify Graph API access
61
169
  - `update-excel`: Update cell values in an Excel worksheet
62
170
  - `create-chart`: Create a chart in an Excel worksheet
63
171
  - `format-range`: Apply formatting to a range of cells
package/auth.mjs CHANGED
@@ -101,18 +101,24 @@ class AuthManager {
101
101
  throw new Error('No valid token found');
102
102
  }
103
103
 
104
- async acquireTokenByDeviceCode() {
104
+ async acquireTokenByDeviceCode(hack) {
105
105
  const deviceCodeRequest = {
106
106
  scopes: this.scopes,
107
107
  deviceCodeCallback: (response) => {
108
- // We need to show this message to the user in console
109
- console.log('\n' + response.message + '\n');
108
+ const text = ['\n', response.message, '\n'].join('');
109
+ if (hack) {
110
+ hack(text + 'After login run the "test login" command');
111
+ } else {
112
+ console.log(text);
113
+ }
110
114
  logger.info('Device code login initiated');
111
115
  },
112
116
  };
113
117
 
114
118
  try {
119
+ logger.info('Requesting device code...');
115
120
  const response = await this.msalApp.acquireTokenByDeviceCode(deviceCodeRequest);
121
+ logger.info('Device code login successful');
116
122
  this.accessToken = response.accessToken;
117
123
  this.tokenExpiry = new Date(response.expiresOn).getTime();
118
124
  await this.saveTokenCache();
@@ -123,6 +129,63 @@ class AuthManager {
123
129
  }
124
130
  }
125
131
 
132
+ async testLogin() {
133
+ try {
134
+ logger.info('Testing login...');
135
+ const token = await this.getToken();
136
+
137
+ if (!token) {
138
+ logger.error('Login test failed - no token received');
139
+ return {
140
+ success: false,
141
+ message: 'Login failed - no token received',
142
+ };
143
+ }
144
+
145
+ logger.info('Token retrieved successfully, testing Graph API access...');
146
+
147
+ try {
148
+ const response = await fetch('https://graph.microsoft.com/v1.0/me', {
149
+ headers: {
150
+ Authorization: `Bearer ${token}`,
151
+ },
152
+ });
153
+
154
+ if (response.ok) {
155
+ const userData = await response.json();
156
+ logger.info('Graph API user data fetch successful');
157
+ return {
158
+ success: true,
159
+ message: 'Login successful',
160
+ userData: {
161
+ displayName: userData.displayName,
162
+ userPrincipalName: userData.userPrincipalName,
163
+ },
164
+ };
165
+ } else {
166
+ const errorText = await response.text();
167
+ logger.error(`Graph API user data fetch failed: ${response.status} - ${errorText}`);
168
+ return {
169
+ success: false,
170
+ message: `Login successful but Graph API access failed: ${response.status}`,
171
+ };
172
+ }
173
+ } catch (graphError) {
174
+ logger.error(`Error fetching user data: ${graphError.message}`);
175
+ return {
176
+ success: false,
177
+ message: `Login successful but Graph API access failed: ${graphError.message}`,
178
+ };
179
+ }
180
+ } catch (error) {
181
+ logger.error(`Login test failed: ${error.message}`);
182
+ return {
183
+ success: false,
184
+ message: `Login failed: ${error.message}`,
185
+ };
186
+ }
187
+ }
188
+
126
189
  async logout() {
127
190
  try {
128
191
  const accounts = await this.msalApp.getTokenCache().getAllAccounts();
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process';
4
+ import fs from 'fs';
5
+
6
+ console.log('Running tests...');
7
+ try {
8
+ execSync('npm test', { stdio: 'inherit' });
9
+ } catch (error) {
10
+ console.error('Tests failed! Aborting release.');
11
+ process.exit(1);
12
+ }
13
+
14
+ console.log('Bumping version...');
15
+ execSync('npm version --no-git-tag-version patch');
16
+
17
+ const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
18
+ const version = packageJson.version;
19
+
20
+ console.log('Committing version change...');
21
+ execSync('git add package.json package-lock.json');
22
+ execSync(`git commit -m "Bump version to ${version}"`);
23
+
24
+ console.log('Pushing to remote...');
25
+ execSync('git push');
26
+
27
+ console.log(`Creating GitHub release for v${version}...`);
28
+ execSync(`gh release create v${version} --title 'v${version}' --notes 'Version ${version}'`, {
29
+ stdio: 'inherit',
30
+ });
31
+
32
+ console.log(`Release v${version} created successfully!`);
33
+ // GitHub Actions workflow will handle the npm publish automatically
package/index.mjs CHANGED
@@ -161,12 +161,14 @@ const server = new McpServer({
161
161
 
162
162
  server.tool('login', {}, async () => {
163
163
  try {
164
- await authManager.getToken(true);
164
+ const text = await new Promise((r) => {
165
+ authManager.acquireTokenByDeviceCode(r);
166
+ });
165
167
  return {
166
168
  content: [
167
169
  {
168
170
  type: 'text',
169
- text: JSON.stringify({ message: 'Authentication successful' }),
171
+ text,
170
172
  },
171
173
  ],
172
174
  };
@@ -175,7 +177,7 @@ server.tool('login', {}, async () => {
175
177
  content: [
176
178
  {
177
179
  type: 'text',
178
- text: JSON.stringify({ error: 'Authentication failed' }),
180
+ text: JSON.stringify({ error: `Authentication failed: ${error.message}` }),
179
181
  },
180
182
  ],
181
183
  };
@@ -205,6 +207,32 @@ server.tool('logout', {}, async () => {
205
207
  }
206
208
  });
207
209
 
210
+ server.tool('test-login', {}, async () => {
211
+ const result = await authManager.testLogin();
212
+ return {
213
+ content: [
214
+ {
215
+ type: 'text',
216
+ text: JSON.stringify(result),
217
+ },
218
+ ],
219
+ };
220
+ });
221
+
222
+ server.tool('verify-login', {}, async () => {
223
+ // Test the login after the user has completed the device code authentication
224
+ const testResult = await authManager.testLogin();
225
+
226
+ return {
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: JSON.stringify(testResult),
231
+ },
232
+ ],
233
+ };
234
+ });
235
+
208
236
  server.tool(
209
237
  'update-excel',
210
238
  {
@@ -480,28 +508,15 @@ async function main() {
480
508
 
481
509
  if (args.login) {
482
510
  await authManager.acquireTokenByDeviceCode();
483
- logger.info('Login completed, proceeding with session creation');
484
- process.exit();
511
+ logger.info('Login completed, testing connection with Graph API...');
512
+ const result = await authManager.testLogin();
513
+ console.log(JSON.stringify(result));
514
+ process.exit(0);
485
515
  }
486
516
 
487
517
  if (args.testLogin) {
488
- try {
489
- logger.info('Testing login...');
490
- const token = await authManager.getToken();
491
- if (token) {
492
- logger.info('Login test successful');
493
-
494
- console.log(JSON.stringify({ success: true, message: 'Login successful' }));
495
- } else {
496
- logger.error('Login test failed - no token received');
497
- console.log(
498
- JSON.stringify({ success: false, message: 'Login failed - no token received' })
499
- );
500
- }
501
- } catch (error) {
502
- logger.error(`Login test failed: ${error.message}`);
503
- console.log(JSON.stringify({ success: false, message: `Login failed: ${error.message}` }));
504
- }
518
+ const result = await authManager.testLogin();
519
+ console.log(JSON.stringify(result));
505
520
  process.exit(0);
506
521
  }
507
522
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Microsoft 365 MCP Server",
5
5
  "type": "module",
6
6
  "main": "index.mjs",