msteams-mcp 0.26.0 → 0.27.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.
- package/README.md +1 -1
- package/dist/auth/session-store.d.ts +5 -0
- package/dist/auth/session-store.js +29 -2
- package/dist/auth/session-store.test.d.ts +5 -0
- package/dist/auth/session-store.test.js +69 -0
- package/dist/cli.js +0 -0
- package/dist/index.js +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Teams MCP Server
|
|
1
|
+
# Teams MCP Server & CLI
|
|
2
2
|
|
|
3
3
|
[](https://github.com/m0nkmaster/msteams-mcp/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -83,5 +83,10 @@ export declare function clearTokenCache(): void;
|
|
|
83
83
|
/**
|
|
84
84
|
* Gets the Teams origin from session state.
|
|
85
85
|
* Checks multiple known Teams domains to support government clouds.
|
|
86
|
+
*
|
|
87
|
+
* Prefers an origin that has a SubstrateSearch token so Outlook/email and
|
|
88
|
+
* Substrate search keep working when both teams.cloud.microsoft and
|
|
89
|
+
* teams.microsoft.com are present in the saved session (common after the
|
|
90
|
+
* New Teams host migration).
|
|
86
91
|
*/
|
|
87
92
|
export declare function getTeamsOrigin(state: SessionState): SessionState['origins'][number] | null;
|
|
@@ -208,18 +208,45 @@ export function clearTokenCache() {
|
|
|
208
208
|
* Used to find the correct origin in session state.
|
|
209
209
|
*/
|
|
210
210
|
const TEAMS_ORIGINS = [
|
|
211
|
-
'https://teams.microsoft
|
|
211
|
+
'https://teams.cloud.microsoft', // New Teams URL (often holds SubstrateSearch tokens)
|
|
212
|
+
'https://teams.microsoft.com', // Commercial legacy
|
|
212
213
|
'https://teams.microsoft.us', // GCC-High
|
|
213
214
|
'https://dod.teams.microsoft.us', // DoD
|
|
214
|
-
'https://teams.cloud.microsoft', // New Teams URL
|
|
215
215
|
];
|
|
216
|
+
/**
|
|
217
|
+
* Returns true if localStorage has a SubstrateSearch MSAL token entry.
|
|
218
|
+
*/
|
|
219
|
+
function hasSubstrateSearchToken(localStorage) {
|
|
220
|
+
for (const item of localStorage ?? []) {
|
|
221
|
+
try {
|
|
222
|
+
const entry = JSON.parse(item.value);
|
|
223
|
+
if (entry.target?.includes('SubstrateSearch') &&
|
|
224
|
+
typeof entry.secret === 'string' &&
|
|
225
|
+
entry.secret.startsWith('ey')) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// ignore non-JSON localStorage values
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
216
235
|
/**
|
|
217
236
|
* Gets the Teams origin from session state.
|
|
218
237
|
* Checks multiple known Teams domains to support government clouds.
|
|
238
|
+
*
|
|
239
|
+
* Prefers an origin that has a SubstrateSearch token so Outlook/email and
|
|
240
|
+
* Substrate search keep working when both teams.cloud.microsoft and
|
|
241
|
+
* teams.microsoft.com are present in the saved session (common after the
|
|
242
|
+
* New Teams host migration).
|
|
219
243
|
*/
|
|
220
244
|
export function getTeamsOrigin(state) {
|
|
221
245
|
if (!state.origins)
|
|
222
246
|
return null;
|
|
247
|
+
const withSubstrate = state.origins.find(o => hasSubstrateSearchToken(o.localStorage));
|
|
248
|
+
if (withSubstrate)
|
|
249
|
+
return withSubstrate;
|
|
223
250
|
// Try known Teams origins in priority order
|
|
224
251
|
for (const knownOrigin of TEAMS_ORIGINS) {
|
|
225
252
|
const origin = state.origins.find(o => o.origin === knownOrigin);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for getTeamsOrigin — especially New Teams (teams.cloud.microsoft)
|
|
3
|
+
* SubstrateSearch token selection when multiple origins are present.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { getTeamsOrigin } from './session-store.js';
|
|
7
|
+
function makeOrigin(origin, localStorage = []) {
|
|
8
|
+
return { origin, localStorage };
|
|
9
|
+
}
|
|
10
|
+
function makeSubstrateEntry(target = 'https://substrate.office.com/SubstrateSearch-Internal.ReadWrite') {
|
|
11
|
+
return {
|
|
12
|
+
name: 'msal-substrate-token',
|
|
13
|
+
value: JSON.stringify({
|
|
14
|
+
credentialType: 'AccessToken',
|
|
15
|
+
target,
|
|
16
|
+
// JWT-shaped secret (header.payload.sig) — extractor only checks prefix
|
|
17
|
+
secret: 'eyJhbGciOiJub25lIn0.eyJzdWIiOiJ0ZXN0In0.sig',
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function makeChatSvcEntry() {
|
|
22
|
+
return {
|
|
23
|
+
name: 'msal-chatsvc-token',
|
|
24
|
+
value: JSON.stringify({
|
|
25
|
+
credentialType: 'AccessToken',
|
|
26
|
+
target: 'https://chatsvcagg.teams.microsoft.com/.default',
|
|
27
|
+
secret: 'eyJhbGciOiJub25lIn0.eyJzdWIiOiJjaGF0In0.sig',
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
describe('getTeamsOrigin', () => {
|
|
32
|
+
it('prefers the origin that holds a SubstrateSearch token', () => {
|
|
33
|
+
const state = {
|
|
34
|
+
cookies: [],
|
|
35
|
+
origins: [
|
|
36
|
+
makeOrigin('https://teams.microsoft.com', [makeChatSvcEntry()]),
|
|
37
|
+
makeOrigin('https://teams.cloud.microsoft', [makeSubstrateEntry()]),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
const chosen = getTeamsOrigin(state);
|
|
41
|
+
expect(chosen?.origin).toBe('https://teams.cloud.microsoft');
|
|
42
|
+
});
|
|
43
|
+
it('falls back to teams.cloud.microsoft when no Substrate token exists', () => {
|
|
44
|
+
const state = {
|
|
45
|
+
cookies: [],
|
|
46
|
+
origins: [
|
|
47
|
+
makeOrigin('https://teams.microsoft.com', [makeChatSvcEntry()]),
|
|
48
|
+
makeOrigin('https://teams.cloud.microsoft', [makeChatSvcEntry()]),
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
const chosen = getTeamsOrigin(state);
|
|
52
|
+
expect(chosen?.origin).toBe('https://teams.cloud.microsoft');
|
|
53
|
+
});
|
|
54
|
+
it('returns teams.microsoft.com when it is the only known origin', () => {
|
|
55
|
+
const state = {
|
|
56
|
+
cookies: [],
|
|
57
|
+
origins: [makeOrigin('https://teams.microsoft.com', [makeChatSvcEntry()])],
|
|
58
|
+
};
|
|
59
|
+
const chosen = getTeamsOrigin(state);
|
|
60
|
+
expect(chosen?.origin).toBe('https://teams.microsoft.com');
|
|
61
|
+
});
|
|
62
|
+
it('returns null when no Teams origins are present', () => {
|
|
63
|
+
const state = {
|
|
64
|
+
cookies: [],
|
|
65
|
+
origins: [makeOrigin('https://login.microsoftonline.com', [])],
|
|
66
|
+
};
|
|
67
|
+
expect(getTeamsOrigin(state)).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|