Kurzfassung
Ein Agent im Teams-Chat fügt sich in die Arbeitsweise ein, die Dein Team ohnehin hat. Jemand tippt, der Agent erledigt die Aufgabe in seiner eigenen isolierten MicroVM, und die Antwort steht im selben Thread. Dafür brauchst Du zwei Bausteine. Das MCP-Gateway von zwrm bringt einen eigenen Microsoft-Connector mit, der dem Agent Microsoft-365-Tools gibt, darunter das Tool, mit dem er antwortet. Und der MS-Teams-Trigger macht aus jeder eingehenden Chat-Nachricht einen Agent-Lauf. Setzt Du den Session Key Path des Triggers auf chat_id, teilt sich das ganze Gespräch einen Workspace. Die fünfte Nachricht baut dann auf der zweiten auf, ohne dass jemand etwas wiederholen muss. Zwei Dinge solltest Du vorher richtig machen. Vergib Chat.ReadWrite statt Chat.Read, denn wer einen Chat lesen darf, darf darin noch lange nicht schreiben. Und setz ein Budget, bevor Du anfängst, weil ein Agent am Chat-Trigger dann läuft, wenn andere Leute tippen.
Claude Cowork hat vielen Leuten eine Arbeitsweise begreiflich gemacht. Du redest in einem Chat mit einem Agent, er erledigt echte Arbeit, und die Session weiß noch, was vorher war. Wir liefern kein Cowork, wir sind nicht dazu kompatibel, und mit Anthropics Produkt haben wir nichts zu tun. Wir bauen dasselbe Prinzip auf unserer eigenen Runtime nach, weil das Prinzip gut ist und bei den meisten Teams das Chat-Fenster ohnehin offen steht.
Das Interessante an einem Agent im Chat ist nicht der Chat. Es ist, dass ein Gesprächsverlauf eine natürliche Klammer um ein Stück Arbeit legt. Ein Thread, eine Aufgabe, ein Workspace, der sich erinnert. Wie Du das in Teams hinbekommst, und zwar an einem Nachmittag, steht hier drunter.
Was Du bekommst
Einen Agent, der Nachrichten in einem Teams-Chat liest, die Arbeit in einer isolierten MicroVM erledigt und im selben Chat antwortet. Dafür zwei Bausteine:
- Der Microsoft-365-Connector gibt dem Agent Microsoft-365-Tools, darunter das Tool, mit dem er die Antwort verschickt.
- Der MS-Teams-Trigger beobachtet die Chat-Nachrichten eines verbundenen Accounts und startet für jede einen Lauf.
In der Praxis sieht das so aus: Jemand wirft “zieh mir die fehlgeschlagenen Zahlungen der letzten Woche und pack sie in eine Tabelle” in den Chat, und ein paar Minuten später liegt die Datei im selben Thread. Ein Trigger ohne Connector ergibt einen Agent, der alles mithört und nichts sagen kann.
Microsoft 365 verbinden
Du bringst Deine eigene Entra-ID-App mit. Der Zugriff des Agents wird also gegen Deinen Tenant ausgestellt und bleibt auf die Berechtigungen begrenzt, die Du vergeben hast. Registrier eine Single-Tenant-App mit der Redirect-URI https://mcp.zwrm.io/oauth/callback und trag die Delegated Permissions ein, die Du brauchst.
Schreib Dir das Ablaufdatum des Client Secrets an eine Stelle, an der Du es wiedersiehst. Es erinnert Dich niemand daran, und der Fehlerfall ist ein Agent, der ohne viel Aufhebens aufhört zu antworten. Rotierst Du das Secret später, trägst Du Client ID, Scope und Authorization Server zusammen mit dem neuen Secret erneut ein. Die Nutzer müssen nicht noch einmal zustimmen.
Danach fügst Du den Connector hinzu, über Dashboard, MCP, Add integration, Microsoft 365, oder über die CLI:
zwrm mcp upstream add ms365 https://mcp-ms365.zwrm.io/mcp \
--oauth \
--oauth-client-id <client-id> \
--oauth-client-secret <client-secret> \
--oauth-scope "User.Read Chat.ReadWrite offline_access" \
--oauth-issuer "https://login.microsoftonline.com/<tenant-id>/v2.0"
Beim Authorization Server gehört Deine Tenant-ID fest hinein. common und organizations scheitern an der Issuer-Prüfung. Nur die Variante mit fester Tenant-ID ist einen Versuch wert.
Jetzt der Scope, an dem Leute einen halben Tag verlieren. Vergib Chat.ReadWrite, nicht Chat.Read. Lesen schließt Senden nicht ein, und ein Agent mit Chat.Read bekommt jede Nachricht mit, hat aber kein einziges Tool, um darauf zu antworten. Das sieht dann nach einem kaputten Agent aus und nicht nach einer fehlenden Berechtigung. Für Arbeit in Teams willst Du außerdem ChannelMessage.Read.All, ChannelMessage.Send und Team.ReadBasic.All, dazu offline_access. Presence.ReadWrite nimmst Du nur dann dazu, wenn Du später die Presence-Option nutzen willst.
Gib dem Agent seinen eigenen Account
Die Zustimmung wird pro Nutzer oder pro Agent erteilt. Führ zwrm mcp connect ms365 aus oder klick auf Connect in der Zeile der Verbindung. Agents autorisieren sich über ihre eigene Agent-Seite. Ein Agent ohne eigene Freigabe wird abgewiesen, statt still auf den Account eines Menschen gemappt zu werden. Das Token einer Kollegin mitzubenutzen ist die Sorte Bequemlichkeit, die sich in einer Demo gut anfühlt und im Audit-Log übel aussieht.
Leg dem Agent also einen eigenen Microsoft-Account an. Was dieser Account in Teams sieht, ist exakt die Reichweite des Agents. Keine noch so vorsichtige Formulierung im Prompt macht sie kleiner.
Der Connector deckt Microsoft 365 breit ab: Teams, Mail, Kalender und Dateien. Die Obergrenze setzt Deine Entra-App, denn eine Anbindung mit “allen Tools” zeigt immer nur die Tools innerhalb der Scopes, die Du konfiguriert hast. Was der Agent davon anfassen darf, entscheidest Du. Willst Du statt einer breiten Liste eine enge, bau Dir einen virtuellen MCP-Server mit genau den paar Tools, die die Aufgabe wirklich braucht.
Den Agent anlegen
zwrm agent create teams-assistant --runtime zwrm
Als Runtime kannst Du zwrm, claude oder codex wählen. Ein Agent behält seine Identität über alle Läufe hinweg: Instructions, Memory, Skills, Secrets, Connectors. Die VM, in der ein Lauf ausgeführt wird, wird danach zerstört. Die Identität nicht.
Was bei jedem Lauf gelten soll, unabhängig davon, was jemand getippt hat, gehört in die Instructions:
zwrm agent update teams-assistant --instructions "..."
Teams-Nachrichten starten den Agent
Wähl im Trigger-Editor des Dashboards den Provider “MS Teams — chat messages from a connected Microsoft account” und den MS365-Connector, auf dem er aufsetzt. Gib dem Trigger einen Namen, schreib die Instruction, die für jede angenommene Zustellung gilt, und wähl den Agent aus.
Ab da hält sich die Verbindung selbst am Leben. zwrm hält die Teams-Subscription aktiv, solange der Trigger läuft, setzt sie aus, wenn Du den Trigger pausierst, und stellt sie wieder her, falls Microsoft sie fallen lässt. Auf Deiner Seite gibt es nichts zu erneuern.
Zwei Optionen solltest Du kennen:
- Include my own messages löst auch dann aus, wenn der verbundene Account selbst sendet, nicht nur wenn er empfängt.
- Show as available in Teams zeigt den Account als Available, solange der Trigger aktiv ist, und lässt ihn auf offline zurückfallen, sobald Du pausierst. Braucht
Presence.ReadWrite.
Jede eingehende Nachricht kommt als kleines JSON-Event an, mit Chat, Absender, Text und Zeitstempel. Match Conditions entscheiden, welche Nachrichten durchkommen, Routes schicken unterschiedliche Nachrichten an unterschiedliche Agents, und Message Templates formatieren das Event unter Deiner Instruction mit {dot.path}-Tokens wie {body.content} und {from.display_name}.
Der Teil, der sich wie ein Gespräch anfühlt
Setz den Session Key Path des Triggers auf chat_id.
Das ist ein Feld in einem Formular, und es verändert, was das Ganze ist. Gleicher Chat, gleicher Workspace. Der Agent behält seine Dateien und seinen Kontext über jede Nachricht in diesem Gespräch hinweg. Die fünfte Nachricht baut auf dem auf, was bei der zweiten passiert ist, ohne dass es jemand noch einmal erzählt. Lass ihn etwas prüfen und sag danach “und jetzt dasselbe für das letzte Quartal”, dann weiß er, worauf sich “dasselbe” bezieht.
Lässt Du den Session Key leer, bekommt jede Nachricht einen frischen Einweg-Workspace, der sich an nichts erinnert. Für einen Trigger, der wie eine Benachrichtigung funktioniert, ist das genau richtig. Für ein Gespräch ist es falsch. An diesem einen Feld hängt der Unterschied zwischen einem Bot, der Fragen beantwortet, und etwas, das einen Nachmittag lang mit Dir arbeitet.
Die Antwort landet im richtigen Chat
zwrm selbst kann keine Nachrichten verschicken. Der Agent antwortet, indem er ein Microsoft-365-Tool aufruft, das in den Chat schreibt, aus dem die Nachricht kam. Deshalb ist der Connector tragend und nicht bloß Zubehör.
Daraus ergeben sich zwei Zusagen. Der Agent weiß immer, in welchem Chat er antworten muss, weil die Plattform ihm den Chat als Teil des Laufs mitgibt. Und wenn er keinen Weg zum Senden hat, schreibt er das offen in sein Ergebnis, statt eine Antwort zu melden, die nie jemand bekommen hat. Ein sichtbarer Fehlschlag ist mehr wert als ein Erfolg, den Du nicht nachprüfen kannst.
Chat.ReadWrite sorgt dafür, dass der zweite Fall gar nicht erst entsteht.
Die Kontrolle behalten
Ein Agent, der an einem Chat hängt, läuft dann, wenn andere Leute tippen, und nicht dann, wenn Du es entscheidest. Setz die Grenzen, bevor Du ihn anschließt:
zwrm agent budget teams-assistant --daily-usd 20 --max-runs 50
Zusätzlich zum Agent-Budget hat jeder Trigger ein eigenes Tageslimit. Jede Zustellung wird protokolliert. Wenn Dich ein Lauf überrascht, lies nach, was tatsächlich angekommen ist, statt zu raten:
zwrm triggers deliveries <trigger-id>
Meistens war es eine Match Condition, die weiter gefasst war als gedacht.
Bevor Du loslegst
Vier Dinge, die Du vorher wissen solltest.
- Nur Geschäfts- und Schulkonten. Persönliche Microsoft-Accounts funktionieren hier nicht, und das ist eine harte Grenze und kein offener Backlog-Punkt.
- Provider und Connector stehen fest, sobald der Trigger existiert. Hast Du den falschen Connector erwischt, leg einen neuen Trigger an. Alles andere am Trigger bleibt änderbar.
- Jeder Agent braucht seinen eigenen verbundenen Account. Er kann sich keinen von einer Kollegin leihen und wird abgewiesen, statt still auf einen fremden auszuweichen.
- Die Reichweite des Agents ist das, was dieser Account in Teams sieht. Wähl den Account entsprechend, denn hier verläuft die eigentliche Berechtigungsgrenze. Alles Weitere ist Geschmackssache.
Das Setup mit Vonk planen
Vonk, der Assistent auf dieser Seite, geht das Setup mit Dir durch. Welche Scopes Dein Fall wirklich braucht, was in den Instructions des Agents stehen sollte, wie der Trigger aussehen muss, damit er zu der Art passt, wie Dein Team Teams benutzt. Durch das Entra-Portal klickt er nicht für Dich. Und ganz nebenbei ist er genau das, was Du gerade bauen willst. Ein Agent in einer VM, der in einem Chat antwortet.
Loslegen
Fang schmal an. Ein Chat, ein eigener Account, eine Scope-Liste ohne alles Spekulative darin, und ein Budget, das Du auch zweimal ausgeben würdest, ohne zu zucken. Setz den Session Key am ersten Tag auf chat_id, weil sich der Unterschied schon in den ersten zehn Minuten zeigt. Lies nach einem Tag echter Gespräche das Delivery-Log, und erweitere dann anhand dessen, was da wirklich steht.
Willst Du Agents auf Infrastruktur betreiben, die Du kontrollierst, in Europa? Starte einen kostenlosen 14-tägigen Test auf zwrm.eu.