Skip to main content

Microsoft Teams Bridge for Hermes Agent

CI PyPI version downloads Python docs License: MIT PRs Welcome

Microsoft Teams voice/video (Conversational Video Interface) for Hermes Agent, packaged as a standalone, pip-installable plugin: install it on top of a normal Hermes install, no fork required.

The plugin (name msteams_call) hosts the HMAC-authenticated WebSocket bridge that the hosted StandIn media bridge dials into, and drives the call: realtime (OpenAI/Azure speech-to-speech) or streaming (STT→agent→TTS), camera/screen vision, the avatar driver cues (expression / visemes / show-to-caller), group-call etiquette, DTMF, bilingual EN/AR, and meeting recap/minutes (posted to the chat, with a local .docx artifact).

Two ways to connect to Teams

StandIn Managed Bot (recommended). StandIn provides the Teams bot: install StandIn from the Teams Store, connect this agent in the StandIn portal, and paste one secret. No Azure bot registration, no App ID or client secret, no separate chat plane to run. Voice and chat are two lanes of the SAME StandIn connection - a WebSocket on the calling port and HTTP on the messages port - hosted by one process, whether that is msteams-call serve or the gateway-resident platform.

plugins:
  entries:
    msteams_call:
      config:
        # ONE connection secret from the StandIn portal - covers calls AND chat.
        secret: ${MSTEAMS_CALL_SECRET}

The chat listener defaults to 0.0.0.0:8444 because the StandIn gateway must reach it. If you reach your agent over a private network (Tailscale, VPN, a reverse proxy), set host to that interface, or firewall the port - the HMAC keeps unauthenticated callers out, but an open port is still an open port.

One agent instance serves one StandIn connection: the secret is a single value scoped to one tenant binding. Serving several tenants means several instances, each with its own secret. Never share one secret across tenants.

Bring your own Azure bot (advanced). You own the Entra app, client secret and Azure Bot resource, and the Teams chat plane is handled by Hermes's own platforms/teams adapter rather than here. Choose this when the bot must live entirely inside your tenant.

Getting started

This plugin adds voice and video (CVI) on top of Hermes Agent's Microsoft Teams messaging. Set those up first:

  1. Install Hermes Agent using the official docs at hermes-agent.nousresearch.com.

  2. Set up Microsoft Teams messaging in Hermes (bot app + credentials): Teams messaging docs.

  3. Subscribe to StandIn (standin.komaa.com, free tier), the hosted media bridge that joins the Teams call and connects to this plugin.

  4. Add this plugin. The one-line installer detects your Hermes venv and walks you through the config (mode, shared secret, provider key):

    curl -fsSL https://standin.komaa.com/install.sh | bash
    

    Prefer to do it by hand? See Install on Hermes and Configure.

Install on Hermes

Install into the same Python environment as Hermes: it discovers the plugin via the hermes_agent.plugins entry-point and imports it in-process.

First locate the Hermes venv (the installer puts it under ~/.hermes/.../venv):

find ~ -path "*/.hermes/*/venv" -type d 2>/dev/null

Then install into that venv, targeting its interpreter (Linux/macOS <venv>/bin/python, Windows <venv>\Scripts\python.exe), or activate the venv first and drop --python.

A. from PyPI (recommended):

uv pip install --python /path/to/hermes/venv/bin/python hermes-msteams-bridge

Or, with the Hermes venv activated:

pip install hermes-msteams-bridge

B. from GitHub (latest / pre-release):

uv pip install --python /path/to/hermes/venv/bin/python \
  "git+https://github.com/komaa-com/hermes-msteams-bridge.git"

C. from a local checkout (development):

git clone https://github.com/komaa-com/hermes-msteams-bridge.git
uv pip install --python /path/to/hermes/venv/bin/python -e ./hermes-msteams-bridge

Installing into the wrong environment means Hermes won't see the plugin. Faster audio (optional): add the numpy extra, e.g. hermes-msteams-bridge[numpy].

Enable + run

Entry-point plugins are opt-in: add msteams_call to plugins.enabled in ~/.hermes/config.yaml (see Configure below). hermes plugins enable does not work for pip-installed plugins (it only sees bundled/user-dir plugins), so enable it in config:

plugins:
  enabled:
    - msteams_call
  entries:
    msteams_call:
      config:
        secret: ${MSTEAMS_CALL_SECRET}   # the StandIn connection secret - covers calling AND messages
        host: 127.0.0.1                  # both lanes; the tunnel terminates TLS and proxies to loopback
        # WITHOUT a caller policy the bridge accepts NOTHING: the allowlist IS the policy and an empty
        # one denies every inbound call, so a setup that otherwise looks finished answers nothing.
        # Name trusted callers here, or set allow_all: true to take whatever StandIn routes to you.
        allow_all: true
platforms:
  msteams_call:
    enabled: true                        # the gateway hosts the bridge; without this nothing listens

Then run the bridge (handlers: realtime | streaming | echo | logging):

hermes msteams-call serve --handler realtime

And, separately, the Teams chat plane + cron:

hermes gateway run

Configure

Config lives in Hermes's own files (this package ships none). Non-secret settings go in config.yaml; secrets go in .env and are referenced with ${VAR}.

~/.hermes/config.yaml, under plugins.entries.msteams_call.config:

plugins:
  enabled:
    - msteams_call                          # entry-point plugins are opt-in
  entries:
    msteams_call:
      config:
        secret: ${MSTEAMS_CALL_SECRET}   # MUST match the secret StandIn gave you
        host: 127.0.0.1
        port: 8443                         # voice WS StandIn dials: ws://host:port/msteams/calling
        max_call_duration_s: 0             # hard wall-clock cap per call in seconds (0 = unlimited)
        meeting_recap: true                # optional: post minutes at call end
        # share_point_site_id: ${TEAMS_SHAREPOINT_SITE_ID}  # optional: future large-file path (file card itself needs only the bot creds)
        allowlist: []                      # caller AAD object ids (empty = deny all inbound callers)
        allow_all: false                   # explicit opt-in: accept any caller when the allowlist is empty
        allowlist_allow_names: false       # also match the allowlist against display names (weaker; default off)
        session_scope: per-call            # per-call | per-thread | per-aad
        wake_phrases: [assistant, hermes]  # group-call wake phrases (speak only when addressed)
        show_file_root: ""                 # show_file containment root (default <hermes home>/workspace/teams_call_show)
        # Outbound "call me back" (StandIn places the return call over its loopback endpoint):
        worker_base_url: http://127.0.0.1:9440   # loopback endpoint StandIn exposes for place-call
        allow_remote_worker: false         # refuse a non-loopback place-call target unless set
        # Realtime (speech-to-speech) brain - Azure OpenAI Realtime:
        realtime:
          backend: azure                   # azure | openai
          azure_endpoint: https://<your-azure-resource>.cognitiveservices.azure.com
          azure_deployment: gpt-realtime
          azure_api_version: 2025-04-01-preview
          voice: cedar
          api_key: ${AZURE_FOUNDRY_API_KEY}
          vad_threshold: 0.5
          prefix_padding_ms: 300
          silence_duration_ms: 500
          languages: []                  # e.g. [en, fr, de, ar]; empty = auto-detect and mirror

Public OpenAI instead of Azure: set backend: openai, model: gpt-realtime, api_key: ${OPENAI_API_KEY}, and drop the azure_* keys. Streaming (STT→agent→TTS) instead of realtime: omit the realtime: block and run hermes msteams-call serve --handler streaming (needs ffmpeg on PATH).

~/.hermes/.env, the secrets referenced above (plus Teams chat-plane creds if you also run hermes gateway run):

# Voice bridge
TEAMS_CALL_SHARED_SECRET=<same value you set in StandIn>
AZURE_FOUNDRY_API_KEY=<azure-openai-key>                 # or OPENAI_API_KEY for public OpenAI

# Teams chat plane (platforms/teams) - only if you run the gateway:
TEAMS_CLIENT_ID=<bot-app-id>
TEAMS_CLIENT_SECRET=<bot-app-secret>
TEAMS_TENANT_ID=<azure-ad-tenant-id>

secret must match the secret StandIn gave you or the HMAC handshake fails. Full key reference (every option, defaults, env vars, streaming mode, the wire protocol): the Configuration Reference and Wire Protocol docs pages. Contributor architecture notes live in DESIGN.md; the module-level guide is in src/hermes_msteams_bridge/README.md.

Upgrade / uninstall

Upgrade:

uv pip install --upgrade hermes-msteams-bridge

Uninstall (it then disappears from hermes plugins list):

uv pip uninstall hermes-msteams-bridge

How it loads

Hermes discovers pip plugins via the hermes_agent.plugins entry-point group. This package exposes:

[project.entry-points."hermes_agent.plugins"]
msteams_call = "hermes_msteams_bridge"

Hermes imports hermes_msteams_bridge and calls its register(ctx), registering the teams-call CLI, the status tool, and the session hook. Entry-point plugins are opt-in, so msteams_call must be in plugins.enabled (add it in config.yaml; hermes plugins enable does not see pip-installed plugins).

Requirements

  • A working Hermes Agent install (the host; not a PyPI package).
  • Python ≥ 3.10 and aiohttp; ffmpeg on PATH for streaming-mode TTS decode.
  • StandIn (standin.komaa.com), the hosted media bridge that joins the Teams call and connects to this plugin over the HMAC WebSocket.

Relationship to the bundled plugin

This is the same code as the original in-tree plugin, repackaged for pip distribution so you don't have to fork Hermes. Install it on vanilla Hermes; don't also keep a bundled msteams_call (same name → the entry-point would shadow it).

  • Voice/CVI works fully on vanilla Hermes.
  • Meeting minutes post to the chat with the Word .docx attached as a native file card (the same Bot Framework attachment contract the Hermes Teams adapter uses; needs the chat plane's TEAMS_CLIENT_ID/SECRET/ TENANT_ID), degrading to text when creds are absent; a Word-openable copy is always kept under the Hermes workspace.

License

MIT - see LICENSE. Copyright (c) 2026 Komaa DigiTech. This is an independent plugin; it is not affiliated with or endorsed by Nous Research. "Hermes" is a project of Nous Research. Docs at https://docs.komaa.com/

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hermes_msteams_bridge-0.5.0.tar.gz (172.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hermes_msteams_bridge-0.5.0-py3-none-any.whl (141.9 kB view details)

Uploaded Python 3

File details

Details for the file hermes_msteams_bridge-0.5.0.tar.gz.

File metadata

  • Download URL: hermes_msteams_bridge-0.5.0.tar.gz
  • Upload date:
  • Size: 172.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for hermes_msteams_bridge-0.5.0.tar.gz
Algorithm Hash digest
SHA256 de50252458f5d3953d07ec1fd004d9d4dcf23ceed3f8095ee3b123dce01a0003
MD5 b8cd63ad149f7265ef5ba9c79a91b7d5
BLAKE2b-256 c3f86db143d56b28da5c701390c51d9e4744982332761b570e4a0ba5b54835e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_msteams_bridge-0.5.0.tar.gz:

Publisher: pypi-publish.yml on komaa-com/hermes-msteams-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hermes_msteams_bridge-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for hermes_msteams_bridge-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35c2c98753e115cb75a42bf277ccc380e412868dad66c1c447ca3775f5217ffe
MD5 b443c895a5f6da6c492ed1ad3d32375d
BLAKE2b-256 4cc1bca07f4fea9bcb730a814881dffcfeef9a45b31c1952b45a86a7102aa37d

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_msteams_bridge-0.5.0-py3-none-any.whl:

Publisher: pypi-publish.yml on komaa-com/hermes-msteams-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.3

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

This release

0.5.0 This release

2 files

0.4.0

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.7

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page