自带代理到 MS Teams
Bring your own Agent to MS Teams

原始链接: https://microsoft.github.io/teams-sdk/blog/bring-your-agent-to-teams/

本文详细介绍了如何使用Teams TypeScript SDK将使用LangChain、Slack Bolt或Azure AI Foundry等工具构建的现有代理集成到Microsoft Teams中。核心原理是利用HTTP服务器适配器将您的现有服务器连接到Teams的消息端点(`/api/messages`),而无需进行重大代码更改。 该过程包括三个关键步骤:为您的本地服务器获取公共HTTPS URL(使用Dev Tunnels或ngrok等工具),使用Teams SDK CLI注册您的机器人(处理应用程序注册和清单创建),以及将应用程序旁加载到Teams中进行测试。 SDK通过验证传入请求和自动路由消息来简化集成。示例演示了如何将Slack机器人、LangChain链和Azure Foundry代理桥接到Teams,所有这些都共享一致的代码结构。还提供了使用FastAPI的Python等效版本。 最终,SDK充当无缝连接器,允许您以最少的对现有基础设施的破坏,将现有代理的功能扩展到Teams用户。完整文档请参见“Self-Managing Your Server”。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 将你的代理带到 MS Teams (microsoft.github.io) 8 分,umangsehgal93 发表于 2 小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 帮助 thegagne 发表于 2 分钟前 [–] 如果它能修复 Teams 中 Mac 屏幕共享的延迟就好了。回复 考虑申请 YC 2026 年夏季项目!申请截止至 5 月 4 日 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

You've already built the agent. It lives somewhere: a LangChain chain, an Azure Foundry deployment, a Slack bot. Your users live in Teams. Teams is where most enterprise work happens: decisions get made, customers get answered, and projects move forward there. Getting your agent into that context, before you build anything Teams-specific, is already worth doing.

It comes down to one pattern in the Teams TypeScript SDK: the HTTP server adapter. You point it at your HTTP server, it registers a messaging endpoint, and your existing server keeps running as-is. The scenarios below cover three different starting points: a Slack bot, a LangChain chain, and an Azure Foundry agent.

The SDK also handles the parts you don't want to think about: it verifies every incoming request is legitimately from Teams before invoking your handler, and routes messages to the right event handlers automatically.

Every example in this post uses the same three-step shape:

import { App as TeamsApp, ExpressAdapter } from '@microsoft/teams.apps';

const adapter = new ExpressAdapter(expressApp);
const teamsApp = new TeamsApp({ httpServerAdapter: adapter });

teamsApp.on('message', async ({ send, activity }) => {
await send();
});

await teamsApp.initialize();

The SDK injects a POST /api/messages route into your existing Express app. /api/messages is the well-known endpoint Teams uses to deliver messages to your bot, the Teams-shaped interface your HTTP server needs to have. Your server stays yours; the Teams SDK just adds that one endpoint.


You have a Slack bot built with Bolt (or any other kind of bot deployed as a web service). Your team uses both Slack and Teams. Rather than maintaining two codebases, run both on the same Express server.

ExpressReceiver lets Bolt mount onto your Express app instead of owning the server. The Teams SDK does the same thing, so both platforms share the same process.

slack-app.ts: existing Slack logic, untouched
import { App as BoltApp, ExpressReceiver } from '@slack/bolt';
import type { Express } from 'express';

export function mountSlack(expressApp: Express) {
const slackReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
app: expressApp,
endpoints: { events: '/slack/events' },
});

const slackApp = new BoltApp({
token: process.env.SLACK_BOT_TOKEN,
receiver: slackReceiver,
});

slackApp.message('hello', async ({ say }) => {
await say('Hey! Caught you on Slack.');
});
}

teams-app.ts:

import express from 'express';
import { App as TeamsApp, ExpressAdapter } from '@microsoft/teams.apps';
import { mountSlack } from './slack-app';

const expressApp = express();
mountSlack(expressApp);


const adapter = new ExpressAdapter(expressApp);
const teamsApp = new TeamsApp({ httpServerAdapter: adapter });

teamsApp.on('message', async ({ send, activity }) => {
await send(`Hey ${activity.from.name}! You said: "${activity.text}"`);
});

export { expressApp, teamsApp };

Both platforms run in the same process. Slack hits /slack/events, Teams hits /api/messages, and any shared agent logic (LLM calls, database lookups, business rules) lives in plain functions that both handlers call.


You have a LangChain chain. You want Teams users to talk to it.

chain.ts: existing LangChain logic, untouched
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

let _chain: ReturnType<typeof buildChain> | null = null;

function buildChain() {
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant embedded in Microsoft Teams. Be concise.'],
['human', '{input}'],
]);
return prompt.pipe(new ChatOpenAI({ model: 'gpt-4o-mini' })).pipe(new StringOutputParser());
}

export function getChain() {
if (!_chain) _chain = buildChain();
return _chain;
}

teams-app.ts (the bridge):

import express from 'express';
import { App as TeamsApp, ExpressAdapter } from '@microsoft/teams.apps';
import { getChain } from './chain';

const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
const teamsApp = new TeamsApp({ httpServerAdapter: adapter });

teamsApp.on('message', async ({ send, activity }) => {
await send({ type: 'typing' });

const reply = await getChain().invoke({ input: activity.text ?? '' });
await send(reply);
});

export { expressApp, teamsApp };

index.ts (start it):

import 'dotenv/config';
import http from 'http';
import { expressApp, teamsApp } from './teams-app';

await teamsApp.initialize();
http.createServer(expressApp).listen(3978);

Your chain runs on every message. The typing indicator fires before the LLM responds so users know something's happening.


You have an agent deployed in Azure AI Foundry. The Teams SDK gives you the message; you forward it to Foundry and relay the reply.

foundry-agent.ts
import { AIProjectClient } from '@azure/ai-projects';
import { DefaultAzureCredential } from '@azure/identity';

let _client: AIProjectClient | null = null;

function getClient() {
if (!_client) {
_client = AIProjectClient.fromEndpoint(
process.env.AZURE_AI_FOUNDRY_ENDPOINT!,
new DefaultAzureCredential(),
);
}
return _client;
}

export async function askFoundryAgent(userMessage: string): Promise<string> {
const client = getClient();
const thread = await client.agents.threads.create();
await client.agents.messages.create(thread.id, 'user', userMessage);

const run = await client.agents.runs.createAndPoll(
thread.id,
process.env.AZURE_AGENT_ID!,
);

if (run.status !== 'completed') throw new Error(`Run ended: ${run.status}`);

const messages = client.agents.messages.list(thread.id);
for await (const msg of messages) {
if (msg.role === 'assistant') {
return msg.content
.filter((c): c is { type: 'text'; text: { value: string } } => c.type === 'text')
.map((c) => c.text.value)
.join('');
}
}
return 'No response from agent.';
}

teams-app.ts:

import express from 'express';
import { App as TeamsApp, ExpressAdapter } from '@microsoft/teams.apps';
import { askFoundryAgent } from './foundry-agent';

const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
const teamsApp = new TeamsApp({ httpServerAdapter: adapter });

teamsApp.on('message', async ({ send, activity }) => {

const reply = await askFoundryAgent(activity.text ?? '');
await send(reply);
});

export { expressApp, teamsApp };

A Python SDK is also available. The same three-step pattern applies with FastAPI and other ASGI frameworks.

Show Python equivalent
from fastapi import FastAPI
from microsoft_teams.apps import App, FastAPIAdapter

fastapi_app = FastAPI()

adapter = FastAPIAdapter(app=fastapi_app)
teams_app = App(http_server_adapter=adapter)

@teams_app.on_message
async def handle_message(ctx):
await ctx.send("your agent's response")

await teams_app.initialize()

See Self-Managing Your Server for the full Python guide.

All three scenarios share the same registration step.

Step 1: Get a public URL for your local server.

Teams needs to reach your bot over HTTPS. For local development, Dev tunnels is the recommended option — it's built into VS Code and the Azure CLI. ngrok works too. Either way, you'll get a URL like https://abc123.devtunnels.ms that forwards to your local port.

Step 2: Register your bot using the Teams SDK CLI.

npm install -g @microsoft/teams.cli@preview
teams login
teams app create --name "My Bot" --endpoint https://your-tunnel-url/api/messages --env .env

This handles AAD app registration, client secret generation, manifest creation, and bot setup in one command. Your .env gets populated with CLIENT_ID, CLIENT_SECRET, and TENANT_ID automatically.

Step 3: Sideload the app into Teams.

After teams app create, follow the sideloading instructions in the CLI output to install the app in your Teams client for testing.


Every scenario in this post follows the same shape because the SDK is built around one idea: your server is yours. The adapter is the seam between your existing infrastructure and Teams. Whether you're running Express or any other HTTP server, the SDK doesn't care what's underneath. It just needs something that can register a route and handle a request.

const adapter = new <YourAdapter>(yourServer); 
const teamsApp = new TeamsApp({ httpServerAdapter: adapter });
teamsApp.on('message', async ({ send, activity }) => { });

If you're already running a bot somewhere, wiring it into Teams is a few lines of glue code. Full docs at Self-Managing Your Server.

联系我们 contact @ memedata.com