Safety
Written against the MCP specification 2026-07-28 (tools, authorization and the security best practices page) and Microsoft’s guidance for Entra-protected MCP servers. Each control names the test that pins it.
Writes go through preview, then apply
Section titled “Writes go through preview, then apply”A change reaches Exchange Online only as the replay of a preview:
exonaut_preview_changeruns the route with?dryRun=true. The API injects-WhatIf, so the cmdlet changes nothing; cmdlets whose-WhatIfis a no-op answer422 DryRunUnsupportedbefore they run, and the tool reports that the change cannot go through MCP.- On a successful preview the server mints a confirmation token: an opaque random handle (32 bytes), stored server-side with the SHA-256 of method, path, canonical body and the caller’s object id, valid for 20 minutes, usable once.
exonaut_apply_changeconsumes the token and replays exactly the stored request. A token that is unknown, expired or already used answersPreviewRequired; a token of another caller, or a method, path or body that differs from what was previewed, answersTokenMismatchand discards the token. Nothing in the request can stand in for the token; a client-supplied “confirm” flag is never trusted.- Every apply writes one log line naming the method, path, caller (
oid), the first eight characters of the confirmation token and the route’s status; the same values are also log scopes. A completed write whose answer exceeds the result budget is still reported as a success (resultTruncated).
Pinned by McpWriteGateTests (apply without a token, with a used token, with another caller’s
token, with a differing body, on a host without writes, and the Enterprise RestOnly case).
Gates before the tool runs
Section titled “Gates before the tool runs”- Plan gate: a host whose licence lacks the
mcpfeature (Community) answersPlanRequiredfor every tool exceptexonaut_status.McpPlanGateTests. - Write gate:
exonaut_apply_changeanswersWritesDisabledunless the operator setMcp:AllowWrites=true.McpWriteGateTests. - Tier gate: routes whose policy maps to Enterprise answer
RestOnly; the MCP server applies Pro-tier writes only.McpWriteGateTests. - Route gate: a change is only sent to a route the documentation manifest knows; an unmatched path
answers
UnknownRouteinstead of being tried.McpWriteGateTests.
Authorization
Section titled “Authorization”- The endpoint requires an authenticated principal of the API’s audience; the REST route a tool
calls enforces its own scope or role, so a read-only token can never write.
McpReadToolTests(aMailbox.Readtoken reads mailboxes and gets 403 on groups). - Audience: the API accepts tokens issued for its client id,
api://<client-id>, the configuredAzureAd:Audienceand the canonical MCP URL (RFC 8707). A token minted for another audience answers 401 with the metadata pointer.McpAuthTests. - The loopback is not token passthrough. The specification forbids forwarding a client’s token to a downstream API it was not issued for. The loopback calls the same resource on the same host with a token issued for that resource, which the host validated before the tool ran, and the REST route validates it again. No third-party API sees the token.
- Scope minimization: the metadata advertises the sixteen read scopes by default, so a first
consent is read-only and a stolen token cannot write;
Mcp:ScopesSupported=Alladvertises the writes. A write that lacks its scope answers the API’s 403 naming the scope to add.McpAuthTests. - The challenge routing changes nothing for REST: only
/mcpanswers with theresource_metadatapointer.McpAuthTests.
Bounded, validated, rate limited
Section titled “Bounded, validated, rate limited”- Inputs: paths are validated against the OData key alphabet before any request is made (no query
syntax, fragments, parent segments or percent-encoded separators such as
%2eand%2f, which the server would decode before routing);topis clamped; methods are an allowlist of three. The loopback client refuses, before sending, any target that does not resolve under/exchange/v1.0/,/readyzor/explorer/examples/, so no tool can reach the diagnostics, explorer or MCP endpoints through it.McpReadToolTests,McpWriteGateTests,McpHardeningTests. - Results: every result stays under 20 KB. Null-valued fields are dropped,
selecttrims,querypages and saystruncatedwhenever either its own probe or the route’s page size cut the list (links are always relative, never the loopback origin),describekeeps the head of a wide route list, and a filter replaces anything still larger withResultTooLarge.McpReadToolTests. - Rate limit:
/mcpsits behind the same per-principal token bucket as the REST routes, and each loopback call counts against the same principal. - Tool surface: exactly eight tools in a fixed order, every description under 2,048 characters,
server instructions under 1,500.
McpToolCensusTests.
What the client is expected to do
Section titled “What the client is expected to do”The specification places the human in the loop on the client: it shows the tool inputs, asks for
confirmation on sensitive operations and may refuse. The server’s annotations make that possible:
every tool but exonaut_apply_change is read-only and idempotent; exonaut_apply_change is
destructive and not idempotent.
Known limits
Section titled “Known limits”- Step-up authorization (a
WWW-Authenticatescope challenge from inside a tool result) is not implemented; a missing scope surfaces as the API’s 403 in the tool result with the scope named. - Observability is the host’s console logging; OpenTelemetry is on the product roadmap.
- The SDK advertises the protocol’s
loggingcapability in the handshake, but the server never sends log notifications;logging/setLevelis accepted and has no effect.

