Microsoft Graph scopes — what a deployment can add

The Microsoft connection is one delegated Graph grant per person (EaGraphAuth): it already owns consent, the encrypted refresh token and the token refresh. A feature that needs another part of Graph asks for one more scope on that grant. It does not need a package, a second app registration or a second connection (maintainer, 2026-11-04).

Adding a scope

Set the key in the deployment configuration. Entries are separated by spaces, commas or semicolons:

Authentication:Microsoft:AdditionalGraphScopes = Files.Read.All Sites.Read.All

As an environment variable it is Authentication__Microsoft__AdditionalGraphScopes. A short name and https://graph.microsoft.com/Files.Read.All are the same scope. An instance that sets nothing requests exactly what it did before.

The key belongs to the instance's operator, not to the person at the consent screen, because the token it produces is the one every agent tool and script of that person uses.

🚨 Adding a scope asks every person on the instance to reconnect once, and until they do, their mail, calendar and Teams connection reads as not connected. Entra refuses to redeem a refresh token for scopes nobody consented to, so the connection shows the reconnect link (/auth/ea/connect) rather than failing on every call. Plan the change. Removing a scope disturbs nobody: a refresh may ask for a subset of what was consented.

Tenant policy still applies. A scope that needs an administrator's approval shows "Need admin approval" until a tenant administrator has consented once. The grant stays delegated: it never reaches further than the connected account's own permissions.

🚨 Try a new value on a non-production instance first. Only the shape of a name is checked, not that Graph has such a permission: Microsoft publishes no list a portal could validate against, and a copy here would refuse the next real permission. A misspelt Files.Read.Al passes, every existing grant then reads as not connected, and the reconnect fails at Entra until the key is corrected.

What is refused

These are left out and logged as an error naming the key, because one bad entry would otherwise fail the whole token request and take the mailbox down with it:

Reading SharePoint

Do not replicate. SharePoint already indexes its documents, so copying them into the mesh or building a second index adds a store that drifts. Search there, and read the file live:

POST https://graph.microsoft.com/v1.0/search/query

{ "requests": [ { "entityTypes": ["driveItem"], "query": { "queryString": "annual report 2026" } } ] }

GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/content

The token comes from IEaGraphAuth.GetAccessToken(user).

The Executive Assistant's tools SearchSharePoint and ReadSharePointFile (ExecutiveAssistantPlugin, module MeshWeaver.Mail.MicrosoftGraph) wrap exactly these two calls. The search returns names, drive and item ids, links, sizes and snippets. ListSharePointFolder adds the one thing a search cannot answer, everything in a folder: GET /drives/{driveId}/items/{itemId}/children, under the same scopes, up to 200 direct entries with moreEntries true when the folder holds more. The read hands the model the file's text — a PDF, Word or Excel file through the platform's text extractor, a text file decoded, any other binary file described and not read — up to 20 MB, the listed size deciding before the stream is opened. A partly scanned PDF is handed over as incomplete, naming the pages no OCR read. The read tool is [HiddenTool]: the chat client drops a hidden tool's call and result instead of forwarding them to the thread's tool-call log, on the agent's own round and on a round it received by hand-off alike, so a document's text reaches the model within the round and never lands in the mesh. A 403 from Graph is answered with the two scopes, this key and the reconnect link, because that is the operator's change, not the user's.

Writing to SharePoint: create-only, behind a switch

Writing is a separate decision from reading, and it is off unless the deployment turns it on (Plugins#4982, option 1: create-only, off by default):

SharePoint:AgentWrite = Write

As an environment variable it is SharePoint__AgentWrite. The switch does two things, and with any other value (unset, true, 1, a typo) it does neither:

  1. The connection asks for Files.ReadWrite.All. EaGraphAuth.RequestedScopes adds it through EaGraphScopes.SwitchedScopes, so the operator does not list it in AdditionalGraphScopes (listing it as well requests it once). Like any added scope, every person on the instance reconnects once.
  2. The Executive Assistant is handed CreateSharePointFile (MailAgentOptions.SharePointWriteMode). Off, the tool is not in the model's toolset at all, and the method refuses if reached directly.

CreateSharePointFile(driveId, folderItemId, name, text | sourcePath) creates ONE new file and answers with its link:

GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{folderId}:/{name}
PUT https://graph.microsoft.com/v1.0/drives/{driveId}/items/{folderId}:/{name}:/content?@microsoft.graph.conflictBehavior=fail

🔒 It never replaces a file. The by-name lookup refuses a name the folder already holds before a byte is sent, and the upload repeats the guarantee at Graph: conflictBehavior=fail answers 409 for a name that appeared in between, which the tool reports as "nothing was written". A wrong file is therefore always a new file the person can delete. Overwrite would be irreversible and is not offered; it waits for a per-write approval (an activity the person signs, as in Governed mail).

The content is either text (written as UTF-8) or sourcePath, a file already in the mesh ({space}/content/{subpath}/{file}), read server-side for the caller with the content route's own access check, so binary bytes never pass through the model. One PUT, up to 4 MB. The grant is delegated: a folder the person cannot write answers 403 at Graph, and the tool names the scope and the switch. The tool is not hidden: a write outside the mesh is shown in the thread. Where the switch is on, the Mail accounts view tells the person, in their language, that the assistant may create files and why Microsoft asks for consent again.

What was not established