A Matrix-Microsoft Teams puppeting bridge built on the
mautrix-go bridgev2 framework.
The bridge logs into teams.microsoft.com using the same web-client tokens
the Teams web app receives, so it works with a normal personal, work, or
school account and does not require an Azure app registration, tenant
admin consent, or a paid Microsoft Communication Services SDK.
Looking for a new maintainer. I don't plan to keep running this long term.
Ideal outcome is a transfer into the mautrix
org as mautrix/teams. Otherwise happy to hand the repo to any capable
maintainer willing to take it over fully. Open an issue if you're
interested.
Functional. Most day-to-day chat features round-trip in both directions; calls and a couple of niche Teams-only features are still stubs (see the matrix below).
| Feature | Matrix -> Teams | Teams -> Matrix |
|---|---|---|
| Plain text messages | yes | yes |
| Formatted messages (bold/italic/etc) | yes | yes |
| Mentions | yes | yes |
| Replies | yes | yes |
| Threads (channel) | yes | yes |
| Threads in DM/group (rendered as quoted reply) | yes | yes |
| Edits | yes | yes |
| Deletions (soft + hard, configurable) | yes | yes |
| Reactions (legacy + full unicode emoji) | yes | yes |
| Code blocks with language | yes | yes |
| Images | yes | yes |
| Stickers / Giphy | - | yes |
| GIFs (animated) | yes | yes |
| Videos (with transcoding) | yes | yes |
| Voice messages | yes | yes |
| AMS file attachments (chat-service hosted) | yes | yes |
| SharePoint/OneDrive file attachments | - | yes |
| Typing indicators | yes | yes |
| Read receipts | yes | - |
| Backfill (history on join) | - | yes |
| Backfill attachments + reactions + replies | - | yes |
| Presence | - | - |
| Send invites / kick / power level | - | - |
| User profile sync (name, avatar, contact) | - | yes |
| Directory metadata (job title, dept, phones) | - | yes |
| DM room topic populated from directory card | - | yes |
| Group chat name fallback from members | - | yes |
| Search and start-chat (people picker) | yes | - |
| Group creation | yes | - |
| Call notices (started / ended / recording) | - | yes |
| Call join link (click-through to Teams) | - | yes |
| Call media bridging | - | - |
| End-to-bridge encryption | yes | yes |
| End-to-end encryption (Teams side) | - | - |
| Teams concept | Matrix counterpart |
|---|---|
| Personal filtering space (root for the user) | Main space named after the tenant ("Acme Corp"); "Microsoft Teams" on consumer accounts |
| Team | Sub-space inside the main space |
| Team channel | Room nested inside the team sub-space |
| Channel thread | Matrix thread in the channel room |
1:1 DM (@unq.gbl.spaces) |
Matrix DM room |
Group chat (@thread.v2) |
Matrix group room |
Meeting chat (19:meeting_*@thread.v2) |
Room nested under a synthetic "Meetings" sub-space (configurable) |
./build.sh # local Go build, output: ./mautrix-teamsArch users can build the AUR-style PKGBUILD under packaging/arch/.
-
Copy
pkg/connector/example-config.yamltomautrix-teams.yamland adjust the homeserver / appservice / database sections. -
Generate the appservice registration:
./mautrix-teams -g
-
Add the registration file to your homeserver's
app_service_config_filesand restart the homeserver. -
Start the bridge:
./mautrix-teams
-
In Matrix, start a chat with the bridge bot (
@msteamsbot:<your-server>) and runlogin. Pick the device-code flow and visit the Microsoft login URL the bot prints. After consent, all your DMs, groups, channels and team spaces appear in your Matrix account.
Run login-matrix <access-token> in the management room to make the bridge
write your own messages as your real MXID instead of as a ghost. See the
mautrix double-puppet
docs for ways
to obtain the token.
Without it, an edit you make on the Teams side to a message you originally sent from Matrix won't sync back - Matrix won't let the bridge ghost edit an event your own account owns. Edits to other people's messages, and to messages you sent on Teams, are unaffected. Double-puppet backfill also leans on appservice timestamp massaging, so weigh that trade-off before turning it on.
On a fresh login the bridge creates a portal room per DM, group, and channel,
invites your MXID to each, and backfills history. With 30+ chats this hits
Synapse's rc_invites.per_user limit and the sync slows to a crawl while
bridgev2 retries with backoff. Setting rate_limited: false in the
registration file exempts the bridge bot but not the invite target (you).
Disable the per-user cap for your own MXID once, before first login:
curl -X POST -H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
https://<your-synapse>/_synapse/admin/v1/users/@you:example.org/override_ratelimit \
-d '{"messages_per_second": 0, "burst_count": 0}'A single admin-token can be minted with
register_new_matrix_user -a or pulled from an existing admin session.
See pkg/connector/example-config.yaml for the full set of options. Notable
knobs:
sync_channels,sync_meeting_chats,sync_system_threads- enable/disable specific Teams thread classes at startupmark_deleted_as_edit- keep redacted messages as(deleted)placeholders on Matrix instead of removing the eventpresence.send_matrix_typing/presence.send_matrix_read_receipts- control which Matrix EDUs propagate to Teams
- Call media: call events render as
m.noticebubbles with a join link that opens in the Teams client. Bridging the actual call media is out of scope (different SFU stacks, no public SDK). Ad-hoc DM/group call detection requires an additional TroutercallAgentregistration that's currently a probe. - Voice messages on Teams: Teams's web/desktop client doesn't have a voice-recording feature, so audio sent from Matrix renders as a downloadable attachment rather than an inline player on the Teams side.
- Custom emoji reactions: arbitrary unicode emoji round-trip via
<hex>_<name>keys, but Teams's renderer only draws bubble graphics for emoji that exist in its own catalog. Anything outside the catalog shows as the raw hex on the Teams side. - SharePoint uploads from Matrix: files sent from Teams land on Matrix as
native
m.file/m.imageattachments (bridge downloads via the SharePoint OAuth token). The reverse direction (uploading a Matrix file into the Teams chat's SharePoint folder) is not implemented; files sent from Matrix go through the AMS pipeline, which Teams renders as a plain attachment. - Presence and Teams-side read receipts: not bridged to Matrix. The bridge sends your Matrix read receipts and typing to Teams, but the reverse (Teams presence / read state) is not forwarded.
- Cross-tenant federation: starting a chat works only for users your Teams tenant can already address (own tenant + accepted federation partners). Teams's directory rejects unknown MRIs server-side.
Issues and PRs welcome on the GitHub repository.
- purple-teams by
Eion Robb - the reference for the undocumented
Teams web-client protocol: the auth flow, the chat-service URL layout, the
AMS upload pipeline, and the Trouter signalling.
pkg/msteamsis an independent Go reimplementation of that protocol - the endpoints, scopes and wire formats are Microsoft's, and purple-teams is what mapped them out. Without it this would have taken months to reverse-engineer. Massive thanks. - mautrix-slack by Tulir Asokan - the bridge structure (NetworkConnector layout, login flows, identifier mapping, double-puppet hooks, capabilities advertising) is modelled directly on it. A lot of code shape was lifted wholesale and adapted.
- mautrix-discord - reference for reaction sync, attachment handling, edit propagation, and group creation.
- mautrix-go / bridgev2 by Tulir Asokan - the framework that does all the Matrix-side heavy lifting: room creation, ghost intents, encryption, backfill, command processing, double puppeting.
- gekiclaws/matrix-teams - cross-checks for the consumer-tenant auth path and a few mention/edit edge cases.
AGPL-3.0-or-later. See LICENSE.