@komaa/livekit-msteams-bridge
puts a LiveKit Agent - including
avatar agents (bitHuman,
Tavus, and friends) - onto a Microsoft Teams call. It is the LiveKit analogue of the
OpenClaw and Hermes plugins and the
ElevenLabs bridge: it hosts the HMAC WebSocket that StandIn connects to,
then bridges the call into a LiveKit room where your agent runs.
Like the ElevenLabs bridge, this is a standalone Node service, not a framework plugin. You do
not install it into an agent runtime - you run the bridge and point StandIn at it. Your LiveKit
agent runs as its own worker and is dispatched into a per-call room.
Prefer Python? The same bridge exists as a Python package:
livekit-msteams-bridge on PyPI
(pip install livekit-msteams-bridge), with its own
docs site and
repo. Same wire protocol, same
environment variables, same hardening - pick the runtime that fits your stack. This page follows
the Node package.How it works
Per call, the bridge creates one LiveKit room, dispatches your agent into it (explicit dispatch byagentName), joins as a participant, publishes the caller’s audio, and relays the
agent’s audio back to Teams. Both sides speak 16 kHz mono PCM16 - the wire protocol natively, the
room via the SDK’s resampling AudioSource/AudioStream - so the bridge itself never transcodes.
Turn-taking (VAD, interruption, endpointing) runs inside your LiveKit agent session, exactly
as it does for WebRTC callers. Your agent needs no Teams-specific code - it receives per-call
metadata (caller name, tenant, direction, AAD id when known) and can listen on two data topics.
Requirements
- Node.js
>= 20. - A LiveKit server - a LiveKit Cloud project or self-hosted - with an API key and secret.
- A LiveKit agent registered with an agent name for explicit dispatch.
- A StandIn subscription (standin.komaa.com, free package works) - the hosted media bridge that joins the Teams call and connects to this service. See Architecture.
- Your own Microsoft Teams bot connected to StandIn - the Teams setup walks through the Azure bot, the app package, and the upload. To try it without one, use the sandbox.
Run
Env-configured, no install step needed:npm i @komaa/livekit-msteams-bridge) and embed it - see the
library API for the programmatic
surface and how to inject a fake room in tests.
The bridge binds its media WebSocket (default ws://<host>:8080/voice/msteams/stream; StandIn
appends /{callId} per call).
Expose the WebSocket (Agent voice URL)
StandIn connects to the bridge from the internet, so the port must be reachable - a public host or a tunnel. With Tailscale Funnel:Connect it to StandIn
Register that URL (with a matching shared secret) on your identity in the dashboard, or use the sandbox to try it without your own Teams bot:- Set the identity’s Agent voice URL to where the bridge listens.
- Set
WORKER_SHARED_SECRETto the shared secret from pairing - both sides must match exactly, or the WebSocket handshake is rejected with401. - Place a Teams call (or join the sandbox meeting). StandIn joins, connects to the bridge, the bridge creates the room and dispatches your agent, and the agent answers.
Try the runnable examples
The repo ships two example projects so you can see a full working setup - an agent and the bridge - before wiring your own. A LiveKit call needs both: your agent runs as a worker, the bridge dispatches it into a per-call room.Run the examples, step by step
Run the example voice agent, start the bridge pointed at it, expose the port, connect StandIn,
and take a call - then swap in the bitHuman avatar agent. Docker and the agent integration
points included.
Next: the full configuration reference - every environment variable, the
call governor, and dispatch. Deep protocol, agent, and library docs live on the
project site.