msteams-mcp 0.2.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.

Potentially problematic release.


This version of msteams-mcp might be problematic. Click here for more details.

Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/dist/__fixtures__/api-responses.d.ts +254 -0
  4. package/dist/__fixtures__/api-responses.js +245 -0
  5. package/dist/api/calendar-api.d.ts +66 -0
  6. package/dist/api/calendar-api.js +179 -0
  7. package/dist/api/chatsvc-api.d.ts +352 -0
  8. package/dist/api/chatsvc-api.js +1100 -0
  9. package/dist/api/csa-api.d.ts +64 -0
  10. package/dist/api/csa-api.js +200 -0
  11. package/dist/api/index.d.ts +7 -0
  12. package/dist/api/index.js +7 -0
  13. package/dist/api/substrate-api.d.ts +50 -0
  14. package/dist/api/substrate-api.js +305 -0
  15. package/dist/auth/crypto.d.ts +32 -0
  16. package/dist/auth/crypto.js +66 -0
  17. package/dist/auth/index.d.ts +7 -0
  18. package/dist/auth/index.js +7 -0
  19. package/dist/auth/session-store.d.ts +87 -0
  20. package/dist/auth/session-store.js +230 -0
  21. package/dist/auth/token-extractor.d.ts +185 -0
  22. package/dist/auth/token-extractor.js +674 -0
  23. package/dist/auth/token-refresh.d.ts +25 -0
  24. package/dist/auth/token-refresh.js +85 -0
  25. package/dist/browser/auth.d.ts +53 -0
  26. package/dist/browser/auth.js +603 -0
  27. package/dist/browser/context.d.ts +40 -0
  28. package/dist/browser/context.js +122 -0
  29. package/dist/constants.d.ts +104 -0
  30. package/dist/constants.js +195 -0
  31. package/dist/index.d.ts +8 -0
  32. package/dist/index.js +12 -0
  33. package/dist/research/auth-research.d.ts +10 -0
  34. package/dist/research/auth-research.js +175 -0
  35. package/dist/research/explore.d.ts +11 -0
  36. package/dist/research/explore.js +270 -0
  37. package/dist/research/search-research.d.ts +17 -0
  38. package/dist/research/search-research.js +317 -0
  39. package/dist/server.d.ts +66 -0
  40. package/dist/server.js +295 -0
  41. package/dist/test/debug-search.d.ts +10 -0
  42. package/dist/test/debug-search.js +147 -0
  43. package/dist/test/mcp-harness.d.ts +17 -0
  44. package/dist/test/mcp-harness.js +474 -0
  45. package/dist/tools/auth-tools.d.ts +26 -0
  46. package/dist/tools/auth-tools.js +191 -0
  47. package/dist/tools/index.d.ts +56 -0
  48. package/dist/tools/index.js +34 -0
  49. package/dist/tools/meeting-tools.d.ts +33 -0
  50. package/dist/tools/meeting-tools.js +64 -0
  51. package/dist/tools/message-tools.d.ts +269 -0
  52. package/dist/tools/message-tools.js +856 -0
  53. package/dist/tools/people-tools.d.ts +46 -0
  54. package/dist/tools/people-tools.js +112 -0
  55. package/dist/tools/registry.d.ts +23 -0
  56. package/dist/tools/registry.js +63 -0
  57. package/dist/tools/search-tools.d.ts +91 -0
  58. package/dist/tools/search-tools.js +222 -0
  59. package/dist/types/errors.d.ts +58 -0
  60. package/dist/types/errors.js +132 -0
  61. package/dist/types/result.d.ts +43 -0
  62. package/dist/types/result.js +51 -0
  63. package/dist/types/server.d.ts +27 -0
  64. package/dist/types/server.js +7 -0
  65. package/dist/types/teams.d.ts +85 -0
  66. package/dist/types/teams.js +4 -0
  67. package/dist/utils/api-config.d.ts +103 -0
  68. package/dist/utils/api-config.js +158 -0
  69. package/dist/utils/auth-guards.d.ts +67 -0
  70. package/dist/utils/auth-guards.js +147 -0
  71. package/dist/utils/http.d.ts +29 -0
  72. package/dist/utils/http.js +112 -0
  73. package/dist/utils/parsers.d.ts +247 -0
  74. package/dist/utils/parsers.js +731 -0
  75. package/dist/utils/parsers.test.d.ts +7 -0
  76. package/dist/utils/parsers.test.js +511 -0
  77. package/package.json +62 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Playwright browser context management.
3
+ * Creates and manages browser contexts with session persistence.
4
+ *
5
+ * Uses the system's installed Chrome or Edge browser rather than downloading
6
+ * Playwright's bundled Chromium. This significantly reduces install size.
7
+ */
8
+ import { chromium } from 'playwright';
9
+ import { ensureUserDataDir, hasSessionState, SESSION_STATE_PATH, isSessionLikelyExpired, writeSessionState, readSessionState, } from '../auth/session-store.js';
10
+ import { clearRegionCache } from '../utils/auth-guards.js';
11
+ const DEFAULT_OPTIONS = {
12
+ headless: true,
13
+ viewport: { width: 1280, height: 800 },
14
+ };
15
+ /**
16
+ * Determines the browser channel to use based on the platform.
17
+ * - Windows: Use Microsoft Edge (always installed on Windows 10+)
18
+ * - macOS/Linux: Use Chrome
19
+ *
20
+ * @returns The browser channel name for Playwright
21
+ */
22
+ function getBrowserChannel() {
23
+ return process.platform === 'win32' ? 'msedge' : 'chrome';
24
+ }
25
+ /**
26
+ * Creates a browser context with optional session state restoration.
27
+ *
28
+ * Uses the system's installed Chrome or Edge browser rather than downloading
29
+ * Playwright's bundled Chromium (~180MB savings).
30
+ *
31
+ * @param options - Browser configuration options
32
+ * @returns Browser manager with browser, context, and page
33
+ * @throws Error if system browser is not found (with helpful suggestions)
34
+ */
35
+ export async function createBrowserContext(options = {}) {
36
+ const opts = { ...DEFAULT_OPTIONS, ...options };
37
+ ensureUserDataDir();
38
+ const channel = getBrowserChannel();
39
+ let browser;
40
+ try {
41
+ browser = await chromium.launch({
42
+ headless: opts.headless,
43
+ channel,
44
+ });
45
+ }
46
+ catch (error) {
47
+ const browserName = channel === 'msedge' ? 'Microsoft Edge' : 'Google Chrome';
48
+ const installHint = channel === 'msedge'
49
+ ? 'Edge should be pre-installed on Windows. Try updating Windows or reinstalling Edge.'
50
+ : 'Install Chrome from https://www.google.com/chrome/ or run: npx playwright install chromium';
51
+ throw new Error(`Could not launch ${browserName}. ${installHint}\n\n` +
52
+ `Original error: ${error instanceof Error ? error.message : String(error)}`);
53
+ }
54
+ const hasSession = hasSessionState();
55
+ const sessionExpired = isSessionLikelyExpired();
56
+ // Restore session if we have one and it's not ancient
57
+ const shouldRestoreSession = hasSession && !sessionExpired;
58
+ let context;
59
+ if (shouldRestoreSession) {
60
+ try {
61
+ // Read the decrypted session state
62
+ const state = readSessionState();
63
+ if (state) {
64
+ // Create a temporary file for Playwright (it needs a file path)
65
+ // We write the decrypted state to a temp location
66
+ const tempPath = SESSION_STATE_PATH + '.tmp';
67
+ const fs = await import('fs');
68
+ fs.writeFileSync(tempPath, JSON.stringify(state), { mode: 0o600 });
69
+ try {
70
+ context = await browser.newContext({
71
+ storageState: tempPath,
72
+ viewport: opts.viewport,
73
+ });
74
+ }
75
+ finally {
76
+ // Clean up temp file
77
+ fs.unlinkSync(tempPath);
78
+ }
79
+ }
80
+ else {
81
+ throw new Error('Failed to read session state');
82
+ }
83
+ }
84
+ catch (error) {
85
+ console.warn('Failed to restore session state, starting fresh:', error);
86
+ context = await browser.newContext({
87
+ viewport: opts.viewport,
88
+ });
89
+ }
90
+ }
91
+ else {
92
+ context = await browser.newContext({
93
+ viewport: opts.viewport,
94
+ });
95
+ }
96
+ const page = await context.newPage();
97
+ return {
98
+ browser,
99
+ context,
100
+ page,
101
+ isNewSession: !shouldRestoreSession,
102
+ };
103
+ }
104
+ /**
105
+ * Saves the current browser context's session state.
106
+ */
107
+ export async function saveSessionState(context) {
108
+ const state = await context.storageState();
109
+ writeSessionState(state);
110
+ // Clear cached region config so new session values are picked up
111
+ clearRegionCache();
112
+ }
113
+ /**
114
+ * Closes the browser and optionally saves session state.
115
+ */
116
+ export async function closeBrowser(manager, saveSession = true) {
117
+ if (saveSession) {
118
+ await saveSessionState(manager.context);
119
+ }
120
+ await manager.context.close();
121
+ await manager.browser.close();
122
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Shared constants used across the codebase.
3
+ *
4
+ * Centralising these values makes the code more maintainable and
5
+ * allows for easier configuration changes.
6
+ */
7
+ /** Minimum content length to be considered valid (characters). */
8
+ export declare const MIN_CONTENT_LENGTH = 5;
9
+ /** Maximum length for config values in debug output (characters). */
10
+ export declare const MAX_DEBUG_CONFIG_VALUE_LENGTH = 5000;
11
+ /** Default page size for search results. */
12
+ export declare const DEFAULT_PAGE_SIZE = 25;
13
+ /** Maximum page size for search results. */
14
+ export declare const MAX_PAGE_SIZE = 100;
15
+ /** Default limit for thread messages. */
16
+ export declare const DEFAULT_THREAD_LIMIT = 50;
17
+ /** Maximum limit for thread messages. */
18
+ export declare const MAX_THREAD_LIMIT = 200;
19
+ /** Default limit for people search. */
20
+ export declare const DEFAULT_PEOPLE_LIMIT = 10;
21
+ /** Maximum limit for people search. */
22
+ export declare const MAX_PEOPLE_LIMIT = 50;
23
+ /** Default limit for frequent contacts. */
24
+ export declare const DEFAULT_CONTACTS_LIMIT = 50;
25
+ /** Maximum limit for frequent contacts. */
26
+ export declare const MAX_CONTACTS_LIMIT = 500;
27
+ /** Default limit for channel search. */
28
+ export declare const DEFAULT_CHANNEL_LIMIT = 10;
29
+ /** Maximum limit for channel search. */
30
+ export declare const MAX_CHANNEL_LIMIT = 50;
31
+ /** Default timeout for waiting for search results. */
32
+ export declare const SEARCH_RESULT_TIMEOUT_MS = 10000;
33
+ /** Default HTTP request timeout. */
34
+ export declare const HTTP_REQUEST_TIMEOUT_MS = 30000;
35
+ /** Short delay for UI interactions. */
36
+ export declare const UI_SHORT_DELAY_MS = 300;
37
+ /** Medium delay for UI state changes. */
38
+ export declare const UI_MEDIUM_DELAY_MS = 1000;
39
+ /** Long delay for API responses to settle. */
40
+ export declare const UI_LONG_DELAY_MS = 2000;
41
+ /** Authentication check interval. */
42
+ export declare const AUTH_CHECK_INTERVAL_MS = 2000;
43
+ /** Default login timeout (5 minutes). */
44
+ export declare const LOGIN_TIMEOUT_MS: number;
45
+ /** Pause after showing progress overlay step (ms). */
46
+ export declare const OVERLAY_STEP_PAUSE_MS = 1500;
47
+ /** Pause after showing final "All done" overlay (ms). */
48
+ export declare const OVERLAY_COMPLETE_PAUSE_MS = 2000;
49
+ /** Session expiry threshold in hours. */
50
+ export declare const SESSION_EXPIRY_HOURS = 12;
51
+ /** Default maximum retry attempts for HTTP requests. */
52
+ export declare const DEFAULT_MAX_RETRIES = 3;
53
+ /** Base delay for exponential backoff (milliseconds). */
54
+ export declare const RETRY_BASE_DELAY_MS = 1000;
55
+ /** Maximum delay between retries (milliseconds). */
56
+ export declare const RETRY_MAX_DELAY_MS = 10000;
57
+ /**
58
+ * Virtual Conversation IDs.
59
+ *
60
+ * These special IDs are used with the standard chatsvc messages endpoint
61
+ * (/users/ME/conversations/{id}/messages) to retrieve aggregated views
62
+ * across all conversations. See docs/API-REFERENCE.md for details.
63
+ */
64
+ /** Prefix for virtual conversation IDs (48:saved, 48:notifications, etc). */
65
+ export declare const VIRTUAL_CONVERSATION_PREFIX = "48:";
66
+ /** Self-chat (notes) conversation ID. */
67
+ export declare const SELF_CHAT_ID = "48:notes";
68
+ /** Activity feed (notifications) conversation ID. */
69
+ export declare const NOTIFICATIONS_ID = "48:notifications";
70
+ /** Saved messages virtual conversation ID. */
71
+ export declare const SAVED_MESSAGES_ID = "48:saved";
72
+ /** Followed threads virtual conversation ID. */
73
+ export declare const FOLLOWED_THREADS_ID = "48:threads";
74
+ /** Default limit for activity feed items. */
75
+ export declare const DEFAULT_ACTIVITY_LIMIT = 50;
76
+ /** Maximum limit for activity feed items. */
77
+ export declare const MAX_ACTIVITY_LIMIT = 200;
78
+ /** Maximum conversations to check when aggregating unread status. */
79
+ export declare const MAX_UNREAD_AGGREGATE_CHECK = 20;
80
+ /** Default number of days ahead to fetch meetings. */
81
+ export declare const DEFAULT_MEETING_DAYS_AHEAD = 7;
82
+ /** Default limit for meeting results. */
83
+ export declare const DEFAULT_MEETING_LIMIT = 50;
84
+ /** Maximum limit for meeting results. */
85
+ export declare const MAX_MEETING_LIMIT = 200;
86
+ /** Threshold for proactive token refresh (10 minutes before expiry). */
87
+ export declare const TOKEN_REFRESH_THRESHOLD_MS: number;
88
+ /** MRI type prefix for Teams/AAD users (type 8). */
89
+ export declare const MRI_TYPE_PREFIX = "8:";
90
+ /** Identity type prefix for organisation users. */
91
+ export declare const ORGID_PREFIX = "orgid:";
92
+ /** Full MRI prefix for organisation users (8:orgid:). */
93
+ export declare const MRI_ORGID_PREFIX = "8:orgid:";
94
+ /** Standard Teams emoji entry. */
95
+ export interface StandardEmoji {
96
+ key: string;
97
+ description: string;
98
+ category: 'reaction' | 'expression' | 'affection' | 'action' | 'animal' | 'object' | 'other';
99
+ }
100
+ /**
101
+ * Standard Teams emoji shortcuts (built-in, no API call needed).
102
+ * These are the common emojis available in Teams for reactions and messages.
103
+ */
104
+ export declare const STANDARD_EMOJIS: readonly StandardEmoji[];
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Shared constants used across the codebase.
3
+ *
4
+ * Centralising these values makes the code more maintainable and
5
+ * allows for easier configuration changes.
6
+ */
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // Content Thresholds
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ /** Minimum content length to be considered valid (characters). */
11
+ export const MIN_CONTENT_LENGTH = 5;
12
+ /** Maximum length for config values in debug output (characters). */
13
+ export const MAX_DEBUG_CONFIG_VALUE_LENGTH = 5000;
14
+ // ─────────────────────────────────────────────────────────────────────────────
15
+ // Pagination Defaults
16
+ // ─────────────────────────────────────────────────────────────────────────────
17
+ /** Default page size for search results. */
18
+ export const DEFAULT_PAGE_SIZE = 25;
19
+ /** Maximum page size for search results. */
20
+ export const MAX_PAGE_SIZE = 100;
21
+ /** Default limit for thread messages. */
22
+ export const DEFAULT_THREAD_LIMIT = 50;
23
+ /** Maximum limit for thread messages. */
24
+ export const MAX_THREAD_LIMIT = 200;
25
+ /** Default limit for people search. */
26
+ export const DEFAULT_PEOPLE_LIMIT = 10;
27
+ /** Maximum limit for people search. */
28
+ export const MAX_PEOPLE_LIMIT = 50;
29
+ /** Default limit for frequent contacts. */
30
+ export const DEFAULT_CONTACTS_LIMIT = 50;
31
+ /** Maximum limit for frequent contacts. */
32
+ export const MAX_CONTACTS_LIMIT = 500;
33
+ /** Default limit for channel search. */
34
+ export const DEFAULT_CHANNEL_LIMIT = 10;
35
+ /** Maximum limit for channel search. */
36
+ export const MAX_CHANNEL_LIMIT = 50;
37
+ // ─────────────────────────────────────────────────────────────────────────────
38
+ // Timeouts (milliseconds)
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+ /** Default timeout for waiting for search results. */
41
+ export const SEARCH_RESULT_TIMEOUT_MS = 10000;
42
+ /** Default HTTP request timeout. */
43
+ export const HTTP_REQUEST_TIMEOUT_MS = 30000;
44
+ /** Short delay for UI interactions. */
45
+ export const UI_SHORT_DELAY_MS = 300;
46
+ /** Medium delay for UI state changes. */
47
+ export const UI_MEDIUM_DELAY_MS = 1000;
48
+ /** Long delay for API responses to settle. */
49
+ export const UI_LONG_DELAY_MS = 2000;
50
+ /** Authentication check interval. */
51
+ export const AUTH_CHECK_INTERVAL_MS = 2000;
52
+ /** Default login timeout (5 minutes). */
53
+ export const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
54
+ /** Pause after showing progress overlay step (ms). */
55
+ export const OVERLAY_STEP_PAUSE_MS = 1500;
56
+ /** Pause after showing final "All done" overlay (ms). */
57
+ export const OVERLAY_COMPLETE_PAUSE_MS = 2000;
58
+ // ─────────────────────────────────────────────────────────────────────────────
59
+ // Session Management
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ /** Session expiry threshold in hours. */
62
+ export const SESSION_EXPIRY_HOURS = 12;
63
+ // ─────────────────────────────────────────────────────────────────────────────
64
+ // Retry Configuration
65
+ // ─────────────────────────────────────────────────────────────────────────────
66
+ /** Default maximum retry attempts for HTTP requests. */
67
+ export const DEFAULT_MAX_RETRIES = 3;
68
+ /** Base delay for exponential backoff (milliseconds). */
69
+ export const RETRY_BASE_DELAY_MS = 1000;
70
+ /** Maximum delay between retries (milliseconds). */
71
+ export const RETRY_MAX_DELAY_MS = 10000;
72
+ // ─────────────────────────────────────────────────────────────────────────────
73
+ // Conversation IDs
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+ /**
76
+ * Virtual Conversation IDs.
77
+ *
78
+ * These special IDs are used with the standard chatsvc messages endpoint
79
+ * (/users/ME/conversations/{id}/messages) to retrieve aggregated views
80
+ * across all conversations. See docs/API-REFERENCE.md for details.
81
+ */
82
+ /** Prefix for virtual conversation IDs (48:saved, 48:notifications, etc). */
83
+ export const VIRTUAL_CONVERSATION_PREFIX = '48:';
84
+ /** Self-chat (notes) conversation ID. */
85
+ export const SELF_CHAT_ID = '48:notes';
86
+ /** Activity feed (notifications) conversation ID. */
87
+ export const NOTIFICATIONS_ID = '48:notifications';
88
+ /** Saved messages virtual conversation ID. */
89
+ export const SAVED_MESSAGES_ID = '48:saved';
90
+ /** Followed threads virtual conversation ID. */
91
+ export const FOLLOWED_THREADS_ID = '48:threads';
92
+ // ─────────────────────────────────────────────────────────────────────────────
93
+ // Activity Feed
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+ /** Default limit for activity feed items. */
96
+ export const DEFAULT_ACTIVITY_LIMIT = 50;
97
+ /** Maximum limit for activity feed items. */
98
+ export const MAX_ACTIVITY_LIMIT = 200;
99
+ // ─────────────────────────────────────────────────────────────────────────────
100
+ // Unread Status
101
+ // ─────────────────────────────────────────────────────────────────────────────
102
+ /** Maximum conversations to check when aggregating unread status. */
103
+ export const MAX_UNREAD_AGGREGATE_CHECK = 20;
104
+ // ─────────────────────────────────────────────────────────────────────────────
105
+ // Calendar/Meetings
106
+ // ─────────────────────────────────────────────────────────────────────────────
107
+ /** Default number of days ahead to fetch meetings. */
108
+ export const DEFAULT_MEETING_DAYS_AHEAD = 7;
109
+ /** Default limit for meeting results. */
110
+ export const DEFAULT_MEETING_LIMIT = 50;
111
+ /** Maximum limit for meeting results. */
112
+ export const MAX_MEETING_LIMIT = 200;
113
+ // ─────────────────────────────────────────────────────────────────────────────
114
+ // Token Refresh
115
+ // ─────────────────────────────────────────────────────────────────────────────
116
+ /** Threshold for proactive token refresh (10 minutes before expiry). */
117
+ export const TOKEN_REFRESH_THRESHOLD_MS = 10 * 60 * 1000;
118
+ // ─────────────────────────────────────────────────────────────────────────────
119
+ // User Identity
120
+ // ─────────────────────────────────────────────────────────────────────────────
121
+ /** MRI type prefix for Teams/AAD users (type 8). */
122
+ export const MRI_TYPE_PREFIX = '8:';
123
+ /** Identity type prefix for organisation users. */
124
+ export const ORGID_PREFIX = 'orgid:';
125
+ /** Full MRI prefix for organisation users (8:orgid:). */
126
+ export const MRI_ORGID_PREFIX = `${MRI_TYPE_PREFIX}${ORGID_PREFIX}`;
127
+ /**
128
+ * Standard Teams emoji shortcuts (built-in, no API call needed).
129
+ * These are the common emojis available in Teams for reactions and messages.
130
+ */
131
+ export const STANDARD_EMOJIS = [
132
+ // Quick reactions (shown in reaction picker)
133
+ { key: 'like', description: 'Thumbs up 👍', category: 'reaction' },
134
+ { key: 'heart', description: 'Heart ❤️', category: 'reaction' },
135
+ { key: 'laugh', description: 'Laughing 😂', category: 'reaction' },
136
+ { key: 'surprised', description: 'Surprised 😮', category: 'reaction' },
137
+ { key: 'sad', description: 'Sad 😢', category: 'reaction' },
138
+ { key: 'angry', description: 'Angry 😠', category: 'reaction' },
139
+ // Expressions
140
+ { key: 'smile', description: 'Smiley 😊', category: 'expression' },
141
+ { key: 'wink', description: 'Winking 😉', category: 'expression' },
142
+ { key: 'cry', description: 'Crying 😭', category: 'expression' },
143
+ { key: 'cwl', description: 'Crying with laughter 😂', category: 'expression' },
144
+ { key: 'rofl', description: 'Rolling on floor laughing 🤣', category: 'expression' },
145
+ { key: 'blush', description: 'Blushing 😊', category: 'expression' },
146
+ { key: 'speechless', description: 'Speechless 😶', category: 'expression' },
147
+ { key: 'wonder', description: 'Wondering 🤔', category: 'expression' },
148
+ { key: 'sleepy', description: 'Sleepy 😴', category: 'expression' },
149
+ { key: 'yawn', description: 'Yawning 🥱', category: 'expression' },
150
+ { key: 'eyeroll', description: 'Eye roll 🙄', category: 'expression' },
151
+ { key: 'worry', description: 'Worried 😟', category: 'expression' },
152
+ { key: 'puke', description: 'Puking 🤮', category: 'expression' },
153
+ { key: 'giggle', description: 'Giggling 🤭', category: 'expression' },
154
+ { key: 'tongueout', description: 'Tongue out 😛', category: 'expression' },
155
+ // Affection
156
+ { key: 'kiss', description: 'Kiss 😘', category: 'affection' },
157
+ { key: 'inlove', description: 'In love 😍', category: 'affection' },
158
+ { key: 'hug', description: 'Hug 🤗', category: 'affection' },
159
+ { key: 'lips', description: 'Kissing lips 💋', category: 'affection' },
160
+ // Actions
161
+ { key: 'facepalm', description: 'Facepalm 🤦', category: 'action' },
162
+ { key: 'sweat', description: 'Sweating 😓', category: 'action' },
163
+ { key: 'dance', description: 'Dancing 💃', category: 'action' },
164
+ { key: 'bow', description: 'Bowing 🙇', category: 'action' },
165
+ { key: 'headbang', description: 'Banging head on wall', category: 'action' },
166
+ { key: 'wasntme', description: "It wasn't me 🤷", category: 'action' },
167
+ { key: 'hungover', description: 'Hungover', category: 'action' },
168
+ { key: 'shivering', description: 'Shivering 🥶', category: 'action' },
169
+ // Animals
170
+ { key: 'penguin', description: 'Penguin 🐧', category: 'animal' },
171
+ { key: 'cat', description: 'Cat 🐱', category: 'animal' },
172
+ { key: 'monkey', description: 'Monkey 🐵', category: 'animal' },
173
+ { key: 'polarbear', description: 'Polar bear 🐻‍❄️', category: 'animal' },
174
+ { key: 'elephant', description: 'Elephant 🐘', category: 'animal' },
175
+ // Objects
176
+ { key: 'flower', description: 'Flower 🌸', category: 'object' },
177
+ { key: 'sun', description: 'Sun ☀️', category: 'object' },
178
+ { key: 'star', description: 'Star ⭐', category: 'object' },
179
+ { key: 'xmastree', description: 'Christmas tree 🎄', category: 'object' },
180
+ { key: 'cake', description: 'Cake 🎂', category: 'object' },
181
+ { key: 'gift', description: 'Gift 🎁', category: 'object' },
182
+ { key: 'cash', description: 'Cash 💵', category: 'object' },
183
+ { key: 'champagne', description: 'Champagne 🍾', category: 'object' },
184
+ // Other
185
+ { key: 'yes', description: 'Yes/Thumbs up ✅', category: 'other' },
186
+ { key: 'cool', description: 'Cool 😎', category: 'other' },
187
+ { key: 'party', description: 'Party 🎉', category: 'other' },
188
+ { key: 'hi', description: 'Wave/Hello 👋', category: 'other' },
189
+ { key: 'angel', description: 'Angel 😇', category: 'other' },
190
+ { key: 'devil', description: 'Devil 😈', category: 'other' },
191
+ { key: 'holidayspirit', description: 'Holiday spirit 🎅', category: 'other' },
192
+ { key: 'lipssealed', description: 'Lips sealed 🤐', category: 'other' },
193
+ { key: 'makeup', description: 'Make-up 💄', category: 'other' },
194
+ { key: 'snowangel', description: 'Snow angel', category: 'other' },
195
+ ];
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Teams MCP Server entry point.
4
+ *
5
+ * This MCP server enables AI assistants to search Microsoft Teams
6
+ * messages using browser automation.
7
+ */
8
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Teams MCP Server entry point.
4
+ *
5
+ * This MCP server enables AI assistants to search Microsoft Teams
6
+ * messages using browser automation.
7
+ */
8
+ import { runServer } from './server.js';
9
+ runServer().catch((error) => {
10
+ console.error('Fatal error:', error);
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Research script to understand Teams authentication redirect behaviour.
3
+ *
4
+ * This script observes:
5
+ * 1. What happens when navigating to Teams WITH a valid session
6
+ * 2. What happens when navigating to Teams WITHOUT a session
7
+ *
8
+ * Goal: Understand the fastest way to detect if we're authenticated.
9
+ */
10
+ export {};
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Research script to understand Teams authentication redirect behaviour.
3
+ *
4
+ * This script observes:
5
+ * 1. What happens when navigating to Teams WITH a valid session
6
+ * 2. What happens when navigating to Teams WITHOUT a session
7
+ *
8
+ * Goal: Understand the fastest way to detect if we're authenticated.
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { hasSessionState } from '../auth/session-store.js';
12
+ const TEAMS_URL = 'https://teams.microsoft.com';
13
+ async function observeNavigation(page, scenario, startTime) {
14
+ const events = [];
15
+ const log = (type, url, extra) => {
16
+ const now = Date.now();
17
+ const event = {
18
+ timestamp: now,
19
+ elapsed: now - startTime,
20
+ type,
21
+ url,
22
+ ...extra,
23
+ };
24
+ events.push(event);
25
+ console.log(` [${event.elapsed}ms] ${type}: ${url}${extra?.status ? ` (${extra.status})` : ''}${extra?.note ? ` - ${extra.note}` : ''}`);
26
+ };
27
+ // Track all frame navigations
28
+ page.on('framenavigated', (frame) => {
29
+ if (frame === page.mainFrame()) {
30
+ const url = frame.url();
31
+ const isLogin = url.includes('login.microsoftonline.com') ||
32
+ url.includes('login.live.com') ||
33
+ url.includes('login.microsoft.com');
34
+ const isTeams = url.includes('teams.microsoft.com') ||
35
+ url.includes('teams.microsoft.us');
36
+ log('navigation', url, {
37
+ note: isLogin ? 'LOGIN PAGE' : isTeams ? 'TEAMS' : undefined
38
+ });
39
+ }
40
+ });
41
+ // Track responses (especially redirects)
42
+ page.on('response', (response) => {
43
+ const url = response.url();
44
+ const status = response.status();
45
+ // Only log main document responses and redirects
46
+ if (response.request().resourceType() === 'document' ||
47
+ (status >= 300 && status < 400)) {
48
+ log('response', url, {
49
+ status,
50
+ note: status >= 300 && status < 400 ? 'REDIRECT' : undefined
51
+ });
52
+ }
53
+ });
54
+ return events;
55
+ }
56
+ async function runScenario(browser, scenario, useSession) {
57
+ console.log(`\n${'='.repeat(60)}`);
58
+ console.log(`SCENARIO: ${scenario}`);
59
+ console.log(`Session: ${useSession ? 'WITH existing session' : 'NO session (cleared)'}`);
60
+ console.log('='.repeat(60));
61
+ let context;
62
+ if (useSession && hasSessionState()) {
63
+ console.log('Loading existing session state...');
64
+ // Handle encrypted session
65
+ try {
66
+ const { readSessionState } = await import('../auth/session-store.js');
67
+ const state = readSessionState();
68
+ if (state) {
69
+ context = await browser.newContext({
70
+ storageState: state,
71
+ viewport: { width: 1280, height: 800 },
72
+ });
73
+ }
74
+ else {
75
+ throw new Error('Could not read session');
76
+ }
77
+ }
78
+ catch {
79
+ console.log('Could not load session, using fresh context');
80
+ context = await browser.newContext({
81
+ viewport: { width: 1280, height: 800 },
82
+ });
83
+ }
84
+ }
85
+ else {
86
+ console.log('Using fresh context (no session)...');
87
+ context = await browser.newContext({
88
+ viewport: { width: 1280, height: 800 },
89
+ });
90
+ }
91
+ const page = await context.newPage();
92
+ const startTime = Date.now();
93
+ console.log(`\nNavigating to ${TEAMS_URL}...`);
94
+ console.log('Watching for navigations and redirects:\n');
95
+ const events = await observeNavigation(page, scenario, startTime);
96
+ try {
97
+ // Navigate and wait for initial load
98
+ await page.goto(TEAMS_URL, {
99
+ waitUntil: 'domcontentloaded',
100
+ timeout: 30000,
101
+ });
102
+ const afterGoto = Date.now();
103
+ console.log(`\n [${afterGoto - startTime}ms] goto() completed (domcontentloaded)`);
104
+ // Wait a bit more to see if any late redirects happen
105
+ console.log('\n Waiting 10 seconds to observe any delayed redirects...\n');
106
+ for (let i = 1; i <= 10; i++) {
107
+ await page.waitForTimeout(1000);
108
+ const currentUrl = page.url();
109
+ const isLogin = currentUrl.includes('login.microsoftonline.com') ||
110
+ currentUrl.includes('login.live.com') ||
111
+ currentUrl.includes('login.microsoft.com');
112
+ const isTeams = currentUrl.includes('teams.microsoft.com') ||
113
+ currentUrl.includes('teams.microsoft.us');
114
+ console.log(` [${Date.now() - startTime}ms] Check ${i}: ${isLogin ? 'ON LOGIN' : isTeams ? 'ON TEAMS' : 'OTHER'} - ${currentUrl.substring(0, 80)}...`);
115
+ // If we're on login page, we know we're not authenticated
116
+ if (isLogin) {
117
+ console.log('\n ✓ Detected redirect to login page - NOT authenticated');
118
+ break;
119
+ }
120
+ }
121
+ const finalUrl = page.url();
122
+ const duration = Date.now() - startTime;
123
+ console.log(`\n${'─'.repeat(60)}`);
124
+ console.log(`RESULT for "${scenario}":`);
125
+ console.log(` Final URL: ${finalUrl}`);
126
+ console.log(` Total duration: ${duration}ms`);
127
+ console.log(` Navigation events: ${events.length}`);
128
+ const isOnLogin = finalUrl.includes('login.microsoftonline.com') ||
129
+ finalUrl.includes('login.live.com') ||
130
+ finalUrl.includes('login.microsoft.com');
131
+ const isOnTeams = finalUrl.includes('teams.microsoft.com') ||
132
+ finalUrl.includes('teams.microsoft.us');
133
+ console.log(` Authenticated: ${isOnTeams && !isOnLogin ? 'YES (on Teams)' : 'NO (on login page)'}`);
134
+ return { events, finalUrl, duration };
135
+ }
136
+ finally {
137
+ await context.close();
138
+ }
139
+ }
140
+ async function main() {
141
+ console.log('🔬 Teams Authentication Research');
142
+ console.log('================================\n');
143
+ console.log('This script observes redirect behaviour during Teams navigation.\n');
144
+ const browser = await chromium.launch({
145
+ headless: false, // Visible so we can see what's happening
146
+ });
147
+ try {
148
+ // Scenario 1: With existing session (if available)
149
+ if (hasSessionState()) {
150
+ await runScenario(browser, 'WITH SESSION', true);
151
+ }
152
+ else {
153
+ console.log('\n⚠️ No existing session found. Run `npm run cli -- login` first to create one.');
154
+ console.log(' Skipping "WITH SESSION" scenario.\n');
155
+ }
156
+ // Scenario 2: Without session (cleared context)
157
+ await runScenario(browser, 'WITHOUT SESSION', false);
158
+ console.log('\n\n' + '='.repeat(60));
159
+ console.log('SUMMARY');
160
+ console.log('='.repeat(60));
161
+ console.log(`
162
+ Key observations to look for:
163
+ 1. How quickly does the redirect to login.microsoftonline.com happen?
164
+ 2. Does the redirect happen BEFORE or AFTER domcontentloaded?
165
+ 3. Are there any intermediate URLs?
166
+ 4. What HTTP status codes are used for redirects?
167
+
168
+ Based on this, we can tune LOGIN_REDIRECT_TIMEOUT_MS in auth.ts
169
+ `);
170
+ }
171
+ finally {
172
+ await browser.close();
173
+ }
174
+ }
175
+ main().catch(console.error);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Research script to explore Teams web app behaviour.
3
+ *
4
+ * This script:
5
+ * 1. Launches a browser with persistent context
6
+ * 2. Navigates to Teams and handles authentication
7
+ * 3. Monitors network requests to discover API endpoints
8
+ * 4. Allows manual interaction to trigger searches
9
+ * 5. Logs discovered endpoints and data structures
10
+ */
11
+ export {};