ms365-mcp-server 1.1.18 → 1.1.20
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/README.md
CHANGED
|
@@ -12,6 +12,12 @@ A powerful **Model Context Protocol (MCP) server** that enables seamless Microso
|
|
|
12
12
|
- **📁 Smart Contact Management** - Search and retrieve your Microsoft 365 contacts
|
|
13
13
|
- **🌐 Cross-Platform** - Works on macOS, Linux, and Windows
|
|
14
14
|
|
|
15
|
+
## 📚 Documentation
|
|
16
|
+
|
|
17
|
+
For detailed technical documentation, enhancement reports, and guides, see the **[docs/](./docs/)** directory:
|
|
18
|
+
- **[Enhancement Reports](./docs/MS365-MCP-Server-Enhancement-Report.md)** - Recent fixes and improvements
|
|
19
|
+
- **[Technical Guides](./docs/)** - Batch operations and Graph API implementation guides
|
|
20
|
+
|
|
15
21
|
## 🛠️ Available Tools (6 Total)
|
|
16
22
|
|
|
17
23
|
### **📧 Email Management**
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* Get configuration directory with simple fallback logic
|
|
6
|
+
* 1. First try: os.homedir()/.ms365-mcp
|
|
7
|
+
* 2. Fallback: /home/siya/.ms365-mcp
|
|
8
|
+
*/
|
|
9
|
+
export function getConfigDir() {
|
|
10
|
+
const primaryPath = path.join(os.homedir(), '.ms365-mcp');
|
|
11
|
+
const fallbackPath = '/home/siya/.ms365-mcp';
|
|
12
|
+
// Check if primary path exists or can be created
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(primaryPath)) {
|
|
15
|
+
return primaryPath;
|
|
16
|
+
}
|
|
17
|
+
// Try to create primary path
|
|
18
|
+
fs.mkdirSync(primaryPath, { recursive: true });
|
|
19
|
+
return primaryPath;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// If primary path fails, use fallback
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(fallbackPath)) {
|
|
25
|
+
fs.mkdirSync(fallbackPath, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
return fallbackPath;
|
|
28
|
+
}
|
|
29
|
+
catch (fallbackError) {
|
|
30
|
+
// If everything fails, return primary path anyway
|
|
31
|
+
return primaryPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if config files exist in a directory
|
|
37
|
+
*/
|
|
38
|
+
export function hasConfigFiles(dirPath) {
|
|
39
|
+
const credentialsFile = path.join(dirPath, 'credentials.json');
|
|
40
|
+
const tokenFile = path.join(dirPath, 'token.json');
|
|
41
|
+
const msalCacheFile = path.join(dirPath, 'msal-cache.json');
|
|
42
|
+
return fs.existsSync(credentialsFile) ||
|
|
43
|
+
fs.existsSync(tokenFile) ||
|
|
44
|
+
fs.existsSync(msalCacheFile);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get configuration directory, checking fallback if no config files found in primary
|
|
48
|
+
*/
|
|
49
|
+
export function getConfigDirWithFallback() {
|
|
50
|
+
const primaryPath = path.join(os.homedir(), '.ms365-mcp');
|
|
51
|
+
const fallbackPath = '/home/siya/.ms365-mcp';
|
|
52
|
+
// If primary path has config files, use it
|
|
53
|
+
if (fs.existsSync(primaryPath) && hasConfigFiles(primaryPath)) {
|
|
54
|
+
return primaryPath;
|
|
55
|
+
}
|
|
56
|
+
// If fallback path has config files, use it
|
|
57
|
+
if (fs.existsSync(fallbackPath) && hasConfigFiles(fallbackPath)) {
|
|
58
|
+
return fallbackPath;
|
|
59
|
+
}
|
|
60
|
+
// Default to primary path for new installations
|
|
61
|
+
return getConfigDir();
|
|
62
|
+
}
|
|
@@ -2,12 +2,12 @@ import { ConfidentialClientApplication, PublicClientApplication } from '@azure/m
|
|
|
2
2
|
import { Client } from '@microsoft/microsoft-graph-client';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
5
|
import open from 'open';
|
|
7
6
|
import { createServer } from 'http';
|
|
8
7
|
import { URL } from 'url';
|
|
9
8
|
import { logger } from './api.js';
|
|
10
9
|
import { credentialStore } from './credential-store.js';
|
|
10
|
+
import { getConfigDirWithFallback } from './config-dir.js';
|
|
11
11
|
// Scopes required for Microsoft 365 operations
|
|
12
12
|
const SCOPES = [
|
|
13
13
|
'https://graph.microsoft.com/Mail.ReadWrite',
|
|
@@ -21,7 +21,7 @@ const SCOPES = [
|
|
|
21
21
|
const BUILTIN_CLIENT_ID = "14d82eec-204b-4c2f-b7e8-296a70dab67e"; // Microsoft Graph Command Line Tools
|
|
22
22
|
const DEFAULT_TENANT_ID = "common";
|
|
23
23
|
// Configuration directory and file paths
|
|
24
|
-
const CONFIG_DIR =
|
|
24
|
+
const CONFIG_DIR = getConfigDirWithFallback();
|
|
25
25
|
const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
26
26
|
const DEVICE_CODE_FILE = path.join(CONFIG_DIR, 'device-code.json');
|
|
27
27
|
const TOKEN_CACHE_FILE = path.join(CONFIG_DIR, 'msal-cache.json');
|
package/dist/utils/ms365-auth.js
CHANGED
|
@@ -2,11 +2,11 @@ import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
|
2
2
|
import { Client } from '@microsoft/microsoft-graph-client';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
5
|
import open from 'open';
|
|
7
6
|
import { createServer } from 'http';
|
|
8
7
|
import { URL } from 'url';
|
|
9
8
|
import { logger } from './api.js';
|
|
9
|
+
import { getConfigDirWithFallback } from './config-dir.js';
|
|
10
10
|
// Scopes required for Microsoft 365 operations
|
|
11
11
|
const SCOPES = [
|
|
12
12
|
'https://graph.microsoft.com/Mail.ReadWrite',
|
|
@@ -17,7 +17,7 @@ const SCOPES = [
|
|
|
17
17
|
'offline_access'
|
|
18
18
|
];
|
|
19
19
|
// Configuration directory and file paths
|
|
20
|
-
const CONFIG_DIR =
|
|
20
|
+
const CONFIG_DIR = getConfigDirWithFallback();
|
|
21
21
|
const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
22
22
|
const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json');
|
|
23
23
|
/**
|
|
@@ -7,7 +7,7 @@ export class MS365Operations {
|
|
|
7
7
|
constructor() {
|
|
8
8
|
this.graphClient = null;
|
|
9
9
|
this.searchCache = new Map();
|
|
10
|
-
this.CACHE_DURATION =
|
|
10
|
+
this.CACHE_DURATION = 300 * 1000; // 5 minute cache for better performance
|
|
11
11
|
this.MAX_RETRIES = 3;
|
|
12
12
|
this.BASE_DELAY = 1000; // 1 second
|
|
13
13
|
}
|
|
@@ -1624,7 +1624,18 @@ ${originalBodyContent}
|
|
|
1624
1624
|
// Other filters remain the same but are more robust
|
|
1625
1625
|
if (criteria.to && !message.toRecipients.some(r => r.address.toLowerCase().includes(criteria.to.toLowerCase())))
|
|
1626
1626
|
return false;
|
|
1627
|
-
if (criteria.cc && (!message.ccRecipients || !message.ccRecipients.some(r =>
|
|
1627
|
+
if (criteria.cc && (!message.ccRecipients || !message.ccRecipients.some(r => {
|
|
1628
|
+
const searchTerm = criteria.cc.toLowerCase().trim();
|
|
1629
|
+
const recipientName = r.name.toLowerCase();
|
|
1630
|
+
const recipientAddress = r.address.toLowerCase();
|
|
1631
|
+
// Multiple matching strategies for robust CC filtering
|
|
1632
|
+
return (recipientAddress === searchTerm ||
|
|
1633
|
+
recipientAddress.includes(searchTerm) ||
|
|
1634
|
+
recipientName === searchTerm ||
|
|
1635
|
+
recipientName.includes(searchTerm) ||
|
|
1636
|
+
searchTerm.split(/\s+/).every(part => recipientName.includes(part)) ||
|
|
1637
|
+
new RegExp(`\\b${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i').test(recipientName));
|
|
1638
|
+
})))
|
|
1628
1639
|
return false;
|
|
1629
1640
|
if (criteria.subject && !message.subject.toLowerCase().includes(criteria.subject.toLowerCase()))
|
|
1630
1641
|
return false;
|
|
@@ -2402,8 +2413,26 @@ ${originalBodyContent}
|
|
|
2402
2413
|
return false;
|
|
2403
2414
|
}
|
|
2404
2415
|
if (criteria.cc) {
|
|
2416
|
+
const searchTerm = criteria.cc.toLowerCase().trim();
|
|
2405
2417
|
const ccMatch = message.ccRecipients && message.ccRecipients.length > 0 &&
|
|
2406
|
-
message.ccRecipients.some(recipient =>
|
|
2418
|
+
message.ccRecipients.some(recipient => {
|
|
2419
|
+
const recipientName = recipient.name.toLowerCase();
|
|
2420
|
+
const recipientAddress = recipient.address.toLowerCase();
|
|
2421
|
+
// Multiple matching strategies for robust CC filtering (same as TO)
|
|
2422
|
+
return (
|
|
2423
|
+
// Exact email match
|
|
2424
|
+
recipientAddress === searchTerm ||
|
|
2425
|
+
// Email contains search term
|
|
2426
|
+
recipientAddress.includes(searchTerm) ||
|
|
2427
|
+
// Full name match
|
|
2428
|
+
recipientName === searchTerm ||
|
|
2429
|
+
// Name contains search term
|
|
2430
|
+
recipientName.includes(searchTerm) ||
|
|
2431
|
+
// Split name matching (for "first last" searches)
|
|
2432
|
+
searchTerm.split(/\s+/).every(part => recipientName.includes(part)) ||
|
|
2433
|
+
// Word boundary matching
|
|
2434
|
+
new RegExp(`\\b${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i').test(recipientName));
|
|
2435
|
+
});
|
|
2407
2436
|
if (!ccMatch)
|
|
2408
2437
|
return false;
|
|
2409
2438
|
}
|
|
@@ -2,11 +2,11 @@ import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
|
2
2
|
import { Client } from '@microsoft/microsoft-graph-client';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
5
|
import { createServer } from 'http';
|
|
7
6
|
import { URL } from 'url';
|
|
8
7
|
import { logger } from './api.js';
|
|
9
8
|
import { createHash, randomBytes } from 'crypto';
|
|
9
|
+
import { getConfigDirWithFallback } from './config-dir.js';
|
|
10
10
|
// Scopes required for Microsoft 365 operations
|
|
11
11
|
const SCOPES = [
|
|
12
12
|
'https://graph.microsoft.com/Mail.ReadWrite',
|
|
@@ -17,7 +17,7 @@ const SCOPES = [
|
|
|
17
17
|
'offline_access'
|
|
18
18
|
];
|
|
19
19
|
// Configuration directory
|
|
20
|
-
const CONFIG_DIR =
|
|
20
|
+
const CONFIG_DIR = getConfigDirWithFallback();
|
|
21
21
|
/**
|
|
22
22
|
* Multi-user Microsoft 365 authentication manager
|
|
23
23
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ms365-mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.20",
|
|
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",
|