How to Connect Hermes to Microsoft Teams?
A recipe-style guide to wiring NousResearch’s Hermes agent into Microsoft Teams — mentions, file cards, inline approvals, and meeting summaries, all from your own self-hosted gateway.
Ingredients
- A running Hermes gateway — see How to Claw? or How to Run Hermes in a macOS Sandbox? if you need one
- A Microsoft 365 tenant where you can upload a custom Teams app (or an admin who can)
- An Azure account — the Azure Bot resource sits on the free tier
- Node.js 18+ for the Microsoft Teams CLI
- A public HTTPS URL reaching your gateway on port
3978— a real domain in production, a tunnel for testing - The AAD (Entra) object IDs of the people allowed to use the bot
Why Teams is different from Slack or Telegram
On Slack, Hermes uses socket mode and holds a connection open. Teams does
not work that way. Microsoft’s Bot Framework
calls you: it makes an HTTPS POST to a public webhook
whenever someone messages the bot, and Hermes answers back through the Bot
Framework API. So the one hard requirement is a
publicly reachable HTTPS endpoint — a dev tunnel for
local work, a real domain with a valid certificate in production.
Hermes’s Teams adapter is not a thin bridge. It speaks the full Teams
vocabulary: @-mentions, Adaptive Cards, FileConsent cards and
file.download.info handling, polls, direct sends, DM file
uploads, SharePoint channel uploads, inbound images and documents (PDF,
DOCX, XLSX, PPTX, ZIP, TXT, MD), and message edits for streaming-style
replies. When the agent wants to run something risky, it posts an
Adaptive Card with approve/deny buttons instead of making
you type /approve — one click resolves it inline.
1 Install the Teams extra and CLI
Teams support is an optional extra on Hermes. On a source install, sync it in; then install Microsoft’s Teams CLI, which you will use to create the bot and the app package:
# Hermes Teams extra (source installs)
uv sync --extra teams
# Microsoft Teams CLI
npm install -g @microsoft/teams.cli@preview
teams login
2 Expose a public HTTPS webhook
The gateway listens on port 3978 by default (override with
TEAMS_PORT). Teams needs to reach it over HTTPS. For local
development, open a tunnel to that port — pick whichever tool you
have:
# devtunnel
devtunnel create hermes-bot --allow-anonymous
devtunnel port create hermes-bot -p 3978 --protocol https
# ── or ngrok ──
ngrok http 3978
# ── or cloudflared ──
cloudflared tunnel --url http://localhost:3978
Note the public URL it gives you. The endpoint Microsoft needs is that URL
with /api/messages on the end.
3 Create the bot application
One command registers the Azure Bot, the app, and a secret, pointing it at your webhook:
teams app create --name "Hermes" \
--endpoint "https://<your-tunnel-url>/api/messages"
It outputs CLIENT_ID, CLIENT_SECRET, and
TENANT_ID. Save the secret immediately —
it is shown only once.
4 Configure the environment
Add the credentials — and, critically, the allowlist — to
~/.hermes/.env:
TEAMS_CLIENT_ID=<your-client-id>
TEAMS_CLIENT_SECRET=<your-client-secret>
TEAMS_TENANT_ID=<your-tenant-id>
TEAMS_ALLOWED_USERS=<aad-object-id-1>,<aad-object-id-2>
# optional: change the listen port (default 3978)
# TEAMS_PORT=3978
TEAMS_ALLOWED_USERS. Without it,
anyone who can find or install your bot can drive your agent — an
agent that runs shell commands on your server. Fill it with the AAD object
IDs of authorized users. You can read your own with
teams status --verbose.
5 Start the gateway
Bring up the gateway service (the Hermes compose stack passes your UID/GID through so files it writes are owned by you):
HERMES_UID=$(id -u) HERMES_GID=$(id -g) docker compose up -d gateway
Confirm it is healthy:
curl http://localhost:3978/health
# → ok
6 Install the bot into Teams
The Teams CLI builds the app package for you; get an install link and open it in the Teams client:
teams app get <teamsAppId> --install-link
Open the link, add Hermes to a team or start a DM with it, then send a test
message. With TEAMS_ALLOWED_USERS set, only listed users get a
reply — everyone else is ignored.
7 Optional: meeting summaries
If you enable the Teams meeting pipeline plugin, the same adapter handles outbound delivery of meeting summaries. After a meeting’s transcript is summarized, Hermes posts the summary into a Teams target you choose — a channel or a chat. It is a separate plugin from the chat channel, but it rides on the connection you just set up, so there is nothing new to authenticate.
Moving from tunnel to production
When you swap the throwaway tunnel for a real domain, repoint the bot’s endpoint so Azure delivers there:
teams app update --id <teamsAppId> \
--endpoint "https://your-domain.com/api/messages"
Make sure the port behind your reverse proxy is internet-accessible and
served with a valid TLS certificate — the Bot Framework refuses
self-signed certs. Keep TEAMS_ALLOWED_USERS current as the
team changes.
What you end up with
Hermes living inside Microsoft Teams: it answers DMs and
@-mentions, accepts and returns files, streams replies by
editing messages, and asks for dangerous-command approval with a card
you tap instead of a slash command — all restricted to the AAD
users you allowlisted. Turn on the meeting pipeline and it will drop
meeting summaries into the channel of your choice as a bonus.
If messages do not arrive
- Endpoint mismatch — the URL registered with
teams app create/updatemust exactly match your live/api/messagesURL. - TLS — valid certificate required; self-signed will be rejected.
- Allowlist — if you are silently ignored, your AAD object ID is probably missing from
TEAMS_ALLOWED_USERS. - Health —
curl http://localhost:3978/healthshould returnokbefore you blame Teams.
Further reading
- Hermes — Microsoft Teams setup docs
- Hermes — Teams meetings pipeline
- Hermes on GitHub
- Microsoft Teams developer platform
- How to Run Hermes in a macOS Sandbox? — get Hermes running first
- How to Connect OpenClaw to Microsoft Teams? — the same job with the other framework