msteams-mcp 0.2.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.
Files changed (80) hide show
  1. package/README.md +229 -0
  2. package/dist/__fixtures__/api-responses.d.ts +228 -0
  3. package/dist/__fixtures__/api-responses.js +217 -0
  4. package/dist/api/chatsvc-api.d.ts +171 -0
  5. package/dist/api/chatsvc-api.js +459 -0
  6. package/dist/api/csa-api.d.ts +44 -0
  7. package/dist/api/csa-api.js +148 -0
  8. package/dist/api/index.d.ts +6 -0
  9. package/dist/api/index.js +6 -0
  10. package/dist/api/substrate-api.d.ts +50 -0
  11. package/dist/api/substrate-api.js +305 -0
  12. package/dist/auth/crypto.d.ts +32 -0
  13. package/dist/auth/crypto.js +66 -0
  14. package/dist/auth/index.d.ts +6 -0
  15. package/dist/auth/index.js +6 -0
  16. package/dist/auth/session-store.d.ts +82 -0
  17. package/dist/auth/session-store.js +136 -0
  18. package/dist/auth/token-extractor.d.ts +69 -0
  19. package/dist/auth/token-extractor.js +330 -0
  20. package/dist/browser/auth.d.ts +43 -0
  21. package/dist/browser/auth.js +232 -0
  22. package/dist/browser/context.d.ts +40 -0
  23. package/dist/browser/context.js +121 -0
  24. package/dist/browser/session.d.ts +34 -0
  25. package/dist/browser/session.js +92 -0
  26. package/dist/constants.d.ts +54 -0
  27. package/dist/constants.js +72 -0
  28. package/dist/index.d.ts +8 -0
  29. package/dist/index.js +12 -0
  30. package/dist/research/explore.d.ts +11 -0
  31. package/dist/research/explore.js +267 -0
  32. package/dist/research/search-research.d.ts +17 -0
  33. package/dist/research/search-research.js +317 -0
  34. package/dist/server.d.ts +64 -0
  35. package/dist/server.js +291 -0
  36. package/dist/teams/api-interceptor.d.ts +54 -0
  37. package/dist/teams/api-interceptor.js +391 -0
  38. package/dist/teams/direct-api.d.ts +321 -0
  39. package/dist/teams/direct-api.js +1305 -0
  40. package/dist/teams/messages.d.ts +14 -0
  41. package/dist/teams/messages.js +142 -0
  42. package/dist/teams/search.d.ts +40 -0
  43. package/dist/teams/search.js +458 -0
  44. package/dist/test/cli.d.ts +12 -0
  45. package/dist/test/cli.js +328 -0
  46. package/dist/test/debug-search.d.ts +10 -0
  47. package/dist/test/debug-search.js +147 -0
  48. package/dist/test/manual-test.d.ts +11 -0
  49. package/dist/test/manual-test.js +160 -0
  50. package/dist/test/mcp-harness.d.ts +17 -0
  51. package/dist/test/mcp-harness.js +427 -0
  52. package/dist/tools/auth-tools.d.ts +26 -0
  53. package/dist/tools/auth-tools.js +127 -0
  54. package/dist/tools/index.d.ts +45 -0
  55. package/dist/tools/index.js +12 -0
  56. package/dist/tools/message-tools.d.ts +139 -0
  57. package/dist/tools/message-tools.js +433 -0
  58. package/dist/tools/people-tools.d.ts +46 -0
  59. package/dist/tools/people-tools.js +123 -0
  60. package/dist/tools/registry.d.ts +23 -0
  61. package/dist/tools/registry.js +61 -0
  62. package/dist/tools/search-tools.d.ts +79 -0
  63. package/dist/tools/search-tools.js +168 -0
  64. package/dist/types/errors.d.ts +58 -0
  65. package/dist/types/errors.js +132 -0
  66. package/dist/types/result.d.ts +43 -0
  67. package/dist/types/result.js +51 -0
  68. package/dist/types/teams.d.ts +79 -0
  69. package/dist/types/teams.js +5 -0
  70. package/dist/utils/api-config.d.ts +66 -0
  71. package/dist/utils/api-config.js +113 -0
  72. package/dist/utils/auth-guards.d.ts +29 -0
  73. package/dist/utils/auth-guards.js +54 -0
  74. package/dist/utils/http.d.ts +29 -0
  75. package/dist/utils/http.js +111 -0
  76. package/dist/utils/parsers.d.ts +187 -0
  77. package/dist/utils/parsers.js +574 -0
  78. package/dist/utils/parsers.test.d.ts +7 -0
  79. package/dist/utils/parsers.test.js +360 -0
  80. package/package.json +58 -0
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # Teams MCP Server
2
+
3
+ An MCP (Model Context Protocol) server that enables AI assistants to interact with Microsoft Teams-search messages, send replies, manage favourites, and more.
4
+
5
+ ## How It Works
6
+
7
+ This server calls Microsoft's internal Teams APIs directly (Substrate, chatsvc, CSA)-the same APIs the Teams web app uses. No Azure AD app registration or admin consent required.
8
+
9
+ **Authentication flow:**
10
+ 1. Run `teams_login` to open a browser and log in
11
+ 2. OAuth tokens are extracted and cached
12
+ 3. All operations use cached tokens directly (no browser needed)
13
+ 4. When tokens expire (~1 hour), run `teams_login` again
14
+
15
+ ## Installation
16
+
17
+ ### Prerequisites
18
+
19
+ - Node.js 18+
20
+ - A Microsoft account with Teams access
21
+ - Google Chrome, Microsoft Edge, or Chromium browser installed
22
+
23
+ ### Configure Your MCP Client
24
+
25
+ Add to your MCP client configuration (e.g., Claude Desktop, Cursor):
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "teams": {
31
+ "command": "npx",
32
+ "args": ["-y", "msteams-mcp"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ That's it. The server uses your system's Chrome (macOS/Linux) or Edge (Windows) for authentication.
39
+
40
+ ### Manual Installation (optional)
41
+
42
+ If you prefer to install globally:
43
+
44
+ ```bash
45
+ npm install -g msteams-mcp
46
+ ```
47
+
48
+ Then configure:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "teams": {
54
+ "command": "msteams-mcp"
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Available Tools
61
+
62
+ ### Search & Discovery
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `teams_search` | Search messages with operators (`from:`, `sent:`, `in:`, `hasattachment:`, etc.) |
67
+ | `teams_get_thread` | Get messages from a conversation/thread |
68
+ | `teams_find_channel` | Find channels by name (your teams + org-wide discovery) |
69
+
70
+ ### Messaging
71
+
72
+ | Tool | Description |
73
+ |------|-------------|
74
+ | `teams_send_message` | Send a message (default: self-chat/notes) |
75
+ | `teams_reply_to_thread` | Reply to a channel message as a threaded reply |
76
+ | `teams_edit_message` | Edit one of your own messages |
77
+ | `teams_delete_message` | Delete one of your own messages (soft delete) |
78
+
79
+ ### People & Contacts
80
+
81
+ | Tool | Description |
82
+ |------|-------------|
83
+ | `teams_get_me` | Get current user profile (email, name, ID) |
84
+ | `teams_search_people` | Search for people by name or email |
85
+ | `teams_get_frequent_contacts` | Get frequently contacted people (useful for name resolution) |
86
+ | `teams_get_chat` | Get conversation ID for 1:1 chat with a person |
87
+
88
+ ### Organisation
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `teams_get_favorites` | Get pinned/favourite conversations |
93
+ | `teams_add_favorite` | Pin a conversation |
94
+ | `teams_remove_favorite` | Unpin a conversation |
95
+ | `teams_save_message` | Bookmark a message |
96
+ | `teams_unsave_message` | Remove bookmark from a message |
97
+
98
+ ### Session
99
+
100
+ | Tool | Description |
101
+ |------|-------------|
102
+ | `teams_login` | Trigger manual login (opens browser) |
103
+ | `teams_status` | Check authentication and session state |
104
+
105
+ ### Search Operators
106
+
107
+ The search supports Teams' native operators:
108
+
109
+ ```
110
+ from:sarah@company.com # Messages from person
111
+ sent:today # Messages from today
112
+ sent:lastweek # Messages from last week
113
+ in:project-alpha # Messages in channel
114
+ "Rob Smith" # Find @mentions (name in quotes)
115
+ hasattachment:true # Messages with files
116
+ NOT from:email@co.com # Exclude results
117
+ ```
118
+
119
+ Combine operators: `from:sarah@co.com sent:lastweek hasattachment:true`
120
+
121
+ **Note:** `@me`, `from:me`, `to:me` do NOT work. Use `teams_get_me` first to get your email/displayName, then use those values.
122
+
123
+ ## MCP Resources
124
+
125
+ The server also exposes passive resources for context discovery:
126
+
127
+ | Resource URI | Description |
128
+ |--------------|-------------|
129
+ | `teams://me/profile` | Current user's profile |
130
+ | `teams://me/favorites` | Pinned conversations |
131
+ | `teams://status` | Authentication status |
132
+
133
+ ## CLI Tools (Development)
134
+
135
+ For local development, CLI tools are available for testing and debugging:
136
+
137
+ ```bash
138
+ # Check authentication status
139
+ npm run cli -- status
140
+
141
+ # Search messages
142
+ npm run cli -- search "meeting notes"
143
+ npm run cli -- search "project" --from 0 --size 50
144
+
145
+ # Send messages
146
+ npm run cli -- send "Hello from Teams MCP!"
147
+ npm run cli -- send "Message" --to "conversation-id"
148
+
149
+ # Force login
150
+ npm run cli -- login --force
151
+
152
+ # Output as JSON
153
+ npm run cli -- search "query" --json
154
+ ```
155
+
156
+ ### MCP Test Harness
157
+
158
+ Test the server through the actual MCP protocol:
159
+
160
+ ```bash
161
+ # List available tools
162
+ npm run test:mcp
163
+
164
+ # Call any tool
165
+ npm run test:mcp -- search "your query"
166
+ npm run test:mcp -- status
167
+ npm run test:mcp -- people "john smith"
168
+ npm run test:mcp -- favorites
169
+ ```
170
+
171
+ ## Limitations
172
+
173
+ - **Login required** - Run `teams_login` to authenticate (opens browser)
174
+ - **Token expiry** - Tokens expire after ~1 hour; run `teams_login` again when needed
175
+ - **Undocumented APIs** - Uses Microsoft's internal APIs which may change without notice
176
+ - **Search limitations** - Full-text search only; thread replies not matching search terms won't appear (use `teams_get_thread` for full context)
177
+ - **Own messages only** - Edit/delete only works on your own messages
178
+
179
+ ## Session Files
180
+
181
+ These files are created locally and gitignored:
182
+
183
+ - `session-state.json` - Encrypted browser session
184
+ - `token-cache.json` - Encrypted OAuth tokens
185
+ - `.user-data/` - Browser profile
186
+
187
+ If your session expires, call `teams_login` or delete these files.
188
+
189
+ ## Development
190
+
191
+ For local development:
192
+
193
+ ```bash
194
+ git clone https://github.com/m0nkmaster/microsoft-teams-mcp.git
195
+ cd microsoft-teams-mcp
196
+ npm install
197
+ npm run build
198
+ ```
199
+
200
+ Development commands:
201
+
202
+ ```bash
203
+ npm run dev # Run MCP server in dev mode
204
+ npm run build # Compile TypeScript
205
+ npm run research # Explore Teams APIs (logs network calls)
206
+ npm test # Run unit tests
207
+ npm run typecheck # TypeScript type checking
208
+ ```
209
+
210
+ For development with hot reload, configure your MCP client:
211
+
212
+ ```json
213
+ {
214
+ "mcpServers": {
215
+ "teams": {
216
+ "command": "npx",
217
+ "args": ["tsx", "/path/to/microsoft-teams-mcp/src/index.ts"]
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ See [AGENTS.md](AGENTS.md) for detailed architecture and contribution guidelines.
224
+
225
+ ---
226
+
227
+ ## Teams Chat Export Bookmarklet
228
+
229
+ This repo also includes a standalone bookmarklet for exporting Teams chat messages to Markdown. See [teams-bookmarklet/README.md](teams-bookmarklet/README.md).
@@ -0,0 +1,228 @@
1
+ /**
2
+ * API response fixtures for testing.
3
+ *
4
+ * These are based on real API response structures documented in docs/API-RESEARCH.md.
5
+ * They represent the shape of data returned by Teams/Substrate APIs.
6
+ */
7
+ /**
8
+ * Substrate v2 search result item.
9
+ * From: POST https://substrate.office.com/searchservice/api/v2/query
10
+ */
11
+ export declare const searchResultItem: {
12
+ Id: string;
13
+ ReferenceId: string;
14
+ HitHighlightedSummary: string;
15
+ Summary: string;
16
+ Source: {
17
+ ReceivedTime: string;
18
+ From: {
19
+ EmailAddress: {
20
+ Name: string;
21
+ Address: string;
22
+ };
23
+ };
24
+ ChannelName: string;
25
+ TeamName: string;
26
+ Extensions: {
27
+ SkypeSpaces_ConversationPost_Extension_SkypeGroupId: string;
28
+ };
29
+ ClientConversationId: string;
30
+ };
31
+ };
32
+ /**
33
+ * Search result with HTML content that needs stripping.
34
+ */
35
+ export declare const searchResultWithHtml: {
36
+ Id: string;
37
+ ReferenceId: string;
38
+ HitHighlightedSummary: string;
39
+ Source: {
40
+ ReceivedTime: string;
41
+ From: string;
42
+ ClientThreadId: string;
43
+ };
44
+ };
45
+ /**
46
+ * Minimal search result with only required fields.
47
+ */
48
+ export declare const searchResultMinimal: {
49
+ Id: string;
50
+ HitHighlightedSummary: string;
51
+ };
52
+ /**
53
+ * Search result too short to be valid (content < 5 chars).
54
+ */
55
+ export declare const searchResultTooShort: {
56
+ Id: string;
57
+ HitHighlightedSummary: string;
58
+ };
59
+ /**
60
+ * Full EntitySets response structure from v2 query.
61
+ */
62
+ export declare const searchEntitySetsResponse: {
63
+ EntitySets: {
64
+ ResultSets: {
65
+ Total: number;
66
+ Results: ({
67
+ Id: string;
68
+ ReferenceId: string;
69
+ HitHighlightedSummary: string;
70
+ Summary: string;
71
+ Source: {
72
+ ReceivedTime: string;
73
+ From: {
74
+ EmailAddress: {
75
+ Name: string;
76
+ Address: string;
77
+ };
78
+ };
79
+ ChannelName: string;
80
+ TeamName: string;
81
+ Extensions: {
82
+ SkypeSpaces_ConversationPost_Extension_SkypeGroupId: string;
83
+ };
84
+ ClientConversationId: string;
85
+ };
86
+ } | {
87
+ Id: string;
88
+ ReferenceId: string;
89
+ HitHighlightedSummary: string;
90
+ Source: {
91
+ ReceivedTime: string;
92
+ From: string;
93
+ ClientThreadId: string;
94
+ };
95
+ })[];
96
+ }[];
97
+ }[];
98
+ };
99
+ /**
100
+ * Person suggestion from Substrate suggestions API.
101
+ * From: POST https://substrate.office.com/search/api/v1/suggestions
102
+ */
103
+ export declare const personSuggestion: {
104
+ Id: string;
105
+ MRI: string;
106
+ DisplayName: string;
107
+ GivenName: string;
108
+ Surname: string;
109
+ EmailAddresses: string[];
110
+ CompanyName: string;
111
+ Department: string;
112
+ JobTitle: string;
113
+ };
114
+ /**
115
+ * Person with minimal info using a proper GUID (no optional fields).
116
+ */
117
+ export declare const personMinimal: {
118
+ Id: string;
119
+ DisplayName: string;
120
+ };
121
+ /**
122
+ * Person with base64-encoded GUID (real-world API response format).
123
+ * The base64 '93qkaTtFGWpUHjyRafgdhg==' decodes to GUID '69a47af7-453b-6a19-541e-3c9169f81d86'.
124
+ */
125
+ export declare const personWithBase64Id: {
126
+ Id: string;
127
+ MRI: string;
128
+ DisplayName: string;
129
+ EmailAddresses: string[];
130
+ };
131
+ /**
132
+ * Groups response from suggestions API.
133
+ */
134
+ export declare const peopleGroupsResponse: {
135
+ Groups: {
136
+ Suggestions: {
137
+ Id: string;
138
+ DisplayName: string;
139
+ }[];
140
+ }[];
141
+ };
142
+ /**
143
+ * JWT payload with full user info.
144
+ */
145
+ export declare const jwtPayloadFull: {
146
+ oid: string;
147
+ name: string;
148
+ upn: string;
149
+ preferred_username: string;
150
+ email: string;
151
+ given_name: string;
152
+ family_name: string;
153
+ tid: string;
154
+ exp: number;
155
+ iat: number;
156
+ };
157
+ /**
158
+ * JWT payload with minimal info (only required fields).
159
+ */
160
+ export declare const jwtPayloadMinimal: {
161
+ oid: string;
162
+ name: string;
163
+ exp: number;
164
+ };
165
+ /**
166
+ * JWT payload for name parsing tests - "Surname, GivenName" format.
167
+ */
168
+ export declare const jwtPayloadCommaName: {
169
+ oid: string;
170
+ name: string;
171
+ upn: string;
172
+ };
173
+ /**
174
+ * JWT payload for name parsing tests - "GivenName Surname" format.
175
+ */
176
+ export declare const jwtPayloadSpaceName: {
177
+ oid: string;
178
+ name: string;
179
+ upn: string;
180
+ };
181
+ /**
182
+ * Message source with explicit message ID.
183
+ */
184
+ export declare const sourceWithMessageId: {
185
+ MessageId: string;
186
+ ReceivedTime: string;
187
+ ClientConversationId: string;
188
+ };
189
+ /**
190
+ * Message source with ID in ClientConversationId.
191
+ */
192
+ export declare const sourceWithConvIdMessageId: {
193
+ ReceivedTime: string;
194
+ ClientConversationId: string;
195
+ };
196
+ /**
197
+ * Thread message from chatsvc API.
198
+ * From: GET /api/chatsvc/{region}/v1/users/ME/conversations/{id}/messages
199
+ */
200
+ export declare const threadMessage: {
201
+ id: string;
202
+ content: string;
203
+ messagetype: string;
204
+ contenttype: string;
205
+ from: string;
206
+ imdisplayname: string;
207
+ originalarrivaltime: string;
208
+ composetime: string;
209
+ clientmessageid: string;
210
+ };
211
+ /**
212
+ * Favorites folder response.
213
+ * From: POST /api/csa/{region}/api/v1/teams/users/me/conversationFolders
214
+ */
215
+ export declare const favoritesFolderResponse: {
216
+ folderHierarchyVersion: number;
217
+ conversationFolders: {
218
+ id: string;
219
+ sortType: string;
220
+ name: string;
221
+ folderType: string;
222
+ conversationFolderItems: {
223
+ conversationId: string;
224
+ createdTime: number;
225
+ lastUpdatedTime: number;
226
+ }[];
227
+ }[];
228
+ };
@@ -0,0 +1,217 @@
1
+ /**
2
+ * API response fixtures for testing.
3
+ *
4
+ * These are based on real API response structures documented in docs/API-RESEARCH.md.
5
+ * They represent the shape of data returned by Teams/Substrate APIs.
6
+ */
7
+ /**
8
+ * Substrate v2 search result item.
9
+ * From: POST https://substrate.office.com/searchservice/api/v2/query
10
+ */
11
+ export const searchResultItem = {
12
+ Id: 'AAMkAGE1OWFlZjc0LWYxMjQtNGM1Mi05NzJlLTU0MTU2ZGU1OGM1YQBGAAAAAACaT2h4EH4ZT5pQgKA-example',
13
+ ReferenceId: 'abc123-def456.1000.1',
14
+ HitHighlightedSummary: 'Let me check the <c0>budget</c0> report for Q3',
15
+ Summary: 'Let me check the budget report for Q3',
16
+ Source: {
17
+ ReceivedTime: '2026-01-20T14:30:00.000Z',
18
+ From: {
19
+ EmailAddress: {
20
+ Name: 'Smith, John',
21
+ Address: 'john.smith@company.com',
22
+ },
23
+ },
24
+ ChannelName: 'General',
25
+ TeamName: 'Finance Team',
26
+ Extensions: {
27
+ SkypeSpaces_ConversationPost_Extension_SkypeGroupId: '19:abcdef123456@thread.tacv2',
28
+ },
29
+ ClientConversationId: '19:abcdef123456@thread.tacv2;messageid=1705760000000',
30
+ },
31
+ };
32
+ /**
33
+ * Search result with HTML content that needs stripping.
34
+ */
35
+ export const searchResultWithHtml = {
36
+ Id: 'AAMkBGFiY2RlZg',
37
+ ReferenceId: 'xyz789.1000.1',
38
+ HitHighlightedSummary: '<p>Meeting <strong>notes</strong> from &amp; yesterday&apos;s call</p><br/><div>Action items:</div>',
39
+ Source: {
40
+ ReceivedTime: '2026-01-21T09:00:00.000Z',
41
+ From: 'Jane Doe',
42
+ ClientThreadId: '19:meeting123@thread.v2',
43
+ },
44
+ };
45
+ /**
46
+ * Minimal search result with only required fields.
47
+ */
48
+ export const searchResultMinimal = {
49
+ Id: 'minimal-id',
50
+ HitHighlightedSummary: 'A short message here',
51
+ };
52
+ /**
53
+ * Search result too short to be valid (content < 5 chars).
54
+ */
55
+ export const searchResultTooShort = {
56
+ Id: 'short-id',
57
+ HitHighlightedSummary: 'Hi',
58
+ };
59
+ /**
60
+ * Full EntitySets response structure from v2 query.
61
+ */
62
+ export const searchEntitySetsResponse = {
63
+ EntitySets: [
64
+ {
65
+ ResultSets: [
66
+ {
67
+ Total: 4307,
68
+ Results: [
69
+ searchResultItem,
70
+ searchResultWithHtml,
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ };
77
+ /**
78
+ * Person suggestion from Substrate suggestions API.
79
+ * From: POST https://substrate.office.com/search/api/v1/suggestions
80
+ */
81
+ export const personSuggestion = {
82
+ Id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890@company.onmicrosoft.com',
83
+ MRI: '8:orgid:a1b2c3d4-e5f6-7890-abcd-ef1234567890',
84
+ DisplayName: 'Smith, John',
85
+ GivenName: 'John',
86
+ Surname: 'Smith',
87
+ EmailAddresses: ['john.smith@company.com'],
88
+ CompanyName: 'Acme Corp',
89
+ Department: 'Engineering',
90
+ JobTitle: 'Senior Engineer',
91
+ };
92
+ /**
93
+ * Person with minimal info using a proper GUID (no optional fields).
94
+ */
95
+ export const personMinimal = {
96
+ Id: 'b1c2d3e4-f5a6-7890-bcde-1234567890ab',
97
+ DisplayName: 'Jane Doe',
98
+ };
99
+ /**
100
+ * Person with base64-encoded GUID (real-world API response format).
101
+ * The base64 '93qkaTtFGWpUHjyRafgdhg==' decodes to GUID '69a47af7-453b-6a19-541e-3c9169f81d86'.
102
+ */
103
+ export const personWithBase64Id = {
104
+ Id: '93qkaTtFGWpUHjyRafgdhg==',
105
+ MRI: '8:orgid:93qkaTtFGWpUHjyRafgdhg==',
106
+ DisplayName: 'Rob MacDonald',
107
+ EmailAddresses: ['rob@company.com'],
108
+ };
109
+ /**
110
+ * Groups response from suggestions API.
111
+ */
112
+ export const peopleGroupsResponse = {
113
+ Groups: [
114
+ {
115
+ Suggestions: [
116
+ personSuggestion,
117
+ personMinimal,
118
+ ],
119
+ },
120
+ ],
121
+ };
122
+ /**
123
+ * JWT payload with full user info.
124
+ */
125
+ export const jwtPayloadFull = {
126
+ oid: 'user-object-id-guid',
127
+ name: 'Macdonald, Rob',
128
+ upn: 'rob.macdonald@company.com',
129
+ preferred_username: 'rob@company.com',
130
+ email: 'rob.m@personal.com',
131
+ given_name: 'Rob',
132
+ family_name: 'Macdonald',
133
+ tid: 'tenant-id-guid',
134
+ exp: 1705850000,
135
+ iat: 1705846400,
136
+ };
137
+ /**
138
+ * JWT payload with minimal info (only required fields).
139
+ */
140
+ export const jwtPayloadMinimal = {
141
+ oid: 'another-user-guid',
142
+ name: 'Alice Smith',
143
+ exp: 1705850000,
144
+ };
145
+ /**
146
+ * JWT payload for name parsing tests - "Surname, GivenName" format.
147
+ */
148
+ export const jwtPayloadCommaName = {
149
+ oid: 'comma-name-user',
150
+ name: 'Jones, David',
151
+ upn: 'david.jones@company.com',
152
+ };
153
+ /**
154
+ * JWT payload for name parsing tests - "GivenName Surname" format.
155
+ */
156
+ export const jwtPayloadSpaceName = {
157
+ oid: 'space-name-user',
158
+ name: 'Sarah Connor',
159
+ upn: 'sarah.connor@company.com',
160
+ };
161
+ /**
162
+ * Message source with explicit message ID.
163
+ */
164
+ export const sourceWithMessageId = {
165
+ MessageId: '1705760000000',
166
+ ReceivedTime: '2026-01-20T12:00:00.000Z',
167
+ ClientConversationId: '19:thread@tacv2',
168
+ };
169
+ /**
170
+ * Message source with ID in ClientConversationId.
171
+ */
172
+ export const sourceWithConvIdMessageId = {
173
+ ReceivedTime: '2026-01-20T12:00:00.000Z',
174
+ ClientConversationId: '19:thread@tacv2;messageid=1705770000000',
175
+ };
176
+ /**
177
+ * Thread message from chatsvc API.
178
+ * From: GET /api/chatsvc/{region}/v1/users/ME/conversations/{id}/messages
179
+ */
180
+ export const threadMessage = {
181
+ id: '1705760000000',
182
+ content: '<p>Hello team!</p>',
183
+ messagetype: 'RichText/Html',
184
+ contenttype: 'text',
185
+ from: '8:orgid:user-guid-123',
186
+ imdisplayname: 'John Smith',
187
+ originalarrivaltime: '2026-01-20T12:00:00.000Z',
188
+ composetime: '2026-01-20T11:59:58.000Z',
189
+ clientmessageid: 'client-msg-123',
190
+ };
191
+ /**
192
+ * Favorites folder response.
193
+ * From: POST /api/csa/{region}/api/v1/teams/users/me/conversationFolders
194
+ */
195
+ export const favoritesFolderResponse = {
196
+ folderHierarchyVersion: 1705850000000,
197
+ conversationFolders: [
198
+ {
199
+ id: 'tenant-guid~user-guid~Favorites',
200
+ sortType: 'UserDefinedCustomOrder',
201
+ name: 'Favorites',
202
+ folderType: 'Favorites',
203
+ conversationFolderItems: [
204
+ {
205
+ conversationId: '19:abc@thread.tacv2',
206
+ createdTime: 1705700000000,
207
+ lastUpdatedTime: 1705800000000,
208
+ },
209
+ {
210
+ conversationId: '19:xyz@thread.v2',
211
+ createdTime: 1705600000000,
212
+ lastUpdatedTime: 1705750000000,
213
+ },
214
+ ],
215
+ },
216
+ ],
217
+ };