Skip to main content
Glama

office365-mcp

Ein mehrbenutzerfähiger Remote-MCP-Server für Microsoft 365 – Outlook-E-Mail, Teams und SharePoint/OneDrive über Microsoft Graph. Jeder Benutzer verbindet sich mit seinem eigenen Microsoft-Konto über eine normale Browser-Anmeldung; der Server speichert diese Autorisierung verschlüsselt und handelt als dieser Benutzer bei jedem späteren Tool-Aufruf, sodass eine einmal hergestellte Verbindung weiter funktioniert, ohne dass der Client jemals ein Microsoft-Token besitzt. Freigegebene und Dienstpostfächer (support@, billing@, info@) sind eine Identität erster Klasse und kein Parameter, der an ein paar Tools angehängt ist.

Eine TypeScript-Codebasis, drei Bereitstellungsziele:

Plattform

Einstiegspunkt

Build / Bereitstellung

AWS Lambda (Function URL)

src/entries/lambda.ts

npm run build:lambda && npm run deploy:lambda

Validieren Sie die Konfiguration zuerst, ohne AWS zu berühren:

DRY_RUN=1 npm run deploy:lambda

| Azure Functions (v4 Node) | src/entries/azure.ts | npm run build:azure && func azure functionapp publish … | | Plain Node (dev / self-host) | src/entries/node.ts | npm run dev |

Wie sich dieser Server von den anderen Microsoft 365 MCP-Servern unterscheidet

Das Open-Source-Umfeld ist groß, und mehrere Projekte sind gut. Dieser Server ist um ein anderes Bereitstellungs- und Identitätsmodell herum gebaut, nicht um eine größere Graph-Abdeckung.

  • Der Server vermittelt Refresh-Tokens; der Client bekommt nie ein Microsoft-Token zu sehen. Die meisten vorhandenen Server speichern ein Token für einen Benutzer auf einer Maschine – ~/.outlook-mcp-tokens.json, ~/.microsoft_mcp_token_cache.json, ~/.office-mcp-tokens.json – im Klartext. Der einzige ausgereifte Remote-Server (Softerias ms-365-mcp-server, im HTTP-Modus) erklärt ausdrücklich, dass die Token-Erneuerung Sache des Clients ist, sodass eine Sitzung endet, wenn das Graph-Zugriffstoken nach ungefähr einer Stunde abläuft. Hier wird das benutzerspezifische Entra-Refresh-Token mit AES-256-GCM versiegelt und serverseitig gespeichert, und der Server erneuert Zugriffstoken im Namen des Benutzers, ohne dass dieser etwas davon mitbekommt.

  • Er ist zustandslos und auf Serverless ausgelegt. Keine SSE-Sitzungsaffinität, kein langlebiger Prozess, der gesamte Sitzungs- und Anmeldedatenzustand liegt in DynamoDB. Die HTTP-fähigen Alternativen setzen einen dauerhaft laufenden Container voraus (Express, Azure Container Apps, ein App-Service-Backend hinter einem lokalen stdio-Shim).

  • Freigegebene Postfächer werden modelliert, einschließlich des App-only-Pfads. Wenn der Aufrufer Exchange-Rechte besitzt, verwendet der Server dessen eigenes delegiertes Token gegen /users/{mailbox}; wenn sich niemand in das Postfach anmeldet, kann er eingeschränkte Anwendungsanmeldedaten verwenden. Kein anderer Open-Source-Server bietet diesen zweiten, kontrollierten Pfad, und die Exchange-seitige Eingrenzung wird hier als Skript (deploy/entra/scope-app-only.ps1) mitgeliefert, statt sie als Übung offen zu lassen.

  • Der Benutzer wählt, welche Postfächer der Assistent verwenden darf. Entra kennt keine Postfach-bezogene Zustimmung für delegiertes Mail.*.Shared: Die Erteilung dieser Bereiche ergibt ein Token, das jedes Postfach öffnen kann, das Exchange dieser Person öffnen lässt, und Microsoft bietet keine Möglichkeit, das einzuschränken. Deshalb folgt auf die Anmeldung eine von diesem Server bereitgestellte Genehmigungsseite, und was der Benutzer dort ankreuzt, wird bei jeder Anfrage serverseitig durchgesetzt. Siehe Postfach-Genehmigung.

  • Es gibt eine Governance-Geschichte. Benutzerbezogene Tool-Zulassungslisten, die für neue Tools standardmäßig verweigern, benutzerbezogene Ratenlimits, eine Postfach-Obergrenze des Administrators zusätzlich zur eigenen Genehmigung des Benutzers, eine Sperre auf Bereitstellungsebene gegen irreversible Löschung und ein strukturiertes Prüfprotokoll für jeden Aufruf, das den Benutzer, das Tool, die Argumente und das tatsächlich berührte Postfach benennt.

  • Das Tool-Angebot ist bewusst klein. 27 aufgabenorientierte Tools, nicht 300 endpunktorientierte. Nicht die Breite ist der Wettbewerbspunkt: Softeria deckt Excel-Bereiche und OneNote-Seiten ab, und Microsofts eigene Work-IQ-Server bieten semantische Suche und Ablaufverfolgung auf Defender-Niveau. Was keiner von beiden bietet, ist ein Server, den Sie selbst hosten, in Ihrer eigenen Region, ohne eine Microsoft-365-Copilot-Lizenz.

Es gibt zwei First-Party-Optionen, die man kennen sollte. Microsofts MCP Server for Enterprise ist kostenlos, aber schreibgeschützt und auf Entra-Verzeichnisdaten beschränkt – eine Ergänzung, kein Konkurrent. Agent 365 / Work IQ deckt zwar E-Mail, Kalender, Teams und SharePoint ab, ist aber nur als Preview und nur von Microsoft gehostet verfügbar und erfordert eine Microsoft-365-Copilot-Lizenz.

Schnellstart

1. Entra-Anwendung registrieren. Dieser Schritt geht am häufigsten schief. Folgen Sie deploy/entra/SETUP.md – registrieren Sie die Plattform insbesondere als Web, nicht als SPA (eine SPA-Weiterleitungs-URI begrenzt Refresh-Tokens stillschweigend auf 24 Stunden, und diese Ablaufzeit wird von jedem daraus abgeleiteten Token geerbt, was das Einmal-Verbinden-Prinzip zerstören würde).

2. Die beiden Schlüssel erzeugen. Es sind verschiedene Schlüssel mit unterschiedlichen Aufgaben, und keiner kann den anderen ersetzen.

npm install
npm run gen:oauth-key   # RS256 keypair — signs the tokens Claude presents to US
npm run gen:enc-key     # AES-256-GCM key — seals the tokens WE present to Microsoft

Sichern Sie die Ausgabe von gen:enc-key in einem Secrets-Manager, getrennt von der Bereitstellung. Wenn sie verloren geht, wird jede gespeicherte Verbindung unentschlüsselbar und alle Benutzer müssen sich sofort neu anmelden.

3. Bereitstellen.

npm run build:lambda
npm run deploy:lambda        # wraps `sam deploy` against deploy/aws/template.yaml

Der Stack gibt eine Ausgabe namens EntraRedirectUri aus. Registrieren Sie genau diese URI bei der App-Registrierung – das ist der eine Schritt, der nicht automatisiert werden kann.

4. Einen Client verbinden. Fügen Sie https://<your-deployment>/mcp als benutzerdefinierten Connector hinzu. Der Client entdeckt die OAuth-Endpunkte über /.well-known/oauth-protected-resource, registriert sich selbst und führt den Benutzer durch die Microsoft-Anmeldung. Auf Microsofts Zustimmungsbildschirm folgt die eigene Postfach-Genehmigungsseite dieses Servers, auf der der Benutzer ankreuzt, welche freigegebenen Postfächer der Assistent verwenden darf; der Client erhält sein Token erst danach. Rufen Sie dann o365_whoami auf – es zeigt den Verbindungsstatus, die gewährten Berechtigungen, die vom Benutzer genehmigten Postfächer und welche Tools der Aufrufer aktuell besitzt.

Für die lokale Entwicklung legen Sie die Variablen aus Konfiguration in einer .env-Datei ab – mindestens die Entra-Registrierung, die beiden Schlüssel und MCP_USERS_FILE + MCP_GRAPH_FILE + MCP_OAUTH_FILE für dateigestützte Speicher, genau das ermöglicht es npm run dev, den echten Browser-Anmeldevorgang und den Postfach-Genehmigungsablauf ohne AWS auszuführen. .env.example ist die kommentierte Version. Dann:

npm run dev                              # http://localhost:3000/mcp

Fügen Sie http://localhost:3000/oauth/callback als zweite Redirect-URI bei der App-Registrierung hinzu.

Architektur

  Claude / MCP client
        │  1. POST /mcp  (Bearer: our RS256 JWT)
        ▼
┌──────────────────────────────────────────────────────────┐
│  office365-mcp   (Lambda Function URL / Azure Fn / Node)  │
│                                                           │
│  Hono ── /mcp ── JSON-RPC 2.0 ── tool registry            │
│    │                                                       │
│    ├─ OAuth 2.1 authorization server (for the MCP client)  │
│    │    /.well-known/*  /oauth/register  /authorize        │
│    │    /callback  /consent  /token  /jwks.json            │
│    │                                                       │
│    └─ Graph token broker ── actor resolution ── client     │
└───────┬──────────────────────────┬────────────────────────┘
        │                          │
        │ 2. browser sign-in       │ 5. Bearer: Graph access token
        ▼                          ▼
  Microsoft Entra ID        Microsoft Graph
  login.microsoftonline     graph.microsoft.com/v1.0
        │
        │ 3. refresh token ──► AES-256-GCM ──► DynamoDB (MCP_GRAPH_TABLE)
        │                       (row-bound AAD)
        ▼
  4. the browser lands back here, on /oauth/consent — the user ticks
     which mailboxes the assistant may use, and only then is the
     authorization code handed to the MCP client

Transport. Streamable HTTP, zustandsloser Modus. JSON-RPC 2.0 an POST /mcp, eine Nachricht pro Anfrage – JSON-RPC-Batching wird mit -32600 abgelehnt, weil es in Revision 2025-06-18 aus MCP entfernt wurde und weil ein Array von Aufrufen als eine einzige Ratenlimit-Belastung zählen würde. Eine nicht authentifizierte Anfrage erhält eine 401 mit der RFC-9728-Herausforderung WWW-Authenticate: Bearer realm="mcp", resource_metadata=…, die einen Client dazu veranlasst, den Connector-OAuth-Flow zu starten. Das Dokument der geschützten Ressource wird sowohl unter /.well-known/oauth-protected-resource als auch unter /.well-known/oauth-protected-resource/mcp bereitgestellt und gibt resource als {origin}/mcp an – den Endpunkt, den der Benutzer tatsächlich eingegeben hat und mit dem ein Client vergleicht.

Das Identitätsmodell

Es gibt zwei getrennte OAuth-Beziehungen, und sie getrennt zu halten ist das gesamte Design:

  1. MCP-Client ↔ dieser Server. Wir sind der Autorisierungsserver. Der Client registriert sich dynamisch (RFC 7591), führt einen Authorization-Code- und PKCE-Flow gegen /oauth/authorize und /oauth/token aus und erhält ein von uns signiertes RS256-JWT. Entra unterstützt keine dynamische Client-Registrierung und bekommt diesen Austausch nie zu sehen.

  2. Dieser Server ↔ Entra. Wir sind ein vertraulicher Client mit einer einzigen statischen Web-Redirect-URI. Während der Anmeldung des Benutzers führen wir eine eigene, unabhängige PKCE-Kette gegenüber Entra durch, lösen den Code mit unserem Client-Secret oder -Zertifikat ein und erhalten ein id_token, ein Graph-Zugriffstoken und – weil wir offline_access anfordern – ein Refresh-Token.

Dieses Refresh-Token ist das Produkt. Es wird mit AES-256-GCM unter zusätzlichen authentifizierten Daten versiegelt, die an die Zeile gebunden sind ({tid}:{oid}:refresh), sodass ein Blob, der aus dem Datensatz eines Benutzers entnommen wurde, nicht in den eines anderen wiedereingespielt werden kann; geschrieben wird es in eine Tabelle, die ausschließlich für Anmeldedaten existiert. Jeder spätere Tool-Aufruf verläuft so: JWT → (tid, oid) → zwischengespeichertes Zugriffstoken oder eine Refresh-Einlösung → Graph. Der Schlüssel des Benutzers ist das unveränderliche Paar (tid, oid) aus dem id_token, niemals email, preferred_username oder upn, die alle veränderbar und administrativ steuerbar sind.

Zwischen beiden pausiert /oauth/callback. Sobald die Microsoft-Autorisierung gespeichert ist, übergibt es dem MCP-Client nicht seinen Autorisierungscode; es leitet den Browser mit einem Einmal-Ticket zu /oauth/consent weiter, und der Benutzer wählt, welche Postfächer dieser Assistent ansprechen darf. Erst wenn diese Seite abgeschickt wird, wird der Code erzeugt und der Client zurück zur Anwendung geleitet. Siehe Postfach-Genehmigung.

Die Tenant-Zulassungsliste (O365_ALLOWED_TENANTS) wird bei jeder Anfrage erneut geprüft, nicht nur bei der Anmeldung, sodass das Entfernen eines Tenants sofort und nicht erst bei der nächsten Anmeldung wirksam wird.

Delegiert vs. App-only

Jeder Graph-Aufruf ermittelt zuerst deterministisch aus der Konfiguration einen Akteur – ein Modell kann den Server nie dazu bringen, Rechte zu erweitern:

Akteur

Adresse

Wann

Sieht

delegated-self

/me

Kein mailbox-Argument, oder es ist die eigene Adresse des Aufrufers

Genau das, was der angemeldete Benutzer sieht

delegated-shared

/users/{upn}

Ein anderes Postfach, das der Benutzer beim Verbinden genehmigt hat, das die Admin-Richtlinie erlaubt und für das der Aufrufer Exchange-Rechte besitzt

Was Exchange diesem Benutzer für dieses Postfach gewährt hat

app-only

/users/{upn}

Das Postfach steht in O365_APP_ONLY_MAILBOXES, App-only ist aktiviert und die Richtlinie des Aufrufers enthält allowAppOnly

Was auch immer Exchange RBAC der Anwendung zuweist

Jeder Akteur außer delegated-self durchläuft zuerst policyAllowsMailbox – zuerst die eigene Genehmigung des Benutzers, dann die Obergrenze des Administrators – bevor Graph überhaupt etwas gefragt wird. Delegiert ist der Standard und der normale Pfad: Nach diesen beiden Toren bleiben die vorhandenen Berechtigungen des Tenants das eigentliche Tor, und ein abgelehnter Aufruf erzeugt einen ehrlichen 403, den der Server in „bitten Sie einen Administrator um Full Access für dieses Postfach“ übersetzt. App-only existiert nur für Postfächer, in die sich niemand anmeldet, ist standardmäßig deaktiviert und wird weiter unten vollständig beschrieben. /me bedeutet nie ein freigegebenes Postfach – es gibt keinen /me-Pfad in ein solches hinein – und ein App-only-Token darf /me überhaupt nicht verwenden, weil es keinen angemeldeten Benutzer gibt.

Teams ist konstruktionsbedingt dauerhaft nur delegiert. Siehe Einschränkungen.

Tools, die dem Modell zur Verfügung stehen

27 Tools. W markiert ein Tool, das den Zustand des Tenants verändert; diese sind für einen neu bereitgestellten Benutzer standardmäßig deaktiviert und erfordern zusätzlich allowWrites in seiner Richtlinie. Jedes Outlook-Tool akzeptiert ein optionales mailbox-Argument (eine UPN- oder SMTP-Adresse), mit dem das Postfach ausgewählt wird, auf das gewirkt werden soll; lassen Sie es für Ihr eigenes Postfach weg. Alle zurückgegebenen IDs sind undurchsichtige Microsoft-Graph-IDs – geben Sie sie unverändert zurück und erzeugen Sie niemals eine.

Outlook – Lesen

Tool

Beschreibung

o365_mail_search

Volltextsuche in einem Postfach mithilfe der Outlook-Suchsyntax (from:, subject:, attachment:, hasAttachments:true, …). Immer nach Datum sortiert, von Microsoft auf 1.000 Ergebnisse begrenzt und nicht mit Filtern kombinierbar.

o365_mail_list

Nachrichten in einem Ordner mit strukturellen Filtern und Sortierung auflisten – nur ungelesen, Absender, Datumsbereich, eine Reihenfolge. Das Gegenstück zu o365_mail_search: präzise Filterung, keine Schlüsselwortsuche.

o365_mail_get

Eine Nachricht vollständig, Textkörper als Klartext, optional mit Internet-Nachrichtenkopfzeilen und Metadaten für Anhänge. Lange Inhalte werden gekürzt und die ursprüngliche Länge wird mit angegeben.

o365_mail_folders

Mailordner auflisten – erst die oberste Ebene oder alle Ordner – mit nachrichten- und Ungelesen-Zählern. Damit eine Ordner-ID auflösen, bevor Sie Nachrichten verschieben.

o365_mail_attachments_list

Eine Aufstellung Anhänge: Namen, Typen, Größen und Inline-Flags für eine Nachricht. Nur Metadaten – nie Dateiinhalte.

o365_mail_attachment_download

Anhang herunterladen und eine kurzlebige presignierte URL zurückgeben. Gibt niemals Bytes inline zurück.

Outlook – Schreiben

Tool

Bedeutung

W o365_mail_send

Eine Nachricht sofort senden, inline erstellt oder aus einem Entwurf. Graph akzeptiert die Zustellung und gibt keine ID zurück; daher wird „accepted“ gemeldet, nicht „delivered“.

W o365_mail_reply

Antworten, Allen antworten oder eine bestehende Nachricht in einem Schritt weiterleiten. Ihr Text steht über dem zitierten Original.

W o365_mail_draft_create

Einen ungesendeten Entwurf erstellen, von Grund auf oder als Antwort/Weiterleitung, die das Original bereits zitiert. Gibt die Entwurfs-ID zurück.

W o365_mail_draft_update

Betreff, Textkörper oder Empfänger eines ungesendeten Entwurfs bearbeiten. Funktioniert nur bei Entwürfen.

W o365_mail_move

Nachricht in einen anderen Ordner verschieben oder kopieren. Das Verschieben ändert die Nachrichten-ID – die neue wird zurückgegeben, die alte funktioniert nicht mehr.

W o365_mail_delete

Nachrichten löschen: trash (wiederherstellbar, Standard), soft oder permanent – unwiderruflich und mit der Zusatz, die durch O365_ALLOW_PERMANENT_DELETE abgesichert ist.

W o365_mail_flags

Bis zu 20 Nachrichten gleichzeitig als gelesen/ungelesen markieren, kennzeichnen, kategoriesieren und Wichtigkeit setzen.

W o365_mail_folder_manage

Mailordner erstellen, umbenennen, verschieben oder löschen.

W o365_mail_attachment_add

Datei an einen Entwurf anhängen. Unter 3 MB inline, bis zu 150 MB über eine Chunked-Upload-Sitzung.

Teams

Tool

Beschreibung

o365_teams_list

Ihre Teams oder die Kanäle in einem Team. So lösen Sie eine Team- oder Kanalh-Kanalname in die IDs auf, die die anderen Teams-Tools benötigen.

o365_teams_chats_list

Ihre Chats – einzeln, Gruppe und Einzel-Besprechung – zuerst die zuletzt aktiven. Einzelchats haben keinen eigenen Namen; daher wird ein Name aus den Teilnehmerbn erzeugt.

o365_teams_messages_list

Nachrichten aus einem Kanal oder in einem Chat lesen. Chats unterstützen einen Bereich; Kanäle nicht, weil die Kanal-API von Graph keinen Datumsfilter akzeptiert.

W o365_teams_message_send

In einem Kanal, in einer Kanalthread oder in einem Chat als Sie selbst posten – einschließlich an Personen per E-Mail, wodurch der Einzelchat gefunden oder erstellt wird. Akzeptiert kein user-Argument: Teams schreibt jede Nachricht dem angemeldeten Benutzer zu; würde ein Argument anbieten, wäre das eine vorgespiegelte Identität, die nicht eintreten kann.

o365_teams_search

Stichwortsuche über alle sichtbaren Chats und Kanäle. Der einzige Weg, Teams zu durchsuchen; die Listen-APIs haben überhaupt keine Suche. Akzeptiert kein user-Argument/search/query ist auf den Token-Inhaber begrenzt und hat keinen „search as“-Parameter.

SharePoint und OneDrive

Tool

Beschreibung

o365_files_search

Durchsucht Dateien in SharePoint und OneDrive oder in einer einzelnen Website oder Bibliothek. Unterstützt KQL Suchbegriffe (filetype:, author:, path:).

o365_files_sites

Findet Websites oder listet der Dokumentbibliotheken einer Website und deren Laufwerk-Ids auf. Die Grundlage am Anfang SharePoint-Arbeiten.

o365_files_list

Einen Ordner in OneDrive oder einer Dokumentbibliothek auflisten – nach Laufwerk + Element-ID, nach Pfad, oder die Wurzel des eigenen OneDrive.

o365_files_get

Details zu einer Datei oder Ordner – auch von einer eingefügten Freigabe-URL, die aufgelöst wird. Optional wird gemeldet, wer Zugriff hat.

o365_files_download

Datei als presignierte URL herunterladen, optional auf dem Weg nach PDF konvertieren. Nie inline.

W o365_files_share

Per Link oder durch Einladen von Personen teilen. Meldet den tatsächlich gewährten Zugriff, weil die Freigaberichtlinie des Mandats das Angeforderte stillschweigend und auf die Stufe als angeforderte.

Kern

Tool

Beschreibung

o365_whoami

Wer Sie angemeldet haben, ob die Microsoft-Verbindung aktiv ist und wann zuletzt aktualisiert wurde, welche Berechtigungen erteilt wurden, über welche Tools Sie verfügen, welche freigegebenen Postfächer Sie genehmigt haben und jetzt wirklich nutzen können, und für jedes davon, ob ein Aufruf als Sie selbst oder als Dienstkonto ausgeführt würde. Rufen Sie dies zuerst, wenn etwas fehlschlägt.

Konfiguration

Alles erfolgt ausschließlich über Umgebungsvariablen. Die maßgebliche Liste ist der Env-Typ in src/config.ts.

Entra App-Registrierung

| Variable                                      | Erforderlich | Beschreibung                                                                                                                                                                                                                                                                                                           |
| --------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OAUTH_ENTRA_TENANT_ID`                       | ja           | Mandanten-GUID oder verifizierte Domäne. Jeder Aufruf zielt auf diesen konkreten Mandanten – `/common` führt zu Token-Cache-Fehlschlägen und unnötiger erneuter Authentifizierung und ist für Client-Anmeldedaten ungültig. `common`/`organizations` machen die Bereitstellung mandantenübergreifend. |
| `OAUTH_ENTRA_CLIENT_ID`                       | ja           | Anwendungs-ID (Client-ID). Die Registrierung muss den Plattformtyp **Web** verwenden.                                                                                                                                                                                                                 |
| `OAUTH_ENTRA_CLIENT_SECRET`                   | eines von    | Client-Geheimmittel. Am einfachsten, aber Entra beschränkt dessen Gültigkeitsdauer auf 24 Monate.                                                                                                                                                                                                                     |
| `OAUTH_ENTRA_CLIENT_CERT_PEM`                 | eines von    | PKCS#8-PEM-Privatekey (Literal- oder Base64) für die zertifikatbasierte Client-Authentifizierung. In Produktion bevorzugt.                                                                                                                                                                                            |
| `OAUTH_ENTRA_CLIENT_CERT_THUMBPRINT`          | mit Zertifikat | Hex-SHA-1-Fingerabdruck, wie im Portal angezeigt. Entra gleicht die Assertion per Fingerabdruck mit dem Zertifikat ab; daher sind beide Komponenten erforderlich.                                                                                                                                                      |
| `O365_ALLOWED_TENANTS`                        |                 | Kommagetrennte Mandanz-IDs, die im Multi-Mandant-Betrieb akzeptiert werden. Die Überprüfung erfolgt bei der Anmeldung **und erneut bei jeder Anfrage**, sodass das Entfernen eines Mandanten hier bestehende Verbindungen sofort sperrt, nicht erst bei deren nächster Anmeldung. Leer zusammen mit `common` bedeutet, *irgendein* Mandant, der zustimmt, kann sich verbinden; der Server warnt beim Start deutlich. |

### Schlüssel

| Variable | Erforderliche | Beschreibung                                                                                                                                    |
| -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OAUTH_SIGNING_KEY_PRIVATE` | Ja | Base64-PKCS#8-PEM des RS-Schlüssels, mit dem **unsere** MCP-Zugriffstoken signiert werden. `npm run gen:oauth-key`.                              |
| `OAUTH_SIGNING_KEY_PUBLIC`  | Ja | Base64- SPKI-PEM des zugehörigen öffentlichen Schlüssels. Veröffentlicht unter `/.well-known/jwks.json`.                                            |
| `OAUTH_SIGNING_KEY_KID`     |        | Schlüssel-ID in der JWKS und in den Token-Header. Bei einer Schlüsselpaar-Rotation mitändern. Standard: `primary`.                                    |
| `O365_TOKEN_ENC_KEY`        | Ja | `<kid>:<base64 32 bytes>` – verschlüsselt jedes gespeicherte Entra-Aktualisierungs-Token und jedes zwischengespeicherte Zugriffstoken. `npm run gen:enc-key`. **Bei Verlust sind alle gespeicherten Verbindungen dauerhaft zerstört.** |
| `O365_TOKEN_ENC_KEYS_PREVIOUS` | | Kommagetrennte zuvor verwendete Schlüssel im gleichen Format, die nur zur Entschlüsselung akzeptiert werden. Das macht die Rotation zu einem kontinuierlichen Prozess statt zu einem einmaligen Stichtag. |

### Speicher

| Variable | Erforderlich | Beschreibung                                                                                                                                                                                                                                    |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MCP_GRAPH_TABLE`     | ja¹      | DynamoDB-Tabelle für versiegelte Aktualisierungstoken (ohne TTL) und zwischengespeicherte Zugriffstoken (mit TTL). Bewusst getrennt von der OAuth-Tabelle, damit Zugangsdaten eine eigene IAM-Grenze und Backup-Richtlinie erhalten.  |
| `MCP_GRAPH_FILE`      |          | JSON-Datei-Fallback für Self-Hosting und Entwicklung. Wird ignoriert, wenn `MCP_GRAPH_TABLE` gesetzt ist; auf Lambda nicht geeignet.                                                                                                     |
| `MCP_OAUTH_TABLE`     | ja¹      | DynamoDB-Tabelle für den eigenen OAuth-Zustand – registrierte Clients, Login-Zustände, Auth-Codes und Aktualisierungstoken. TTL auf `expiresAt`.                                                                                     |
| `MCP_OAUTH_FILE`      |          | JSON-Datei-Fallback für denselben Zustand, damit `npm run dev` den echten Browser-Anmelde-Flow ohne AWS ausführen kann. Wird ignoriert, wenn `MCP_OAUTH_TABLE` gesetzt ist; auf Lambda nicht sinnvoll, da jeder Container anderen Zustand sehen würde. |
| `MCP_USERS_TABLE`     |          | DynamoDB-Tabelle für Benutzer und Richtlinien, mit den GSIs `keyPrefix-index` und `oid-index`.                                                                                                                      |
| `MCP_USERS_FILE`      |          | JSON-Datei für Benutzerverwaltung und Entwicklung. Wird ignoriert, wenn `MCP_USERS_TABLE` gesetzt ist.                                                                                                                            |
| `MCP_SHARED_SECRET`   |          | Legacy-Bearer für einen einzigen Administrator, der den Benutzerspeicher umgeht. Nützlich für Smoke-Tests. Er besitzt keine eigene Graph-Verbindung, da erhebt Graph-Tools eine Fehlermeldung auf einige zurück, solange er keinem angemeldeten Benutzer zugeordnet ist. |

¹ oder die entsprechende `_FILE`-Variante (`MCP_GRAPH_FILE` / `MCP_OAUTH_FILE`) für die lokale Entwicklung.

### Version – Verhalten

| Variable                               | Standard                              | Beschreibung                                                                                                                                                                                                                             |
| ------------------------------------ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `O365_SCOPE_PROFILE`        | `work`                      | `work` umfasst die Bereiche für freigegebene Postkästen, SharePoint-Sites und Teams-Kanäle; mehrere davon erfordern die Zustimmung eines.* Admin* (Mandantened). `personal` fragt nur Bereiche, die ein Benutzer selbst nachteilen. |
| `O365_SCOPES`               | `derived`                          | Leerzeichengetrennte Gesamtüberschreibung. Wird beim Laden validiert: `/.default` darf nicht mit benannten Bereichen vermischt werden (AADSTS70011) und `offline_access` ist vorgeschrieben. |
| `O365_GRAPH_BASE`   | `https://graph.microsoft.com/v1.0`  | Nur für Sovereign Clouds ändern, in denen mehrere der hier verwendeten Fähigkeiten nicht `Gibt` (existieren).                                                                                                                     |
| `O365_IMMUTABLE_IDS`        | `true`                    | Sendet `Prefer: IdType="ImmutableId"` an Outlook-Aufrufe, damit ID beim Ummzug bleiben erhalten bleiben. **Beim ersten Einsatz einmalig entscheiden und nie an einem Live-System umstellen.** |
| `O365_BODY_FORMAT`          | `text`                    | `text` fordert Klartext-Nachrichtentexte – genau das will eine an Modelle gerichtete Server-HAT. HTML-Nachrichten bestehen hauptssächlich aus Tracking-Markup.      |
| `O365_GRAPH_TIMEOUT_MS`     | `2100`                      | Timeout pro Anfrage, weit unter dem 300-Sekunden-Tool-Timeout des Clients.                                                                                          |
| `O365_MAX_CONCURRENCY_PER_POSTK` | `4`                  | Obergrenze gleichzeitiger Vorgänge pro (Application, Mailbox). Exchange lässt genau vier zu; der Wert wird darauf begrenzt, da pereine Plate keine 429s, weiter `wird` nur in 429s umgewandelt. |
| `O365_USER_AGENT`           | `NONISV\|SelfHosted\|office365-mcp/0.1` | Microsoft stuft nicht markierten Datenverkehr mit niedriger Prio ein. Behalte die dokumentierte Struktur bei und setze den eigenen Firmennamen in das mittlere Feld. |
| `O365_SEARCH_REGION`        | auto                            | SharePoint-Geographie (`NAM`, `EUR`, `APC`) für `POST /search/query`/Auskunft. Erforderlich mit App-only-Suche; bei Multi-Geo-Mandaten experimentLayer експлizit an. |

### Gates und Output

| Variable                      | Standard     | Beschreibung                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ----------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `O365_APP_ONLY_ENABLED`        | `false`      | Hauptschalter für den App-only-Modus. Solange `false` ist, ist der Code-Pfad unerreichbar, unabhängig von einer Allowlist.                                                                                                                                                                                                                                                                                                                                                  |
| `O365_APP_ONLY_MAILBOXES`      | leer         | Kommagetrennte Postfachadressen, die mit Anwendungsanmeldeinformationen erreichbar sind. Ein Wildcard wird rucksichtslos abgelehnt.                                                                                                                                                                                                                                                                                                                                         |
| `O365_APP_ONLY_SITES`          | leer          | Kommagetrennte Site-IDs oder URLs, die mit Anwendungsanmeldeinformationen erreichbar sind; durchgesetzt bei jedem Aufruf, der eine Site oder ein Drive adressiert und mit einem App-only-Akteur ausgeführt wird. Eine leere Liste bedeutet, dass App-only SharePoint überhaupt nicht erreicht, und ein App-only-Aufruf, der keine Site benennt, wird verweigert statt erlaubt. Der Abgleich ist case-insensitiv und entweder exakt oder ein Präfix, das auf einer `/`-Pfadgrenze endet, sodass ein Eintrag eine Site und alles darüber abdecken kann, ohne dass eine verkürzte Referenz den Zugriff erweitert. Passt zu einer `Sites.Selected`-Registrierung. |
| `O365_ALLOW_PERMANENT_DELETE`  | `false`      | Sperre auf Deployment-Ebene für den Modus `permanent` von `o365_mail_delete`, zusätzlich zur Politik pro Benutzer.                                                                                                                                                                                                                                                                                                                                                           |
| `MCP_OUTPUT_FORMAT`            | `toon`       | `toon` erzeugt kompakte tabellarische Ausgabe, die den Tokenverbrauch bei Auflistungen deutlich senkt; `json` erzeugt hübsch formatiertes JSON für programmatische Verbraucher.                                                                                                                                                                                                                                                                                                 |
| `MCP_ARTIFACT_BUCKET`          | leer          | S3-Bucket für Downloads. Von jedem Download-Tool erforderlich — es gibt bewusst keinen Base64-Fallback.                                                                                                                                                                                                                                                                                                                                                                     |
| `MCP_ARTIFACT_URL_TTL_SECONDS` | `3600`       | Lebensdauer von vorunterzeichneten URLs. Wer eine besitzt, kann die Datei ohne Authentifizierung abrufen, also kurz halten.                                                                                                                                                                                                                                                                                                                                               |
| `MCP_ARTIFACT_REGION`          | `AWS_REGION` | Überschreibt die Region für den Artefakt-Bucket.                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `MCP_AUDIT_FILE`               | leer          | JSONL-Senke für Audit- und Sicherheitsdatensätze, zusätzlich zu stderr. Für selbst gehostete Deployments; auf Lambda erreicht stderr bereits CloudWatch.                                                                                                                                                                                                                                                                                                                   |
| `MCP_AUDIT_READS`              | nicht gesetzt | `1` legt Lesezugriffe sowie Schreiboperationsprotokoll an. Standardmäßig aus, weil Lesen das Volumen dominiert. Unabhängig von dieser Einstellung werden jeder mutierende Aufruf, jeder Fehler und **jeder Aufruf, der ein anderes Postfach oder einen anderen Benutzer benennt**, immer aufgezeichnet — das Handeln im Postfach eines anderen ist genau das, wonach eine Compliance-Überprüfung fragt.                                                                                           |
| `PORT`                         | `3000`       | Hörport für den einfachen Node-Einstiegspunkt. Wird auf Lambda und Azure Functions nicht verwendet.                                                                                                                                                                                                                                                                                                                                                                       |

## Zugriff auf freigegebene Postfächer

Dies ist das Funktion, um die das Design herumgebaut ist, und die Fähigkeit selbst hängt vom Mandanten ab, nicht von diesem Server.

**Zwei Dinge müssen beide wahr sein, bevor Graph es überhaupt tut.** Die Verbindung benötigt die delegierten `.Shared` Graph-Bereiche (`Mail.Read.Shared`, `Mail.ReadWrite.Shared`, `Mail.Send.Shared` — alle im `work`-Bereichsprofil enthalten), *und* Exchange Online muss dem angemeldeten Benutzer Rechte am Zielpostfach erteilt haben. Der Bereich gibt nur die Fähigkeit frei; **Exchange ist das eigentliche Tor.** Ohne die Exchange-Berechtigung gibt Graph 403 zurück, egal was zugestimmt wurde.

Ein Administrator gewährt eine oder mehrere davon im Exchange Admin Center (Empfänger → Postfächer → das freigegebene Postfach → Delegations):

| Berechtigung       | Was sie erlaubt                                                              | Auswirkung                                                                                                                                                                  |
| ------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Vollzugriff**    | Lesen, Auflisten, Verschieben, Löschen, Entwürfe im Postfach                 | Erforderlich für jedes Lese- und Schreibwerkzeug auf dieses Postfach. Auch erforderlich, wenn ein Gesendet-Vorgang eine Kopie im Gesendet-Ordner des freigegebenen Postfachs ablegen soll. |
| **Senden als**     | Senden mit dem freigegebenen Postfach als Absender                           | Der Empfänger sieht nur das freigegebene Postfach.                                                         |
| **Senden im Auftrag** | Senden im Namen des Postfachs                                                 | Der Empfänger sieht "*Benutzer* im Auftrag von *freigegebenen Postfach*. Benutzer können dies selbst in Outlook durchführen; nur ein Administrator kann Send-as gewähren. |

**Diese können bis zu einer Stunde dauern, um wirksam zu werden.** Ein 403 direkt nach einer Gewährung ist normalerweise genau das, und der Server sagt das im Fehler.

Zusätzlich zu diesen beiden fügt der Server selbst zwei hinzu: Der Benutzer muss das Postfach beim Verbinden genehmigt haben, und die `allowedMailboxes`-Grenze des Administrators muss es erlauben. Beide werden geprüft, bevor Graph irgendetwas gefragt wird — siehe [Postfachgenehmigung](#mailbox-approval).

Dann einfach die Adresse übergeben: `o365_mail_list({ mailbox: "support@contoso.com", unreadOnly: true })`. Der Server löst den Absender auf, ruft `/users/support@contoso.com/…` und nie `/me` — es gibt keinen `/me`-Pfad in ein freigegebenes Postfach. Die Adresse muss außerdem vom Benutzer beim Verbinden genehmigt worden sein; siehe unten [Postfachgenehmigung](#mailbox-approval).

Zwei Einschränkungen, die man vorher kennen sollte. Es gibt **keine Graph-API, die auflistet, auf welche Postfächer ein Benutzer Rechte hat** — deshalb *verifiziert* die Genehmigungsseite eine Kandidatenadresse, statt sie für Sie aufzulisten, und deshalb nennt der Aufrufer bei einem Tool-Aufruf weiterhin das Postfach. Und der angemeldete Benutzer benötigt im Allgemeinen ein eigenes lizenziertes Postfach, obgleich das freigegebene Postfach selbst keine Lizenz benötigt.

**Weitere Eingrenzung.** `policy.allowedMailboxes` ist die **Obergrenze des Administrators**, zusätzlich zu dem, was der Benutzer selbst genehmigt hat; tatsächlicher Zugriff ist die Schnittstelle der beiden. Der Standardwert ist `"*"`, was die verbleibende Entscheidung an Exchange abtret, wo sie hingehört. Sie auf eine explizite Liste setzen schränkt einen Benutzer unterhalb seiner Exchange-Rechte ein; setzen Sie sie auf `null`, um ein `mailbox`-Argument grundsätzlich abzulehnen:

GXP6

## Postfachgenehmigung

Adressen werden von Hand eingetragen. Microsoft stellt keine API bereit, die die Postfächer qelt, die eine Person öffnen kann, und sie aus den Korrespondenzen abzuleiten ergab eine Liste, die größtenteils falsch war — also macht die Seite keine Vermutung. Was sie tut, ist *verifizieren*: Jede eingegebene Adresse wird, bevor sie genehmigt werden kann, gegen Exchange geprüft und mit dem Grund als verfügbar beziehungsweise nicht verfügbar angezeigt.

Das eigene Postfach des angemeldeten Benutzers ist eine normaler Eintrag auf dieser Liste und kann entfernt werden. Ein Assistent, der um ein freigegebenes Support-Postfach herum baut, hat kein Recht, das persönliche Postfach des Betreibers zu lesen; das muss also sichbar sein. Wer den Zugriff verweigert, bringt jedes Outlook-Tool dazu, einen Aufruf, der kein anderes genehmigtes Postfach benennt, abzulehnen; Teams und SharePoint sind nicht betroffen, weil keiner von beiden das Postfach-Gat durchläuft.

**Microsoft kann diese Gewährung nicht einschränken, also tut es dieser Server.** Entra bietet keine Pro-Postfach-Zustimmung für delegierte `Mail.*.Shared`-Berechtigungen. Sobald ein Benutzer diese Bereiche gewährt, kann das resultierende Token jedes Postfach öffnen, das Exchange dieser Person öffnen lässt, und es gibt keine Microsoft-seitige Möglichkeit, dies einzugrenzen – SharePoint hat 2024 delegierte `Sites.Selected`-Berechtigungen erhalten, Exchange hat kein Äquivalent und keinen Eintrag in der Roadmap. Die untenstehende Genehmigungsseite ist daher keine Nettigkeit: Sie ist das Einzige, was die Gewährung einschränkt, und sie wird **serverseitig, bei jeder Anfrage, vor jedem Graph-Aufruf** durchgesetzt (`policyAllowsMailbox` in [`src/users.ts`](src/users.ts), erreicht über `resolveActor`).

**Der Ablauf.** `/oauth/authorize` → Entra-Anmeldung → `/oauth/callback` speichert das versiegelte Aktualisierungstoken und leitet dann, anstatt dem MCP-Client seinen Autorisierungscode auszuhändigen, zu `/oauth/consent` mit einem Einmal-Ticket (15 Minuten) weiter. Die Seite zeigt das eigene Postfach des Benutzers – immer enthalten, nie entfernbar – sowie Kandidaten für freigegebene Postfächer, die jeweils bereits geprüft wurden, sodass eine Adresse, die nicht geöffnet werden kann, ausgegraut mit dem Grund angezeigt wird, anstatt akzeptiert zu werden und später zu scheitern. Sie kreuzen an, was dieser Assistent verwenden darf, und erst dann wird der Autorisierungscode erstellt und der Client zurück zur Startseite geleitet. Eine erneute Verbindung führt die Seite erneut aus, wobei die vorherige Auswahl bereits angekreuzt ist; so kann ein Benutzer später auch ein Postfach *entfernen*.

**Was die Prüfung nicht sehen kann.** Ein 403 auf dem Posteingang hat drei verschiedene Ursachen, und nur eine davon ist „überhaupt kein Zugriff“. Ein Benutzer, der nur **Send As** besitzt oder dem Zugriff auf einen *einzelnen Ordner* statt auf das gesamte Postfach gewährt wurde, scheitert an der Posteingangsprüfung, obwohl dieser engere Zugriff für die gewünschte Operation funktionieren würde. Die Seite sagt dies dort, wo das Postfach erscheint; die ehrliche Zusammenfassung ist, dass die Prüfung unter- statt überberichtet. Sie erzeugt nie ein falsches Positiv – eine Adresse, die mit 200 antwortet, ist eine, die der Server wirklich öffnen kann.

**Zwei Tore, beide serverseitig.** `grantedMailboxes` ist das, was der **Benutzer** genehmigt hat; `allowedMailboxes` ist die **Obergrenze des Administrators**. Ein Postfach ist nur erreichbar, wenn es in beiden erscheint, und `o365_whoami` meldet diese Schnittmenge als `usableSharedMailboxes`, sodass das Modell nur Postfächer sieht, die es tatsächlich verwenden darf. Das eigene Postfach des Benutzers ist immer erlaubt und erscheint in keiner der beiden Listen.

Ein **API-Schlüssel-Dienstkonto sieht die Seite nie** – es gibt keinen Browser und keinen Menschen, den man fragen könnte –, daher gilt das Zustimmungsgate für es nicht. Das ist beabsichtigt: Der Administrator, der den Schlüssel erstellt hat, ist die zustimmende Partei, und `allowedMailboxes` regiert allein. Die Unterscheidung wird danach getroffen, ob die Identität eine Entra-`oid` hat, d. h. ob sie jemals eine Browser-Anmeldung durchlaufen hat.

## App-only-Modus

App-only existiert für eine Situation: ein Postfach, in das sich niemand anmeldet, dem niemand delegiert wurde, das ein Agent dennoch bearbeiten soll. Es verwendet die eigene Anmeldeinformation der Anwendung anstelle der eines Benutzers, daher gibt es keinen angemeldeten Benutzer und `/me` ist ungültig.

**Es ist standardmäßig deaktiviert und sollte deaktiviert bleiben, es sei denn, Sie benötigen es**, da eine administrativ zugestimmte Anwendung `Mail.ReadWrite` Zugriff auf **jedes Postfach in der Organisation** gewährt. Das Aktivieren erfordert drei unabhängige Dinge: `O365_APP_ONLY_ENABLED=true`, das Postfach in `O365_APP_ONLY_MAILBOXES` und `allowAppOnly` in der Richtlinie des aufrufenden Benutzers. Ein Postfach, das auf der Whitelist steht, dessen Aufrufer jedoch kein `allowAppOnly` besitzt, fällt einfach auf delegiert zurück und lässt Exchange antworten. Die Eskalation zu App-only überspringt die beiden Postfach-Gates nicht: `resolveActor` wendet sie an, bevor es den App-only-Zweig überhaupt berücksichtigt, sodass ein angemeldeter Benutzer die Adresse auf der Genehmigungsseite weiterhin genehmigt haben muss. Der typische App-only-Aufrufer ist ein API-Schlüssel-Dienstkonto, das nie gefragt wird und für das `allowedMailboxes` die gesamte Kontrolle darstellt.

**`O365_APP_ONLY_MAILBOXES` ist nur die halbe Kontrolle, und es ist die schwächere Hälfte.** Es schränkt ein, was *dieser Code* anfordern wird. Es tut nichts für die Anmeldeinformation selbst: Jeder, der sie erhält, erreicht jedes Postfach im Mandanten. Die eigentliche Kontrolle ist **Exchange RBAC for Applications**, mandantenseitig angewendet. [`deploy/entra/scope-app-only.ps1`](deploy/entra/scope-app-only.ps1) skriptet dies: Registrieren Sie den Dienstprinzipal in Exchange, erstellen Sie einen Verwaltungsbereich über eine mail-aktivierte Sicherheitsgruppe, weisen Sie die auf diesen Bereich beschränkte Rolle `Application Mail.*` zu und verifizieren Sie mit `Test-ServicePrincipalAuthorization`.

**Die Falle, die das Ganze zunichtemacht:** RBAC-Gewährungen sind **additiv** zu Entra-Gewährungen. Wenn die nicht eingeschränkte Anwendungsberechtigung weiterhin in der App-Registrierung zugestimmt bleibt, gilt die Vereinigung der beiden, und die Eingrenzung bewirkt nichts. Die Entra-Anwendungsberechtigung muss entfernt werden. Planen Sie auch den Berechtigungscache ein: Änderungen benötigen **30 Minuten bis 2 Stunden**, um wirksam zu werden (`Test-ServicePrincipalAuthorization` umgeht den Cache, weshalb das Skript damit endet).

Teams hat hier überhaupt keinen App-only-Pfad – siehe unten.

## Pro-Benutzer-Toolberechtigungen

Wenn ein Benutzerspeicher konfiguriert ist (`MCP_USERS_TABLE` oder `MCP_USERS_FILE` für die Entwicklung), trägt jeder Benutzerdatensatz eine `policy`:

| Feld                | Bedeutung                                                                                                                                                                                                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowedTools`      | `"*"` gewährt alles, einschließlich zukünftig ausgelieferter Tools – die All-Access-Einstellung für Administratoren und Dienstkonten. Ein Array ist eine feste Whitelist.                                                                                                                |
| `toolPermissions`   | Pro-Tool-`{name: boolean}`-Zuordnung. **Standardmäßig verweigert**: Ein Tool ist nur aufrufbar, wenn sein Eintrag `true` ist. Überschreibt ein `allowedTools`-Array; `"*"` gewinnt weiterhin.                                                                                           |
| `allowWrites`       | Zusätzlich erforderlich für jedes Tool, das als verändernd markiert ist.                                                                                                                                                                                                                 |
| `grantedMailboxes`  | Was der **Benutzer** auf der Postfach-Genehmigungsseite beim Verbinden genehmigt hat. Fehlt es, wurden sie nie gefragt, und nur ihr eigenes Postfach ist erreichbar. Geschrieben von `/oauth/consent`, nicht von der Admin-CLI.                                                          |
| `allowedMailboxes`  | Die **Obergrenze des Administrators** darüber: `"*"` (Standard), eine explizite Liste von Adressen oder `null` nur für das eigene Postfach. Effektiver Zugriff ist die Schnittmenge mit `grantedMailboxes`. Für eine API-Schlüssel-Identität, die die Genehmigungsseite nie sieht, ist dies die gesamte Kontrolle. |
| `allowAppOnly`      | Pro-Benutzer-Gate für den App-only-Akteur. Standard `false`, sodass ein unvorsichtiger oder kompromittierter Benutzer niemals stillschweigend über seine eigenen Exchange-Rechte hinaus eskalieren kann.                                                                                |
| `rateLimitPerMin`   | Pro-Benutzer-Aufrufobergrenze. Standard 60.                                                                                                                                                                                                                                              |
| `disabled`          | Deaktiviert die Identität, ohne sie zu löschen.                                                                                                                                                                                                                                          |

Tools, die ein Benutzer nicht aufrufen kann, sind auch **vor `tools/list` verborgen**, sodass das Modell sie nie sieht. Bei jedem OAuth-Login wird die Zuordnung mit dem Live-Registry abgeglichen: **Neu ausgelieferte Tools werden als `false` hinzugefügt**, sodass ein neues – möglicherweise destruktives – Tool nie stillschweigend gewährt wird; entfernte Tools werden entfernt. Der Speicher wird nur geschrieben, wenn sich etwas geändert hat.

Ein neu bereitgestellter Benutzer erhält jedes schreibgeschützte Tool aktiviert und jedes verändernde Tool deaktiviert.

### Admin-CLIs

GXP7

GXP8

`connection revoke` stoppt *diesen Server* bei der Verwendung der Anmeldeinformation. Der maßgebliche mandantenseitige Kill-Switch ist **Revoke Sessions** am Benutzerobjekt in Entra – beachten Sie, dass eine alleinige Passwortänderung ein Aktualisierungstoken eines vertraulichen Clients **nicht** ungültig macht (siehe die Widerrufsmatrix in [SECURITY.md](SECURITY.md)).

## Einschränkungen und bekannte Probleme

* **Auf AWS wird der `WWW-Authenticate`-Challenge-Header umbenannt.** Eine Lambda-Funktions-URL schreibt ihn in `x-amzn-Remapped-WWW-Authenticate` um, und nichts innerhalb der Funktion kann das verhindern. Das bricht die Erkennung nicht: Die MCP-Spezifikation verlangt, dass Clients direkt auf `/.well-known/oauth-protected-resource/mcp` (dann die Root-Variante) zurückfallen, und das Referenz-SDK tut dies bei einem 401 bedingungslos – weshalb dieser Server beide Dokumente ausliefert und einen `resource`-Wert meldet, der byte-identisch mit seiner MCP-Endpunkt-URL ist. Wenn Sie auf einen Client treffen, der den Header wirklich benötigt, setzen Sie CloudFront davor mit einer Lambda\@Edge-**Origin-Response**-Funktion, die den umbenannten Namen zurückkopiert; eine Viewer-Response-Funktion funktioniert nicht, da CloudFront diese nicht aufruft, wenn der Ursprung 400 oder höher zurückgibt.

Klar gesagt, weil die meisten davon sonst als Fehler wahrgenommen werden.

**Aktualisierungstoken sterben auf eine Weise, die zufällig wirkt.** Das 90-Tage-Fenster ist *gleitende Inaktivität*, kein festes Ablaufdatum: Ein Benutzer, der wöchentlich eine Verbindung herstellt, läuft praktisch nie ab, während einer, der 91 Tage lang still ist, tot zurückkommt (AADSTS70008 / 700082). Unabhängig davon erzwingt die Anmeldehäufigkeit von Conditional Access eine erneute Authentifizierung in ihrem eigenen Rhythmus, und kein serverseitiger Code kann das verhindern. Ein Administrator, der ein Passwort über das Entra- oder Microsoft-365-Admincenter zurücksetzt, widerruft Token sofort; ein Benutzer, der sein eigenes Passwort ändert, tut dies **nicht**. Jeder dieser Fälle erscheint als ein einzelner klarer Tool-Fehler mit der URL zur erneuten Verbindung.

**Ein Ablauf des Client-Geheimnisses ist eine Klippe, keine Steigung.** Entra begrenzt die Lebensdauer von Geheimnissen auf 24 Monate, und wenn es abläuft, scheitern *alle Benutzer der Bereitstellung gleichzeitig* mit AADSTS7000222 – nicht allmählich, nicht einer nach dem anderen. Zertifikatsanmeldeinformationen vermeiden diesen Fehlermodus; rotieren Sie beide rechtzeitig vor dem Datum.

**Der Verlust des Verschlüsselungsschlüssels ist nicht wiederherstellbar.** Kein Schlüssel, keine gespeicherten Verbindungen, und jeder Benutzer muss sich gleichzeitig erneut anmelden. Sichern Sie ihn separat und rotieren Sie ihn über `O365_TOKEN_ENC_KEYS_PREVIOUS` + `connection rewrap`, niemals durch Ersetzen.

**Teams-Senden ist ausschließlich delegiert, dauerhaft.** Jeder Graph-Send-Endpunkt bietet `Teamwork.Migrate.All` als einzige Anwendungsberechtigung, und Microsoft beschränkt sie auf Migrationsszenarien. Es gibt keinen konformen Weg, Teams-Nachrichten von einem Dienstkonto in dieser Architektur zu posten; die Alternativen sind ein Bot-Framework-Bot oder ein Teams-App-Paket mit ressourcenspezifischer Zustimmung, das pro Team installiert wird, und keines davon passt zu einem eigenständigen Remote-MCP-Server. Unbeaufsichtigtes Teams-Posten ist nicht möglich. (Teams-*Messung* ist kein Problem: Das Abrechnungsmodell Modell A / Modell B endete am 25. August 2025, trotzdem, was die meisten vorhandenen Dokumentationen noch sagen.)

**Teams-Kanal-Scopes erfordern die Zustimmung des Mandantenadministrators.** Insbesondere `ChannelMessage.Read.All` kann nicht selbst zugestimmt werden. Ein Selbst-Hoster ohne Administratorrechte, der `O365_SCOPE_PROFILE=personal` ausführt, erhält funktionierende E-Mail, Dateien und Chat sowie Kanal-Tools, die mit einer Erklärung fehlschlagen statt mit einem rätselhaften 403. `personal` lässt auch `User.ReadBasic.All` aus, sodass eine @-Erwähnung einer Person außerhalb der Konversation nicht aufgelöst werden kann: Die Nachricht wird trotzdem gesendet, der Name bleibt als Klartext im Textkörper, und das Tool gibt eine Warnung zurück, dass die Person nicht benachrichtigt wurde.

**Die Genehmigungsseite für Postfächer meldet zu wenig, nie zu viel.** Sie entscheidet, ob Sie ein Postfach verwenden können, indem sie versucht, dessen Posteingang zu öffnen, und ein 403 dort hat drei Ursachen. Wenn Sie nur *Senden als* besitzen oder Zugriff auf einen Ordner statt auf das gesamte Postfach haben, wird die Adresse als nicht verfügbar angezeigt und kann nicht angehakt werden, obwohl dieser eingeschränktere Zugriff für das, was Sie wollten, funktioniert hätte. Der umgekehrte Fehler kann nicht passieren — eine Adresse, die Sie anhaken können, ist eine, die der Server tatsächlich öffnen kann.

**Die Suche hat harte Obergrenzen, die wie Datenverlust aussehen.** Outlook `$search` gibt höchstens 1.000 Ergebnisse zurück und kann nicht mit Filtern oder benutzerdefinierter Sortierung kombiniert werden. Die Teams-Suche meldet eine Seitenzahl statt einer Gesamtzahl, sodass sie nie als Trefferzahl dargestellt werden kann. SharePoint-Tiefenpaginierung stoppt jenseits von Ergebnis 1.000, und eine reine App-Suche schließt private OneDrive-Inhalte standardmäßig aus — das Aktivieren stellt einen neuen Index bereit, der Tage bis zu einer Woche dauern kann, während dieser Zeit sind die Ergebnisse stillschweigend unvollständig, ohne jeden Fehler.

**Die Freigaberichtlinie des Mandanten überschreibt stillschweigend, was `o365_files_share` erzeugt.** Einstellungen auf Organisationsebene und pro Website können einen anonymen Link auf organisationsweit herabstufen, ein Ablaufdatum erzwingen oder Links schreibgeschützt machen. Schlimmer noch, `createLink` ist idempotent pro (Anwendung, Linktyp), sodass eine Anfrage für einen neuen Sieben-Tage-Link einen jahrealten, nie ablaufenden Link mit anderem Umfang zurückgeben kann. Das Tool liest immer zurück und meldet die *tatsächliche* Berechtigung, was die einzige Verteidigung ist.

**Nachrichten-IDs ändern sich, wenn Nachrichten verschoben werden, und Teams-IDs sind nicht global eindeutig.** `O365_IMMUTABLE_IDS` ist aus genau diesem Grund standardmäßig aktiviert, aber es ist effektiv eine Einbahnstraße: IDs, die in einem Format ausgegeben wurden, funktionieren nicht im anderen, und das Umschalten auf einem laufenden Stack erzeugt `ErrorInvalidIdMalformed`. Separately: Eine Teams-Nachrichten-ID ist nur innerhalb ihres Chats oder Kanals eindeutig, daher werden Nachrichten-IDs immer mit ihren Konversationskoordinaten zurückgegeben.

**Drosselung ist der wahrscheinlichste alltägliche Fehler.** Outlook erlaubt vier gleichzeitige Anfragen pro (Anwendung, Postfach) und 10.000 pro zehn Minuten; Teams erlaubt ungefähr eine Anfrage pro Sekunde pro Kanal, pro Chat und pro Benutzer; SharePoint berechnet fünf Ressourceneinheiten pro Berechtigungsaufruf und drosselt die Suche viel stärker als den Rest von Graph. Batching hilft nicht — Graph leitet höchstens vier Unteranfragen aus einem Batch gleichzeitig an Outlook weiter. Der Server begrenzt seinen eigenen Fan-out und beachtet `Retry-After` genau, aber ein eifriger Agent wird trotzdem irgendwann auf eine 429 stoßen.

**Anhänge über 3 MB funktionieren in einem freigegebenen Postfach nicht.** Microsoft dokumentiert, dass ein delegierter Aufrufer eine 403 erhält, wenn er große Dateien an eine Nachricht in einem freigegebenen oder delegierten Postfach anhängt. Unter 3 MB ist in Ordnung. Das Tool sagt dies, anstatt eine nackte 403 anzuzeigen.

**Sovereign Clouds fehlen stillschweigend Funktionen.** Permanentes Löschen, Chat-Delta und die Teams-Export-APIs sind in US Government L4/L5 und China 21Vianet nicht verfügbar, und standortübergreifender Zugriff kann aus Gründen fehlschlagen, die nichts mit App-Berechtigungen zu tun haben. Multi-Geo-Mandanten benötigen eine Suchanfrage pro Region, sonst fehlen Inhalte anderswo stillschweigend.

**Refresh-Token-Rotationswettläufe sind harmlos, aber real.** Entra stellt bei jeder Einlösung ein neues Refresh-Token aus und widerruft das alte nicht, sodass zwei gleichzeitige Aufrufe für denselben Benutzer jeweils einen gültigen Nachfolger erhalten. Ein bedingtes Schreiben bedeutet, dass einer gewinnt und der Verlierer seine Kopie verwirft; ein verlorener Wettlauf lässt einen Tool-Aufruf nie fehlschlagen. Der Zugriffstoken-Cache ist es, der es selten hält.

## Roadmap

Dies sind **bewusste Einschränkungen des v1-Umfangs, keine Versehen**:

* **Kalender.** Das Zweite, was jeder nach E-Mail erwartet. `Calendars.ReadWrite` ist benutzerzustimmbar und das bereits für freigegebene Postfächer gebaute Akteursmodell gilt unverändert für freigegebene Kalender. Als Nächstes geplant.
* **Dateiupload** zu OneDrive und SharePoint. v1 deckt Suche, Auflisten, Abrufen, Herunterladen und Teilen ab.
* **Kontakte / Personensuche**, um einen Namen in eine E-Mail-Adresse aufzulösen. Jedes Sende-Tool geht derzeit davon aus, dass der Aufrufer bereits eine hat.
* **Posteingangsregeln** (`messageRules`, benötigt `MailboxSettings.ReadWrite`) für serverseitige Triage-Automatisierung.

Ebenfalls in Betracht gezogen: ein pluggbarer Audit-Sink (Firehose → S3 → Athena) über stderr hinaus, Workload-Identity-Föderation als dritter Client-Anmeldedatentyp auf AWS, sodass überhaupt kein langlebiges Geheimnis existiert, und vom Server ausgestellte undurchsichtige Seiten-Handles anstelle von rohen `@odata.nextLink`-Zeichenfolgen.

Die Verzeichnisverwaltung ist bewusst ausgeschlossen — Microsofts kostenloser MCP-Server für Unternehmen deckt bereits schreibgeschützte Entra-Abfragen ab.

## Mitwirken

Siehe [CONTRIBUTING.md](CONTRIBUTING.md). Sicherheitsprobleme: [SECURITY.md](SECURITY.md) — bitte kein öffentliches Issue eröffnen.

## Lizenz

MIT. Siehe [LICENSE](LICENSE).
-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LotzerDigital/aws-office365mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server