msteams-mcp 0.23.0 → 0.25.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 +7 -5
- package/dist/api/chatsvc-api.d.ts +2 -0
- package/dist/api/chatsvc-api.js +2 -0
- package/dist/api/chatsvc-conversations.d.ts +37 -0
- package/dist/api/chatsvc-conversations.js +85 -0
- package/dist/api/chatsvc-conversations.test.d.ts +4 -0
- package/dist/api/chatsvc-conversations.test.js +51 -0
- package/dist/api/chatsvc-messaging.d.ts +155 -3
- package/dist/api/chatsvc-messaging.js +382 -32
- package/dist/api/chatsvc-messaging.test.js +191 -1
- package/dist/api/chatsvc-readstatus.d.ts +4 -0
- package/dist/api/chatsvc-readstatus.js +38 -18
- package/dist/api/graph-api.d.ts +55 -0
- package/dist/api/graph-api.js +148 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/profile-api.d.ts +47 -0
- package/dist/api/profile-api.js +96 -0
- package/dist/auth/token-extractor.js +7 -6
- package/dist/browser/auth.js +9 -6
- package/dist/browser/chrome-cookie-import.d.ts +27 -0
- package/dist/browser/chrome-cookie-import.js +294 -0
- package/dist/browser/chrome-cookie-import.test.d.ts +1 -0
- package/dist/browser/chrome-cookie-import.test.js +122 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +13 -0
- package/dist/server.js +4 -4
- package/dist/test/dump-tokens.d.ts +7 -0
- package/dist/test/dump-tokens.js +90 -0
- package/dist/test/test-transcript.d.ts +6 -0
- package/dist/test/test-transcript.js +47 -0
- package/dist/tools/auth-tools.js +4 -0
- package/dist/tools/graph-tools.d.ts +42 -0
- package/dist/tools/graph-tools.js +99 -0
- package/dist/tools/message-tools.d.ts +120 -10
- package/dist/tools/message-tools.js +176 -6
- package/dist/tools/people-tools.d.ts +14 -0
- package/dist/tools/people-tools.js +37 -1
- package/dist/tools/search-tools.d.ts +54 -10
- package/dist/tools/search-tools.js +41 -7
- package/dist/tools/search-tools.test.js +15 -1
- package/dist/types/errors.js +2 -2
- package/dist/types/errors.test.js +1 -1
- package/dist/types/result.d.ts +0 -16
- package/dist/types/result.js +0 -33
- package/dist/types/result.test.js +1 -61
- package/dist/utils/api-config.d.ts +12 -0
- package/dist/utils/api-config.js +17 -0
- package/dist/utils/auth-guards.d.ts +14 -0
- package/dist/utils/auth-guards.js +18 -0
- package/dist/utils/parsers-html.d.ts +10 -0
- package/dist/utils/parsers-html.js +37 -0
- package/dist/utils/parsers-identifiers.d.ts +21 -0
- package/dist/utils/parsers-identifiers.js +38 -0
- package/dist/utils/parsers-reactions.d.ts +38 -0
- package/dist/utils/parsers-reactions.js +123 -0
- package/dist/utils/parsers.d.ts +4 -2
- package/dist/utils/parsers.js +4 -2
- package/dist/utils/parsers.test.js +168 -1
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -76,8 +76,9 @@ The server uses your system's Chrome (macOS/Linux) or Edge (Windows) for authent
|
|
|
76
76
|
|------|-------------|
|
|
77
77
|
| `teams_search` | Search Teams messages with operators (`from:`, `sent:`, `in:`, `hasattachment:`, etc.) |
|
|
78
78
|
| `teams_search_email` | Search emails in your mailbox (same auth as Teams — no extra login) |
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
79
|
+
| `teams_list_chats` | List recent conversations (1:1, group, meeting, channel) with a last-message preview |
|
|
80
|
+
| `teams_get_message` | Get a single message by ID with full content (any age); includes reactions |
|
|
81
|
+
| `teams_get_thread` | Get messages from a conversation/thread; includes reactions; `threadRootId` scopes to one channel thread; `fromUrl` accepts a Teams message deep link |
|
|
81
82
|
| `teams_find_channel` | Find channels by name (your teams + org-wide discovery) |
|
|
82
83
|
| `teams_get_activity` | Get activity feed (mentions, reactions, replies, notifications) |
|
|
83
84
|
|
|
@@ -85,8 +86,9 @@ The server uses your system's Chrome (macOS/Linux) or Edge (Windows) for authent
|
|
|
85
86
|
|
|
86
87
|
| Tool | Description |
|
|
87
88
|
|------|-------------|
|
|
88
|
-
| `teams_send_message` | Send a message (default: self-chat/notes).
|
|
89
|
-
| `
|
|
89
|
+
| `teams_send_message` | Send a message (default: self-chat/notes). `replyToMessageId` for thread replies, `subject` for a new channel thread, `scheduleAt` to schedule, `contentType` (`auto`/`text`/`html`/`markdown`) to control formatting |
|
|
90
|
+
| `teams_wait_for_reply` | Block until a new message arrives (server-side poll, capped ~110s); idempotent `after`/`nextAfter` cursor — pair with `teams_send_message` |
|
|
91
|
+
| `teams_edit_message` | Edit one of your own messages (`contentType` supported) |
|
|
90
92
|
| `teams_delete_message` | Delete one of your own messages (soft delete) |
|
|
91
93
|
|
|
92
94
|
### People & Contacts
|
|
@@ -96,6 +98,7 @@ The server uses your system's Chrome (macOS/Linux) or Edge (Windows) for authent
|
|
|
96
98
|
| `teams_get_me` | Get current user profile (email, name, ID) |
|
|
97
99
|
| `teams_search_people` | Search for people by name or email |
|
|
98
100
|
| `teams_get_frequent_contacts` | Get frequently contacted people (useful for name resolution) |
|
|
101
|
+
| `teams_get_person` | Resolve one or more MRIs to full profiles (name, email, job title, department) |
|
|
99
102
|
| `teams_get_chat` | Get conversation ID for 1:1 chat with a person |
|
|
100
103
|
| `teams_create_group_chat` | Create a new group chat with multiple people (2+ others) |
|
|
101
104
|
|
|
@@ -257,7 +260,6 @@ Development commands:
|
|
|
257
260
|
npm run dev # Run MCP server in dev mode
|
|
258
261
|
npm run build # Compile TypeScript
|
|
259
262
|
npm run lint # Run ESLint
|
|
260
|
-
npm run research # Explore Teams APIs (logs network calls)
|
|
261
263
|
npm test # Run unit tests
|
|
262
264
|
npm run typecheck # TypeScript type checking
|
|
263
265
|
```
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - chatsvc-reactions: add/remove emoji reactions
|
|
11
11
|
* - chatsvc-virtual: saved messages, followed threads, save/unsave
|
|
12
12
|
* - chatsvc-readstatus: consumption horizons, mark as read, unread counts
|
|
13
|
+
* - chatsvc-conversations: recent conversation list (chats, groups, channels)
|
|
13
14
|
* - chatsvc-common: shared utilities (date formatting)
|
|
14
15
|
*/
|
|
15
16
|
export * from './chatsvc-messaging.js';
|
|
@@ -17,4 +18,5 @@ export * from './chatsvc-activity.js';
|
|
|
17
18
|
export * from './chatsvc-reactions.js';
|
|
18
19
|
export * from './chatsvc-virtual.js';
|
|
19
20
|
export * from './chatsvc-readstatus.js';
|
|
21
|
+
export * from './chatsvc-conversations.js';
|
|
20
22
|
export * from './chatsvc-common.js';
|
package/dist/api/chatsvc-api.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - chatsvc-reactions: add/remove emoji reactions
|
|
11
11
|
* - chatsvc-virtual: saved messages, followed threads, save/unsave
|
|
12
12
|
* - chatsvc-readstatus: consumption horizons, mark as read, unread counts
|
|
13
|
+
* - chatsvc-conversations: recent conversation list (chats, groups, channels)
|
|
13
14
|
* - chatsvc-common: shared utilities (date formatting)
|
|
14
15
|
*/
|
|
15
16
|
// Re-export everything from sub-modules to maintain backward compatibility
|
|
@@ -18,4 +19,5 @@ export * from './chatsvc-activity.js';
|
|
|
18
19
|
export * from './chatsvc-reactions.js';
|
|
19
20
|
export * from './chatsvc-virtual.js';
|
|
20
21
|
export * from './chatsvc-readstatus.js';
|
|
22
|
+
export * from './chatsvc-conversations.js';
|
|
21
23
|
export * from './chatsvc-common.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Service API - Conversation list.
|
|
3
|
+
*
|
|
4
|
+
* Lists the user's recent conversations (1:1 chats, group chats, meetings and
|
|
5
|
+
* channels) with a last-message preview, via the chatsvc conversations endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { type Result } from '../types/result.js';
|
|
8
|
+
/** A summarised conversation as returned by the chat list. */
|
|
9
|
+
export interface ChatSummary {
|
|
10
|
+
conversationId: string;
|
|
11
|
+
/** Channel/group topic, or empty for 1:1 chats. */
|
|
12
|
+
topic: string;
|
|
13
|
+
chatType: 'channel' | 'meeting' | 'group' | 'oneOnOne' | 'chat';
|
|
14
|
+
/** ISO timestamp of the last message, or empty if unknown. */
|
|
15
|
+
lastMessageTime: string;
|
|
16
|
+
/** Display name of the last message's sender. */
|
|
17
|
+
lastMessageFrom: string;
|
|
18
|
+
/** Plain-text preview of the last message, truncated. */
|
|
19
|
+
lastMessagePreview: string;
|
|
20
|
+
}
|
|
21
|
+
/** Result of listing conversations. */
|
|
22
|
+
export interface GetConversationsResult {
|
|
23
|
+
conversations: ChatSummary[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parses a raw chatsvc conversation into a {@link ChatSummary}, or null if it
|
|
27
|
+
* has no usable ID. Pure — no IO — so the field mapping and truncation are
|
|
28
|
+
* unit-testable against fixture JSON.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseConversation(raw: Record<string, unknown>): ChatSummary | null;
|
|
31
|
+
/**
|
|
32
|
+
* Lists the user's recent conversations (chats, group chats, meetings and
|
|
33
|
+
* channels), newest activity first, capped at `limit`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getConversations(options?: {
|
|
36
|
+
limit?: number;
|
|
37
|
+
}): Promise<Result<GetConversationsResult>>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Service API - Conversation list.
|
|
3
|
+
*
|
|
4
|
+
* Lists the user's recent conversations (1:1 chats, group chats, meetings and
|
|
5
|
+
* channels) with a last-message preview, via the chatsvc conversations endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { httpRequest } from '../utils/http.js';
|
|
8
|
+
import { CHATSVC_API, getSkypeAuthHeaders } from '../utils/api-config.js';
|
|
9
|
+
import { ok } from '../types/result.js';
|
|
10
|
+
import { requireMessageAuthWithConfig } from '../utils/auth-guards.js';
|
|
11
|
+
import { stripHtml } from '../utils/parsers.js';
|
|
12
|
+
import { DEFAULT_THREAD_LIMIT } from '../constants.js';
|
|
13
|
+
/** Maximum length of a last-message preview before truncation. */
|
|
14
|
+
const PREVIEW_MAX_LENGTH = 120;
|
|
15
|
+
/** Classifies a conversation type from its ID. */
|
|
16
|
+
function classifyChatType(id) {
|
|
17
|
+
if (id.includes('meeting_'))
|
|
18
|
+
return 'meeting';
|
|
19
|
+
if (id.includes('@thread.tacv2'))
|
|
20
|
+
return 'channel';
|
|
21
|
+
if (id.includes('@unq.gbl.spaces'))
|
|
22
|
+
return 'oneOnOne';
|
|
23
|
+
if (id.includes('@thread.v2'))
|
|
24
|
+
return 'group';
|
|
25
|
+
return 'chat';
|
|
26
|
+
}
|
|
27
|
+
/** Truncates a preview to {@link PREVIEW_MAX_LENGTH} characters, adding an ellipsis. */
|
|
28
|
+
function truncatePreview(text) {
|
|
29
|
+
if (text.length <= PREVIEW_MAX_LENGTH)
|
|
30
|
+
return text;
|
|
31
|
+
return `${text.slice(0, PREVIEW_MAX_LENGTH)}…`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parses a raw chatsvc conversation into a {@link ChatSummary}, or null if it
|
|
35
|
+
* has no usable ID. Pure — no IO — so the field mapping and truncation are
|
|
36
|
+
* unit-testable against fixture JSON.
|
|
37
|
+
*/
|
|
38
|
+
export function parseConversation(raw) {
|
|
39
|
+
const id = typeof raw.id === 'string' ? raw.id : '';
|
|
40
|
+
if (!id)
|
|
41
|
+
return null;
|
|
42
|
+
const threadProperties = (raw.threadProperties ?? {});
|
|
43
|
+
const topic = typeof threadProperties.topic === 'string' ? threadProperties.topic : '';
|
|
44
|
+
const lastMessage = (raw.lastMessage ?? {});
|
|
45
|
+
const lastMessageTime = (typeof lastMessage.originalarrivaltime === 'string' && lastMessage.originalarrivaltime) ||
|
|
46
|
+
(typeof lastMessage.composetime === 'string' && lastMessage.composetime) ||
|
|
47
|
+
'';
|
|
48
|
+
const lastMessageFrom = typeof lastMessage.imdisplayname === 'string' ? lastMessage.imdisplayname : '';
|
|
49
|
+
const rawContent = typeof lastMessage.content === 'string' ? lastMessage.content : '';
|
|
50
|
+
return {
|
|
51
|
+
conversationId: id,
|
|
52
|
+
topic,
|
|
53
|
+
chatType: classifyChatType(id),
|
|
54
|
+
lastMessageTime,
|
|
55
|
+
lastMessageFrom,
|
|
56
|
+
lastMessagePreview: truncatePreview(stripHtml(rawContent)),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Lists the user's recent conversations (chats, group chats, meetings and
|
|
61
|
+
* channels), newest activity first, capped at `limit`.
|
|
62
|
+
*/
|
|
63
|
+
export async function getConversations(options = {}) {
|
|
64
|
+
const authResult = requireMessageAuthWithConfig();
|
|
65
|
+
if (!authResult.ok) {
|
|
66
|
+
return authResult;
|
|
67
|
+
}
|
|
68
|
+
const { auth, region, baseUrl } = authResult.value;
|
|
69
|
+
const limit = options.limit ?? DEFAULT_THREAD_LIMIT;
|
|
70
|
+
const response = await httpRequest(CHATSVC_API.conversations(region, baseUrl), {
|
|
71
|
+
method: 'GET',
|
|
72
|
+
headers: getSkypeAuthHeaders(auth.skypeToken, auth.authToken, baseUrl),
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
return response;
|
|
76
|
+
}
|
|
77
|
+
const raw = Array.isArray(response.value.data.conversations)
|
|
78
|
+
? response.value.data.conversations
|
|
79
|
+
: [];
|
|
80
|
+
const conversations = raw
|
|
81
|
+
.map(c => parseConversation(c))
|
|
82
|
+
.filter((c) => c !== null)
|
|
83
|
+
.slice(0, limit);
|
|
84
|
+
return ok({ conversations });
|
|
85
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for conversation-list parsing.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { parseConversation } from './chatsvc-conversations.js';
|
|
6
|
+
describe('parseConversation', () => {
|
|
7
|
+
it('parses a 1:1 chat with a last-message preview', () => {
|
|
8
|
+
const result = parseConversation({
|
|
9
|
+
id: '19:aaa_bbb@unq.gbl.spaces',
|
|
10
|
+
lastMessage: {
|
|
11
|
+
imdisplayname: 'Bob',
|
|
12
|
+
content: '<p>Hi there</p>',
|
|
13
|
+
originalarrivaltime: '2026-06-30T10:00:00Z',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
expect(result).toEqual({
|
|
17
|
+
conversationId: '19:aaa_bbb@unq.gbl.spaces',
|
|
18
|
+
topic: '',
|
|
19
|
+
chatType: 'oneOnOne',
|
|
20
|
+
lastMessageTime: '2026-06-30T10:00:00Z',
|
|
21
|
+
lastMessageFrom: 'Bob',
|
|
22
|
+
lastMessagePreview: 'Hi there',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
it('classifies channel, group and meeting conversation IDs', () => {
|
|
26
|
+
expect(parseConversation({ id: '19:x@thread.tacv2', threadProperties: { topic: 'General' } })?.chatType).toBe('channel');
|
|
27
|
+
expect(parseConversation({ id: '19:x@thread.v2' })?.chatType).toBe('group');
|
|
28
|
+
expect(parseConversation({ id: '19:meeting_x@thread.v2' })?.chatType).toBe('meeting');
|
|
29
|
+
});
|
|
30
|
+
it('uses the channel/group topic when present', () => {
|
|
31
|
+
const result = parseConversation({ id: '19:x@thread.tacv2', threadProperties: { topic: 'General' } });
|
|
32
|
+
expect(result?.topic).toBe('General');
|
|
33
|
+
});
|
|
34
|
+
it('truncates a long preview to 120 characters with an ellipsis', () => {
|
|
35
|
+
const long = 'a'.repeat(200);
|
|
36
|
+
const result = parseConversation({ id: '19:x@thread.v2', lastMessage: { content: long } });
|
|
37
|
+
expect(result?.lastMessagePreview.length).toBe(121); // 120 chars + ellipsis
|
|
38
|
+
expect(result?.lastMessagePreview.endsWith('…')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('returns null when the conversation has no id', () => {
|
|
41
|
+
expect(parseConversation({})).toBeNull();
|
|
42
|
+
expect(parseConversation({ id: '' })).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
it('falls back to composetime when originalarrivaltime is absent', () => {
|
|
45
|
+
const result = parseConversation({
|
|
46
|
+
id: '19:x@thread.v2',
|
|
47
|
+
lastMessage: { composetime: '2026-06-30T09:00:00Z', content: 'hey' },
|
|
48
|
+
});
|
|
49
|
+
expect(result?.lastMessageTime).toBe('2026-06-30T09:00:00Z');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* Conversation properties and participant extraction.
|
|
6
6
|
*/
|
|
7
7
|
import { type Result } from '../types/result.js';
|
|
8
|
-
import { type ExtractedLink } from '../utils/parsers.js';
|
|
8
|
+
import { type ExtractedLink, type Reaction, type ReactionSummary } from '../utils/parsers.js';
|
|
9
9
|
/** Result of sending a message. */
|
|
10
10
|
export interface SendMessageResult {
|
|
11
11
|
messageId: string;
|
|
12
12
|
timestamp?: number;
|
|
13
|
+
/** For scheduled messages: the ISO 8601 UTC time the message will be sent. */
|
|
14
|
+
scheduledFor?: string;
|
|
13
15
|
}
|
|
14
16
|
/** A message from a thread/conversation. */
|
|
15
17
|
export interface ThreadMessage {
|
|
@@ -32,6 +34,10 @@ export interface ThreadMessage {
|
|
|
32
34
|
threadRootId?: string;
|
|
33
35
|
/** True if this message is a reply within a channel thread (not a top-level post) */
|
|
34
36
|
isThreadReply?: boolean;
|
|
37
|
+
/** Emoji reactions on this message, with reactor identity (names resolved where possible). */
|
|
38
|
+
reactions?: Reaction[];
|
|
39
|
+
/** Reaction counts keyed by emoji (e.g. { like: 3, heart: 1 }). */
|
|
40
|
+
reactionSummary?: ReactionSummary;
|
|
35
41
|
}
|
|
36
42
|
/** Result of getting thread messages. */
|
|
37
43
|
export interface GetThreadResult {
|
|
@@ -68,6 +74,24 @@ export interface SendMessageOptions {
|
|
|
68
74
|
* are part of the same flat conversation.
|
|
69
75
|
*/
|
|
70
76
|
replyToMessageId?: string;
|
|
77
|
+
/**
|
|
78
|
+
* How to interpret the content. Defaults to `markdown` (convert markdown +
|
|
79
|
+
* @mentions + links to Teams HTML — the historical behaviour). Use `text` to
|
|
80
|
+
* send verbatim with no reinterpretation, `html` for caller-supplied Teams
|
|
81
|
+
* HTML, or `auto` to convert only when markdown/mention syntax is present.
|
|
82
|
+
*/
|
|
83
|
+
contentType?: ContentType;
|
|
84
|
+
/**
|
|
85
|
+
* For channel posts: a thread subject/title. Starts a new titled thread.
|
|
86
|
+
* Ignored for 1:1/group chats (they have no per-message subject).
|
|
87
|
+
*/
|
|
88
|
+
subject?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Schedule the message for future delivery. ISO 8601 (e.g.
|
|
91
|
+
* "2026-04-11T09:00:00Z"); a timezone-less value is treated as UTC. Cannot be
|
|
92
|
+
* combined with mentions or a thread reply.
|
|
93
|
+
*/
|
|
94
|
+
scheduleAt?: string;
|
|
71
95
|
}
|
|
72
96
|
/** Result of getting a 1:1 conversation. */
|
|
73
97
|
export interface GetOneOnOneChatResult {
|
|
@@ -116,11 +140,13 @@ export declare function getMessage(conversationId: string, messageId: string): P
|
|
|
116
140
|
* @param options.limit - Maximum messages to return (default 50)
|
|
117
141
|
* @param options.startTime - Fetch messages from this timestamp onwards
|
|
118
142
|
* @param options.order - Sort order: 'desc' (newest-first, default) or 'asc' (oldest-first)
|
|
143
|
+
* @param options.replyToMessageId - For channels: scope to replies of a specific top-level post
|
|
119
144
|
*/
|
|
120
145
|
export declare function getThreadMessages(conversationId: string, options?: {
|
|
121
146
|
limit?: number;
|
|
122
147
|
startTime?: number;
|
|
123
148
|
order?: 'asc' | 'desc';
|
|
149
|
+
replyToMessageId?: string;
|
|
124
150
|
}): Promise<Result<GetThreadResult>>;
|
|
125
151
|
/**
|
|
126
152
|
* Edits an existing message.
|
|
@@ -132,9 +158,10 @@ export declare function getThreadMessages(conversationId: string, options?: {
|
|
|
132
158
|
*
|
|
133
159
|
* @param conversationId - The conversation containing the message
|
|
134
160
|
* @param messageId - The ID of the message to edit
|
|
135
|
-
* @param newContent - New content (markdown; supports mentions like sendMessage)
|
|
161
|
+
* @param newContent - New content (markdown by default; supports mentions like sendMessage)
|
|
162
|
+
* @param contentType - How to interpret the content (default `markdown`)
|
|
136
163
|
*/
|
|
137
|
-
export declare function editMessage(conversationId: string, messageId: string, newContent: string): Promise<Result<EditMessageResult>>;
|
|
164
|
+
export declare function editMessage(conversationId: string, messageId: string, newContent: string, contentType?: ContentType): Promise<Result<EditMessageResult>>;
|
|
138
165
|
/**
|
|
139
166
|
* Deletes a message (soft delete).
|
|
140
167
|
*
|
|
@@ -206,3 +233,128 @@ export declare function parseContentWithMentionsAndLinks(content: string): {
|
|
|
206
233
|
html: string;
|
|
207
234
|
mentions: Mention[];
|
|
208
235
|
};
|
|
236
|
+
/** How message content should be interpreted before sending. */
|
|
237
|
+
export type ContentType = 'auto' | 'text' | 'html' | 'markdown';
|
|
238
|
+
/** Content resolved into the shape the chatsvc/Graph message body expects. */
|
|
239
|
+
export interface ResolvedContent {
|
|
240
|
+
/** The body content to send. */
|
|
241
|
+
content: string;
|
|
242
|
+
/** The wire message type: plain `Text` or `RichText/Html`. */
|
|
243
|
+
messagetype: 'Text' | 'RichText/Html';
|
|
244
|
+
/** Any @mentions extracted from the content (empty for text/html modes). */
|
|
245
|
+
mentions: Mention[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolves outbound message content according to the requested content type.
|
|
249
|
+
*
|
|
250
|
+
* - `markdown` (default): convert markdown + @mentions + links to Teams HTML.
|
|
251
|
+
* This preserves the historical behaviour where all content was converted.
|
|
252
|
+
* - `text`: send verbatim as plain text — no markdown, mention or link
|
|
253
|
+
* processing. The escape hatch for content that should not be reinterpreted
|
|
254
|
+
* (e.g. `5*3*2`, `a_b_c`).
|
|
255
|
+
* - `html`: caller-supplied Teams HTML, sent as `RichText/Html` unchanged.
|
|
256
|
+
* - `auto`: convert only when the content actually contains markdown formatting
|
|
257
|
+
* or mention/link syntax; otherwise send plain text.
|
|
258
|
+
*/
|
|
259
|
+
export declare function resolveMessageContent(content: string, contentType?: ContentType): ResolvedContent;
|
|
260
|
+
/**
|
|
261
|
+
* Parses and validates a schedule datetime for a scheduled message.
|
|
262
|
+
*
|
|
263
|
+
* Accepts ISO 8601 (with or without a timezone) and a space-separated
|
|
264
|
+
* `YYYY-MM-DD HH:mm[:ss]` form. A timezone-less value is treated as UTC (so
|
|
265
|
+
* results are deterministic regardless of the host clock). Rejects unparseable
|
|
266
|
+
* or past times. `now` is injectable for testing.
|
|
267
|
+
*/
|
|
268
|
+
export declare function parseScheduleTime(input: string, now?: number): Result<{
|
|
269
|
+
iso: string;
|
|
270
|
+
epochMs: number;
|
|
271
|
+
}>;
|
|
272
|
+
/**
|
|
273
|
+
* Builds the chatsvc `/drafts` request body for a scheduled message — the same
|
|
274
|
+
* mechanism the Teams web client uses. `sendAt` must be epoch milliseconds.
|
|
275
|
+
*/
|
|
276
|
+
export declare function buildScheduledDraftBody(opts: {
|
|
277
|
+
conversationId: string;
|
|
278
|
+
content: string;
|
|
279
|
+
messagetype: 'Text' | 'RichText/Html';
|
|
280
|
+
epochMs: number;
|
|
281
|
+
userMri: string;
|
|
282
|
+
isoNow: string;
|
|
283
|
+
conversationLink: string;
|
|
284
|
+
clientMessageId: string;
|
|
285
|
+
}): Record<string, unknown>;
|
|
286
|
+
/**
|
|
287
|
+
* Extracts the two member object IDs from a 1:1 conversation ID of the form
|
|
288
|
+
* `19:<a>_<b>@unq.gbl.spaces`. Returns null for any other conversation type or
|
|
289
|
+
* a malformed ID (so the caller only attempts thread creation for real 1:1s).
|
|
290
|
+
*/
|
|
291
|
+
export declare function extractOneOnOneMemberIds(conversationId: string): string[] | null;
|
|
292
|
+
/**
|
|
293
|
+
* Builds the `/v1/threads` body that creates the unique-roster 1:1 thread — what
|
|
294
|
+
* the Teams client does the first time you message someone. Both members are
|
|
295
|
+
* added as Admins with the fixed unique-roster properties.
|
|
296
|
+
*/
|
|
297
|
+
export declare function buildOneOnOneThreadBody(memberIds: string[]): Record<string, unknown>;
|
|
298
|
+
/** Result of waiting for a new message in a conversation. */
|
|
299
|
+
export interface WaitForReplyResult {
|
|
300
|
+
/** True if the wait timed out before any new message arrived. */
|
|
301
|
+
timedOut: boolean;
|
|
302
|
+
/** The baseline message ID the wait started from. */
|
|
303
|
+
after: string;
|
|
304
|
+
/** Resume cursor: pass as `after` to a follow-up wait to continue from here. */
|
|
305
|
+
nextAfter: string;
|
|
306
|
+
/** The new messages that arrived (oldest first), or empty on timeout. */
|
|
307
|
+
newMessages: ThreadMessage[];
|
|
308
|
+
/** How many poll iterations ran. */
|
|
309
|
+
polls: number;
|
|
310
|
+
/** How many seconds the wait blocked for. */
|
|
311
|
+
waitedSeconds: number;
|
|
312
|
+
}
|
|
313
|
+
/** Options for {@link waitForReply}. */
|
|
314
|
+
export interface WaitForReplyOptions {
|
|
315
|
+
/** Only return messages newer than this message ID. Defaults to the caller's most recent message. */
|
|
316
|
+
after?: string;
|
|
317
|
+
/** Maximum seconds to block (clamped to MAX_WAIT_SECONDS). */
|
|
318
|
+
maxWaitSeconds?: number;
|
|
319
|
+
/** Seconds between polls. */
|
|
320
|
+
intervalSeconds?: number;
|
|
321
|
+
/** Include the caller's own messages in the result (default false). */
|
|
322
|
+
includeSelf?: boolean;
|
|
323
|
+
/** Messages fetched per poll (default 50). */
|
|
324
|
+
limit?: number;
|
|
325
|
+
}
|
|
326
|
+
/** Clamps the wait timing knobs into their sane range (pure, for testability). */
|
|
327
|
+
export declare function clampWaitParams(maxWaitSeconds: number, intervalSeconds: number): {
|
|
328
|
+
maxWait: number;
|
|
329
|
+
interval: number;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* Selects the messages strictly newer than `afterId`, oldest first. Excludes the
|
|
333
|
+
* caller's own messages unless `includeSelf` is set; ignores non-numeric IDs.
|
|
334
|
+
* Pure so the filter/sort semantics are unit-testable.
|
|
335
|
+
*/
|
|
336
|
+
export declare function selectNewMessages<T extends {
|
|
337
|
+
id: string;
|
|
338
|
+
isFromMe?: boolean;
|
|
339
|
+
}>(messages: readonly T[], afterId: number, includeSelf: boolean): T[];
|
|
340
|
+
/**
|
|
341
|
+
* Resolves the baseline message ID for a wait with no explicit `after`: the
|
|
342
|
+
* caller's own most recent message, else the most recent message overall, else
|
|
343
|
+
* 0 for an empty thread. Anchoring to the caller's own last message makes a wait
|
|
344
|
+
* restarted after a send idempotent without remembering the sent ID. Pure.
|
|
345
|
+
*/
|
|
346
|
+
export declare function resolveWaitBaseline(messages: readonly {
|
|
347
|
+
id: string;
|
|
348
|
+
isFromMe?: boolean;
|
|
349
|
+
}[]): number;
|
|
350
|
+
/**
|
|
351
|
+
* Blocks until a new message appears in a conversation, then returns only the
|
|
352
|
+
* new messages. Polls inside the call so a waiting agent makes a single tool
|
|
353
|
+
* call instead of a poll loop. Read-only and idempotent: it never posts, and the
|
|
354
|
+
* `after` baseline makes a restart resume from the same point without replaying.
|
|
355
|
+
*
|
|
356
|
+
* The block is capped at MAX_WAIT_SECONDS so an MCP client timeout never kills
|
|
357
|
+
* the call; on timeout it returns `timedOut: true` with `nextAfter` so the
|
|
358
|
+
* caller can simply call again to keep waiting.
|
|
359
|
+
*/
|
|
360
|
+
export declare function waitForReply(conversationId: string, options?: WaitForReplyOptions): Promise<Result<WaitForReplyResult>>;
|