@floriscornel/teams-mcp 0.7.0 ā 0.9.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 +231 -55
- package/dist/index.js +77 -32
- package/dist/index.js.map +1 -1
- package/dist/services/graph.d.ts +10 -0
- package/dist/services/graph.d.ts.map +1 -1
- package/dist/services/graph.js +24 -5
- package/dist/services/graph.js.map +1 -1
- package/dist/tools/auth.d.ts +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +1 -1
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/chats.d.ts +2 -1
- package/dist/tools/chats.d.ts.map +1 -1
- package/dist/tools/chats.js +278 -6
- package/dist/tools/chats.js.map +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +1 -1
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/teams.d.ts +1 -1
- package/dist/tools/teams.d.ts.map +1 -1
- package/dist/tools/teams.js +668 -465
- package/dist/tools/teams.js.map +1 -1
- package/dist/tools/users.d.ts +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +1 -1
- package/dist/tools/users.js.map +1 -1
- package/dist/types/graph.d.ts +16 -2
- package/dist/types/graph.d.ts.map +1 -1
- package/dist/utils/attachments.d.ts +7 -0
- package/dist/utils/attachments.d.ts.map +1 -1
- package/dist/utils/attachments.js +22 -0
- package/dist/utils/attachments.js.map +1 -1
- package/dist/utils/content-type.d.ts +6 -0
- package/dist/utils/content-type.d.ts.map +1 -0
- package/dist/utils/content-type.js +43 -0
- package/dist/utils/content-type.js.map +1 -0
- package/dist/utils/file-upload.d.ts +51 -0
- package/dist/utils/file-upload.d.ts.map +1 -0
- package/dist/utils/file-upload.js +258 -0
- package/dist/utils/file-upload.js.map +1 -0
- package/dist/utils/html-to-markdown.d.ts +5 -2
- package/dist/utils/html-to-markdown.d.ts.map +1 -1
- package/dist/utils/html-to-markdown.js +92 -7
- package/dist/utils/html-to-markdown.js.map +1 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://github.com/floriscornel/teams-mcp/stargazers)
|
|
8
8
|
|
|
9
|
-
A Model Context Protocol (MCP) server that provides seamless integration with Microsoft Graph APIs, enabling AI assistants to interact with Microsoft Teams, users, and organizational data.
|
|
9
|
+
A Model Context Protocol (MCP) server that provides seamless integration with Microsoft Graph APIs, enabling AI assistants to interact with Microsoft Teams, users, chats, files, and organizational data.
|
|
10
10
|
|
|
11
11
|
<a href="https://glama.ai/mcp/servers/@floriscornel/teams-mcp">
|
|
12
12
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/@floriscornel/teams-mcp/badge" alt="Teams MCP server" />
|
|
@@ -30,9 +30,11 @@ To use this MCP server in Cursor/Claude/VS Code, add the following configuration
|
|
|
30
30
|
## š Features
|
|
31
31
|
|
|
32
32
|
### š Authentication
|
|
33
|
-
- OAuth 2.0 authentication flow with Microsoft Graph
|
|
34
|
-
- Secure token management and refresh
|
|
35
|
-
- Authentication status checking
|
|
33
|
+
- OAuth 2.0 device code authentication flow with Microsoft Graph
|
|
34
|
+
- Secure token management, cache persistence, and refresh token renewal
|
|
35
|
+
- Authentication status checking and logout support
|
|
36
|
+
- Read-only mode with reduced scopes
|
|
37
|
+
- Direct `AUTH_TOKEN` support for pre-issued Microsoft Graph access tokens
|
|
36
38
|
|
|
37
39
|
### š„ User Management
|
|
38
40
|
- Get current user information
|
|
@@ -44,22 +46,27 @@ To use this MCP server in Cursor/Claude/VS Code, add the following configuration
|
|
|
44
46
|
- **Teams Management**
|
|
45
47
|
- List user's joined teams
|
|
46
48
|
- Access team details and metadata
|
|
47
|
-
|
|
49
|
+
|
|
48
50
|
- **Channel Operations**
|
|
49
51
|
- List channels within teams
|
|
50
|
-
- Retrieve channel messages
|
|
52
|
+
- Retrieve channel messages and replies
|
|
51
53
|
- Send messages to team channels
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
+
- Reply to existing channel threads
|
|
55
|
+
- Edit and soft delete channel messages and replies
|
|
56
|
+
- Support for message importance levels (`normal`, `high`, `urgent`)
|
|
57
|
+
- Support for inline image attachments via URL or base64 data
|
|
58
|
+
|
|
54
59
|
- **Team Members**
|
|
55
60
|
- List team members and their roles
|
|
56
61
|
- Access member information
|
|
62
|
+
- Search users for `@mentions`
|
|
57
63
|
|
|
58
64
|
### š¬ Chat & Messaging
|
|
59
65
|
- **1:1 and Group Chats**
|
|
60
66
|
- List user's chats
|
|
61
67
|
- Create new 1:1 or group conversations
|
|
62
|
-
- Retrieve chat message history with filtering and pagination
|
|
68
|
+
- Retrieve chat message history with filtering, ordering, and pagination
|
|
69
|
+
- Fetch all available messages via `@odata.nextLink` pagination
|
|
63
70
|
- Send messages to existing chats
|
|
64
71
|
- Edit previously sent chat messages
|
|
65
72
|
- Soft delete chat messages
|
|
@@ -75,29 +82,38 @@ To use this MCP server in Cursor/Claude/VS Code, add the following configuration
|
|
|
75
82
|
- **Hosted Content**
|
|
76
83
|
- Download hosted content (images, files) from chat and channel messages
|
|
77
84
|
- Access inline images and attachments shared in conversations
|
|
85
|
+
- Optionally save hosted content directly to disk
|
|
86
|
+
|
|
87
|
+
- **File Upload**
|
|
88
|
+
- Upload and send any file type (PDF, DOCX, XLSX, ZIP, images, etc.) to channels and chats
|
|
89
|
+
- Large file support (>4 MB) via resumable upload sessions
|
|
90
|
+
- Channel uploads go to SharePoint and chat uploads go to OneDrive
|
|
91
|
+
- Optional message text, custom filename, formatting, and importance levels
|
|
78
92
|
|
|
79
93
|
### š Advanced Search & Discovery
|
|
80
94
|
- **Message Search**
|
|
81
95
|
- Search across all Teams channels and chats using Microsoft Search API
|
|
82
96
|
- Support for KQL (Keyword Query Language) syntax
|
|
83
|
-
- Filter by sender, mentions, attachments,
|
|
97
|
+
- Filter by sender, mentions, attachments, read state, and date ranges
|
|
84
98
|
- Get recent messages with advanced filtering options
|
|
85
|
-
- Find messages mentioning
|
|
99
|
+
- Find messages mentioning the current user
|
|
86
100
|
|
|
87
101
|
## Rich Message Formatting Support
|
|
88
102
|
|
|
89
|
-
The following tools
|
|
103
|
+
The following tools support rich message formatting in Teams channels and chats:
|
|
90
104
|
- `send_channel_message`
|
|
91
105
|
- `send_chat_message`
|
|
92
106
|
- `reply_to_channel_message`
|
|
93
107
|
- `update_channel_message`
|
|
94
108
|
- `update_chat_message`
|
|
109
|
+
- `send_file_to_channel`
|
|
110
|
+
- `send_file_to_chat`
|
|
95
111
|
|
|
96
112
|
### Format Options
|
|
97
113
|
|
|
98
114
|
You can specify the `format` parameter to control the message formatting:
|
|
99
115
|
- `text` (default): Plain text
|
|
100
|
-
- `markdown`: Markdown formatting (bold, italic, lists, links, code, etc.)
|
|
116
|
+
- `markdown`: Markdown formatting (bold, italic, lists, links, code, etc.) converted to sanitized HTML
|
|
101
117
|
|
|
102
118
|
When `format` is set to `markdown`, the message content is converted to HTML using a secure markdown parser and sanitized to remove potentially dangerous content before being sent to Teams.
|
|
103
119
|
|
|
@@ -110,7 +126,8 @@ If `format` is not specified, the message will be sent as plain text.
|
|
|
110
126
|
"teamId": "...",
|
|
111
127
|
"channelId": "...",
|
|
112
128
|
"message": "**Bold text** and _italic text_\n\n- List item 1\n- List item 2\n\n[Link](https://example.com)",
|
|
113
|
-
"format": "markdown"
|
|
129
|
+
"format": "markdown",
|
|
130
|
+
"importance": "high"
|
|
114
131
|
}
|
|
115
132
|
```
|
|
116
133
|
|
|
@@ -126,17 +143,16 @@ If `format` is not specified, the message will be sent as plain text.
|
|
|
126
143
|
|
|
127
144
|
- **HTML Sanitization**: All markdown content is converted to HTML and sanitized to remove potentially dangerous elements (scripts, event handlers, etc.)
|
|
128
145
|
- **Allowed Tags**: Only safe HTML tags are permitted (p, strong, em, a, ul, ol, li, h1-h6, code, pre, etc.)
|
|
129
|
-
- **Safe Attributes**: Only safe attributes are allowed
|
|
146
|
+
- **Safe Attributes**: Only safe attributes are allowed
|
|
130
147
|
- **XSS Prevention**: Content is automatically sanitized to prevent cross-site scripting attacks
|
|
131
148
|
|
|
132
149
|
### Supported Markdown Features
|
|
133
150
|
|
|
134
151
|
- **Text formatting**: Bold (`**text**`), italic (`_text_`), strikethrough (`~~text~~`)
|
|
135
|
-
- **Links**: `[text](url)`
|
|
152
|
+
- **Links**: `[text](url)`
|
|
136
153
|
- **Lists**: Bulleted (`- item`) and numbered (`1. item`)
|
|
137
|
-
- **Code**: Inline `` `code` `` and
|
|
154
|
+
- **Code**: Inline `` `code` `` and fenced code blocks
|
|
138
155
|
- **Headings**: `# H1` through `###### H6`
|
|
139
|
-
- **Line breaks**: Automatic conversion of newlines to `<br>` tags
|
|
140
156
|
- **Blockquotes**: `> quoted text`
|
|
141
157
|
- **Tables**: GitHub-flavored markdown tables
|
|
142
158
|
|
|
@@ -158,19 +174,23 @@ Use the `contentFormat` parameter to control how message content is returned:
|
|
|
158
174
|
|
|
159
175
|
### What Gets Converted
|
|
160
176
|
|
|
161
|
-
| HTML Element
|
|
162
|
-
|
|
163
|
-
| `<at id="0">Name</at>` (Teams mention) | `@Name` |
|
|
164
|
-
| `<strong>text</strong>`
|
|
165
|
-
| `<em>text</em>`
|
|
166
|
-
| `<code>text</code>`
|
|
167
|
-
| `<a href="url">text</a>`
|
|
168
|
-
| `<ul><li>item</li></ul>`
|
|
169
|
-
| `<table>...</table>`
|
|
170
|
-
| `<attachment id="...">`
|
|
171
|
-
| `<systemEventMessage/>`
|
|
172
|
-
| `<hr>`
|
|
173
|
-
| ` `, `&`, etc.
|
|
177
|
+
| HTML Element | Markdown Output |
|
|
178
|
+
| -------------------------------------- | --------------------------------------------------------- |
|
|
179
|
+
| `<at id="0">Name</at>` (Teams mention) | `@Name` (multi-word names merged using mentions metadata) |
|
|
180
|
+
| `<strong>text</strong>` | `**text**` |
|
|
181
|
+
| `<em>text</em>` | `*text*` |
|
|
182
|
+
| `<code>text</code>` | `` `text` `` |
|
|
183
|
+
| `<a href="url">text</a>` | `[text](url)` |
|
|
184
|
+
| `<ul><li>item</li></ul>` | `- item` |
|
|
185
|
+
| `<table>...</table>` | GFM Markdown table |
|
|
186
|
+
| `<attachment id="...">` | `{attachment:id}` |
|
|
187
|
+
| `<systemEventMessage/>` | *(removed)* |
|
|
188
|
+
| `<hr>` | `---` |
|
|
189
|
+
| ` `, `&`, etc. | Decoded to plain characters |
|
|
190
|
+
|
|
191
|
+
### Attachment Metadata
|
|
192
|
+
|
|
193
|
+
Messages that contain file attachments or inline images include an `attachments` array in the response with metadata for each attachment (id, name, contentType, contentUrl, thumbnailUrl). The inline `{attachment:id}` markers in the markdown content correlate with entries in this array, allowing consumers to identify and download attachments via `download_message_hosted_content` or `download_chat_hosted_content`.
|
|
174
194
|
|
|
175
195
|
### Example Usage
|
|
176
196
|
|
|
@@ -182,7 +202,7 @@ Use the `contentFormat` parameter to control how message content is returned:
|
|
|
182
202
|
}
|
|
183
203
|
```
|
|
184
204
|
|
|
185
|
-
To get the original HTML
|
|
205
|
+
To get the original HTML:
|
|
186
206
|
|
|
187
207
|
```json
|
|
188
208
|
{
|
|
@@ -210,22 +230,66 @@ npm run auth
|
|
|
210
230
|
### Prerequisites
|
|
211
231
|
- Node.js 18+
|
|
212
232
|
- Microsoft 365 account with appropriate permissions
|
|
213
|
-
-
|
|
233
|
+
- Microsoft Graph delegated permissions for the scopes below
|
|
214
234
|
|
|
215
235
|
### Required Microsoft Graph Permissions
|
|
236
|
+
|
|
237
|
+
**Full mode (default):**
|
|
216
238
|
- `User.Read` - Read user profile
|
|
217
239
|
- `User.ReadBasic.All` - Read basic user info
|
|
218
240
|
- `Team.ReadBasic.All` - Read team information
|
|
219
241
|
- `Channel.ReadBasic.All` - Read channel information
|
|
220
242
|
- `ChannelMessage.Read.All` - Read channel messages
|
|
221
|
-
- `ChannelMessage.Send` - Send channel messages
|
|
243
|
+
- `ChannelMessage.Send` - Send channel messages and replies
|
|
222
244
|
- `ChannelMessage.ReadWrite` - Edit and delete channel messages
|
|
223
|
-
- `Chat.Read` - Read chat messages
|
|
224
|
-
- `Chat.ReadWrite` - Create and manage chats
|
|
225
|
-
- `
|
|
226
|
-
- `
|
|
227
|
-
|
|
228
|
-
- `
|
|
245
|
+
- `Chat.Read` - Read chat messages (included via read-only scopes)
|
|
246
|
+
- `Chat.ReadWrite` - Create and manage chats, send/edit/delete chat messages (supersedes `Chat.Read`)
|
|
247
|
+
- `TeamMember.Read.All` - Read team members
|
|
248
|
+
- `Files.ReadWrite.All` - Required for file uploads to channels and chats
|
|
249
|
+
|
|
250
|
+
**Read-only mode** (`TEAMS_MCP_READ_ONLY=true`) ā only these scopes are requested:
|
|
251
|
+
- `User.Read`
|
|
252
|
+
- `User.ReadBasic.All`
|
|
253
|
+
- `Team.ReadBasic.All`
|
|
254
|
+
- `Channel.ReadBasic.All`
|
|
255
|
+
- `ChannelMessage.Read.All`
|
|
256
|
+
- `TeamMember.Read.All`
|
|
257
|
+
- `Chat.Read`
|
|
258
|
+
|
|
259
|
+
### Authentication Modes
|
|
260
|
+
|
|
261
|
+
**Full access:**
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npx @floriscornel/teams-mcp@latest authenticate
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Read-only access:**
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npx @floriscornel/teams-mcp@latest authenticate --read-only
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Direct token injection with an existing Microsoft Graph JWT:**
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"mcpServers": {
|
|
278
|
+
"teams-mcp": {
|
|
279
|
+
"command": "npx",
|
|
280
|
+
"args": ["-y", "@floriscornel/teams-mcp@latest"],
|
|
281
|
+
"env": {
|
|
282
|
+
"AUTH_TOKEN": "<jwt-for-https://graph.microsoft.com>"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Token Storage
|
|
290
|
+
|
|
291
|
+
- Auth metadata is stored locally at `~/.msgraph-mcp-auth.json`
|
|
292
|
+
- Token cache is stored locally at `~/.teams-mcp-token-cache.json`
|
|
229
293
|
|
|
230
294
|
## š ļø Usage
|
|
231
295
|
|
|
@@ -236,43 +300,105 @@ npm run dev
|
|
|
236
300
|
|
|
237
301
|
# Production mode
|
|
238
302
|
npm run build && node dist/index.js
|
|
303
|
+
|
|
304
|
+
# Start in read-only mode (disables all write tools)
|
|
305
|
+
TEAMS_MCP_READ_ONLY=true node dist/index.js
|
|
239
306
|
```
|
|
240
307
|
|
|
308
|
+
### CLI Commands
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npx @floriscornel/teams-mcp@latest authenticate # Authenticate with full scopes
|
|
312
|
+
npx @floriscornel/teams-mcp@latest authenticate --read-only # Authenticate with read-only scopes
|
|
313
|
+
npx @floriscornel/teams-mcp@latest check # Check authentication status
|
|
314
|
+
npx @floriscornel/teams-mcp@latest logout # Clear authentication
|
|
315
|
+
npx @floriscornel/teams-mcp@latest auth # Alias for authenticate
|
|
316
|
+
npx @floriscornel/teams-mcp@latest # Start MCP server (default)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Environment Variables
|
|
320
|
+
|
|
321
|
+
- `TEAMS_MCP_READ_ONLY=true` - Start the MCP server in read-only mode
|
|
322
|
+
- `AUTH_TOKEN=<jwt>` - Use a pre-existing Microsoft Graph access token instead of MSAL login
|
|
323
|
+
|
|
324
|
+
### Read-Only Mode
|
|
325
|
+
|
|
326
|
+
The server supports a read-only mode that disables all write operations (sending messages, creating chats, uploading files, editing/deleting messages) and requests only read-permission scopes from Microsoft Graph.
|
|
327
|
+
|
|
328
|
+
**Enable read-only mode** using either:
|
|
329
|
+
- Environment variable: `TEAMS_MCP_READ_ONLY=true`
|
|
330
|
+
- CLI flag: `--read-only`
|
|
331
|
+
|
|
332
|
+
**Authenticate with reduced scopes:**
|
|
333
|
+
```bash
|
|
334
|
+
npx @floriscornel/teams-mcp@latest authenticate --read-only
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**MCP server configuration (read-only):**
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"mcpServers": {
|
|
341
|
+
"teams-mcp": {
|
|
342
|
+
"command": "npx",
|
|
343
|
+
"args": ["-y", "@floriscornel/teams-mcp@latest"],
|
|
344
|
+
"env": {
|
|
345
|
+
"TEAMS_MCP_READ_ONLY": "true"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Switching modes:** When switching from read-only to full mode, the server detects the scope mismatch and warns you to re-authenticate:
|
|
353
|
+
```bash
|
|
354
|
+
npx @floriscornel/teams-mcp@latest authenticate
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Read-only tools (16):**
|
|
358
|
+
`auth_status`, `get_current_user`, `search_users`, `get_user`, `list_teams`, `list_channels`, `get_channel_messages`, `get_channel_message_replies`, `list_team_members`, `search_users_for_mentions`, `download_message_hosted_content`, `list_chats`, `get_chat_messages`, `download_chat_hosted_content`, `search_messages`, `get_my_mentions`
|
|
359
|
+
|
|
360
|
+
**Write tools disabled in read-only mode (10):**
|
|
361
|
+
`send_channel_message`, `reply_to_channel_message`, `update_channel_message`, `delete_channel_message`, `send_file_to_channel`, `send_chat_message`, `create_chat`, `update_chat_message`, `delete_chat_message`, `send_file_to_chat`
|
|
362
|
+
|
|
241
363
|
### Available MCP Tools
|
|
242
364
|
|
|
243
365
|
#### Authentication
|
|
244
|
-
- `
|
|
245
|
-
- `logout` - Clear authentication tokens
|
|
246
|
-
- `get_current_user` - Get authenticated user information
|
|
366
|
+
- `auth_status` - Check current authentication status
|
|
247
367
|
|
|
248
368
|
#### User Operations
|
|
369
|
+
- `get_current_user` - Get authenticated user information
|
|
249
370
|
- `search_users` - Search for users by name or email
|
|
250
371
|
- `get_user` - Get detailed user information by ID or email
|
|
251
372
|
|
|
252
373
|
#### Teams Operations
|
|
253
374
|
- `list_teams` - List user's joined teams
|
|
254
375
|
- `list_channels` - List channels in a specific team
|
|
255
|
-
- `get_channel_messages` - Retrieve messages from a team channel with
|
|
256
|
-
- `
|
|
257
|
-
- `
|
|
258
|
-
- `
|
|
376
|
+
- `get_channel_messages` - Retrieve messages from a team channel with attachment summaries and content format selection
|
|
377
|
+
- `get_channel_message_replies` - Get replies to a specific channel message
|
|
378
|
+
- `send_channel_message` - Send a message to a team channel with optional mentions, importance, and image attachments
|
|
379
|
+
- `reply_to_channel_message` - Reply to an existing channel message
|
|
380
|
+
- `update_channel_message` - Edit a previously sent channel message or reply
|
|
381
|
+
- `delete_channel_message` - Soft delete a channel message or reply
|
|
259
382
|
- `list_team_members` - List members of a specific team
|
|
383
|
+
- `search_users_for_mentions` - Search for team members to @mention in messages
|
|
384
|
+
- `send_file_to_channel` - Upload a local file and send it as a message to a channel
|
|
260
385
|
|
|
261
386
|
#### Chat Operations
|
|
262
387
|
- `list_chats` - List user's chats (1:1 and group)
|
|
263
|
-
- `get_chat_messages` - Retrieve messages from a specific chat with pagination and
|
|
388
|
+
- `get_chat_messages` - Retrieve messages from a specific chat with pagination, filters, ordering, and `fetchAll`
|
|
264
389
|
- `send_chat_message` - Send a message to a chat
|
|
265
390
|
- `create_chat` - Create a new 1:1 or group chat
|
|
266
391
|
- `update_chat_message` - Edit a previously sent chat message
|
|
267
392
|
- `delete_chat_message` - Soft delete a chat message
|
|
393
|
+
- `send_file_to_chat` - Upload a local file and send it as a message to a chat
|
|
268
394
|
|
|
269
395
|
#### Media Operations
|
|
270
|
-
- `download_message_hosted_content` - Download hosted content (images, files) from messages
|
|
396
|
+
- `download_message_hosted_content` - Download hosted content (images, files) from channel messages
|
|
397
|
+
- `download_chat_hosted_content` - Download hosted content (images, files) from chat messages
|
|
271
398
|
|
|
272
399
|
#### Search Operations
|
|
273
400
|
- `search_messages` - Search across all Teams messages using KQL syntax
|
|
274
|
-
- `
|
|
275
|
-
- `get_my_mentions` - Find messages mentioning the current user
|
|
401
|
+
- `get_my_mentions` - Find recent messages mentioning the current user
|
|
276
402
|
|
|
277
403
|
## š Examples
|
|
278
404
|
|
|
@@ -281,7 +407,11 @@ npm run build && node dist/index.js
|
|
|
281
407
|
First, authenticate with Microsoft Graph:
|
|
282
408
|
|
|
283
409
|
```bash
|
|
410
|
+
# Full access (default)
|
|
284
411
|
npx @floriscornel/teams-mcp@latest authenticate
|
|
412
|
+
|
|
413
|
+
# Read-only (reduced permission scopes)
|
|
414
|
+
npx @floriscornel/teams-mcp@latest authenticate --read-only
|
|
285
415
|
```
|
|
286
416
|
|
|
287
417
|
Check your authentication status:
|
|
@@ -296,9 +426,52 @@ Logout if needed:
|
|
|
296
426
|
npx @floriscornel/teams-mcp@latest logout
|
|
297
427
|
```
|
|
298
428
|
|
|
429
|
+
### Chat Pagination Example
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"chatId": "19:meeting_...",
|
|
434
|
+
"limit": 100,
|
|
435
|
+
"fetchAll": true,
|
|
436
|
+
"orderBy": "createdDateTime",
|
|
437
|
+
"descending": true,
|
|
438
|
+
"contentFormat": "markdown"
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Channel Message with Mentions and Image
|
|
443
|
+
|
|
444
|
+
```json
|
|
445
|
+
{
|
|
446
|
+
"teamId": "team-id",
|
|
447
|
+
"channelId": "channel-id",
|
|
448
|
+
"message": "Please review **today's update**",
|
|
449
|
+
"format": "markdown",
|
|
450
|
+
"importance": "high",
|
|
451
|
+
"mentions": [
|
|
452
|
+
{
|
|
453
|
+
"mention": "alex.chen",
|
|
454
|
+
"userId": "00000000-0000-0000-0000-000000000000"
|
|
455
|
+
}
|
|
456
|
+
],
|
|
457
|
+
"imageUrl": "https://example.com/status.png"
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### File Upload Example
|
|
462
|
+
|
|
463
|
+
```json
|
|
464
|
+
{
|
|
465
|
+
"chatId": "19:meeting_...",
|
|
466
|
+
"filePath": "/absolute/path/to/report.pdf",
|
|
467
|
+
"message": "Please review the attached report",
|
|
468
|
+
"format": "markdown"
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
299
472
|
### Integrating with Cursor/Claude
|
|
300
473
|
|
|
301
|
-
This MCP server is designed to work with AI assistants like Claude/Cursor/VS Code through the Model Context Protocol.
|
|
474
|
+
This MCP server is designed to work with AI assistants like Claude/Cursor/VS Code through the Model Context Protocol.
|
|
302
475
|
|
|
303
476
|
```json
|
|
304
477
|
{
|
|
@@ -313,9 +486,12 @@ This MCP server is designed to work with AI assistants like Claude/Cursor/VS Cod
|
|
|
313
486
|
|
|
314
487
|
## š Security
|
|
315
488
|
|
|
316
|
-
- All authentication is handled through Microsoft's OAuth 2.0 flow
|
|
489
|
+
- All authentication is handled through Microsoft's OAuth 2.0 flow or a caller-provided Microsoft Graph token
|
|
317
490
|
- **Refresh token support**: Access tokens are automatically renewed using cached refresh tokens, so you don't need to re-authenticate every hour
|
|
318
491
|
- Token cache is stored locally at `~/.teams-mcp-token-cache.json`
|
|
492
|
+
- Auth metadata is stored locally at `~/.msgraph-mcp-auth.json`
|
|
493
|
+
- Markdown content is sanitized before sending HTML to Teams
|
|
494
|
+
- `AUTH_TOKEN` is validated to ensure it targets `https://graph.microsoft.com`
|
|
319
495
|
- No sensitive data is logged or exposed
|
|
320
496
|
- Follows Microsoft Graph API security best practices
|
|
321
497
|
|
|
@@ -328,7 +504,7 @@ MIT License - see LICENSE file for details
|
|
|
328
504
|
1. Fork the repository
|
|
329
505
|
2. Create a feature branch
|
|
330
506
|
3. Make your changes
|
|
331
|
-
4. Run linting and
|
|
507
|
+
4. Run build, linting, and tests
|
|
332
508
|
5. Submit a pull request
|
|
333
509
|
|
|
334
510
|
## š Support
|
|
@@ -336,4 +512,4 @@ MIT License - see LICENSE file for details
|
|
|
336
512
|
For issues and questions:
|
|
337
513
|
- Check the existing GitHub issues
|
|
338
514
|
- Review Microsoft Graph API documentation
|
|
339
|
-
- Ensure proper authentication and permissions are configured
|
|
515
|
+
- Ensure proper authentication and permissions are configured
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { PublicClientApplication, } from "@azure/msal-node";
|
|
|
6
6
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
import { cachePlugin } from "./msal-cache.js";
|
|
9
|
-
import { GraphService } from "./services/graph.js";
|
|
9
|
+
import { FULL_SCOPES, GraphService, READ_ONLY_SCOPES } from "./services/graph.js";
|
|
10
10
|
import { registerAuthTools } from "./tools/auth.js";
|
|
11
11
|
import { registerChatTools } from "./tools/chats.js";
|
|
12
12
|
import { registerSearchTools } from "./tools/search.js";
|
|
@@ -16,23 +16,27 @@ import { registerUsersTools } from "./tools/users.js";
|
|
|
16
16
|
const CLIENT_ID = "14d82eec-204b-4c2f-b7e8-296a70dab67e";
|
|
17
17
|
const AUTHORITY = "https://login.microsoftonline.com/common";
|
|
18
18
|
const AUTH_INFO_PATH = join(homedir(), ".msgraph-mcp-auth.json");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
/** Check whether CLI args contain --read-only. */
|
|
20
|
+
function hasReadOnlyFlag(args) {
|
|
21
|
+
return args.includes("--read-only");
|
|
22
|
+
}
|
|
23
|
+
/** Read the persisted auth info file (best-effort). */
|
|
24
|
+
async function readAuthInfo() {
|
|
25
|
+
try {
|
|
26
|
+
const data = await fs.readFile(AUTH_INFO_PATH, "utf8");
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
31
33
|
// Authentication functions
|
|
32
|
-
async function authenticate() {
|
|
34
|
+
async function authenticate(readOnly) {
|
|
35
|
+
const scopes = readOnly ? READ_ONLY_SCOPES : FULL_SCOPES;
|
|
36
|
+
const modeLabel = readOnly ? "read-only" : "full access";
|
|
33
37
|
console.log("š Microsoft Graph Authentication for MCP Server");
|
|
34
38
|
console.log("=".repeat(50));
|
|
35
|
-
console.log(
|
|
39
|
+
console.log(`Using Microsoft Graph CLI app (${modeLabel})`);
|
|
36
40
|
try {
|
|
37
41
|
console.log("\nš± Using device code flow...");
|
|
38
42
|
const msalConfig = {
|
|
@@ -46,7 +50,7 @@ async function authenticate() {
|
|
|
46
50
|
};
|
|
47
51
|
const client = new PublicClientApplication(msalConfig);
|
|
48
52
|
const result = await client.acquireTokenByDeviceCode({
|
|
49
|
-
scopes
|
|
53
|
+
scopes,
|
|
50
54
|
deviceCodeCallback: (response) => {
|
|
51
55
|
console.log("\nš± Please complete authentication:");
|
|
52
56
|
console.log(`š Visit: ${response.verificationUri}`);
|
|
@@ -62,10 +66,12 @@ async function authenticate() {
|
|
|
62
66
|
timestamp: new Date().toISOString(),
|
|
63
67
|
expiresAt: result.expiresOn?.toISOString(),
|
|
64
68
|
account: result.account?.username,
|
|
69
|
+
grantedScopes: result.scopes,
|
|
65
70
|
};
|
|
66
71
|
await fs.writeFile(AUTH_INFO_PATH, JSON.stringify(authInfo, null, 2));
|
|
67
72
|
console.log("\nā
Authentication successful!");
|
|
68
73
|
console.log(`š¤ Signed in as: ${result.account?.username || "Unknown"}`);
|
|
74
|
+
console.log(`š Mode: ${modeLabel}`);
|
|
69
75
|
console.log(`š¾ Credentials saved to: ${AUTH_INFO_PATH}`);
|
|
70
76
|
console.log("š Refresh token cached for automatic renewal");
|
|
71
77
|
console.log("\nš You can now use the MCP server in Cursor!");
|
|
@@ -96,6 +102,18 @@ async function checkAuth() {
|
|
|
96
102
|
console.log("ā
Authentication found");
|
|
97
103
|
console.log(`š¤ Account: ${authInfo.account || "Unknown"}`);
|
|
98
104
|
console.log(`š
Authenticated on: ${authInfo.timestamp}`);
|
|
105
|
+
// Show granted scope mode
|
|
106
|
+
const grantedScopes = authInfo.grantedScopes;
|
|
107
|
+
if (grantedScopes) {
|
|
108
|
+
const hasWriteScopes = grantedScopes.some((s) => s === "ChannelMessage.Send" ||
|
|
109
|
+
s === "ChannelMessage.ReadWrite" ||
|
|
110
|
+
s === "Chat.ReadWrite" ||
|
|
111
|
+
s === "Files.ReadWrite.All");
|
|
112
|
+
console.log(`š Scope mode: ${hasWriteScopes ? "full access" : "read-only"}`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log("ā ļø Scope mode: unknown (authenticated before read-only support)");
|
|
116
|
+
}
|
|
99
117
|
// Check if we have expiration info
|
|
100
118
|
if (authInfo.expiresAt) {
|
|
101
119
|
const expiresAt = new Date(authInfo.expiresAt);
|
|
@@ -140,34 +158,56 @@ async function logout() {
|
|
|
140
158
|
console.log("š Run 'npx @floriscornel/teams-mcp@latest authenticate' to re-authenticate");
|
|
141
159
|
}
|
|
142
160
|
// MCP Server setup
|
|
143
|
-
async function startMcpServer() {
|
|
161
|
+
async function startMcpServer(readOnly) {
|
|
144
162
|
// Create MCP server
|
|
145
163
|
const server = new McpServer({
|
|
146
164
|
name: "teams-mcp",
|
|
147
|
-
version: "0.
|
|
165
|
+
version: "0.9.0",
|
|
148
166
|
});
|
|
149
167
|
// Initialize Graph service (singleton)
|
|
150
168
|
const graphService = GraphService.getInstance();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
graphService.readOnlyMode = readOnly;
|
|
170
|
+
// Detect scope mismatch: warn when switching from read-only ā full mode
|
|
171
|
+
if (!readOnly && !process.env.AUTH_TOKEN) {
|
|
172
|
+
const authInfo = await readAuthInfo();
|
|
173
|
+
if (authInfo) {
|
|
174
|
+
const grantedScopes = authInfo.grantedScopes;
|
|
175
|
+
const hasWriteScopes = grantedScopes?.some((s) => s === "ChannelMessage.Send" ||
|
|
176
|
+
s === "ChannelMessage.ReadWrite" ||
|
|
177
|
+
s === "Chat.ReadWrite" ||
|
|
178
|
+
s === "Files.ReadWrite.All");
|
|
179
|
+
if (grantedScopes && !hasWriteScopes) {
|
|
180
|
+
console.error("ā ļø Warning: You authenticated with read-only scopes but the server is running in full mode.");
|
|
181
|
+
console.error(" Write operations may fail. Re-authenticate without --read-only:");
|
|
182
|
+
console.error(" npx @floriscornel/teams-mcp@latest authenticate");
|
|
183
|
+
}
|
|
184
|
+
else if (!grantedScopes) {
|
|
185
|
+
console.error("ā ļø Warning: Could not determine granted scopes. If you experience permission errors,");
|
|
186
|
+
console.error(" re-authenticate: npx @floriscornel/teams-mcp@latest authenticate");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Register all tools (write tools are skipped when readOnly is true)
|
|
191
|
+
registerAuthTools(server, graphService, readOnly);
|
|
192
|
+
registerUsersTools(server, graphService, readOnly);
|
|
193
|
+
registerTeamsTools(server, graphService, readOnly);
|
|
194
|
+
registerChatTools(server, graphService, readOnly);
|
|
195
|
+
registerSearchTools(server, graphService, readOnly);
|
|
157
196
|
// Start server
|
|
158
197
|
const transport = new StdioServerTransport();
|
|
159
198
|
await server.connect(transport);
|
|
160
|
-
console.error(
|
|
199
|
+
console.error(`Microsoft Graph MCP Server started${readOnly ? " (read-only mode)" : ""}`);
|
|
161
200
|
}
|
|
162
201
|
// Main function to handle both CLI and MCP server modes
|
|
163
202
|
async function main() {
|
|
164
203
|
const args = process.argv.slice(2);
|
|
165
|
-
const command = args
|
|
204
|
+
const command = args.find((arg) => arg !== "--read-only");
|
|
205
|
+
const readOnly = hasReadOnlyFlag(args) || process.env.TEAMS_MCP_READ_ONLY === "true";
|
|
166
206
|
// CLI commands
|
|
167
207
|
switch (command) {
|
|
168
208
|
case "authenticate":
|
|
169
209
|
case "auth":
|
|
170
|
-
await authenticate();
|
|
210
|
+
await authenticate(readOnly);
|
|
171
211
|
return;
|
|
172
212
|
case "check":
|
|
173
213
|
await checkAuth();
|
|
@@ -181,14 +221,19 @@ async function main() {
|
|
|
181
221
|
console.log("Microsoft Graph MCP Server");
|
|
182
222
|
console.log("");
|
|
183
223
|
console.log("Usage:");
|
|
184
|
-
console.log(" npx @floriscornel/teams-mcp@latest authenticate
|
|
185
|
-
console.log(" npx @floriscornel/teams-mcp@latest
|
|
186
|
-
console.log(" npx @floriscornel/teams-mcp@latest
|
|
187
|
-
console.log(" npx @floriscornel/teams-mcp@latest
|
|
224
|
+
console.log(" npx @floriscornel/teams-mcp@latest authenticate # Authenticate with full scopes");
|
|
225
|
+
console.log(" npx @floriscornel/teams-mcp@latest authenticate --read-only # Authenticate with read-only scopes");
|
|
226
|
+
console.log(" npx @floriscornel/teams-mcp@latest check # Check authentication status");
|
|
227
|
+
console.log(" npx @floriscornel/teams-mcp@latest logout # Clear authentication");
|
|
228
|
+
console.log(" npx @floriscornel/teams-mcp@latest # Start MCP server (default)");
|
|
229
|
+
console.log("");
|
|
230
|
+
console.log("Environment variables:");
|
|
231
|
+
console.log(" TEAMS_MCP_READ_ONLY=true # Start MCP server in read-only mode");
|
|
232
|
+
console.log(" AUTH_TOKEN=<jwt> # Use a pre-existing access token");
|
|
188
233
|
return;
|
|
189
234
|
case undefined:
|
|
190
235
|
// No command = start MCP server
|
|
191
|
-
await startMcpServer();
|
|
236
|
+
await startMcpServer(readOnly);
|
|
192
237
|
return;
|
|
193
238
|
default:
|
|
194
239
|
console.error(`Unknown command: ${command}`);
|