npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bachstudio/teams-card-mcp

v0.6.0

Published

Standalone MCP server: lets an agent send Microsoft Teams messages and Adaptive Cards to a specific user via its own configured bot. Outbound-only, no public endpoint required.

Readme

teams-card-mcp

一个独立的 MCP 服务:agent 绑定它,就能用它自己配置的 Teams bot 给指定用户发消息和卡片。

bach agent ──MCP──> teams-card-mcp ──出站 HTTPS──> Bot Framework ──> Teams 用户
                    (自带 bot 凭据,不依赖任何其他服务)

不需要公网入口、不需要 Power Automate、不需要 Webhook——发消息是纯出站调用。

配置

服务不需要任何环境变量就能启动并列出工具。 缺凭据时工具照常列出, 只有真正调用才会返回一条写明缺什么、怎么补的说明。这样网关永远不会因为配置没送到 而拿到一个"查不出任何工具"的空壳。

要真正能发消息,需要三个值:

| 变量 | 说明 | |---|---| | BOT_APP_ID | Microsoft App ID / Bot ID | | BOT_APP_PASSWORD | Microsoft App Password / Client Secret | | BOT_TENANT_ID | Microsoft Entra Tenant ID |

在 bach 后台配过「外部机器人 → Microsoft Teams」的话,这三个就是那里的 appId / appPassword / tenantId,直接抄过来(也可以用另一个完全独立的 bot—— 本服务不调用 bach 的机器人服务)。

放进 MCP 启动配置(推荐)

{
  "mcpServers": {
    "teams": {
      "command": "npx",
      "args": ["-y", "@bachstudio/teams-card-mcp", "--stdio"],
      "env": {
        "BOT_APP_ID": "...",
        "BOT_APP_PASSWORD": "...",
        "BOT_TENANT_ID": "..."
      }
    }
  }
}

其它放法

也支持 .env 文件(放启动目录自动加载)、ENV_FILE=/path/to/file 指定路径、 systemd 的 EnvironmentFile=、Docker 的 env_file:、K8s 的 env / Secret。 优先级:已存在的环境变量 > .env 文件

其余可选项见 env.example

两种传输方式

| 方式 | 怎么启动 | 用在哪 | |---|---|---| | http(默认) | npx -y @bachstudio/teams-card-mcp | bach 用 HttpClientTransport 直连 /mcp | | stdio | npx -y @bachstudio/teams-card-mcp --stdio | supergateway 等 stdio→SSE 网关托管 |

也可以用 MCP_TRANSPORT=stdio 代替 --stdio

stdio 模式下 stdout 完全属于 MCP 协议,本服务所有日志都写 stderr,不会污染协议流。

supergateway 托管示例

supergateway --stdio "npx -y @bachstudio/[email protected] --stdio" --port 8000

环境变量不要写在 stdio 命令串里——那是一段交给 /bin/sh 解析的字符串,容易出问题 (比如变量名里混进一个肉眼看不出来的非 ASCII 字符,shell 就会把整串当命令执行, 报 not found 且 exit 127,网关侧表现成「连上了但查不到工具」)。

改成在容器/Pod 层面注入,子进程自然继承:

# K8s Deployment
env:
  - name: BOT_APP_ID
    valueFrom: { secretKeyRef: { name: teams-bot, key: appId } }
  - name: BOT_APP_PASSWORD
    valueFrom: { secretKeyRef: { name: teams-bot, key: appPassword } }
  - name: BOT_TENANT_ID
    valueFrom: { secretKeyRef: { name: teams-bot, key: tenantId } }

跑起来

npm install && npm run build
BOT_APP_ID=xxx BOT_APP_PASSWORD=yyy BOT_TENANT_ID=zzz npm start

启动时会自动换一次 token 验证凭据,凭据错了立刻在日志里告警,不用等到第一次发消息。

不接真实 Teams 也能验证整条调用链(内置假的 Entra / Bot Framework / Graph):

npm run smoke

MCP 工具

| 工具 | 用途 | |---|---| | teams_send_message | 发纯文本私聊消息 | | teams_send_notice | 发一张只读信息卡片(标题 + 说明 + 键值对),适合构建结果、告警 | | teams_ask | 发一张带按钮的卡片(审批、确认、二选一) | | teams_send_form | 发一张带输入框的表单卡片 | | teams_send_raw_card | 发调用方自己构造的完整 Adaptive Card | | teams_resolve_user | 邮箱 → Entra 对象 ID,用于确认用户存在 |

收件人怎么填

所有工具的 user 参数由 agent 在调用时传入,服务本身不维护任何用户名单。两种填法:

| 填什么 | 需要什么权限 | |---|---| | 企业邮箱([email protected]) | 应用注册需有 Graph User.Read.All 应用权限(管理员同意一次) | | Entra 对象 ID(aadObjectId) | 不需要额外权限 |

没有 Graph 权限时用邮箱会返回明确报错并提示改用对象 ID,不会静默失败。

调用示例

// 通知
{ "user": "[email protected]", "text": "**构建失败**\n\n分支 dev-master-v6,日志见附件" }

// 信息卡片
{
  "user": "[email protected]",
  "title": "构建失败",
  "description": "流水线 #2841",
  "facts": { "项目": "bach", "分支": "dev-master-v6", "耗时": "3m12s" }
}

// 带按钮
{
  "user": "[email protected]",
  "question": "是否批准这笔报销?",
  "facts": { "申请人": "张三", "金额": "5000 元" },
  "options": [
    { "id": "approve", "title": "同意", "style": "positive" },
    { "id": "reject",  "title": "驳回", "style": "destructive" }
  ]
}

// 带跳转按钮:点了跳到指定 Teams 应用,并把自定义信息送到目标网页
{
  "user": "[email protected]",
  "question": "任务已就绪",
  "options": [
    {
      "id": "open",
      "title": "在 Bach 里查看",
      "style": "positive",
      "deep_link": {
        "app_id": "8b9a2465-fc14-4e9f-b07b-791c8b0c4fd4",
        "entity_id": "bach",
        "data": { "conversationId": "abc-123" }
      }
    }
  ]
}

deep_link 让 MCP 自己拼出 https://teams.microsoft.com/l/entity/{app_id}/{entity_id}?context=... 深链。 data 会被序列化后放进 subEntityId,目标网页用 Teams JS SDK 读回:

const ctx = await microsoftTeams.app.getContext();
const raw = ctx.page.subPageId;                       // MCP 送来的 subEntityId
const payload = JSON.parse(raw);                       // 若 data 是对象
// -> { conversationId: "abc-123" }

也可以自己手拼 URL 走 url 字段(跳任意 https 网址)。urldeep_link 不能同时填

前提条件

  1. 目标用户必须已安装这个 Teams 应用(个人范围)。 bot 只能给装过应用的用户发私聊, 没装会返回 403,服务会把这个原因原样说出来。
  2. bot 的单租户 / 多租户类型要和 BOT_TENANT_TYPE 一致,否则 token 拿得到但发送时 401。

接入 bach

bach 走远程 HTTP MCP(Super.Agent.CoreMcpClient + HttpClientTransportplugin.api_url), 在 bach 后台注册成 MCP 插件即可:

| 配置项 | 填什么 | |---|---| | api_url | http(s)://你的服务地址/mcp | | 认证头 | x-api-key: <你的 MCP_API_KEY>(没配 MCP_API_KEY 就不用带) | | name_space | 例如 teams |

注意:bach 注册工具时会执行 f.Name.Replace("-",""),所以本服务所有工具名都用下划线,已规避。

当前边界

这一版只做发送。 用户点击卡片按钮后,Teams 会往你的 bot 消息端点发一条 text 为空、value 带着按钮 data 的 message 活动——本服务不接收这个回传, 需要由那个端点侧处理。卡片按钮的 data.__action 就是你在 options[].id 里填的值, 端点侧凭它判断用户点了哪个。

要让 agent 能同步拿到点击结果(teams_ask 发出去后直接等到答案), 需要本服务额外提供 /api/messages 端点并接管 bot 的消息路由——那是下一步。

项目结构

src/
  loadEnv.ts       启动前加载 .env / ENV_FILE(必须先于 config 求值)
  config.ts        环境变量读取与启动校验
  logger.ts        分级日志
  cards.ts         Adaptive Card 构造
  botFramework.ts  token / 建会话 / 投递 activity,含 token+会话+用户 ID 三级缓存
  tools.ts         6 个 MCP 工具
  mcp.ts           McpServer 工厂(http / stdio 共用)
  server.ts        Express:/mcp、/healthz、鉴权
  index.ts         按 transport 分支启动、凭据自检、优雅退出
test/
  smoke.ts         HTTP 传输端到端冒烟(内置假微软云)
  stdio.ts         stdio 传输冒烟(复现 supergateway 托管方式)