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,132 @@
1
+ /**
2
+ * Error taxonomy for MCP operations.
3
+ *
4
+ * Provides machine-readable error codes that help LLMs
5
+ * understand failures and take appropriate action.
6
+ */
7
+ /** Enumeration of all error types in the system. */
8
+ export var ErrorCode;
9
+ (function (ErrorCode) {
10
+ /** No valid authentication token or session. */
11
+ ErrorCode["AUTH_REQUIRED"] = "AUTH_REQUIRED";
12
+ /** Token has expired and needs refresh. */
13
+ ErrorCode["AUTH_EXPIRED"] = "AUTH_EXPIRED";
14
+ /** Rate limited by the API. */
15
+ ErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
16
+ /** Requested resource was not found. */
17
+ ErrorCode["NOT_FOUND"] = "NOT_FOUND";
18
+ /** Invalid input parameters. */
19
+ ErrorCode["INVALID_INPUT"] = "INVALID_INPUT";
20
+ /** API returned an error response. */
21
+ ErrorCode["API_ERROR"] = "API_ERROR";
22
+ /** Browser automation failed. */
23
+ ErrorCode["BROWSER_ERROR"] = "BROWSER_ERROR";
24
+ /** Network or connection error. */
25
+ ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
26
+ /** Operation timed out. */
27
+ ErrorCode["TIMEOUT"] = "TIMEOUT";
28
+ /** Unknown or unexpected error. */
29
+ ErrorCode["UNKNOWN"] = "UNKNOWN";
30
+ })(ErrorCode || (ErrorCode = {}));
31
+ /**
32
+ * Creates a standardised MCP error.
33
+ */
34
+ export function createError(code, message, options = {}) {
35
+ const defaultSuggestions = getDefaultSuggestions(code);
36
+ return {
37
+ code,
38
+ message,
39
+ retryable: options.retryable ?? isRetryableByDefault(code),
40
+ retryAfterMs: options.retryAfterMs,
41
+ suggestions: options.suggestions ?? defaultSuggestions,
42
+ };
43
+ }
44
+ /**
45
+ * Returns default suggestions for each error code.
46
+ */
47
+ function getDefaultSuggestions(code) {
48
+ switch (code) {
49
+ case ErrorCode.AUTH_REQUIRED:
50
+ return ['Call teams_login to authenticate'];
51
+ case ErrorCode.AUTH_EXPIRED:
52
+ return ['Call teams_login to refresh authentication'];
53
+ case ErrorCode.RATE_LIMITED:
54
+ return ['Wait before retrying', 'Reduce request frequency'];
55
+ case ErrorCode.NOT_FOUND:
56
+ return ['Check the ID/query is correct', 'Verify the resource exists'];
57
+ case ErrorCode.INVALID_INPUT:
58
+ return ['Check the input parameters', 'Review the tool documentation'];
59
+ case ErrorCode.API_ERROR:
60
+ return ['Retry the request', 'Check teams_status for system health'];
61
+ case ErrorCode.BROWSER_ERROR:
62
+ return ['Call teams_login to restart browser session'];
63
+ case ErrorCode.NETWORK_ERROR:
64
+ return ['Check network connectivity', 'Retry the request'];
65
+ case ErrorCode.TIMEOUT:
66
+ return ['Retry the request', 'Use smaller page sizes'];
67
+ case ErrorCode.UNKNOWN:
68
+ return ['Check teams_status', 'Try teams_login if authentication issues'];
69
+ }
70
+ }
71
+ /**
72
+ * Determines if an error code is retryable by default.
73
+ */
74
+ function isRetryableByDefault(code) {
75
+ switch (code) {
76
+ case ErrorCode.RATE_LIMITED:
77
+ case ErrorCode.NETWORK_ERROR:
78
+ case ErrorCode.TIMEOUT:
79
+ case ErrorCode.API_ERROR:
80
+ return true;
81
+ default:
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Classifies an HTTP status code into an error code.
87
+ */
88
+ export function classifyHttpError(status, message) {
89
+ switch (status) {
90
+ case 401:
91
+ return ErrorCode.AUTH_EXPIRED;
92
+ case 403:
93
+ return ErrorCode.AUTH_REQUIRED;
94
+ case 404:
95
+ return ErrorCode.NOT_FOUND;
96
+ case 429:
97
+ return ErrorCode.RATE_LIMITED;
98
+ case 400:
99
+ case 422:
100
+ return ErrorCode.INVALID_INPUT;
101
+ default:
102
+ if (status >= 500)
103
+ return ErrorCode.API_ERROR;
104
+ if (message?.includes('timeout'))
105
+ return ErrorCode.TIMEOUT;
106
+ if (message?.includes('network') || message?.includes('ECONNRESET')) {
107
+ return ErrorCode.NETWORK_ERROR;
108
+ }
109
+ return ErrorCode.UNKNOWN;
110
+ }
111
+ }
112
+ /**
113
+ * Extracts retry-after value from response headers.
114
+ */
115
+ export function extractRetryAfter(headers) {
116
+ const retryAfter = headers.get('Retry-After');
117
+ if (!retryAfter)
118
+ return undefined;
119
+ // Could be seconds (number) or HTTP date
120
+ const seconds = parseInt(retryAfter, 10);
121
+ if (!isNaN(seconds)) {
122
+ return seconds * 1000;
123
+ }
124
+ // Try parsing as HTTP date
125
+ try {
126
+ const date = new Date(retryAfter);
127
+ return Math.max(0, date.getTime() - Date.now());
128
+ }
129
+ catch {
130
+ return undefined;
131
+ }
132
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Result type for operations that can fail.
3
+ *
4
+ * Provides a discriminated union for success/failure that
5
+ * forces callers to handle errors explicitly.
6
+ */
7
+ import type { McpError } from './errors.js';
8
+ /** Successful result with a value. */
9
+ export interface Ok<T> {
10
+ ok: true;
11
+ value: T;
12
+ }
13
+ /** Failed result with an error. */
14
+ export interface Err<E = McpError> {
15
+ ok: false;
16
+ error: E;
17
+ }
18
+ /** A result that can be either a success or failure. */
19
+ export type Result<T, E = McpError> = Ok<T> | Err<E>;
20
+ /**
21
+ * Creates a successful result.
22
+ */
23
+ export declare function ok<T>(value: T): Ok<T>;
24
+ /**
25
+ * Creates a failed result.
26
+ */
27
+ export declare function err<E = McpError>(error: E): Err<E>;
28
+ /**
29
+ * Unwraps a result, throwing if it's an error.
30
+ */
31
+ export declare function unwrap<T>(result: Result<T>): T;
32
+ /**
33
+ * Unwraps a result with a default value for errors.
34
+ */
35
+ export declare function unwrapOr<T>(result: Result<T>, defaultValue: T): T;
36
+ /**
37
+ * Maps a successful result to a new value.
38
+ */
39
+ export declare function map<T, U>(result: Result<T>, fn: (value: T) => U): Result<U>;
40
+ /**
41
+ * Maps a successful result to a new result (flatMap).
42
+ */
43
+ export declare function andThen<T, U>(result: Result<T>, fn: (value: T) => Result<U>): Result<U>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Result type for operations that can fail.
3
+ *
4
+ * Provides a discriminated union for success/failure that
5
+ * forces callers to handle errors explicitly.
6
+ */
7
+ /**
8
+ * Creates a successful result.
9
+ */
10
+ export function ok(value) {
11
+ return { ok: true, value };
12
+ }
13
+ /**
14
+ * Creates a failed result.
15
+ */
16
+ export function err(error) {
17
+ return { ok: false, error };
18
+ }
19
+ /**
20
+ * Unwraps a result, throwing if it's an error.
21
+ */
22
+ export function unwrap(result) {
23
+ if (result.ok) {
24
+ return result.value;
25
+ }
26
+ throw new Error(result.error.message);
27
+ }
28
+ /**
29
+ * Unwraps a result with a default value for errors.
30
+ */
31
+ export function unwrapOr(result, defaultValue) {
32
+ return result.ok ? result.value : defaultValue;
33
+ }
34
+ /**
35
+ * Maps a successful result to a new value.
36
+ */
37
+ export function map(result, fn) {
38
+ if (result.ok) {
39
+ return ok(fn(result.value));
40
+ }
41
+ return result;
42
+ }
43
+ /**
44
+ * Maps a successful result to a new result (flatMap).
45
+ */
46
+ export function andThen(result, fn) {
47
+ if (result.ok) {
48
+ return fn(result.value);
49
+ }
50
+ return result;
51
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Server interface types.
3
+ *
4
+ * Extracted to a shared module to avoid circular dependencies between
5
+ * server.ts and tools/index.ts.
6
+ */
7
+ import type { BrowserManager } from '../browser/context.js';
8
+ /**
9
+ * Interface for the TeamsServer class.
10
+ *
11
+ * This interface is used by tool handlers to interact with the server
12
+ * without creating circular dependencies.
13
+ */
14
+ export interface TeamsServer {
15
+ /** Ensures a browser is running and authenticated. */
16
+ ensureBrowser(headless?: boolean): Promise<BrowserManager>;
17
+ /** Resets browser state (clears manager and initialisation flag). */
18
+ resetBrowserState(): void;
19
+ /** Gets the current browser manager, if any. */
20
+ getBrowserManager(): BrowserManager | null;
21
+ /** Sets the browser manager. */
22
+ setBrowserManager(manager: BrowserManager): void;
23
+ /** Marks the server as initialised. */
24
+ markInitialised(): void;
25
+ /** Checks if the server is initialised. */
26
+ isInitialisedState(): boolean;
27
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Server interface types.
3
+ *
4
+ * Extracted to a shared module to avoid circular dependencies between
5
+ * server.ts and tools/index.ts.
6
+ */
7
+ export {};
@@ -0,0 +1,85 @@
1
+ /**
2
+ * TypeScript interfaces for Teams data structures.
3
+ */
4
+ /** A link extracted from message content. */
5
+ export interface ExtractedLink {
6
+ url: string;
7
+ text: string;
8
+ }
9
+ export interface TeamsSearchResult {
10
+ id: string;
11
+ type: 'message' | 'file' | 'person';
12
+ content: string;
13
+ sender?: string;
14
+ timestamp?: string;
15
+ channelName?: string;
16
+ teamName?: string;
17
+ conversationId?: string;
18
+ messageId?: string;
19
+ /** Direct link to open this message in Teams */
20
+ messageLink?: string;
21
+ /** Links extracted from message content */
22
+ links?: ExtractedLink[];
23
+ }
24
+ export interface TeamsMessage {
25
+ id: string;
26
+ content: string;
27
+ sender: string;
28
+ timestamp: string;
29
+ channelName?: string;
30
+ teamName?: string;
31
+ replyCount?: number;
32
+ reactions?: TeamsReaction[];
33
+ }
34
+ export interface TeamsReaction {
35
+ type: string;
36
+ count: number;
37
+ }
38
+ export interface InterceptedRequest {
39
+ url: string;
40
+ method: string;
41
+ headers: Record<string, string>;
42
+ postData?: string;
43
+ timestamp: Date;
44
+ }
45
+ export interface InterceptedResponse {
46
+ url: string;
47
+ status: number;
48
+ headers: Record<string, string>;
49
+ body?: string;
50
+ timestamp: Date;
51
+ }
52
+ export interface SearchApiEndpoint {
53
+ url: string;
54
+ method: string;
55
+ description: string;
56
+ requestFormat?: unknown;
57
+ responseFormat?: unknown;
58
+ }
59
+ /** Pagination options for search requests. */
60
+ export interface SearchPaginationOptions {
61
+ /** Starting offset (0-based). Default: 0 */
62
+ from?: number;
63
+ /** Page size. Default: 25 */
64
+ size?: number;
65
+ /** Maximum total results to fetch across all pages. */
66
+ maxResults?: number;
67
+ }
68
+ /** Pagination metadata returned with search results. */
69
+ export interface SearchPaginationResult {
70
+ /** Number of results returned in this response. */
71
+ returned: number;
72
+ /** Starting offset used for this request. */
73
+ from: number;
74
+ /** Page size requested. */
75
+ size: number;
76
+ /** Total results available (if known). */
77
+ total?: number;
78
+ /** Whether more results are available. */
79
+ hasMore: boolean;
80
+ }
81
+ /** Search results with pagination metadata. */
82
+ export interface TeamsSearchResultsWithPagination {
83
+ results: TeamsSearchResult[];
84
+ pagination: SearchPaginationResult;
85
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TypeScript interfaces for Teams data structures.
3
+ */
4
+ export {};
@@ -0,0 +1,103 @@
1
+ /**
2
+ * API endpoint configuration and header utilities.
3
+ *
4
+ * Centralises all API URLs and common request headers.
5
+ *
6
+ * Region and base URLs are extracted from the user's session via DISCOVER-REGION-GTM,
7
+ * supporting different Teams environments (commercial, GCC, GCC-High, etc.).
8
+ */
9
+ /** Default Teams base URL (commercial cloud). */
10
+ export declare const DEFAULT_TEAMS_BASE_URL = "https://teams.microsoft.com";
11
+ /** Default Substrate base URL (commercial cloud). */
12
+ export declare const DEFAULT_SUBSTRATE_BASE_URL = "https://substrate.office.com";
13
+ /**
14
+ * Substrate API endpoints.
15
+ *
16
+ * Note: Substrate URLs may differ for GCC/GCC-High tenants. The base URL is
17
+ * hardcoded to substrate.office.com as we haven't found a config source for it.
18
+ * If issues are reported from government cloud users, we may need to add config.
19
+ */
20
+ export declare const SUBSTRATE_API: {
21
+ /** Full-text message search. */
22
+ readonly search: "https://substrate.office.com/searchservice/api/v2/query";
23
+ /** People and message typeahead suggestions. */
24
+ readonly suggestions: "https://substrate.office.com/search/api/v1/suggestions";
25
+ /** Frequent contacts list. */
26
+ readonly frequentContacts: "https://substrate.office.com/search/api/v1/suggestions?scenario=peoplecache";
27
+ /** People search. */
28
+ readonly peopleSearch: "https://substrate.office.com/search/api/v1/suggestions?scenario=powerbar";
29
+ /** Channel search (org-wide, not just user's teams). */
30
+ readonly channelSearch: "https://substrate.office.com/search/api/v1/suggestions?scenario=powerbar&setflight=TurnOffMPLSuppressionTeams,EnableTeamsChannelDomainPowerbar&domain=TeamsChannel";
31
+ };
32
+ /**
33
+ * Chat service API endpoint builders.
34
+ *
35
+ * All functions accept an optional `baseUrl` parameter to support different
36
+ * Teams environments. If not provided, uses DEFAULT_TEAMS_BASE_URL.
37
+ */
38
+ export declare const CHATSVC_API: {
39
+ /**
40
+ * Get messages URL for a conversation.
41
+ *
42
+ * For thread replies in channels, provide replyToMessageId to append
43
+ * `;messageid={id}` to the conversation path. This tells Teams the message
44
+ * is a reply to an existing thread rather than a new top-level post.
45
+ */
46
+ readonly messages: (region: string, conversationId: string, replyToMessageId?: string, baseUrl?: string) => string;
47
+ /** Get conversation metadata URL. */
48
+ readonly conversation: (region: string, conversationId: string, baseUrl?: string) => string;
49
+ /** Save/unsave message metadata URL. */
50
+ readonly messageMetadata: (region: string, conversationId: string, messageId: string, baseUrl?: string) => string;
51
+ /** Edit a specific message URL. */
52
+ readonly editMessage: (region: string, conversationId: string, messageId: string, baseUrl?: string) => string;
53
+ /** Delete a specific message URL (soft delete). */
54
+ readonly deleteMessage: (region: string, conversationId: string, messageId: string, baseUrl?: string) => string;
55
+ /** Get consumption horizons (read receipts) for a thread. */
56
+ readonly consumptionHorizons: (region: string, threadId: string, baseUrl?: string) => string;
57
+ /** Update consumption horizon (mark as read) for a conversation. */
58
+ readonly updateConsumptionHorizon: (region: string, conversationId: string, baseUrl?: string) => string;
59
+ /** Activity feed (notifications) messages. */
60
+ readonly activityFeed: (region: string, baseUrl?: string) => string;
61
+ /** Message emotions (reactions) URL. */
62
+ readonly messageEmotions: (region: string, conversationId: string, messageId: string, baseUrl?: string) => string;
63
+ /** Create a new thread (group chat). */
64
+ readonly createThread: (region: string, baseUrl?: string) => string;
65
+ };
66
+ /**
67
+ * Calendar/Meeting API endpoints.
68
+ *
69
+ * The mt/part endpoints use partitioned regions (e.g., amer-02, emea-03).
70
+ * Some tenants use non-partitioned URLs (e.g., /api/mt/emea).
71
+ * The correct format is extracted from the user's session via DISCOVER-REGION-GTM.
72
+ */
73
+ export declare const CALENDAR_API: {
74
+ /**
75
+ * Get calendar view (meetings) for a date range.
76
+ *
77
+ * Uses OData-style query parameters for filtering and pagination.
78
+ *
79
+ * @param regionPartition - The full region-partition (e.g., "amer-02") or just region (e.g., "emea")
80
+ * @param hasPartition - Whether the tenant uses partitioned URLs
81
+ * @param baseUrl - Teams base URL (from config or default)
82
+ */
83
+ readonly calendarView: (regionPartition: string, hasPartition: boolean, baseUrl?: string) => string;
84
+ };
85
+ /** CSA (Chat Service Aggregator) API endpoints. */
86
+ export declare const CSA_API: {
87
+ /** Conversation folders (favourites) URL. */
88
+ readonly conversationFolders: (region: string, baseUrl?: string) => string;
89
+ /** Teams list (all teams/channels user is a member of). */
90
+ readonly teamsList: (region: string, baseUrl?: string) => string;
91
+ /** Custom emoji metadata. */
92
+ readonly customEmojis: (region: string, baseUrl?: string) => string;
93
+ };
94
+ /** Common request headers for Teams API calls. */
95
+ export declare function getTeamsHeaders(baseUrl?: string): HeadersInit;
96
+ /** Headers for Bearer token authentication. */
97
+ export declare function getBearerHeaders(token: string, baseUrl?: string): HeadersInit;
98
+ /** Headers for Skype token + Bearer authentication. */
99
+ export declare function getSkypeAuthHeaders(skypeToken: string, authToken: string, baseUrl?: string): HeadersInit;
100
+ /** Headers for CSA API (Skype token + CSA Bearer). */
101
+ export declare function getCsaHeaders(skypeToken: string, csaToken: string, baseUrl?: string): HeadersInit;
102
+ /** Client version header for messaging API. */
103
+ export declare function getMessagingHeaders(skypeToken: string, authToken: string, baseUrl?: string): HeadersInit;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * API endpoint configuration and header utilities.
3
+ *
4
+ * Centralises all API URLs and common request headers.
5
+ *
6
+ * Region and base URLs are extracted from the user's session via DISCOVER-REGION-GTM,
7
+ * supporting different Teams environments (commercial, GCC, GCC-High, etc.).
8
+ */
9
+ import { NOTIFICATIONS_ID } from '../constants.js';
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ // Default URLs (fallbacks when session config unavailable)
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+ /** Default Teams base URL (commercial cloud). */
14
+ export const DEFAULT_TEAMS_BASE_URL = 'https://teams.microsoft.com';
15
+ /** Default Substrate base URL (commercial cloud). */
16
+ export const DEFAULT_SUBSTRATE_BASE_URL = 'https://substrate.office.com';
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+ // Substrate API
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ /**
21
+ * Substrate API endpoints.
22
+ *
23
+ * Note: Substrate URLs may differ for GCC/GCC-High tenants. The base URL is
24
+ * hardcoded to substrate.office.com as we haven't found a config source for it.
25
+ * If issues are reported from government cloud users, we may need to add config.
26
+ */
27
+ export const SUBSTRATE_API = {
28
+ /** Full-text message search. */
29
+ search: `${DEFAULT_SUBSTRATE_BASE_URL}/searchservice/api/v2/query`,
30
+ /** People and message typeahead suggestions. */
31
+ suggestions: `${DEFAULT_SUBSTRATE_BASE_URL}/search/api/v1/suggestions`,
32
+ /** Frequent contacts list. */
33
+ frequentContacts: `${DEFAULT_SUBSTRATE_BASE_URL}/search/api/v1/suggestions?scenario=peoplecache`,
34
+ /** People search. */
35
+ peopleSearch: `${DEFAULT_SUBSTRATE_BASE_URL}/search/api/v1/suggestions?scenario=powerbar`,
36
+ /** Channel search (org-wide, not just user's teams). */
37
+ channelSearch: `${DEFAULT_SUBSTRATE_BASE_URL}/search/api/v1/suggestions?scenario=powerbar&setflight=TurnOffMPLSuppressionTeams,EnableTeamsChannelDomainPowerbar&domain=TeamsChannel`,
38
+ };
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+ // Chatsvc API
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+ /**
43
+ * Chat service API endpoint builders.
44
+ *
45
+ * All functions accept an optional `baseUrl` parameter to support different
46
+ * Teams environments. If not provided, uses DEFAULT_TEAMS_BASE_URL.
47
+ */
48
+ export const CHATSVC_API = {
49
+ /**
50
+ * Get messages URL for a conversation.
51
+ *
52
+ * For thread replies in channels, provide replyToMessageId to append
53
+ * `;messageid={id}` to the conversation path. This tells Teams the message
54
+ * is a reply to an existing thread rather than a new top-level post.
55
+ */
56
+ messages: (region, conversationId, replyToMessageId, baseUrl = DEFAULT_TEAMS_BASE_URL) => {
57
+ const conversationPath = replyToMessageId
58
+ ? `${conversationId};messageid=${replyToMessageId}`
59
+ : conversationId;
60
+ return `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationPath)}/messages`;
61
+ },
62
+ /** Get conversation metadata URL. */
63
+ conversation: (region, conversationId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}`,
64
+ /** Save/unsave message metadata URL. */
65
+ messageMetadata: (region, conversationId, messageId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}/rcmetadata/${messageId}`,
66
+ /** Edit a specific message URL. */
67
+ editMessage: (region, conversationId, messageId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}/messages/${messageId}`,
68
+ /** Delete a specific message URL (soft delete). */
69
+ deleteMessage: (region, conversationId, messageId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}/messages/${messageId}?behavior=softDelete`,
70
+ /** Get consumption horizons (read receipts) for a thread. */
71
+ consumptionHorizons: (region, threadId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/threads/${encodeURIComponent(threadId)}/consumptionhorizons`,
72
+ /** Update consumption horizon (mark as read) for a conversation. */
73
+ updateConsumptionHorizon: (region, conversationId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}/properties?name=consumptionhorizon`,
74
+ /** Activity feed (notifications) messages. */
75
+ activityFeed: (region, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(NOTIFICATIONS_ID)}/messages`,
76
+ /** Message emotions (reactions) URL. */
77
+ messageEmotions: (region, conversationId, messageId, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/users/ME/conversations/${encodeURIComponent(conversationId)}/messages/${messageId}/properties?name=emotions`,
78
+ /** Create a new thread (group chat). */
79
+ createThread: (region, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/chatsvc/${region}/v1/threads`,
80
+ };
81
+ // ─────────────────────────────────────────────────────────────────────────────
82
+ // Calendar API
83
+ // ─────────────────────────────────────────────────────────────────────────────
84
+ /**
85
+ * Calendar/Meeting API endpoints.
86
+ *
87
+ * The mt/part endpoints use partitioned regions (e.g., amer-02, emea-03).
88
+ * Some tenants use non-partitioned URLs (e.g., /api/mt/emea).
89
+ * The correct format is extracted from the user's session via DISCOVER-REGION-GTM.
90
+ */
91
+ export const CALENDAR_API = {
92
+ /**
93
+ * Get calendar view (meetings) for a date range.
94
+ *
95
+ * Uses OData-style query parameters for filtering and pagination.
96
+ *
97
+ * @param regionPartition - The full region-partition (e.g., "amer-02") or just region (e.g., "emea")
98
+ * @param hasPartition - Whether the tenant uses partitioned URLs
99
+ * @param baseUrl - Teams base URL (from config or default)
100
+ */
101
+ calendarView: (regionPartition, hasPartition, baseUrl = DEFAULT_TEAMS_BASE_URL) => hasPartition
102
+ ? `${baseUrl}/api/mt/part/${regionPartition}/v2.1/me/calendars/calendarView`
103
+ : `${baseUrl}/api/mt/${regionPartition}/v2.1/me/calendars/calendarView`,
104
+ };
105
+ // ─────────────────────────────────────────────────────────────────────────────
106
+ // CSA API
107
+ // ─────────────────────────────────────────────────────────────────────────────
108
+ /** CSA (Chat Service Aggregator) API endpoints. */
109
+ export const CSA_API = {
110
+ /** Conversation folders (favourites) URL. */
111
+ conversationFolders: (region, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/csa/${region}/api/v1/teams/users/me/conversationFolders?supportsAdditionalSystemGeneratedFolders=true&supportsSliceItems=true`,
112
+ /** Teams list (all teams/channels user is a member of). */
113
+ teamsList: (region, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/csa/${region}/api/v3/teams/users/me?isPrefetch=false&enableMembershipSummary=true`,
114
+ /** Custom emoji metadata. */
115
+ customEmojis: (region, baseUrl = DEFAULT_TEAMS_BASE_URL) => `${baseUrl}/api/csa/${region}/api/v1/customemoji/metadata`,
116
+ };
117
+ // ─────────────────────────────────────────────────────────────────────────────
118
+ // Request Headers
119
+ // ─────────────────────────────────────────────────────────────────────────────
120
+ /** Common request headers for Teams API calls. */
121
+ export function getTeamsHeaders(baseUrl = DEFAULT_TEAMS_BASE_URL) {
122
+ return {
123
+ 'Content-Type': 'application/json',
124
+ 'Accept': 'application/json',
125
+ 'Origin': baseUrl,
126
+ 'Referer': `${baseUrl}/`,
127
+ };
128
+ }
129
+ /** Headers for Bearer token authentication. */
130
+ export function getBearerHeaders(token, baseUrl = DEFAULT_TEAMS_BASE_URL) {
131
+ return {
132
+ ...getTeamsHeaders(baseUrl),
133
+ 'Authorization': `Bearer ${token}`,
134
+ };
135
+ }
136
+ /** Headers for Skype token + Bearer authentication. */
137
+ export function getSkypeAuthHeaders(skypeToken, authToken, baseUrl = DEFAULT_TEAMS_BASE_URL) {
138
+ return {
139
+ ...getTeamsHeaders(baseUrl),
140
+ 'Authentication': `skypetoken=${skypeToken}`,
141
+ 'Authorization': `Bearer ${authToken}`,
142
+ };
143
+ }
144
+ /** Headers for CSA API (Skype token + CSA Bearer). */
145
+ export function getCsaHeaders(skypeToken, csaToken, baseUrl = DEFAULT_TEAMS_BASE_URL) {
146
+ return {
147
+ ...getTeamsHeaders(baseUrl),
148
+ 'Authentication': `skypetoken=${skypeToken}`,
149
+ 'Authorization': `Bearer ${csaToken}`,
150
+ };
151
+ }
152
+ /** Client version header for messaging API. */
153
+ export function getMessagingHeaders(skypeToken, authToken, baseUrl = DEFAULT_TEAMS_BASE_URL) {
154
+ return {
155
+ ...getSkypeAuthHeaders(skypeToken, authToken, baseUrl),
156
+ 'X-Ms-Client-Version': '1415/1.0.0.2025010401',
157
+ };
158
+ }