Microsoft Teams Bridge for LiveKit Agents
@komaa/livekit-msteams-bridge puts a LiveKit Agent on a real Microsoft Teams call - including avatar agents (bitHuman, Tavus, and friends) whose voice the caller hears in Teams.
The hosted StandIn media bridge (standin.komaa.com) joins the Teams call and dials into this bridge over an HMAC-authenticated WebSocket. Per call, the bridge creates one LiveKit room, dispatches your agent into it (explicit dispatch by agentName), joins as a participant, publishes the caller’s audio, and relays the agent’s audio back to Teams.
flowchart TD A[Microsoft Teams call] --> B[StandIn media bridge<br/>hosted, joins the call] B -- "HMAC WebSocket, PCM 16 kHz" --> C[this bridge<br/>you run it] C -- "WebRTC, one room per call" --> D[LiveKit room] D -- dispatch --> E[your LiveKit Agent<br/>STT + LLM + TTS + turn-taking] E --> D
Both sides speak 16 kHz mono PCM16: the wire protocol natively, the room via the SDK’s resampling AudioSource/AudioStream - the bridge itself never transcodes.
Why this exists
Section titled “Why this exists”Your LiveKit agent already handles WebRTC callers. This bridge lets the same agent, unchanged, answer a Microsoft Teams call: no Teams SDK, no Graph calls, no media stack. StandIn owns the Teams side; you own your agent; this small service is the seam between them.
What you get
Section titled “What you get”- Any LiveKit agent answers Teams calls - Python or Node, any STT/LLM/TTS/realtime plugin combo, dispatched by
agentNamewith per-call metadata (caller name, tenant, direction, AAD id when known). - Turn-taking stays inside your agent - VAD, interruption, and endpointing run in your LiveKit agent session exactly as they do for WebRTC users.
- One room per call - created at
session.start, agent dispatched via the join token, room deleted at teardown so the agent job ends immediately. - Two call governors - a StandIn-side cutoff and a bridge-side
MAX_CALL_MINUTEShard cap. - Hardened transport - replay-proof HMAC upgrade, caps before crypto, dead-peer detection, duplicate-call 409, graceful drain. See Architecture.
- Observability -
GET /healthzandGET /metrics(Prometheus text format).