@dbx-tools/teams

中文文档
0.9.144发布于 2026/10/20Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 2911

Server-side Microsoft Teams Adaptive Card runtime, agent tool, and AppKit plugin

原始使用文档
文档目录9 节
  1. Quick start
  2. Configure Teams delivery
  3. Converse in cards
  4. Build a card directly
  5. Use with AppKit
  6. Configuration
  7. Connect a real Teams channel
  8. Routes
  9. Modules

@dbx-tools/teams

Add a Microsoft Teams bot endpoint to an AppKit or Mastra backend, or turn a model response into an Adaptive Card for a preview or channel post. The browser-safe card schemas live in @dbx-tools/shared-teams, and the React renderer (the adaptivecards JavaScript renderer) lives in @dbx-tools/ui-teams.

Quick start

Register the Teams endpoint and give an agent its card tools:

import { createApp, server } from "@databricks/appkit";
import { agents, mastra } from "@dbx-tools/appkit-mastra";
import { teams } from "@dbx-tools/teams";

const support = agents.createAgent({
  instructions: "Return Teams cards for deployment summaries.",
  async tools(plugins) {
    return { ...(await plugins.teams.toolkit()) };
  },
});

await createApp({ plugins: [server(), teams(), mastra({ agents: { support } })] });

Registration adds:

  • AppKit plugin registration that resolves config, logs the effective card version and webhook state at boot, and mounts card-build / card-post routes.
  • Two agent surfaces: an AppKit teams.createCard tool and a standalone Mastra create_teams_card factory, both taking the small CardSpec vocabulary. Each plugin instance keeps its card config, webhook, and executor separate.
  • A deterministic builder that compiles a CardSpec (title, subtitle, text, key/value facts, link actions) into a valid Adaptive Card 1.5 document.
  • Optional posting to a Teams incoming webhook, wrapped in the attachment envelope Teams expects, with a retrying execution policy.
  • POST /api/teams/messages - the Microsoft Teams messaging endpoint: the URL you paste into an Azure Bot registration so a Teams channel can chat with your agents. It validates the Bot Service JWT (signature, issuer, and an audience equal to your bot's app id), acknowledges immediately, and delivers the agent's card back over the Bot Framework Connector API. This is the Teams-shaped analogue of how the Mastra plugin exposes MCP at a path - the protocol is the interface.
  • POST /api/teams/activity - the same turn, run synchronously with the reply activities in the response body. No bot registration required, so this is what a local client, a test, or any non-Teams caller uses. The conversation id maps onto the agent's memory thread, so repeat turns continue one conversation.
  • POST /api/teams/card route a browser dev page posts to for a live card preview, and POST /api/teams/post to push a card to the webhook.

Configure Teams delivery

import { createApp, server } from "@databricks/appkit";
import { teams } from "@dbx-tools/teams";
import { agents, mastra } from "@dbx-tools/appkit-mastra";

const support = agents.createAgent({
  instructions: "Summarize results as Teams cards when asked.",
  async tools(plugins) {
    return { ...(await plugins.teams.toolkit()) };
  },
});

await createApp({
  plugins: [
    server(),
    teams({ webhookUrl: process.env.TEAMS_WEBHOOK_URL }),
    mastra({ agents: support, storage: true }),
  ],
});

plugin.teams() resolves config into an instance-owned runtime, logs the effective card version and whether a webhook is wired up, and mounts the build / post routes under /api/teams. App-integrated agents should consume its native toolkit as shown above, preserving the owning plugin's AppKit execution chain. The teams.createCard tool is auto-inheritable because building a card has no side effects.

Converse in cards

curl -X POST http://localhost:8000/api/teams/activity \
  -H 'content-type: application/json' \
  -d '{
        "activity": {
          "type": "message",
          "text": "did the deploy land?",
          "from": { "id": "user-1", "name": "Reggie" },
          "conversation": { "id": "conv-1" }
        }
      }'

The reply is { "activities": [...] }, each activity carrying attachments[].contentType === "application/vnd.microsoft.card.adaptive" with a compiled Adaptive Card under content - what a Teams client renders.

A turn runs in two passes:

  1. Answer. The agent answers the question normally - its tools available, no structuredOutput, and nothing about cards in the prompt.
  2. Format. A second structuredOutput pass reformats that answer into a CardSpec, which the builder compiles.

Answering first preserves normal tool use. The second pass changes presentation without changing the answer. Agents keep create_teams_card for the opposite direction: answering in prose on a chat endpoint and choosing to attach a card. When the agent calls it during the first pass, that specification wins and the format pass is skipped.

Because the schema is prompt-injected rather than provider-enforced (Databricks Model Serving rejects response_format alongside tools), the format pass is best-effort, so the answer is recovered in layers: the parsed spec, a full Adaptive Card document read back into the spec vocabulary (what a capable model often returns when asked for "an Adaptive Card"), the payload Mastra rejected off the thrown error, JSON embedded in prose, then the prose itself. A formatting miss costs structure, never the answer.

Host embed markers ([data:<id>], [chart:<id>]) are stripped from the answer: a chat UI swaps those for a rendered table or chart, but a card has no such slot, so a marker would render as literal [data:01f1...] where a number belongs. The answering pass asks the agent for the values themselves instead.

The agent is resolved from the sibling Mastra plugin by registered name (agentPlugin, default mastra), so this package stays a leaf add-on and takes no dependency on @dbx-tools/appkit-mastra. Pass agentId in the body to pick a specific agent. Omit it for the default one. An unmounted agent plugin answers 503, an unknown agentId answers 404.

The same lookup fetches the plugin's createRequestContext(), which builds the per-turn Mastra RequestContext the turn passes to both passes. This is required for parity with chat: user-scoped tools read the AppKit user off that context, so without it ask_genie answers "the data source is unreachable" while the chat routes answer with real numbers. A provider that exposes no factory still serves turns without user-scoped tools.

Activities that carry no prompt - a typing indicator, a conversationUpdate when someone joins, an empty message - answer { "activities": [] } rather than erroring, which matches standard bot behavior.

@dbx-tools/ui-teams's TeamsChat is the matching client.

Build a card directly

import { builder } from "@dbx-tools/teams";

const { card } = builder.buildCardResult({
  title: "Deployment succeeded",
  subtitle: "prod • 2m ago",
  facts: [{ title: "Version", value: "1.4.2" }],
  actions: [{ title: "View run", url: "https://example.com/runs/42" }],
});
// `card` is a full Adaptive Card document; render it with @dbx-tools/ui-teams
// or post it with the plugin's `postCard` export.

For a standalone Mastra tool, provide config directly or share an explicit runtime across factories:

import { runtime, tool } from "@dbx-tools/teams";

const teamsRuntime = runtime.createTeamsRuntime({ cardVersion: "1.5" });
const createTeamsCard = tool.teamsCardTool({ runtime: teamsRuntime });

Pass the runtime explicitly or provide the executor to createTeamsRuntime.

Use with AppKit

AppKit has no Teams or Adaptive Card surface, so use this package when an agent should emit a Teams card. The policy layer limits the card shape: the model works in a small, safe vocabulary (CardSpec) instead of raw Adaptive Card JSON, the builder guarantees a schema-valid Adaptive Card 1.5 document, and posting is gated behind an explicitly configured incoming webhook. This package follows the same add-on shape as @dbx-tools/email.

Building a card is a read-only transform and doesn't require approval. Posting to /api/teams/post is an external side effect, but it isn't exposed as an agent tool. The application that exposes that route owns caller authorization and any approval step. Tools used by a Teams conversation keep their owning agent's approval policy.

Configuration

Precedence per field: explicit plugin config wins, then the matching environment variable.

  • cardVersion / TEAMS_CARD_VERSION - Adaptive Card schema version the builder targets. Defaults to 1.5 (what Teams supports).
  • webhookUrl / TEAMS_WEBHOOK_URL - optional Teams incoming-webhook URL. When unset, posting is disabled and the plugin only builds cards for a UI to render. A value that is not an absolute HTTPS URL fails config resolution.
  • agentPlugin / TEAMS_AGENT_PLUGIN - registered name of the sibling plugin whose agents answer a conversation turn. Defaults to mastra. Set it when the Mastra plugin is mounted under a config.name override.
  • appId / TEAMS_APP_ID (alias MICROSOFT_APP_ID) - the Entra app (client) id from your Azure Bot registration. Required before POST /messages accepts anything: it is the audience an inbound token must carry.
  • appPassword / TEAMS_APP_PASSWORD (alias MICROSOFT_APP_PASSWORD) - client secret for that app id, used to fetch the outbound Connector token.
  • appTenantId / TEAMS_APP_TENANT_ID (alias MICROSOFT_APP_TENANT_ID) - set only for a single-tenant bot. Leave it unset for multi-tenant.
  • allowUnauthenticated / TEAMS_ALLOW_UNAUTHENTICATED - serve /messages without token validation, replying in the HTTP response instead of through the Connector API. For local development only, and ignored unless NODE_ENV is development, so a production build cannot be talked into it by an environment variable. See "Connect a real Teams channel".

The MICROSOFT_APP_* aliases are the names the Bot Framework SDK and the Azure portal already use, so an existing bot's environment drops in unchanged.

Connect a real Teams channel

  1. Create an Azure Bot resource and note its Microsoft App ID. Create a client secret for it.
  2. Set its Messaging endpoint to the app host plus /api/teams/messages.
  3. Enable the Microsoft Teams channel on the bot.
  4. Give the app TEAMS_APP_ID and TEAMS_APP_PASSWORD (plus TEAMS_APP_TENANT_ID for a single-tenant registration).
  5. Install the bot in Teams with a Teams app manifest that references the same app ID, and message it. The agent answers in Adaptive Cards.

What the endpoint does on each inbound request:

  • Validates the JWT against the Bot Framework JWKS, requiring a trusted issuer and an audience equal to appId. The audience check is the one that matters: a token the Bot Service issued for another bot is signed by the same keys, so without it anyone with their own bot could drive your agent.
  • Pins the reply destination to the token. serviceUrl arrives in the request body, and replies carry your bot's credentials, so it is honored only when it matches the verified token's own serviceurl claim.
  • Answers 200 before running the agent. Bot Service times out an unacknowledged activity in seconds and retries it, while a card takes longer than that - so a synchronous reply would produce duplicate cards. The typing indicator shows while the agent works, and the card is delivered through the Connector API when it is ready.

For local development without a registration, set allowUnauthenticated: true (with NODE_ENV=development) and /messages answers in the HTTP response, which is how the in-repo demo renders live cards.

Routes

Mounted under the plugin base path /api/teams:

  • POST /messages - the Teams messaging endpoint (see above). Body is a bare Bot Framework activity. 401 on a token that fails validation, 503 when no appId/appPassword is configured, 400 on an invalid activity or a serviceUrl that does not match the token. Answers 200 with an empty body. The reply arrives over the Connector API.
  • POST /activity - run one conversation turn. Body is { activity, agentId?, model? }. The response is { activities }, each carrying Adaptive Card attachments. 400 on an invalid activity, 404 on an unknown agentId, 503 when no agent plugin is registered.
  • POST /card - compile a CardSpec request body into an Adaptive Card document. The dev display page posts here to preview cards live.
  • POST /post - compile then push a CardSpec to the configured Teams incoming webhook. Returns 400 when the body is invalid and an error when no webhook is configured.

Modules

  • plugin - teams (the plugin factory) and TeamsPlugin.
  • tool - teamsCardTool and CREATE_CARD_DESCRIPTION.
  • builder - buildAdaptiveCard / buildCardResult (pure CardSpec -> card compilation).
  • conversation - runCardTurn (one activity in, card activities out), resolveCardAgent, promptOf, toReplyActivity, CARD_TURN_INSTRUCTIONS, and the CardAgentLike / AgentProviderLike shapes.
  • auth - verifyBotToken (inbound Bot Service JWT validation), connectorToken (outbound client-credentials token), isAllowedServiceUrl, and resetTeamsAuth.
  • connector - sendActivity / sendTyping, the Bot Framework Connector API calls a reply is delivered through.
  • messaging - deliverTurn (acknowledge-then-deliver turn) and resolveServiceUrl.
  • runtime - createTeamsRuntime, buildCardWithRuntime, postCardWithRuntime, and standalone buildCard / postCard.
  • config - resolveTeamsConfig, TEAMS_CONFIG_SCHEMA, the env-name constants, and the TeamsPluginConfig / ResolvedTeamsConfig types.
  • defaults - the interceptor execution settings and named caps.

版本与发布记录

@dbx-tools/teams 共发布 260 个版本,首次发布于 2026/07/28;过去 12 个月发布了 260 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.144latest2026/10/2023 天前
0.9.1432026/10/2023 天前
0.9.1412026/10/2023 天前
0.9.1402026/10/2023 天前
0.9.1392026/10/2023 天前
0.9.1382026/10/1923 天前

关于 @dbx-tools/teams 的常见问题

@dbx-tools/teams 是什么?

Server-side Microsoft Teams Adaptive Card runtime, agent tool, and AppKit plugin

如何安装 @dbx-tools/teams?

在项目目录运行 npm install @dbx-tools/teams,或使用 pnpm add / yarn add / bun add。

@dbx-tools/teams 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dbx-tools/teams 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dbx-tools/teams 可以商用吗?

@dbx-tools/teams 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dbx-tools/teams 的下载量有多少?

根据 npm 官方下载统计,@dbx-tools/teams 在 2026/10/12 – 2026/11/10 这 30 天内被下载 2,911 次,日均约 97 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dbx-tools/teams 还在维护吗?

最新版本 0.9.144 发布于 2026/10/20(23 天前);至今共发布 260 个版本,其中过去 12 个月发布了 260 个;首次发布于 2026/07/28。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。