关于 microsoft-teams-mcp
microsoft-teams-mcp 将 MCP 客户端连接到 Microsoft Teams,可读取聊天、频道、未读消息和动态,也可发送消息与回应。它通过已登录的浏览器会话捕获凭据,并要求确认后才执行写操作。
主要功能
- 01列出并读取聊天和频道
- 02读取未读消息和动态
- 03搜索消息并在必要时扫描会话
- 04获取内嵌图片和共享文件
- 05确认后发送消息和回应
- 06导出消息
适用场景
- 01查看 Teams 中的未读消息
- 02搜索近期会话中的特定主题
- 03获取聊天中的共享附件
- 04准备回复并确认发送
MCP 相关
将 Microsoft Teams 的聊天、频道、动态、搜索、附件和导出操作作为 MCP 工具提供。发送消息、回应和标记已读等写操作需要确认。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @chrischall/microsoft-teams-mcpTEAMS_WS_PORTfetchproxy concentrator port used for the browser bridge. Defaults to 37149, the fleet-wide shared port.FETCHPROXY_WS_HOSTBind interface for the bridge (default 127.0.0.1; a literal IP only, anything else is ignored). Keep on loopback — it exists for ONE topology: a hosted child in its own network namespace.FETCHPROXY_IDENTITY_DIRWhere to store the long-term fetchproxy identity keypair (default ~/.fetchproxy/identity/; an absolute path only, anything else is ignored). Exists for a host that runs a separate child per caller, so they can share one identity.TEAMS_ACCESS_TOKEN密钥A pre-obtained Teams chat-service bearer token (the JWT itself). Leave blank to capture one from your signed-in Teams browser tab via the ContextMint Bridge extension.TEAMS_DISABLE_FETCHPROXYSet to true to never capture a token from the browser; then TEAMS_ACCESS_TOKEN is required. Leave blank.TEAMS_CHATSVC_REGIONThe Teams chat-service region in the API path. Defaults to amer. Leave blank unless your tenant lives elsewhere.TEAMS_CHATSVC_BASEFull override for the chat-service API base (default https://teams.cloud.microsoft/api/chatsvc/<region>/v1). Leave blank.TEAMS_CAPTURE_TIMEOUTSeconds to wait for the Teams tab to issue a chat-service request when capturing a token. Defaults to 30.TEAMS_TOKEN_CACHE密钥Set to false to keep the captured token in memory only. Defaults to on: it is cached so a restart within its ~23h lifetime needs no browser.TEAMS_TOKEN_FILE密钥Where the captured token is cached. Defaults to ~/.microsoft-teams-mcp/token.json.TEAMS_SUBSTRATE_TOKEN_FILE密钥Where the captured Substrate search token is cached. Defaults to ~/.microsoft-teams-mcp/substrate-token.json.TEAMS_SHAREPOINT_TOKEN_FILE密钥Where the captured SharePoint tokens are cached. Defaults to ~/.microsoft-teams-mcp/sharepoint-tokens.json.TEAMS_TIME_ZONEDefault IANA time zone (e.g. America/New_York) for time_local on read results. Unset means UTC only.TEAMS_OUTPUT_DIRFolder teams_export_messages writes exports into. Unset means ~/Downloads/microsoft-teams-mcp.MCP_CONFIRM_MODEWhat the writes (teams_send_message, teams_react, teams_mark_read and the read tools' mark_read) do on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop): "ask-user" (default: the first call returns a preview and a confirmToken, and the model must get your approval in chat before using it), "auto" (the model may use the token after reviewing the preview itself) or "refuse". An unrecognised value is treated as refuse.MCP_CONFIRM_ELICITATION"off" never shows a confirmation prompt, so every client gets the MCP_CONFIRM_MODE flow. Set it for a client that says it can show prompts but never does. Defaults to on.MCP_CONFIRM_TTL_SECONDSHow long a confirmToken stays valid, in seconds. Defaults to 600.MCP_CONFIRM_SECRET密钥Signing key for confirmTokens. Random per process by default; set it only if tokens must survive a server restart.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
9收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | integer | 否 | 最多返回的聊天数;保留收藏和置顶项优先,其次按最近活动排序(默认 50,最大 500)。 |
| query | string | 否 | 聊天标题或无标题聊天参与者姓名中不区分大小写的子字符串,或完全匹配的会话 ID。 |
| since_days | integer | 否 | 配合 unread_only 使用:仅当会话的最新未读消息在最近 since_days 天内送达时,才将其计为未读(默认 14,最大 3650)。较早的未读会话将被排除并计入 older_unread_skipped;传入更大的 since_days 可将其纳入。 |
| unread_only | boolean | 否 | 仅返回用户尚未阅读过、包含真实消息(而非通话录音或其他系统事件)的对话,并为每个对话提供其 unread_count(默认值为 false)。确切地说:通过读取已读标记之后的消息来确认每个候选项。绝不会将任何内容标记为已读。 |
详细介绍
microsoft-teams-mcp
Microsoft Teams 的 MCP 服务——列出你加入的所有聊天和频道,按 ID 或名称读取其中任意一个,获取所有未读内容,读取「活动」信息流,获取内嵌图片,并在每次操作前由你确认后发布消息和表情回应,使用你已登录的浏览器会话。
此项目由 AI(Claude Code)开发并维护。请自行斟酌使用。
工作原理
聊天和频道通过 Teams 自己的 chat-service REST API
(https://teams.cloud.microsoft/api/chatsvc/<region>/v1) 读取。服务需要该 API 的 bearer token,而它唯一存在的地方就是你已登录的
teams.cloud.microsoft 标签页:通过
fetchproxy 浏览器桥接,它会从 Teams 网页应用向聊天服务发出的请求中捕获 Authorization 标头——仅一次。此后,每次调用都是普通的服务端请求;token 会在其约 23 小时的有效期内缓存于 ~/.microsoft-teams-mcp/token.json(因此重启后无需使用浏览器),遇到 401 时会重新捕获一次并重放请求。不支持 teams.microsoft.com(旧主机)。
因此,聊天和频道不再需要在浏览器中打开——可以按 ID 或名称读取其中任意一个,列表也会涵盖所有聊天,而不只是侧边栏中显示的聊天。要捕获 token,仅有空闲标签页是不够的:点击进入任意聊天,让 Teams 调用其聊天服务。
「活动」信息流来自同一个 API。 它本身就是一个聊天服务会话(48:notifications),因此 teams_list_activity 会像读取普通聊天一样读取它——无需打开「活动」视图(或任何视图),也不会读取页面内容。桥接仅用于一件事:捕获 token。
默认情况下,此服务不会将任何内容标记为已读。通过此服务读取聊天后,Teams 中的未读徽标会保持原样——除非你选择执行此操作:调用 teams_mark_read,或在 teams_list_messages /
teams_list_unread_messages 中设置 mark_read: true,以推进已读标记。写入操作——teams_send_message、teams_react 和标记为已读——都通过同一个聊天服务 API 执行,并且只有在你确认后才会更改任何内容(请参阅
写入操作)。
SharePoint 文件下载
聊天或频道中的共享文件不在 Teams 自己的对象存储中(内嵌图片存储在那里),而是在 SharePoint 中;SharePoint 不接受聊天服务 token。因此,teams_get_attachment 会捕获一个单独的 SharePoint token,通过 SharePoint 的 v2.1 shares API 获取文件,然后下载该 API 返回的预签名链接(不附带 token)。
由此会有两点需要注意,二者都只需操作一次,且是额外步骤——它们不会改变聊天服务 token 的捕获方式:
- 重新配对一次。 SharePoint 路径声明了第二个信任范围
sharepoint.com,因此 ContextMint Bridge 授权必须涵盖该范围。如果你在此功能推出前已配对microsoft-teams-mcp,请在 Bridge 弹出窗口中撤销并重新配对一次,以添加 SharePoint 范围(下载文件时出现not in declared set错误,就是这个原因,仅此而已)。聊天服务 token 的捕获不受影响——它仍然只信任teams.cloud.microsoft。 - 每个 SharePoint 主机打开一次文件。 SharePoint token 从浏览器向文件所在 SharePoint 主机发出的请求中捕获,有效期较短(约 53 分钟),且仅适用于一个主机系列。因此,在某个会话中首次获取来自特定主机的文件时,你可能需要在已登录的 Teams 标签页中打开一次该文件(token 就是从该浏览器请求中读取的);之后 token 会被缓存并刷新,直到过期。如果你的帐户无法打开某个文件,或该文件所在的 SharePoint 主机不在 token 的适用范围内,服务会返回文件元数据及其打开链接,而不会报错。
Substrate 搜索
teams_search_messages 的搜索方式与 Teams 搜索框相同:通过
Microsoft Substrate(POST https://substrate.office.com/searchservice/api/v2/query)。
Substrate 也不接受聊天服务令牌,因此搜索会捕获第三个令牌——当你在 Teams 标签页中执行搜索时,从该标签页向
substrate.office.com/searchservice/* 发出的请求中获取,并将其缓存到
~/.microsoft-teams-mcp/substrate-token.json。与另外两个令牌一样,该令牌只从请求标头读取,绝不会从页面存储中读取。
-
只需重新配对一次。 搜索路径声明了自己的信任范围
substrate.office.com,ContextMint Bridge 授权必须涵盖此范围。如果你在此功能推出前配对过microsoft-teams-mcp,请在 Bridge 弹出窗口中撤销授权并重新配对一次。聊天服务和 SharePoint 的捕获不受影响。 -
在 Teams 中执行一次搜索。 令牌从 Teams 标签页自身的搜索请求中读取,因此首次搜索时,可能需要在捕获等待期间 在 Teams 搜索框中输入任意内容并按 Enter (
TEAMS_CAPTURE_TIMEOUT,默认值为 30s)。 -
会回退到扫描。 在默认的
mode: "auto"下,如果无法捕获 Substrate 令牌或搜索失败,工具会改为扫描近期对话 (source: "scan",并附带fallback_reason),且 10 分钟内不会再次等待捕获。mode: "substrate"则会直接失败;mode: "scan"则完全不会访问 Substrate。 -
不会循环重新捕获。 如果刚捕获的令牌仍被拒绝(401),同样会进入 10 分钟冷却期,因此
auto会直接扫描,而不是每次调用都等待 Bridge。
已于 2026-10-12 在线验证。 从 Teams 标签页自身的搜索请求中捕获的令牌(有效期约 1.4 小时)已通过 Node 重放,用于执行
deploy等只读查询。已确认:请求正文(必需包含cvid、logicalId和一个scenario;排序方式为{ Field: "Time", SortDirection: "Desc" })、无需 bearer 以外的标头、响应字段 (ClientThreadId中的会话、ClientConversationId中;messageid=前的频道根消息、InternetMessageId中的消息 ID、发送者姓名和 SMTP 地址但没有 MRI、最多约 255 个字符的纯文本Preview、近似的Total)、通过from/size分页(最多 200),以及conversation和since可以在服务端作为ClientThreadId:"…"和不带引号的sent>=…发送。from保留在客户端处理:Substrate 自身的from:会匹配单词前缀,而不是“显示名称包含”。已脱敏的响应见tests/fixtures/substrate-query-response.json。
安装
npm i -g @chrischall/microsoft-teams-mcp
需要安装 ContextMint Bridge 浏览器扩展和 @fetchproxy/cli,且二者主版本号必须匹配。从其发布页面安装 Bridge:
在 Chrome 中,解压 Chrome 压缩包并以未打包扩展的方式加载
(chrome://extensions → 开发者模式 → 加载已解压的扩展程序)。Safari 暂不可用(它将随 ContextMint 应用一起发布,但该应用目前没有公开下载),因此请暂时使用 Chrome。
ContextMint Bridge 是更名后的 fetchproxy 浏览器扩展,由同一位维护者维护;fetchproxy 自身的 README
(https://github.com/chrischall/fetchproxy#extension)中也提供了指向它的链接。其源代码公开于 https://github.com/nullnet-app/contextmint-bridge:你可以自行构建,也可以根据其旁边发布的 .sha256 文件校验发布压缩包(shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256)。
npm i -g @fetchproxy/cli
首次调用会打印一个 6 位配对码,需在 ContextMint Bridge 弹出窗口中确认;授权会持续有效。
在 opencode 中安装
opencode 从 opencode.json(项目级)或
~/.config/opencode/opencode.json(全局)读取 MCP 服务:
{
"mcp": {
"servers": {
"teams": {
"type": "local",
"command": ["npx", "-y", "@chrischall/microsoft-teams-mcp"]
}
}
}
}
配置
所有配置项均为可选项——默认情况下会从浏览器获取令牌。
| 变量 | 用途 |
|---|---|
TEAMS_ACCESS_TOKEN | 要使用的聊天服务持有者令牌(JWT 本身),用于替代自动获取的令牌。不会再从浏览器获取令牌:Teams 拒绝此令牌时,请将其替换。 |
TEAMS_DISABLE_FETCHPROXY | 设为 true 时,永不从浏览器获取令牌(此时必须设置 TEAMS_ACCESS_TOKEN)。 |
TEAMS_CHATSVC_REGION | API 路径中的聊天服务区域。默认值为 amer。 |
TEAMS_CHATSVC_BASE | 完整覆盖 API 基础 URL(默认值为 https://teams.cloud.microsoft/api/chatsvc/<region>/v1)。 |
TEAMS_CAPTURE_TIMEOUT | 等待 Teams 标签页发出聊天服务请求的秒数。默认值为 30。桥接超时时间由此计算(等待时间 + 15 秒)。 |
TEAMS_TOKEN_CACHE | 设为 false 时,仅在内存中保留获取到的令牌。默认启用。 |
TEAMS_TOKEN_FILE | 获取到的令牌缓存位置。默认值为 ~/.microsoft-teams-mcp/token.json。 |
TEAMS_SUBSTRATE_TOKEN_FILE | 获取到的 Substrate 搜索令牌缓存位置。默认值为 ~/.microsoft-teams-mcp/substrate-token.json。设为 TEAMS_TOKEN_CACHE=false 时,也只在内存中保留该令牌。 |
TEAMS_SHAREPOINT_TOKEN_FILE | 获取到的 SharePoint 令牌(每个主机一个,用于下载共享文件)的缓存位置。默认值为 ~/.microsoft-teams-mcp/sharepoint-tokens.json。设为 TEAMS_TOKEN_CACHE=false 时,也只在内存中保留这些令牌。 |
TEAMS_TIME_ZONE | teams_list_messages 和 teams_list_unread_messages 的 tz 参数所用的默认 IANA 时区(例如 America/New_York):之后每条消息除了 UTC time 外,还会包含 time_local(带偏移量的 ISO 8601 格式)。未设置时,除非传入 tz,否则不会包含 time_local;若 Intl 不识别该名称,则会报错。 |
TEAMS_OUTPUT_DIR | teams_export_messages 写入导出文件的文件夹(每次导出都会创建一个带时间戳的子文件夹)。默认值为 ~/Downloads/microsoft-teams-mcp。设置后,每次调用的 dest 必须位于此目录下;未设置时,dest 必须位于 ~/Downloads、~/Documents 或 ~/Desktop 下。 |
TEAMS_WS_PORT | fetchproxy 集中器端口。默认值为 37149,这是全舰队共享端口。 |
FETCHPROXY_WS_HOST | 桥接程序的绑定接口(默认为 127.0.0.1;仅接受字面 IP,其他值均会被忽略)。请保持为 loopback;此设置仅适用于一种拓扑:托管的子进程运行在其自己的网络命名空间中。 |
FETCHPROXY_IDENTITY_DIR | 长期 fetchproxy 身份密钥对的存储位置(默认为 ~/.fetchproxy/identity/;仅接受绝对路径,其他值均会被忽略)。适用于每个调用方由主机运行一个独立子进程的情况,这样它们就能共用同一身份。 |
工具
-
teams_list_conversations— 你参与的所有聊天(1:1、群组和会议聊天,不只是侧边栏中可见的聊天;此外,Teams 列出你的48:notes自聊时,也会将其标记为self类型),先列出收藏和置顶聊天,再按最近活动时间从近到远排列:id、类型、标题、未读提示、ISO-8601 格式的最近活动时间、最后一条消息预览和发送者。unread_only(精确筛选——见下文)、since_days(与unread_only搭配;默认 14)、older_unread(与unread_only搭配;列出since_days筛选掉的内容——见下文)、query(标题、参与者姓名或最后一条消息预览)、search_messages、since/until(ISO 8601 日期时间:保留最近活动时间在指定时间或之后 / 之前的聊天;如果指定任一参数,没有最近活动时间的聊天就会被排除)、sort和limit(默认 50,最大 500;保留排序结果中的前几项)。sort可以是activity(默认——收藏、置顶优先,然后按最近活动时间排列;与unread_only搭配时按最近活动时间排列)、activity_only(忽略收藏和置顶,按最近活动时间从近到远排列)或title(按标题字母顺序排列,不区分大小写——此排序会在应用limit前为列表中所有无标题聊天设置标题,因此读取量比其他排序方式更多)。没有主题的群聊会根据群内成员命名——格式为Group chat: <最近发送消息的三位非本人发送者,按姓名排序>,如果还有其他成员则附加+N;participants中的所有最近发送者也按相同顺序排列。这样一来,最后一条消息由同一人发送的聊天仍能区分开来,而且即使之后由其他人发言,聊天标题也会保持不变。此服务没有成员列表接口,因此会读取每个返回的此类聊天中最多 30 条最近消息(一页短消息,每次读取四个聊天,缓存到有新消息到达为止),并根据发言者姓名命名;只有发言者集合发生变化时,标题才会改变。如果读取失败,聊天标题会保留为最后一位发送者的姓名。所有其他返回对话的工具也会以相同方式为其命名:活动动态中的where、附件说明、搜索结果、读取错误,以及发送消息、添加回应或标记已读所针对的对话。1:1 聊天以对方的姓名命名(id 中会注明对方),并将对方作为唯一一项participants条目——如果最后一条消息由你发送,则从对方的最近消息中读取姓名;如果对方最近没有发言,则使用(1:1 chat)。query会根据此类聊天的参与者进行匹配,而不会匹配虚构的Group chat:或(1:1 chat)标题,因此group或chat不会命中每个无标题聊天。提供query时,每一行都会带有match_reason——按顺序取第一个适用项:id(完全匹配的 id)、title、team、participant(无标题聊天的参与者,包括 1:1 聊天中的对方)或message。message表示在消息内容中找到了查询词,而不是聊天名称中:默认情况下只检查列表已经包含的最后一条消息预览(不会额外读取)。search_messages: true(需要提供query)还会读取未通过名称或预览匹配的每个聊天的最近 50 条消息,每次读取四个聊天,并返回其中包含查询词的人员消息;最新的匹配消息会作为matched_message返回(包括id、time、sender、text,其中text会截取为包含匹配内容的文本窗口)。每个聊天读取一次,因此大型列表应使用since/until或unread_only缩小范围;读取失败的聊天会计入messages_not_searched。 聊天服务标记为收藏或置顶的对话会带有favorite: true/pinned: true,并排在普通列表前面(先收藏,再置顶);unread_only会保留按最近活动时间排列的顺序,但仍会返回这些标记。请注意,Teams 新增的聊天“收藏”部分由另一项 Teams 服务维护,本 MCP 不会读取该服务,因此放入其中的聊天不会带有标记(已于 2026-10-10 实时验证)——这些标记主要反映收藏的频道和置顶的对话。在 Teams 中将聊天静音后,该聊天会带有muted: true。 -
teams_list_channels— 你参与的所有频道,按团队再按标题排列,字段与上文相同——收藏和置顶频道优先,标记方式与teams_list_conversations相同。在 Teams 中隐藏的频道会列在其余频道之后,并带有hidden: true;除非设置include_hidden,否则unread_only会将其排除。unread_only、include_hidden、since_days(与unread_only搭配;默认 14)、query(频道或团队名称,或最后一条消息预览;每个匹配项都会带有match_reason,与teams_list_conversations相同)以及limit(默认 50,最大 500)。 -
teams_list_messages— 按 id 或名称读取任意聊天或频道,按时间从早到晚排列:发送者、时间(UTC,ISO 8601)、文本、主题、已编辑 / 已删除标记和附件。频道会以线程形式返回(根帖子 + 回复)。如果某个线程的根帖子早于读取时间范围(回复是在since之后到达,或读取被limit或after_message_id截断),仍会显示该根帖子:通过 id 获取,并标记为outside_window: true——仅供参考,不计入unread_count或message_count,也绝不会作为next_cursor。每次调用最多获取 25 个根帖子(所有读取的对话共用此上限;优先获取最新线程);超过上限或无法获取的根帖子会显示为{ id, missing: true }。unread_only、since(ISO 8601)、limit(默认 50,最大 500,保留最新消息)和tz(IANA 时区,例如America/New_York;默认TEAMS_TIME_ZONE)。tz会添加time_local,即同一时刻在该时区的 ISO 8601 时间,并带有时区偏移,显示在每条消息的time旁边。消息文本永不截断,表格的单元格以|分隔,每行对应一行表格内容。解析出的conversation会带有match_reason(id、title、team或participant);名称不会根据聊天中说过的内容解析,因为teams_send_message和teams_react也会以相同方式命名目标。名称匹配不唯一不算错误:结果为{ambiguous: true, query, match_count, candidates, hint}——最多列出 10 个候选项,每项包括id、title、kind、team(频道)、lastActivity和match_reason——且不返回消息;你可以通过某个候选项的 id 再次读取(teams_send_message和teams_react仍会直接拒绝无法唯一匹配的名称,因为写入操作绝不猜测目标)。无标题群聊可以用其中一位参与者的姓名命名,1:1 聊天可以用对方的姓名命名(在此操作中,以及在teams_send_message/teams_react中以相同方式命名时,都会先为所有无标题聊天设置标题)。48:notes——或名称notes/self——是你的笔记自聊。after_message_id只返回 id 晚于该 id 的消息(读取会在遇到 id 不晚于该值的第一条消息时停止;可与unread_only、since和limit同时使用);每个结果都会带有next_cursor——最新返回消息的 id;如果没有新消息,则为你传入的after_message_id——因此再次将其作为after_message_id传入,就只会读取此后到达的消息。after_message_id只接受消息 id:若要读取teams_list_changed_conversations统计的消息,请传入该行的after_message_id,而不是next_cursor。请参阅 游标。from则会从指定的 UTC 时间开始按时间从早到晚分页——请参阅 按时间分页。format用于设置返回内容的详细程度:full(默认,返回以上所有内容)、compact(每条消息只返回id、time、time_local、sender、text,以及在有值时返回subject——不返回回应、附件或已编辑 / 已删除标记;对话只返回id、kind、title、team)或summary(不返回消息:返回message_count、newest_time;频道还会为每个线程返回一个{ root_id, subject, reply_count, newest_time },如果根帖子在时间范围外,则附加root_outside_window: true;如果根帖子缺失,则附加root_missing: true)。conversation也可以是最多包含 20 个 id 或名称的数组:此时结果为{ conversations: [...], errors, fetched_at },每个对话各有一份单独读取的结果(其他参数均应用于每个对话),每次读取四个。名称匹配不唯一时,该项会以与单独读取相同的{ambiguous: true, query, match_count, candidates, hint}对象出现在conversations中;无法解析或读取的对话会列在errors中,并注明原因(如果已解析,还会提供其id、title、kind、team和participants);其余对话仍会正常返回。传入单个字符串时,返回的结构与上文完全相同。 默认不会将任何内容标记为已读;mark_read: true还会将你的已读标记移至最新返回消息的位置(绝不向后移动;outside_window根帖子不计入),并返回marked_read——这是一项写入操作,因此会先请求你确认。传入数组时,每个对话都会将已读标记移至该对话的最新消息,并由一次确认涵盖所有操作。它绝不会标记你未收到的消息:format: "summary"时会拒绝执行mark_read(compact则可以);如果limit或since导致较早的未读消息未包含在返回结果中,该对话的标记会保持不变,并返回marked_read: { skipped: "truncated" | "since", hint }(如果limit截掉的仅是已读消息,仍会执行标记)。读取结果为空,或提供after_message_id后没有新消息时,不会标记任何内容(marked_read: false)。 -
teams_list_unread_messages— 列出你所有聊天和频道中的未读消息,按对话分组,按时间从近到远排列(如果提供from,则合并为一个跨所有对话、按时间从早到晚排列的列表——请参阅 按时间分页)。可按kinds(chat、channel、meeting、oneOnOne)或query(标题、频道或团队名称、无标题聊天中参与者的姓名,或最后一条消息预览)筛选;每个对话都会带有match_reason,与teams_list_conversations中的相同;还可设置per_conversation_limit(默认 50)和max_conversations(默认返回 20 个对话及其消息——其余对话会列出名称和unread_count,并放入more_unread_conversations)。除非设置include_hidden,否则隐藏频道会被排除;在 Teams 中静音的聊天(muted: true)会被排除在未读结果之外,并计入muted_skipped,除非设置include_muted。只返回最近since_days天(默认 14,最大 3650)内有未读消息的对话;更早的对话会计入older_unread_skipped;设置older_unread: true也会列出这些对话(见下文)。无标题群聊或 1:1 聊天会根据其参与者命名,与teams_list_conversations相同;该命名也会用于conversations、more_unread_conversations和errors。默认不会将任何内容标记为已读;mark_read: true还会将每个已返回消息的对话(不包括more_unread_conversations)的已读标记移至该对话最新返回的未读消息,并为每个对话返回marked_read(移动失败时还会返回mark_read_warning)。一次确认涵盖所有移动操作。它绝不会标记你未收到的消息:format: "summary"时会拒绝执行mark_read(compact则可以);如果某个对话的per_conversation_limit截掉了消息,则会保留其标记,并返回marked_read: { skipped: "truncated", hint };outside_window根帖子绝不计入。tz(默认TEAMS_TIME_ZONE)会为每条消息添加time_local,与teams_list_messages相同。频道的未读回复会按根帖子分组;如果根帖子已读,则会获取该帖子并标记为outside_window: true(不是未读消息,不计入),每次调用最多获取 25 个,与teams_list_messages相同;超出上限时则返回{ id, missing: true }。cursors——{ "<conversation id>": "<message id>" }——会为每个对话只返回晚于其游标的未读消息(每次读取会在已读标记和游标中较晚者处停止;游标之后没有消息的对话会被排除)。每个返回的对话都会带有next_cursor,结果还会带有next_cursors:其中包含你传入的游标,并将每个返回对话的游标更新为其next_cursor——下次将该值作为cursors传入。format(full、compact或summary)会像teams_list_messages一样精简每个对话;所有格式都会保留对话的id、unread_count和next_cursor。 -
teams_list_changed_conversations— 列出自某个游标以来有新消息的聊天和频道:返回计数,不返回消息。每个发生变化的对话都会附带摘要、new_message_count(仅真实消息——通话录音和其他系统事件,以及已删除的消息,均不计入)、new_from_others_count、newest_time;频道还会附带threads:每个有新消息的线程对应一个{ root_id, new_root, new_reply_count },包括根帖子早于游标的线程;此外还会返回after_message_id——将此值传给teams_list_messages的after_message_id,即可准确读取统计的消息(消息 id 就是其到达时间的毫秒值,因此该 id 是以 id 形式表示的游标时间)。未提供cursor时,会回溯 24 小时。next_cursor是不透明令牌({"t": <epoch ms>}的 base64url 编码;cursor也接受普通 ISO 8601 时间)——再次传入即可只查看此后到达的消息。此操作至少一次投递:同一对话可能返回两次,但不会遗漏;读取失败的对话会保持原样(见errors)。kinds和include_hidden的筛选方式与teams_list_unread_messages相同——后续调用游标时,使用返回该游标的调用所使用的相同筛选项;limit(默认 50,最大 200)限制返回的对话数量,其余对话会列在more_conversations中(每项也带有after_message_id)。然后使用teams_list_messages读取有变化的内容,并传入对应行的after_message_id——不要传入next_cursor;它是仅供teams_list_changed_conversations使用的时间令牌(teams_list_messages会拒绝该令牌,而teams_list_changed_conversations会拒绝消息 id):// first call: the last 24 hours teams_list_changed_conversations({}) // → { since, total_new_messages: 5, conversations: [ // { conversation: { id: "19:…@thread.tacv2", kind: "channel", title: "Design", … }, // new_message_count: 3, new_from_others_count: 3, newest_time: "…", // after_message_id: "1791728801361", // threads: [{ root_id: "1791803726629", new_root: false, new_reply_count: 2 }, …] }, … ], // next_cursor: "eyJ0IjoxNzkxODE1MjAxMzYxfQ", next_cursor_time: "…", fetched_at } // read the messages it counted in one conversation: hand on the row's after_message_id teams_list_messages({ conversation: "19:…@thread.tacv2", after_message_id: "1791728801361" }) // later: only what arrived since — next_cursor goes back to teams_list_changed_conversations only teams_list_changed_conversations({ cursor: "eyJ0IjoxNzkxODE1MjAxMzYxfQ" })频道回复只会更新条目中的
lastUpdatedMessageId(其lastMessage仍指向最新的根帖子),因此该字段用于确定要检查的频道;随后会按最新的变更优先读取每段对话,直到游标位置,因为较早的消息若在游标之后被编辑,就会排在新消息之前。隐藏频道的条目不会再更新,因此使用include_hidden时会读取每个隐藏频道。 -
teams_search_messages— 按文本搜索聊天和频道中的消息,结果按时间从新到旧排列:每条命中结果会列出其所在对话(id、标题、类型、团队),并包含消息的 id、ISO-8601 时间、发送者、fromMe(仅扫描路径)、纯文本、主题和replyTo(频道回复的根帖子)——使用teams_list_messages在上下文中查看。conversation将范围缩小到一个聊天或频道(id 或名称,与teams_list_messages中相同),from将范围缩小到显示名称包含该文本的发送者,since(ISO 8601;默认最近 30 天)设置回溯时间范围,limit限制命中数量(默认 25,最大 200;total_hits和truncated会说明是否还有更多结果)。source表示使用了哪种搜索路径:"substrate"(默认值,mode: "auto")——通过 Microsoft Substrate 使用 Teams 自带的搜索:匹配方式与 Teams 搜索框相同(单词按 AND 组合,并进行词干处理,因此搜索deploy也会找到 "deployed";支持"带引号的短语"和OR),搜索范围涵盖你的所有对话,而非仅最近的对话。conversation和since会直接传给 Substrate;from则会应用于其返回结果,每页 50 条,最多 10 页。Substrate 返回的是消息的纯文本预览(最多约 255 个字符),因此每条命中结果都会标记为excerpt: true;其命中结果不包含fromMe(Substrate 不会说明你是否为命中消息的发送者)。substrate会报告读取的页数,以及被筛选条件排除的结果数量。未出现在你的列表中的对话将以其 id 显示。"scan"— 当 Substrate 不可用或发生故障时(fallback_reason会说明原因),或使用mode: "scan"时启用。query中的每个单词都必须出现(不区分大小写,顺序不限;"带引号的词组"会作为短语匹配);系统会读取最近活跃且在截止时间之后有活动的对话,数量为max_conversations(默认 100,最大 500),每次读取 4 个对话;scan会报告读取的对话数量、因时间范围或数量上限而跳过的数量,以及只读取了部分内容的对话数量(每个对话读取 1,000 条消息)。实时测试中,读取 100 个对话约需 15 秒——条件允许时,请使用conversation或since缩小范围。除非明确指定,否则会跳过 Teams 中隐藏的频道;已删除的消息和系统事件不会匹配。
-
mode: "substrate"仅使用 Substrate,失败时不会回退。 -
teams_get_attachment— 获取一条消息中的一个附件:内嵌图片会以图片本身的形式返回;共享文件会从 SharePoint 下载,并以其内容形式返回——图片以图片形式返回,其他内容则作为携带字节的嵌入资源返回。共享文件下载会使用按 SharePoint 主机分别捕获的独立短效 SharePoint 令牌,因此首次从某个主机下载文件时,可能需要先在 Teams 中打开一次来生成令牌;如果你的帐户无法打开某个文件,或无法获取该文件,则会返回其元数据和打开链接,而不会失败。说明文字会在对话 ID 旁标明对话名称(title;未命名聊天则为participants),命名方式与teams_list_conversations一致。 -
teams_list_activity— 活动源(铃铛图标),按时间从新到旧排列:涵盖所有聊天和频道中的提及(包括 @team / @everyone)、回应、回复、会议通知和未接来电。每一行都包含时间(ISO 8601)、kind(mention、reaction、reply、meeting、call或other)及 Teams 的原始类型/子类型、你是否看过、执行者、发生位置(conversation_id、title、type,以及已知时的team/participants)、message_id和纯文本预览。对于未命名的群聊或一对一聊天,活动源不会提供名称,因此title会取自对话列表,并按照teams_list_conversations的命名方式命名(读取一次列表,并为返回的行读取缓存的名称;如果失败,则使用活动源自身的主题)——传入where.conversation_id到teams_list_messages,即可在上下文中读取消息。使用unread_only、kinds和since_days筛选(默认值为 14,最大值为 3650);limit(默认值为 50,最大值为 200)。数据从聊天服务读取,因此无需打开“活动”视图;此工具不会将任何内容标记为已读(teams_mark_read会将聊天和频道标记为已读,但不会标记活动源)。 -
teams_export_messages— 将聊天和频道批量导出到文件,以便使用 shell 工具归档或搜索数千条消息,且不会让这些消息进入对话:结果仅包含清单(文件所在位置、每个文件的大小和记录数、计数、时间跨度、错误、has_more/cursor),绝不包含消息。每个对话对应一个文件,以对话名称命名(<title>-<8-char id hash>.jsonl;未命名群聊使用参与者名称),另有manifest.json。默认采用 JSONL 格式——先写入一条标头记录({"_export": {...}}:来源、帐户、时间、参数、对话,以及说明内容是不可信第三方文本的注释),然后按时间从旧到新为每条消息写入一条记录,每条记录都带有其所属对话的id、kind、title、team(频道)和participants(聊天:除你以外有消息记录的人员)——也可设置format: "markdown",导出易读的文字记录,频道消息会按根帖分层。conversations(ID 或名称,单个或数组;省略则导出所有对话)、kinds(chat、channel、meeting)、since/until(按到达时间筛选;默认包含全部历史记录)、tz。每次调用达到max_items条消息(默认值为 5000,最大值为 50000)或budget_seconds秒(默认值为 90,最大值为 600)后停止;若has_more: true,请传回cursor(单独传入,或与max_items/budget_seconds一同传入),以继续同一次导出——文件会写入磁盘上的同一文件夹;如果某个对话跨多次调用导出,后续部分会写入其首个文件旁的<name>.part-0002.jsonl(以及manifest.part-0002.json)。预算会在对话之间以及读取的分页之间检查:超过预算的长时间读取会被丢弃,并由下一次调用重新读取(但每次调用中的第一个对话总会完成,以确保每次调用都有进展)。对话按 ID 顺序导出;通常,游标会列出尚未处理的对话,但如果列表过长,无法放入游标,则只保存下一个起始 ID,下一次调用会从该位置重新列出对话——因此,在两次调用之间首次出现且 ID 排在该位置之前的对话,不会包含在本次导出中。如果凭据被拒绝、经过三次退避重试后仍遇到限流,或 Teams 端发生故障,则调用会提前停止并返回游标,同时保留已写入的内容(重新进行身份验证后继续);无法读取的对话(403/404)会列在errors中并跳过。文件的存放位置取决于安装方式:本地(stdio)安装会在dest→TEAMS_OUTPUT_DIR→~/Downloads/microsoft-teams-mcp下新建文件夹,并返回本地绝对路径;托管安装(mcp-host 设置MCP_BLOB_BASE_URL和MCP_BLOB_SIGNING_KEY)会在每次调用时上传一个 zip 文件,并返回一小时后过期的签名下载链接(url_expires_at)。设置attachments: true时,还会在同一文件夹或 zip 中的attachments/<8-char conversation hash>/<message id>-<name>下保存每条消息的内嵌图片和共享文件(SharePoint 文件,每个最大 25 MB;同时下载 4 个),并在每条消息的记录中通过相对路径列出这些附件(Markdown 文字记录会链接到这些附件);无法获取的附件——例如你的帐户无法打开的文件、尚无令牌的 SharePoint 主机上的文件,或过大的文件——会标记为skipped并附上原因,不会导致导出失败(counts.attachments/counts.attachments_skipped)。不会将任何内容标记为已读。 -
teams_healthcheck— 按照每个工具相同的方式解析令牌(在冷启动时捕获令牌),并通过列出一次对话来探测聊天服务。报告令牌来源及其过期时间(未持有令牌时两者均为null,例如捕获失败后),以及列表返回的对话数量——绝不会返回令牌。 写入(每项操作都会先要求你确认——请参阅 写入操作): -
teams_send_message— 以你的身份向任意聊天或频道发送消息,可按 ID 或名称指定。text为纯文本:内容会经过转义,换行符会保留,:key:短代码(:smile:、:1f389_partypopper:)会转换为 Teams 表情符号。reply_to会在频道帖子的主题中回复——它必须是该频道中的根帖子,而非回复(在聊天中会以引用回复的方式回复该消息);quote会在任意位置以引用回复的方式回复消息;mentions会通过 MRI 或在对话近期发件人中匹配的显示名称 @提及他人(若名称不明确,则会拒绝并列出候选对象)。结果会说明是否通过重新读取对话找到了该帖子(verified)。 -
teams_react— 对消息添加或(设置remove: true时)撤回你的回应:可使用表情符号或名称(thumbsup/👍、heart/❤️、laugh/😆、surprised/😮、sad/😢、angry/😡、checkmark/✅),也可使用任意原始 Teams 回应键(fire、1f389_partypopper)。系统会重新读取消息以验证更改(若未再次找到该消息,则verified: null)。此操作只会写入该消息的回应,不会更改你的近期回应列表或任何其他偏好设置。 -
teams_mark_read— 在 Teams 中将聊天或频道标记为已读,清除其未读徽标:将你的已读标记(对话的consumptionhorizon)推进到up_to_message_id指定的位置;默认推进到对话中的最新消息。返回对话、previous_horizon_message_id(如果你从未打开过,则为null)以及new_horizon_message_id。标记不会向后移动:若它已处于目标位置或更靠后,则不会写入任何内容(也不会要求确认),且status为already_read。只有你能看到自己的已读标记。
teams_list_messages 和 teams_list_unread_messages 也会写入数据,但仅当你传入 mark_read: true 时才会写入(见上文)。其他所有工具均为只读,除非你提出要求,否则不会将任何内容标记为已读。
写入操作
帖子和回应对会话中的所有人可见,标记为已读会清除未读徽标,因此每次写入都会先征求你的确认。能够显示确认提示的客户端(Claude Code)会显示提示。无法显示提示的客户端则会在首次调用时不做任何更改,并返回一份 confirmation-required 预览,其中包含会话、确切文本、所有提及、引用的消息、回应,或已读标记将移动到的消息及其原位置;其他人写下的所有内容(标题、姓名、引用文本)都会标记为不可信内容;预览还会附带一个 confirmToken。只有使用该令牌重复调用才会执行写入。对于 teams_list_messages / teams_list_unread_messages 上的 mark_read,首次调用只会返回预览(不返回消息);重复调用才会读取并标记为已读。令牌只能使用一次,会过期,并且绑定到确切的参数:两次调用之间任何内容发生变化,都会被拒绝并返回一份新的预览。
| 变量 | 默认值 | |
|---|---|---|
MCP_CONFIRM_MODE | ask-user | 在无法显示确认提示的客户端(claude.ai、Claude Desktop)上,写入操作的处理方式。ask-user:分两步执行,模型必须先在聊天中获得你的批准,然后才能使用令牌再次调用。auto:同样分两步执行,但模型审阅预览后可以自行使用令牌。refuse:此类客户端上的写入操作会被拒绝。无法识别的值按 refuse 处理。 |
MCP_CONFIRM_ELICITATION | on | off 表示永不显示确认提示,因此所有客户端都会采用 MCP_CONFIRM_MODE 的行为。如果客户端声称能够显示提示但从未显示,请设置此项。 |
MCP_CONFIRM_TTL_SECONDS | 600 | 令牌的有效时长。 |
MCP_CONFIRM_SECRET | 每个进程随机生成 | 签名密钥;只有在令牌必须跨服务器重启保留时才设置。 |
对于无法显示提示的客户端,模型会自行传回令牌,因此请保持 MCP_CONFIRM_MODE=ask-user,并在聊天中批准每份预览,作为控制发布内容的实际保障。
先在自己的笔记中试用。 会话 48:notes(也可用名称 notes 或 self)是你的 Teams 自我聊天(“给自己的笔记”):所有写入操作都接受它,teams_list_messages 可以读取它,其他人看不到这里的内容,而且这是安全查看消息或回应效果的地方。其他所有 48: 系统会话(日历等)都会被拒绝。
发送超时后不会重试。 如果 Teams 未确认帖子(超时、连接断开,或请求发出后返回 5xx),结果为 status: "unknown"——消息可能已经发布,因此请先读取会话再重新发送。4xx 表示请求遭拒,并会作为错误返回。对于 Teams 未确认的回应,会通过重新读取消息来确认;只有读取后仍未显示更改时,结果才为 status: "unknown"。如果 Teams 未确认已读标记移动,结果为 status: "unknown"(读取工具会返回 marked_read: false 和 mark_read_warning);将标记再次移动到同一条消息不会造成影响。标记为已读(PUT …/properties?name=consumptionhorizon)已于 2026-10-12 在 48:notes 上通过实时验证。线程式频道回复、@提及、移除回应,以及 ✅ 回应所用的键遵循 Teams 文档说明或推断出的格式,但尚未在实时租户上验证。
这里没有日历工具。 Teams Web 的日历数据来自 Outlook,并显示在嵌入的 outlook.office.com iframe 中,而不是聊天服务的数据。请改用 office-outlook-mcp 的 outlook_list_events / outlook_get_event / outlook_list_calendars。
值得了解的事项
- 企业网络。 该服务会与多个主机通信:
teams.cloud.microsoft(聊天)、区域性*.asm.skype.com图片存储库 (内嵌图片)、*.sharepoint.com(共享文件)以及substrate.office.com(搜索)。如果其中一个主机无法访问,而其他主机 正常工作,错误会指出该主机和网络原因(ENOTFOUND、ECONNREFUSED、证书代码等),并提供提示,而不是只显示 一句“fetch failed”。如果无法访问图片存储库,内嵌图片会通过 Teams 网关 (us-prod.asyncgw.teams.microsoft.com)重试;该网关提供相同的字节内容, 而且通常在*.asm.skype.com不允许访问时仍可访问。Node 既不会读取系统 密钥链,也不会读取系统代理,因此以下两个设置可以解决大多数其他情况:- TLS 检查:
NODE_EXTRA_CA_CERTS=/path/to/corporate-root-ca.pem。 - 必须使用代理:将
HTTPS_PROXY=<proxy URL>与NODE_USE_ENV_PROXY=1搭配使用(Node 22.21+ / 24.5+)。
- TLS 检查:
- 游标:仅获取新增内容。 消息 ID 是其到达时间的 Unix 毫秒时间戳,因此,“比我上次看到的消息更新”是数值比较,游标就是该消息的 ID——
next_cursor是普通 ID 字符串,原样传回after_message_id(teams_list_messages)或cursors中(teams_list_unread_messages)。服务不保留状态,也没有缓存:每次调用都会实时读取 Teams,fetched_at(ISO 8601,UTC)表示读取时间。没有刷新标记,因为没有任何内容需要刷新。游标不会移动已读标记;设置limit/per_conversation_limit时会保留最新消息,并将next_cursor跳到最新消息,因此在假定没有跳过任何内容之前,请先检查truncated。两种游标:teams_list_changed_conversations使用独立的时间游标(不透明令牌或 ISO 8601 时间),该游标只能传回teams_list_changed_conversations;它不是消息 ID。其每一行会话数据都包含要传给teams_list_messages的消息 ID 游标after_message_id(或传给teams_list_unread_messages的cursors)。每个工具都会拒绝另一种游标。 - 从指定时间分页(数据摄取和回填)。
teams_list_messages和teams_list_unread_messages接受from:UTC 时间(ISO 8601;未指定时区的时间按 UTC 读取),或上一次结果中的next_from。它们会按从旧到新的顺序返回在该时间或之后到达的内容,最多limit条(默认值为 50;teams_list_unread_messages会将每个会话的消息汇总到一个messages列表中,每条消息都带有自己的conversation_id),并返回has_more、next_from和next_from_time。将next_from作为from传回,直到has_more为 false;最后一页(包括空页)也会返回该值,因此相同调用可以继续轮询之后到达的内容。时间戳相同(精确到毫秒)的消息不会被拆分,也不会在分页间重复;除此之外,投递语义为至少一次(next_from不会超出列表已知的最新变更;读取失败时则完全不会前移),因此请按消息 ID 去重。开销: Teams 会按从新到旧的顺序提供消息,且无法跳转读取,因此每次调用都会读取自from以来的所有变更——随着深度回填逐渐追上,速度会越来越快——而某个会话自from以来超过 20,000 条消息时,无法按从旧到新的顺序分页:teams_list_messages会报错,而不会跳过任何消息;teams_list_unread_messages会返回其到达时间晚于已读取到的最早位置的内容,在incomplete中指出无法读取的区段(unread_not_read_from/unread_not_read_until),并继续推进next_from——因此,单个超大规模会话不会阻塞数据流。from会取代since/after_message_id/mark_read(对于teams_list_unread_messages,还会取代since_days、cursors、每会话限制、older_unread和format: "summary")。 - 结果会被标记为不可信。 每个数据工具都会返回
{ untrusted_content: true, note, ... },并在描述中说明这些文本由其他 Teams 用户撰写。消息正文、预览和主题都是需要报告的数据,绝不能当作指令执行——这一点很重要,因为该服务通常与具有写入能力的 MCP 并行运行(邮件、消息),否则注入的指令可能会触达这些 MCP。 - 未读状态遵循 Teams 的定义,并通过读取确认。 如果消息在对话的已读标记(
consumptionhorizon)之后到达、不是你发送的、属于真实消息(Text或RichText/Html),且未被删除,则为未读。系统消息——通话录音和转录文本(RichText/Media_*)、Event/*、ThreadActivity/*、Control/*——永远不会让任何内容变为未读。对话列表本身无法判断:通话录音也会让lastimreceivedtime更新,lastMessage可能已过时,而你从未打开过的聊天则完全没有标记。因此,列表只会提名候选项(任何位于标记之后的消息,或没有标记的对话);unread_only和teams_list_unread_messages会读取每个候选项中位于标记之后的消息(每次读取 4 条),只保留包含真实未读消息的候选项,并附上其unread_count。如果不启用unread_only,列表工具的unread仅是一个低成本提示,并标记为unread_confirmed: false。返回 404 或 403 的候选项(已消失或不再可读的频道)不算未读,会被跳过并计入skipped_unreachable。你在 Teams 中隐藏的频道(hidden: true)不会计为未读,除非传入include_hidden。未读结果也仅限近期活动:只有最新未读消息在最近since_days天内到达的对话才会计入(默认值为 14,最大值为 3650)——实际上,大多数“未读”频道都是你从未打开过、数月或数年没有活动的频道。如果候选项在时间窗口内完全没有活动,则甚至不会读取。时间窗口之外的内容会计入older_unread_skipped(并提示扩大since_days),绝不会被静默丢弃。older_unread: true(适用于teams_list_unread_messages,以及带有unread_only的teams_list_conversations)也会在older_unread_conversations中列出这些对话,字段包括id、kind、title、team(频道所属的团队)、lastActivity,按最近活动优先排列;对于已读取消息以确定其位于时间窗口之外的对话,还会提供unread_count。对于在时间窗口内完全没有活动的对话,不会仅为统计而读取,而是标记unread_count_known: false(标记之后的新内容可能只有系统事件)。静音聊天:对话列表会将你静音的聊天标记为properties.alerts: "false"(实时验证于 2026-10-12:1,435 个对话中有 8 个,其余均为"true"或不存在),并显示为muted: true。teams_list_unread_messages不会读取这些聊天,也不会将其纳入结果,而是计入muted_skipped;include_muted会将它们重新纳入。列表工具的unread_only仍会包含它们。teams_list_messages不受时间窗口限制:它使用显式指定的since和limit。此服务只读取标记,绝不会移动标记。 - 令牌只会发送给 Teams。 聊天服务令牌会发送到聊天服务的基址;对于内嵌图片,只会发送到
.asm.skype.com下的 https 主机(Teams 自己的图片存储)——指向其他任何位置的图片链接都会被拒绝。共享文件下载使用单独的 SharePoint 令牌(见下文),且只发送到文件自身的*.sharepoint.com主机;返回的预签名下载链接则完全不携带令牌。搜索使用第三个 Substrate 令牌,且只发送到https://substrate.office.com/searchservice/api/v2/query。 - 共享文件从 SharePoint 下载。 共享文件存储在 SharePoint 中,而 SharePoint 不接受聊天服务令牌,因此
teams_get_attachment会获取单独的 SharePoint 令牌,并通过 SharePoint 的 v2.1 shares API 获取文件——见下文。 - 范围会悄然扩大。 如果看到
not in declared set错误,说明扩展获批的范围落后于服务声明的范围——请在 ContextMint Bridge 弹出窗口中撤销并重新配对microsoft-teams-mcp。
从 0.4 升级
0.5 只新增了参数和字段,但以下行为有所变化:
teams_list_messages中名称不明确: 过去,如果一个名称匹配到多个会话,就会报错并列出候选项。现在会成功返回{ ambiguous: true, query, match_count, candidates, hint },且不包含消息。读取messages或threads前,请先检查ambiguous。发送消息、添加反应和附件操作在名称不明确时仍会失败。teams_list_unread_messages中的静音聊天: Teams 中静音的聊天不会显示,并会计入muted_skipped。传入include_muted: true可恢复旧行为。- 读取工具不再标注为只读:
teams_list_messages和teams_list_unread_messages接受一个选择启用的mark_read标志,因此发布时会标记为readOnly: false。如果客户端只自动批准只读工具,现在可能会在每次读取前询问。未设置mark_read时,这些工具仍绝不会写入。
从 0.3 升级
0.4 将所有工具迁移到了 Teams 聊天服务 API,且多个输出的结构发生了变化。如果你编写了针对这些工具的脚本:
- 已移除的工具:
teams_get_open_chat_messages和teams_get_open_channel_posts——请改用teams_list_messages,它无需打开聊天或频道,即可通过 ID 或名称读取任意聊天或频道。 teams_list_activity: 现在,列表项直接来自活动源。title和location已替换为type/subtype/kind、from、where: { conversation_id, title, type }、message_id、reply_chain_id和preview;time现在采用 ISO 8601 格式(过去是类似 "1:19 PM" 的文本);新增了read;移除了empty_hint(活动源不再依赖“活动”视图)。teams_get_attachment: 现在,共享文件会返回其内容(图片以图片内容形式返回,其他文件则以包含字节的 MCP 嵌入资源形式返回),而不仅仅是元数据和链接——详见SharePoint 文件下载。无法获取的文件仍会返回旧的元数据和链接结构,因此只读取元数据的脚本仍可正常工作。teams_healthcheck: 顶层的bridge部分已移除——现在 bridge 仅用于捕获令牌。令牌健康状态位于credential和chat_service中;新增的sharepoint说明介绍了按需获取的短期 SharePoint 令牌(healthcheck 不需要该令牌)。- 聊天标题: 无标题聊天现在会命名为
(1:1 chat)(或对方的姓名)、Group chat: A; B; C +N(并附带participants数组)或(untitled group chat)——不再使用最后一位发件人的姓名或(untitled chat)。普通的teams_list_conversations/teams_list_channels列表会将favorite对话排在前面,其次是pinned对话。 - 未读消息: 通过在你的已读位置之后读取消息来确认未读(通话录音和其他系统事件不计入);除非设置
include_hidden,否则不会显示隐藏频道;since_days默认为 14,其余消息会通过older_unread_skipped报告。
开发
npm install
npm run build
npm test # typecheck + suite
npm run test:coverage # CI's gate
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。