ms365-mcp-server 1.1.24 → 2.1.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.
@@ -0,0 +1,195 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { logger } from './api.js';
5
+ // Configuration directory
6
+ const CONFIG_DIR = path.join(os.homedir(), '.outlook-mcp');
7
+ const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
8
+ const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json');
9
+ const MSAL_CACHE_FILE = path.join(CONFIG_DIR, 'msal-cache.json');
10
+ // File permissions
11
+ const FILE_MODE = 0o600; // Owner read/write only
12
+ const DIR_MODE = 0o700; // Owner read/write/execute only
13
+ /**
14
+ * Credential store for Outlook MCP server
15
+ * Uses ~/.outlook-mcp/ for secure storage
16
+ */
17
+ export class OutlookCredentialStore {
18
+ constructor() {
19
+ this.ensureConfigDir();
20
+ }
21
+ /**
22
+ * Ensure configuration directory exists with proper permissions
23
+ */
24
+ ensureConfigDir() {
25
+ try {
26
+ if (!fs.existsSync(CONFIG_DIR)) {
27
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: DIR_MODE });
28
+ logger.log(`Created config directory: ${CONFIG_DIR}`);
29
+ }
30
+ }
31
+ catch (error) {
32
+ logger.error('Failed to create config directory:', error);
33
+ }
34
+ }
35
+ /**
36
+ * Get the config directory path
37
+ */
38
+ getConfigDir() {
39
+ return CONFIG_DIR;
40
+ }
41
+ /**
42
+ * Save credentials to file with secure permissions
43
+ */
44
+ async saveCredentials(credentials) {
45
+ try {
46
+ this.ensureConfigDir();
47
+ const data = JSON.stringify(credentials, null, 2);
48
+ fs.writeFileSync(CREDENTIALS_FILE, data, { mode: FILE_MODE });
49
+ logger.log('Saved credentials to file');
50
+ }
51
+ catch (error) {
52
+ logger.error('Failed to save credentials:', error);
53
+ throw new Error('Failed to save credentials');
54
+ }
55
+ }
56
+ /**
57
+ * Get stored credentials
58
+ */
59
+ async getCredentials() {
60
+ try {
61
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
62
+ return null;
63
+ }
64
+ const data = fs.readFileSync(CREDENTIALS_FILE, 'utf8');
65
+ return JSON.parse(data);
66
+ }
67
+ catch (error) {
68
+ logger.error('Failed to read credentials:', error);
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Save tokens to file with secure permissions
74
+ */
75
+ async saveTokens(tokens) {
76
+ try {
77
+ this.ensureConfigDir();
78
+ const data = JSON.stringify(tokens, null, 2);
79
+ fs.writeFileSync(TOKEN_FILE, data, { mode: FILE_MODE });
80
+ logger.log(`Saved tokens (expires: ${new Date(tokens.expiresOn).toLocaleString()})`);
81
+ }
82
+ catch (error) {
83
+ logger.error('Failed to save tokens:', error);
84
+ throw new Error('Failed to save tokens');
85
+ }
86
+ }
87
+ /**
88
+ * Get stored tokens
89
+ */
90
+ async getTokens() {
91
+ try {
92
+ if (!fs.existsSync(TOKEN_FILE)) {
93
+ return null;
94
+ }
95
+ const data = fs.readFileSync(TOKEN_FILE, 'utf8');
96
+ return JSON.parse(data);
97
+ }
98
+ catch (error) {
99
+ logger.error('Failed to read tokens:', error);
100
+ return null;
101
+ }
102
+ }
103
+ /**
104
+ * Save MSAL cache
105
+ */
106
+ async saveMsalCache(cacheData) {
107
+ try {
108
+ this.ensureConfigDir();
109
+ fs.writeFileSync(MSAL_CACHE_FILE, cacheData, { mode: FILE_MODE });
110
+ }
111
+ catch (error) {
112
+ logger.error('Failed to save MSAL cache:', error);
113
+ }
114
+ }
115
+ /**
116
+ * Get MSAL cache
117
+ */
118
+ async getMsalCache() {
119
+ try {
120
+ if (!fs.existsSync(MSAL_CACHE_FILE)) {
121
+ return null;
122
+ }
123
+ return fs.readFileSync(MSAL_CACHE_FILE, 'utf8');
124
+ }
125
+ catch (error) {
126
+ logger.error('Failed to read MSAL cache:', error);
127
+ return null;
128
+ }
129
+ }
130
+ /**
131
+ * Clear all stored data
132
+ */
133
+ async clearAll() {
134
+ try {
135
+ if (fs.existsSync(TOKEN_FILE)) {
136
+ fs.unlinkSync(TOKEN_FILE);
137
+ logger.log('Deleted token file');
138
+ }
139
+ if (fs.existsSync(CREDENTIALS_FILE)) {
140
+ fs.unlinkSync(CREDENTIALS_FILE);
141
+ logger.log('Deleted credentials file');
142
+ }
143
+ if (fs.existsSync(MSAL_CACHE_FILE)) {
144
+ fs.unlinkSync(MSAL_CACHE_FILE);
145
+ logger.log('Deleted MSAL cache file');
146
+ }
147
+ logger.log('Cleared all stored authentication data');
148
+ }
149
+ catch (error) {
150
+ logger.error('Failed to clear stored data:', error);
151
+ throw new Error('Failed to clear stored data');
152
+ }
153
+ }
154
+ /**
155
+ * Clear only tokens (keep credentials)
156
+ */
157
+ async clearTokens() {
158
+ try {
159
+ if (fs.existsSync(TOKEN_FILE)) {
160
+ fs.unlinkSync(TOKEN_FILE);
161
+ logger.log('Deleted token file');
162
+ }
163
+ if (fs.existsSync(MSAL_CACHE_FILE)) {
164
+ fs.unlinkSync(MSAL_CACHE_FILE);
165
+ logger.log('Deleted MSAL cache file');
166
+ }
167
+ }
168
+ catch (error) {
169
+ logger.error('Failed to clear tokens:', error);
170
+ }
171
+ }
172
+ /**
173
+ * Check if tokens exist
174
+ */
175
+ hasTokens() {
176
+ return fs.existsSync(TOKEN_FILE);
177
+ }
178
+ /**
179
+ * Check if credentials exist
180
+ */
181
+ hasCredentials() {
182
+ return fs.existsSync(CREDENTIALS_FILE);
183
+ }
184
+ /**
185
+ * Get storage location info
186
+ */
187
+ getStorageInfo() {
188
+ return {
189
+ directory: CONFIG_DIR,
190
+ hasTokens: this.hasTokens(),
191
+ hasCredentials: this.hasCredentials()
192
+ };
193
+ }
194
+ }
195
+ export const outlookCredentialStore = new OutlookCredentialStore();
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "ms365-mcp-server",
3
- "version": "1.1.24",
3
+ "version": "2.1.0",
4
4
  "description": "Microsoft 365 MCP Server for managing Microsoft 365 email through natural language interactions with full OAuth2 authentication support",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "ms365-mcp-server": "bin/cli.js"
9
9
  },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/syia-ai/ms365-mcp-server"
13
+ },
14
+ "publishConfig": {
15
+ "registry": "https://registry.npmjs.org"
16
+ },
10
17
  "scripts": {
11
18
  "test": "npx @modelcontextprotocol/inspector node dist/index.js",
12
19
  "build": "tsc",