Skip to main content
hermes-msteams-bridge (plugin name teams_call) is the Python half of the Teams CVI for Hermes Agent. It hosts the HMAC WebSocket that StandIn connects to, runs the dialogue + perception, and emits avatar driver cues. Chat-plane features live in the existing Teams adapter.
This adds voice/video (CVI) on top of Hermes Agent’s existing Teams messaging - run both for chat + calls.

Requirements

  • A working Hermes Agent install (the host).
  • A StandIn subscription (standin.komaa.com, free package) - the hosted media bridge connecting to this plugin. See Architecture.
  • Realtime mode: an OpenAI / Azure OpenAI realtime key. Streaming mode: needs ffmpeg on PATH.

Install

Install into the same Python environment as Hermes so the hermes_agent.plugins entry-point is discovered:
The documents extra enables show_file document rendering (pypdfium2 + Pillow). Optional system tools: LibreOffice (soffice on PATH) for showing Office documents, ffmpeg for streaming-mode TTS decode. The show_web_page tool uses the host Hermes browser tools - no extra install. Or with pip:
Then enable the plugin by adding it to plugins.enabled in ~/.hermes/config.yaml (the configuration page shows the full block):
hermes plugins enable teams_call works too when run interactively, but it may prompt for tool-override consent, so scripts and installers should write the YAML instead.

Verify

Show config + readiness - the status probe checks every Hermes surface the plugin depends on (vision, STT, TTS, chat delivery, browser, document rendering) and reports two levels: boundaries_ok (the contract surface exists) and operational_ok (providers/credentials/dependencies actually work). Anything missing is named explicitly, so a broken Hermes integration surfaces here and at serve startup rather than mid-call. External paths (Teams reachability, the StandIn endpoint, provider round trips) are not probed - those surface on the first real call. Tested against Hermes Agent 0.19.0 (PyPI floor) and the current 0.20.x line:
Run the bridge server (foreground):
The bridge binds its media WebSocket (default ws://<host>:8443/voice/msteams/stream).

Expose the WebSocket (Agent voice URL)

StandIn connects to your plugin from the internet, so port 8443 must be reachable - a public host, or a tunnel. With Tailscale Funnel, put voice on the 8443 slot (keeps the 443 slot free for a Teams chat webhook):
Your Agent voice URL is then:
Register that URL (with a matching shared secret) in the StandIn sandbox or on your identity in the dashboard, then place a Teams call to your bot. Include the port - a bare host means the 443 slot, which is normally a chat webhook, not voice.
Funnel serves up to 3 external ports (443, 8443, 10000). Also running the OpenClaw plugin on the same machine? Use one canonical layout so the two runtimes never fight over a slot: keep OpenClaw voice on 8443 -> 9442 and move Hermes voice to the 10000 slot: tailscale funnel --bg --https=10000 8443wss://<machine>.<tailnet>.ts.net:10000/voice/msteams/stream. Register each runtime as its own agent, each with its own secret. This matches the OpenClaw page, so following both guides sets the same funnel map instead of overwriting the already-registered 8443 slot.
Next: the full configuration reference - config.yaml + .env, realtime backend, and Graph permissions.

Source

PyPI

Configuration

Gateway-managed mode (one process)

With the teams_call platform enabled in the gateway config, hermes gateway run hosts the voice bridge itself - no separate serve process. Two layers must both be on:
  1. the plugin: teams_call under plugins.enabled in config.yaml;
  2. the platform: a teams_call platform entry enabled in the gateway configuration (same place as your Teams chat platform).
The complete config.yaml shape:
In this mode, streaming-mode utterances run as real gateway agent turns (sessions, authorization, approvals), and scheduled jobs can deliver by voice (deliver=teams_call - the bot calls you and speaks the result). If a standalone hermes teams-call serve is already bound to the port, the adapter fails loudly instead of splitting traffic - run one or the other. Using the StandIn one-line installer? Set STANDIN_GATEWAY_MODE=1 in the environment before running it: the installer then writes both layers above, stops any standalone bridge, and restarts the gateway instead of launching serve. hermes teams-call smoke is an offline install check: it proves config resolution, the HMAC handshake, wire decode, and session lifecycle against an ephemeral local listener. It does not test the configured listener, gateway platform activation, Teams connectivity, provider credentials, or the public tunnel - the first real call is the test for those.