> ## Documentation Index
> Fetch the complete documentation index at: https://docs.komaa.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Installation

> Install hermes-msteams-bridge into a Hermes Agent environment.

[`hermes-msteams-bridge`](https://pypi.org/project/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.

<Note>
  This adds **voice/video (CVI)** on top of Hermes Agent's existing
  [Teams messaging](https://hermes-agent.nousresearch.com/docs/user-guide/messaging/teams) - run both
  for chat + calls.
</Note>

## Requirements

* A working **Hermes Agent** install (the host).
* A **StandIn** subscription ([standin.komaa.com](https://standin.komaa.com), free package) - the
  hosted media bridge connecting to this plugin. See [Architecture](/concepts/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:

```bash theme={null}
uv pip install --python /path/to/hermes/venv/bin/python "hermes-msteams-bridge[documents]"
```

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`:

```bash theme={null}
pip install "hermes-msteams-bridge[documents]"
```

Then enable the plugin by adding it to `plugins.enabled` in
`~/.hermes/config.yaml` (the [configuration page](/hermes/configuration) shows
the full block):

```yaml theme={null}
plugins:
  enabled:
    - teams_call
```

`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:

```bash theme={null}
hermes teams-call status
```

Run the bridge server (foreground):

```bash theme={null}
hermes teams-call serve
```

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):

```bash theme={null}
tailscale funnel --bg --https=8443 8443
```

Your **Agent voice URL** is then:

```
wss://<machine>.<tailnet>.ts.net:8443/voice/msteams/stream
```

Register that URL (with a **matching shared secret**) in the
[StandIn sandbox](https://standin.komaa.com/sandbox) or on your identity in the
[dashboard](https://standin.komaa.com/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.

<Note>
  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 8443` → `wss://<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.
</Note>

<Note>
  Next: the full [configuration reference](/hermes/configuration) - config.yaml + `.env`, realtime
  backend, and Graph permissions.
</Note>

## Links

<CardGroup cols={3}>
  <Card title="Source" icon="github" href="https://github.com/komaa-com/hermes-msteams-bridge" />

  <Card title="PyPI" icon="python" href="https://pypi.org/project/hermes-msteams-bridge/" />

  <Card title="Configuration" icon="gear" href="/hermes/configuration" />
</CardGroup>

## 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:

```yaml theme={null}
plugins:
  enabled:
    - teams_call          # layer 1: the plugin
  entries:
    teams_call:
      config:
        shared_secret: ${TEAMS_CALL_SHARED_SECRET}
platforms:
  teams_call:
    enabled: true         # layer 2: the gateway platform
```

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.
