SafetyCard Request a review

MS 365 MCP Server

MCP server for Microsoft 365 (Outlook, Calendar, OneDrive, Teams) via Graph API

github.com/Softeria/ms-365-mcp-server · 2026-10-03 · c471971

Defense-in-depth score

4.9 / 10

As shipped, this server gives the model one delegated token that can read, write and send across the user's mail, OneDrive, calendar, contacts and notes, and it applies no approval of its own. The biggest risk is prompt injection through inbound email: one malicious message can lead the model to send private data out by email or delete items, and the only checks are the host client's prompts. Engineering hygiene is strong (encrypted token cache, log redaction, a structured audit log, a .env allowlist), but one tool's risk annotation is inaccurate and read-only mode does not cover every path. For least privilege, run with --read-only or a preset.

Safest setup

MS 365 MCP Server encrypts its token cache, scrubs tokens and email addresses from its logs, and keeps an audit log of tool calls, all on by default. As shipped it gives the model read, write and send access across your Microsoft 365 account with no approval step of its own, so the safest setup narrows the account and the tool set with the modes the server already includes and builds containment around it.

SetupHowWhat it gives you
A dedicated accountSign in with a Microsoft account that holds only the mail, files and calendars the task needsMicrosoft itself limits what the token can reach, whatever the model asks for C1
Read-only mode--read-only on the server commandSend, delete and other Graph write tools are left out, and the sign-in asks for no write permissions they would need C1
Only the tools needed--preset <names> or --enabled-tools <pattern> naming just the categories the task usesThe model sees a smaller tool set, and the token covers only the permissions those tools need C3
A clean launch directoryStart the server from a directory holding no .env file you didn't writeThe app registration, tenant and cloud you sign in to come only from you C6
A sign-off on sent messages--message-signoff-prefix <text> or --message-signoff-suffix <text>Recipients can tell which mail and Teams messages the agent sent C2
Its own container or VMRun the server in a container or VM with only a scratch directory writable and network access limited to Microsoft's sign-in and Graph hostsAn outer boundary around files the server writes to local disk and a narrower path for data to leave C3
The audit logKeep the default by leaving MS365_MCP_AUDIT_LOG unsetA record of each tool call with its target and recipient domains, outside the workspace C9
Log redactionKeep the default by leaving MS365_MCP_REDACT_PII unsetTokens and email addresses stay out of the server's log files C8

Approving with confidence

  • The server has no approval step of its own, so turn on your MCP client's prompts, read each one and approve calls one at a time.
  • A graph-batch call can mix reads and writes, so read every sub-request before approving it.

Handling data

  • The operational log records every tool's full arguments, message bodies included; store it like the mailbox it covers.
  • get-download-url hands the model a pre-authenticated download link, so treat the conversation like the files it points to.

Key gaps (3)

  1. Default stdio mode holds one delegated token with write access across the user's mail (including send), OneDrive, calendar, contacts, tasks and notes; a hijacked model inherits the whole account. C1 · Identity & least privilege
  2. Default session reads attacker-controllable email and files, holds private data, and can send mail or delete items with no server-side gate (Rule of Two violated). C5 · Untrusted input blast radius
  3. A .env in the working directory silently sets the OAuth client ID, client secret, tenant and cloud when the operator has not, so a cloned repo can redirect which app registration and tenant the user authenticates to. C6 · Memory, context & configuration integrity

Criteria

C1 Identity & least privilege

0.35 / 1.00

The server signs in as the user through Microsoft's login library and holds one delegated token for everything. It does request only the Graph permissions needed by the tools that are switched on, and operators can narrow that with --read-only, presets or an allowed-scopes list. Out of the box, though, that one token can read and write the user's mail, calendar, OneDrive, contacts, tasks and notes and can send mail. Nothing checks individual requests against a policy. If the model is hijacked, it has the user's whole personal Microsoft 365 account.

C2 Approval gates

0.25 / 1.00

The server has no approval step of its own; it relies on the host to ask the user, and gives the host risk labels for each tool. Labels for the 300+ Graph tools follow the HTTP method and are accurate, but one local-file tool's label is inaccurate, the account-management tools have no labels, and graph-batch can mix reads and writes in one call. The optional confirm gate is off by default and is satisfied by the model passing confirm: true, so it is not human approval. By default the model can send email and delete items without any server-side check.

C3 Tool & action scoping

0.45 / 1.00

Most tools are narrow, one per Microsoft Graph endpoint, with typed schemas, URL-encoded path values and a fixed Graph host. Three general tools widen that: graph-batch sends up to 20 arbitrary Graph requests of any method, download-bytes reads any Graph path, and download-bytes-to-file writes downloaded bytes to any absolute path the model chooses. That last one only refuses to overwrite and uses owner-only permissions; it has no directory restriction. Presets, regex filters and --read-only can shrink the tool set, but by default every personal-mode write tool is on.

C4 Code-execution isolation

N/A · full credit 1.00 / 1.00

The server never runs model-generated code or commands. The only child process is an optional token-cache helper that the operator configures through an environment variable as an absolute path, run without a shell; it cannot be set from a project .env file or by the model. The local file writer is scored under tool scoping; the server itself interprets nothing as code.

C5 Untrusted input blast radius

0.25 / 1.00

The server feeds the model content anyone can write: inbound email, calendar invites, shared files and OneNote pages. Results come back as structured Graph JSON, but nothing marks them as untrusted or limits what the model can do afterwards. In the same default session the model can read private mail and files, send email to any address, delete items, and write attachment bytes to the local disk. One malicious email can therefore lead to data leaving by mail and to irreversible actions with no server-side check.

C6 Memory, context & configuration integrity

0.25 / 1.00

The server keeps no memory and loads no instruction files. It does read a .env file from whatever directory it is started in, which MCP clients often set to the open project. After a past advisory this was narrowed to four keys, but those keys are the app registration's client ID and secret, the tenant and the Microsoft cloud. A cloned repository can therefore silently point the sign-in at a different app registration, tenant or cloud whenever the operator has not set them.

C7 Third-party extensions

N/A · full credit 1.00 / 1.00

The server loads no plugins, remote tools or third-party MCP servers. Every tool comes from the bundled endpoints.json, and the only dynamic imports are its own packaged dependencies (keytar, Azure identity, the browser opener). Supply-chain risk in those dependencies is outside this criterion.

C8 Secrets & sensitive-data protection

0.45 / 1.00

Tokens are handled well. The token cache is encrypted with AES-256-GCM, with the key kept in the OS keychain when one is available. Log output is scrubbed of tokens and email addresses by default, and tokens never appear in tool results. Gaps: redaction can be switched off with an environment variable, every tool's full arguments (message bodies included) are written to the operational log, crash dumps go to stderr unredacted, and get-download-url deliberately hands the model a pre-authenticated download link. The refresh token is long-lived and carries broad write scopes.

C9 Audit & traceability

0.38 / 1.00

There is a separate JSON audit log, on by default and kept in the user's home directory with owner-only permissions. It records each Graph and utility tool call with a request ID, tool, HTTP method, status, duration, target resource and recipient domains, plus calls refused by tool filters. In the default local mode it records no user identity, because that is read from a bearer token only present in HTTP mode. Account tools such as select-account and logout, and calls refused by the confirm gate, are not recorded. Writes are best-effort, and a single environment variable turns the log off.

C10 Limits & kill switch

0.50 / 1.00

Every Graph call has a 100-second timeout, a capped number of retries and a circuit breaker. Paginated reads stop after 100 pages or 10,000 items, and all of these limits have sensible defaults the operator can change. Rate limiting exists only in HTTP mode, so the default local mode has none. Nothing stops a cancelled request from finishing, and file downloads to disk have no size cap.