Graph Mail MCP
This server provides controlled Microsoft 365 mail access via MCP, supporting read-only search/read/thread/attachment workflows and managed draft creation/update (no sending, deleting, or modifying read state).
Authentication & status: Check auth state, trigger explicit login/recovery, and inspect cached token metadata without exposing secrets.
Folder listing: List mail folders in your own or a shared mailbox.
Message search: Full-text KQL search, OData filter mode, relevance mode, with pagination, folder bounds, date/participant/hasAttachments filters, and body content control.
Message retrieval: Read a single message by ID/immutable ID/internetMessageId with body mode selection and optional headers.
Thread reconstruction: Rebuild a bounded mailbox-local conversation thread from a seed message, with optional folder bounds and body modes.
Attachment handling: List attachment metadata and retrieve bounded attachment content as metadata, text, or base64.
Managed drafts: Create new drafts or reply/reply-all/forward drafts (without sending), and update MCP-owned drafts with recipients, subject, body, importance, and more.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Graph Mail MCPsearch my inbox for unread emails from Contoso about the Q3 report"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Graph Mail MCP
A local stdio Model Context Protocol server for Microsoft 365 mail. It provides controlled Outlook/Exchange Online search, read, thread, attachment, and draft workflows through Microsoft Graph.
It never sends, deletes, moves, copies, archives, or marks messages as read. Raw Graph URLs, raw OData, and unrestricted Graph requests are not exposed.
Deployment order: clone and build -> create your own configuration -> complete first sign-in -> add the server to a supported MCP client configuration -> verify it in the intended context.
Choose the configuration scope that suits your use: workspace/project, user profile, or another location explicitly supported by your client. User-global installation is optional, not a requirement of this server. The client must load the configuration and resolve the correct executable, arguments, and environment; an arbitrary JSON file is not discovered automatically.
This repository is self-contained. No maintainer-specific workspace, setup script,
Azure CLI login, separate authentication proxy, or other repository is required.
All C:\to\path\... paths below are placeholders: replace them with locations on
the machine being configured. Do not copy another person's account, browser
profile, or token cache.
Requirements
Windows with Microsoft Edge and an interactive desktop for initial sign-in/MFA.
Node.js 22+, npm, and Git.
Windows PowerShell (
powershell.exe), used internally for Windows DPAPI. You can run the setup commands in Windows PowerShell 5.1 or PowerShell 7.A Microsoft 365 work or school account with Exchange Online in the public cloud.
Tenant policy permitting Graph Explorer and delegated
Mail.ReadWriteaccess. Existing grants in one tenant do not guarantee consent in another tenant.A supported, signed-in MCP client: VS Code with Copilot chat, Copilot CLI, or another local stdio client. Client/organization policy must permit this server.
The included provider uses the installed Edge browser through Playwright. You do not need a separate Playwright MCP server, app registration, client secret, or manually supplied access token. Initial consent can still require your tenant administrator; installation does not bypass that requirement.
Personal Microsoft accounts are disabled by default. Shared mailboxes additionally
require Exchange delegation and Mail.ReadWrite.Shared; leave shared access
disabled for the initial own-mailbox setup. The documented deployment is local
Windows, not an unattended service, WSL, a Linux container, or a remote SSH host.
Related MCP server: Mail-MCP
Deploy on a new Windows machine
Run these steps in one PowerShell window. Use a local, writable installation
directory, preferably outside synchronized folders. If you reopen PowerShell,
redefine the path variables before using later commands.
The commands use npm.cmd so PowerShell does not select an npm.ps1 wrapper
that may be blocked by script policy; do not weaken execution policy for setup.
1. Clone and build
Replace $ProjectRoot with your installation directory. On an existing checkout,
use the update procedure instead of cloning
over it.
$ProjectRoot = 'C:\to\path\graph-mail-mcp'
New-Item -ItemType Directory -Path (Split-Path -Parent $ProjectRoot) -Force | Out-Null
git clone https://github.com/hanhandly/graph-mail-mcp.git "$ProjectRoot"
if ($LASTEXITCODE -ne 0) { throw 'Clone failed. Stop and resolve repository access or the destination path.' }
Set-Location -LiteralPath $ProjectRoot
npm.cmd ci
if ($LASTEXITCODE -ne 0) { throw 'Dependency installation failed.' }
npm.cmd run build
if ($LASTEXITCODE -ne 0) { throw 'Build failed.' }
$NodeExe = (Get-Command node -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source
$EntryPoint = Join-Path $ProjectRoot 'dist\cli.js'
$ConfigPath = Join-Path $ProjectRoot 'config.local.json'
$NodeExeExpected result: dist\cli.js exists, and $NodeExe is the absolute path to this
machine's Node executable. node_modules and dist are not checked into Git; each
machine must install dependencies and build.
2. Create local configuration and choose the account
First installation must ask the person deploying the server which Microsoft 365 account to use. Do not infer it from a GitHub login, a Windows username, this README, or a different machine's cached session. This is separate from the GitHub account used to sign in to Copilot.
The following commands prompt for that choice and refuse to overwrite an existing
configuration. Enter an exact sign-in UPN, such as person@example.com, or an
@domain selector, such as @example.com. An exact UPN is recommended when more
than one cached account belongs to the same domain.
if (Test-Path -LiteralPath $ConfigPath) {
throw 'config.local.json already exists. Review or edit it instead of overwriting it.'
}
$AccountSelector = (Read-Host 'Microsoft 365 sign-in UPN or @domain selector').Trim()
if ([string]::IsNullOrWhiteSpace($AccountSelector)) {
throw 'An explicit account choice is required for first installation.'
}
$LocalConfig = Get-Content -LiteralPath (Join-Path $ProjectRoot 'config.example.json') -Raw | ConvertFrom-Json
$LocalConfig.graphExplorer.accountHint = $AccountSelector
$ConfigJson = $LocalConfig | ConvertTo-Json -Depth 20
[System.IO.File]::WriteAllText($ConfigPath, $ConfigJson, [System.Text.UTF8Encoding]::new($false))
& $NodeExe $EntryPoint -c $ConfigPath doctor
if ($LASTEXITCODE -ne 0) { throw 'Configuration validation failed. Correct the local JSON before signing in.' }Save JSON as UTF-8 without a byte-order mark (BOM). The write above works in
both supported PowerShell versions; Windows PowerShell 5.1's
Set-Content -Encoding UTF8 adds a BOM and should not be substituted.
Review these settings in the ignored config.local.json:
Setting | Initial choice and meaning |
| The UPN or domain you just chose. A domain selects the first matching cached account; it does not identify one particular person. |
|
|
| Keep |
| Keep |
| Keep |
| Keep the template's |
| The template explicitly enables |
| Leave |
For compatibility, a missing/null accountHint currently falls back to
@microsoft.com. That fallback is not a substitute for first-install account
configuration, particularly for users in other organizations.
doctor checks configuration and Node compatibility and reports cached auth
metadata. ok: true does not prove Edge can launch, consent exists, or Graph will
authorize a mailbox request. An unauthenticated cache before first sign-in is
expected.
3. Complete first sign-in
& $NodeExe $EntryPoint -c $ConfigPath auth login
if ($LASTEXITCODE -ne 0) { throw 'Sign-in did not complete. Resolve the reported authentication requirement before continuing.' }
& $NodeExe $EntryPoint -c $ConfigPath auth statusThe server opens its isolated Edge profile and uses Graph Explorer for delegated authentication. Complete any password, MFA, or required approval in the Microsoft window. Do not paste passwords, tokens, cookies, or browser storage into a terminal, AI conversation, or configuration file.
Expected result: status identifies the chosen account and reports state: "ready"
and authenticated: true. Scope lists are cached-token observations, not a live
tenant-consent inventory. If consent requires an administrator or policy blocks
access, stop and obtain authorized assistance rather than changing OAuth clients,
loosening scope policy, or repeatedly attempting sign-in.
--account-hint on auth login is a temporary override; edit
graphExplorer.accountHint in the local JSON for a persistent account change.
For intentionally enabled shared-mailbox access, configure the mailbox allowlist
and Exchange delegation first, then use auth login --shared for the additional
Mail.ReadWrite.Shared requirement.
4. Add the server to your MCP client
Configure whichever client you want to use, in its supported configuration scope:
Intended scope | Example configuration locations |
One workspace/project | VS Code: |
Across workspaces for one user/client profile | VS Code: the active profile's user |
Another client or custom configuration source | Use the location or explicit loading mechanism documented by that client, with its required JSON schema. |
The client's MCP configuration describes how to launch the server. It is separate
from the application's config.local.json, which selects the mailbox account and
is passed through -c. If you move that application file, update the -c argument.
The examples below use absolute paths to avoid working-directory dependencies. If you want multiple clients to reuse one account/session, point their launch definitions to the same installation/configuration under the same Windows user and local-data environment. You do not need to configure both clients.
Prepare the following values, even if you reopened PowerShell after signing in:
$ProjectRoot = 'C:\to\path\graph-mail-mcp'
$NodeExe = (Get-Command node -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source
$EntryPoint = Join-Path $ProjectRoot 'dist\cli.js'
$ConfigPath = Join-Path $ProjectRoot 'config.local.json'
foreach ($RequiredFile in @($NodeExe, $EntryPoint, $ConfigPath)) {
if (-not (Test-Path -LiteralPath $RequiredFile -PathType Leaf)) {
throw "Required file is missing: $RequiredFile"
}
}
if (-not $env:LOCALAPPDATA -or -not $env:USERPROFILE -or -not $env:SystemRoot) {
throw 'Run setup in the intended Windows user session with its standard environment.'
}
$McpServer = [ordered]@{
type = 'stdio'
command = $NodeExe
args = @($EntryPoint, '-c', $ConfigPath, 'serve')
env = [ordered]@{
LOCALAPPDATA = $env:LOCALAPPDATA
USERPROFILE = $env:USERPROFILE
SystemRoot = $env:SystemRoot
}
}The environment values are resolved from the current Windows session, not from
the maintainer's machine. Passing them explicitly identifies the local-data root
even when a client filters inherited environment variables.
ConvertTo-Json below supplies the required JSON backslash escaping automatically.
VS Code Copilot
For workspace scope, create or edit .vscode\mcp.json in the intended workspace.
For user-profile scope instead, run MCP: Open User Configuration from the
Command Palette. The default Windows profile normally uses
%APPDATA%\Code\User\mcp.json; other profiles, Insiders, and remote settings can
use different locations. The guided MCP: Add Server flow also lets you choose
Workspace or Global.
Generate the servers-rooted JSON for the location you chose:
[ordered]@{ servers = [ordered]@{ 'graph-mail' = $McpServer } } | ConvertTo-Json -Depth 10If the file is empty, paste the whole output. Otherwise, merge only the
graph-mail entry into the existing servers object. Preserve all other servers
and settings. Save, then use MCP: List Servers to start/restart graph-mail
and review the client trust prompt.
GitHub Copilot CLI
For project scope, use .mcp.json or .github\mcp.json in the applicable
repository context. Project configuration is subject to the client's folder
trust and discovery rules.
For user scope instead, the default file is
%USERPROFILE%\.copilot\mcp-config.json; if COPILOT_HOME is set, use
mcp-config.json under that directory. Generate the CLI's mcpServers-rooted JSON
for your chosen location:
$CliServer = [ordered]@{
type = 'local'
command = $McpServer.command
args = $McpServer.args
env = $McpServer.env
tools = @('*')
}
[ordered]@{ mcpServers = [ordered]@{ 'graph-mail' = $CliServer } } | ConvertTo-Json -Depth 10Create the parent configuration directory if it does not exist. As above, paste
the whole output only into a new/empty file; otherwise merge just graph-mail
into the existing mcpServers object. Save JSON without a BOM. Start a fresh
Copilot CLI session in the intended context and use /mcp to inspect the server.
tools: ["*"] exposes the available tools; it does not mean you should globally
bypass tool approval.
The generated definitions contain resolved, machine-specific paths. Before committing a project-level MCP file, decide whether it is a private local configuration or a shared template. Keep private deployment files out of source control; shared configurations should use client-supported variables or clearly documented placeholders, never credentials or another user's local paths.
Important differences and boundaries
Item | Rule |
JSON root | VS Code uses |
Working directory | These examples use absolute paths and need no |
Process | The client launches |
Scope | Scope is selected by the client configuration, not imposed by Graph Mail MCP. Project scope is valid; user scope is optional and does not automatically cover other machines, profiles, WSL, or containers. |
Duplicate definitions | If |
VS Code Agent Host | VS Code can forward its configuration to Agent Host; some sessions also discover workspace |
An AI assisting deployment must follow the requested configuration scope rather than assume user-global installation. It must ask for the account choice, preserve existing client entries and local configuration, leave token/profile files local, and report any blocked step. It must not substitute another user's paths, invent credentials, disable organizational controls, or claim success solely from a configuration file being written.
5. Verify in the configured scope
For a workspace/project registration, open that workspace or repository in the chosen client. For another explicitly loaded configuration, launch the client with that source. Run the following checks where the configuration is intended to apply:
Confirm that
graph-mailexposes the ten tools listed below.Invoke
mail_auth_status. Check the intended account and the returned state; do not treatrefresh_possibleas an already authenticated result.Optionally invoke
mail_authenticateto complete/reuse authentication without reading a mailbox. It is not a mandatory preflight before every mail tool.If mailbox access verification is authorized, call
mail_list_folderswith{"mailboxScope":"me","pageSize":1}. This is read-only. Do not create a draft merely to verify installation.
If you specifically chose user-global scope, you can additionally repeat the checks from an unrelated workspace to confirm that coverage. This is not a requirement for project-scoped deployments.
Tool discovery proves process startup; cached ready status proves usable local
token metadata; a successful mailbox read proves access to that particular Graph
operation. These are different checks. Do not attach raw mail or auth output to
public issues.
Running serve manually in PowerShell waits for an MCP client on stdin; it is not
an HTTP server or a human-facing mail command. Let VS Code/Copilot CLI own that
process. After rebuilding, existing server processes need a restart to load the
new code and tool definitions.
Authentication lifecycle
Explicit auth login drives Graph Explorer's Sign in, configured account
selection, permissions panel, exact supported-scope Consent, and Microsoft
confirmation. Automation is limited to Mail.ReadWrite and intentionally enabled
Mail.ReadWrite.Shared; it never selects tenant-wide admin consent or runs the
preloaded POST query. Password, MFA, Conditional Access, and administrator approval
remain user-controlled. Cached tokens are checked against the configured account
or domain. auth status returns metadata and next actions, never the token.
Graph Explorer can return permissions previously granted to its shared application.
The default unexpectedTokenScopePolicy: "warn" reports scopes beyond the mail
and sign-in allowlist. Strict deployments can choose "reject", but must handle
the resulting rejection rather than assuming it trims a token's permissions.
A genuinely least-privilege OAuth client would require a separately implemented,
authorized provider; the shipped server has no app-ID/client-secret configuration
switch to replace Graph Explorer.
Automatic renewal after initial sign-in
Normal mail requests reuse a validated token without opening Edge. Within five minutes of expiry, or when the cache is missing, the provider tries the existing Graph Explorer session. The recommended example configuration uses a real Edge window minimized in the background:
{
"graphExplorer": {
"automaticRefresh": true,
"refreshBrowserMode": "background",
"interactionMode": "auto",
"silentRefreshTimeoutMs": 45000,
"loginTimeoutMs": 300000
}
}The browser and its authentication popups are minimized using Edge's own window API, with the resulting state checked. If Microsoft actually requests password, MFA, or permission confirmation, the same existing prompt is restored for your action; the browser is not restarted. Only you complete that action. Renewal does not click permission Consent or approve the Microsoft consent prompt. Explicit auth login remains the initial exact-scope enrollment path.
This is Playwright, not WAM or an invisible Windows service. A minimized window can appear in the taskbar, startup can briefly show a window, and system-owned authentication dialogs are not guaranteed to be hidden. No unrelated Edge profile or window is controlled.
The initial attempt is bounded by silentRefreshTimeoutMs (45 seconds by default). When a human step is detected, the same attempt gets up to loginTimeoutMs measured from its start; repeated polling does not extend that limit. Completion or failure closes the owned browser. Closing a required prompt does not trigger repeated new login windows. An explicit policy or administrator-approval block returns AUTH_POLICY_BLOCKED and stops repeated automatic attempts in that process until explicit login or a new validated cache resolves it; a generic access-denied page is not enough to identify Conditional Access.
Compatibility is opt-in: configurations omitting the new fields retain refreshBrowserMode: "headless" and interactionMode: "manual". manual never allows mail requests to restore a login window. Set automaticRefresh to false for explicit-login-only mail requests. The example configuration recommends background/auto; do not overwrite an existing user's explicit choices when updating.
Concurrent requests share one renewal. A profile-scoped, heartbeat-maintained filesystem lease also coordinates separate VS Code and CLI processes, from browser acquisition through validated cache persistence. Waiting clients reuse the resulting token instead of opening another browser. A recognized profile-busy error still handles older clients that do not participate in the lease. Lease files, profile data, and the DPAPI cache remain under user-local app data; no HTTP proxy, listening port, detached service, or global Azure account change is introduced.
Tokens are cached in memory, with disk revision checks so another client's login or cache clearing is noticed without decrypting on every request. The encrypted disk cache holds one active account/scope context; older wildcard entries remain readable only after account validation.
On HTTP 401, the transport requests a different token and replays the approved request at most once. Returning the same token is not a successful refresh. Failed renewal preserves the disk cache, but expired or rejected tokens are not used, and a failed silent attempt has a 30-second process-local cooldown.
To exercise the same noninteractive path without reading mail:
& $NodeExe $EntryPoint -c $ConfigPath auth refreshauth refresh remains noninteractive even when mail requests use auto mode. auth refresh --force requires a replacement; it can fail if Graph Explorer still supplies the same token. To allow recovery to display a required Microsoft prompt without querying mail, use auth refresh --interactive (optionally with --force). These refresh commands never automate new permission consent or bypass Microsoft controls. auth status remains cache-only and reports authenticationInProgress when a local request or another participating client owns authentication.
MCP client timeouts are not under the server's control. If the client cancels or disconnects during MFA, no later Graph operation is started for that request, including deferred draft writes. Cancellation also stops transport retries. A cancelled request can leave shared authentication running to populate the cache for another request; disconnecting the MCP server closes its owned authentication resources. Complete the Microsoft prompt and retry a read if its client timed out. Do not blindly repeat a draft creation whose HTTP request was already sent: cancellation cannot undo an operation already accepted by Graph.
When the cache is missing or expired and silent renewal is available, status reports authenticated: false, interactionRequired: false, and a next action to try a mail tool or auth refresh. This is not a claim that renewal has succeeded; it avoids incorrectly demanding manual login before the automatic path has been tried. An observed interaction-required failure, disabled automatic renewal, or invalid account/scope policy directs the caller to the corresponding corrective action.
An MSAL/WAM broker is not enabled. An existing Graph Explorer grant does not automatically authorize Microsoft Graph PowerShell or another OAuth client. The selected implementation does not register an app, extract WorkIQ/Office refresh tokens, change Azure CLI accounts, or start an authentication proxy listener. See the authentication design.
Tools
Area | Tools |
Authentication and folders |
|
Search and read |
|
Attachments |
|
Managed drafts |
|
Draft updates are limited to drafts created and registered by this MCP. There is no send tool.
mail_authenticate is an optional, explicit recovery entry that reuses the same configured session. It makes no mailbox request, does not enroll Mail.Read, and does not automate new consent. It is not a required extra step before every mail tool. Normal read and draft workflows continue to use the already configured Mail.ReadWrite permission.
mail_auth_status remains cache-only. Its state distinguishes ready,
refresh_possible, in_progress, login_required, interaction_required,
consent_required, and blocked. A missing/expired cache may be
refresh_possible without promising that renewal will succeed. Consent is
reported only after direct observation or a recorded consent-specific failure;
another process's lease or generic denial does not establish consent requirements.
nextAction directs callers to an existing prompt rather than duplicate logins.
requiredScopes is the configured requirement. grantedScopes contains scopes
observed on the cached token, including an expired token; it is not a live
tenant-consent inventory. extraGrantedScopes is the difference from the
required scopes, so it can include normal ancillary sign-in permissions.
The existing unexpectedScopes warning instead uses the configured allowlist.
mcpCapabilities reports local policy and explicitly sets
mailboxAccessChecked: false; it is not proof that Graph accepted a mailbox call.
Correctness and efficient results
Searches and threads span mailbox folders by default, including incoming replies and sent messages. sentitems is never inferred as a default from sender identity. An explicit single folderIds value can be an ID or a Graph well-known name such as inbox or sentitems; multiple folders are not silently expanded into extra requests.
Message collection $search now quotes the complete KQL expression as required by Graph, including nested subject phrases and literal # characters. Subject and participant roles are preserved; there is no automatic subject-to-full-text fallback on 400. Search/relevance keeps supplied text clauses conjunctive. Filter mode retains its existing substring alternatives for multiple subjects. Date ranges and hasAttachments: false are honored; incompatible conversationId plus indexed-search criteria fail explicitly rather than being ignored. Indexed date/attachment candidates are checked against returned fields before inclusion.
Thread deduplication uses explicit message IDs and Internet Message IDs only. Similar sender, subject, minute, or preview text never discards a different message. The seed is retained, and a capped thread reports partial coverage when additional pages remain.
The existing output stays available, with optional smaller views:
Option | Effect and boundary |
| No content, or the Graph preview; no full-body download is requested. |
| Full original message body in the requested format. |
| Graph's native plain-text |
| Core IDs, subject, from/sender, dates and message state only. Specify additional allowlisted metadata fields when needed; omit this option for the standard envelope. Unselected recipients are omitted, not represented as empty recipients. |
| Tightens the per-message returned-content budget without exceeding server limits. This is post-retrieval truncation, not a promise to reduce Graph's full-body download. |
Thread | Return only reference/Auto-Submitted headers, or omit headers from output. Reconstruction uses the same already-fetched header data. Omit for the existing full-header output. |
MCP JSON is compact; human-facing CLI output remains formatted. Content is also bounded by maxMessageBodyCharacters and maxTotalBodyCharacters; bodyTruncated: true and provenance disclose truncation. There is no default regular-expression removal of signatures, quotations, or forwarded content.
includeSenderIdentity: true adds address-match-based isFromMe and isSentByMe using the account on the token already used for that Graph response. Optional accountAliases may contain explicitly verified aliases, but requires an exact configured account selector. There is no directory lookup or additional scope request. Unrecognized addresses remain null, not a guessed false/other; these fields do not prove human authorship, message authenticity, or who used a send-as permission.
excludeAutomaticMessages: true removes only messages explicitly marked Auto-Submitted: auto-replied or auto-generated. Missing, conflicting, custom, or absent markers do not justify discarding a reply. The option selects headers in the existing collection request, never adds per-message GETs, and reports exclusions/unknown classification in provenance. It is opt-in, not available for relevance search, and may increase upstream header payload. An explicitly requested thread seed is retained even if marked automatic.
Native property references: Graph message / uniqueBody, message search syntax, and well-known folder names.
Local data and updates
Location | Contents and handling |
| Your checkout: source, dependency lockfile, documentation, and locally built |
| Your account/configuration choices; Git-ignored, but not a secret store. Keep credentials out of it. |
| DPAPI-encrypted Graph access-token material plus account/scope metadata. Never commit or share it. |
| Isolated Edge sign-in state from the example configuration. Do not copy it from or to someone else's machine. |
| Default location for other runtime state, including draft ownership, cursor/coordination data, and diagnostics. Keep it out of Git and synchronized folders. |
Your chosen MCP configuration file(s) | Launch settings at the selected workspace, project, user, or other supported scope. Review machine-specific paths before sharing or synchronizing; keep mailbox credentials out of these files. |
DPAPI protects the Microsoft Graph access token, not a Windows logon token. The current Windows user is the encryption context. Treat the cache/profile as per-user, per-machine state and sign in separately on a new machine rather than shipping a portable login bundle. Encryption is not protection from every process running as that same Windows user.
Updating an existing installation
Preserve config.local.json and your chosen MCP registrations. Do not repeat the
first-install copy/configuration step.
Set-Location -LiteralPath 'C:\to\path\graph-mail-mcp'
git status --shortIf tracked changes or untracked work are present, preserve and resolve them before pulling; do not reset or clean the checkout as an update shortcut. With a clean worktree:
git pull --ff-only
if ($LASTEXITCODE -ne 0) { throw 'Update was not a clean fast-forward. Resolve it without discarding local work.' }
npm.cmd ci
if ($LASTEXITCODE -ne 0) { throw 'Dependency installation failed.' }
npm.cmd run build
if ($LASTEXITCODE -ne 0) { throw 'Build failed.' }Restart only this MCP in the affected clients, then check tool discovery and authentication status again. Rebuilding does not hot-reload a running Node process. Do not reinstall the browser, clear the token cache, or grant new scopes as a routine code update.
Troubleshooting
Symptom | Check and action |
| Install the prerequisites/build, re-resolve |
| Confirm the |
Works only in the configured workspace/project | This is expected for project scope. Choose user scope only if you want broader availability; otherwise verify loading and path resolution within the intended context. |
One client unexpectedly sees a different account/cache | If the clients should share a session, compare their config arguments, Windows user, and |
| Renewal has not yet succeeded. Try the normal tool or |
| A local or participating peer process owns authentication. Follow |
| Complete the existing Microsoft prompt when appropriate. |
| Follow the reported requirement with the tenant administrator. Do not bypass Conditional Access, add |
| Correct the configured UPN/domain/tenant and sign in intentionally. A command-line account override does not persist to the MCP config. |
DPAPI/cache cannot be read | Confirm the intended Windows user/session and |
Tool list remains at an older version | Rebuild, verify which entry point the active registration uses, and restart that MCP/client. |
Edge is visible during renewal | Background mode minimizes owned windows; real MFA/policy prompts can still require visibility. It is not a guarantee of completely invisible authentication. |
For VS Code logs, use MCP: List Servers, select graph-mail, then Show
Output. In Copilot CLI use /mcp to inspect the registration and state. Redact
account identifiers and paths from shared diagnostics; never share raw tokens,
cookies, screenshots of credentials, or message bodies.
Security
The Graph transport uses a deny-by-default policy firewall and typed request compiler. Tokens are stored outside the repository and protected with Windows DPAPI by default. Logs and tool responses must not expose access tokens, cookies, or authorization headers.
The server accepts delegated tokens only, always rejects Mail.Send, reports unexpected scopes by default, and can reject them in strict mode. Its request firewall limits runtime operations, but deployments requiring a strictly least-privilege bearer token should use a dedicated app registration or constrained authentication provider.
See Security, Tool reference, Configuration example, and Architecture.
Client configuration references: VS Code MCP servers and GitHub Copilot CLI MCP servers.
Available Tools
9 toolsmail_auth_statusA
Return authentication status, expiry, unexpected-scope warnings, and the next login action without exposing tokens.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses a key safety behavior ('without exposing tokens') and enumerates the returned information. However, it does not state whether authentication itself is required to call this endpoint, whether it has side effects (it clearly does not), or any rate limits. For a read-only status check this is adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, well-structured sentence that front-loads the primary output (authentication status) and lists key details, ending with a security-relevant behavior (without exposing tokens). No fluff, no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-input status-check tool, the description covers the main return values and the token-safety guarantee. It doesn't mention error cases or whether auth status implies the caller is authenticated, but for a simple status check this is near-complete. A 4 reflects the minor gap of not explaining what 'next login action' means or side effects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the description has no obligation to explain inputs. Per calibration, baseline is 4. The description adds no parameter semantics because there are none to explain.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource ('mail authentication') and the exact information returned (status, expiry, unexpected-scope warnings, next login action). It clearly distinguishes itself from sibling tools like mail_list_attachments or mail_search_messages, which operate on mail content rather than authentication state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The purpose implicitly signals when to use the tool (when you need auth state), but there is no explicit guidance about when not to use it or how it relates to siblings. For a status-check tool with zero parameters, the context is largely self-evident, but a mention of typical use cases or prerequisites (e.g., 'call after auth failures') would help.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_create_draftC
Create a new/reply/reply-all/forward draft without sending.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | ||
| mode | Yes | ||
| comment | No | ||
| replyTo | No | ||
| subject | No | ||
| importance | No | ||
| ccRecipients | No | ||
| mailboxScope | No | ||
| toRecipients | No | ||
| bccRecipients | No | ||
| sourceMessageId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It only reveals that the email is not sent; it fails to mention that this persists a draft, may require authentication, or that reply/reply_all/forward modes depend on a sourceMessageId. In contrast to a simple read tool, this mutation's side effects are largely undisclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no wasted words and it front-loads the action. It is appropriately brief for conveying the core purpose, though for a tool with this many parameters it is nearly too terse to be genuinely helpful. It earns its place but nothing more.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is complex: 11 parameters, nested objects, enums, and no output schema. The one-line description omits crucial context, such as the requirement of sourceMessageId for reply/forward modes, the role of comment/body in those modes, the meaning of mailboxScope, and what the tool returns after creating the draft. An agent cannot correctly invoke this tool reliably from the description alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate across the 11 parameters. It only paraphrases the mode enum ('new/reply/reply-all/forward'), providing no meaningful detail on recipients, body, importance, mailboxScope, comment, or sourceMessageId. The one piece of parameter info is redundant with the schema and leaves the remaining parameters unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb and resource: 'Create a ... draft', and it enumerates the possible draft types. The word 'without sending' distinguishes this from any send operation, though it does not explicitly contrast with the sibling mail_update_draft. Still, the core purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage: use this when a draft is needed and should not be sent immediately. However, it provides no explicit guidance on when to prefer a sibling (e.g., mail_update_draft for modifying an existing draft) or when a different mode is required. The 'without sending' phrasing gives context but no exclusions or alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_get_attachmentC
Get bounded attachment metadata/text/base64 content.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | metadata | |
| maxBytes | No | ||
| messageId | Yes | ||
| attachmentId | Yes | ||
| mailboxScope | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided. The description does not disclose side effects, permissions required, rate limits, or the exact output format. It does not explain the meaning of 'bounded' or the maxBytes parameter's effect.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise, using only 5 words, and is well-structured as a single phrase without redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 5 parameters, 2 required, and no output schema, the description is insufficient. It does not explain return types, error handling, or any additional context needed for correct usage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema includes parameters like mode, maxBytes, messageId, attachmentId, and mailboxScope, but the description offers no explanation for any of them. The schema itself provides some structure, but the description adds no semantic detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description 'Get bounded attachment metadata/text/base64 content' clearly indicates the tool retrieves attachment content with a verb and resource. It distinguishes from sibling tools like mail_list_attachments. However, the term 'bounded' is vague and could be misinterpreted.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not specify when to use this tool versus alternatives. It does not mention conditions or exclusions, leaving usage guidelines unclear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_get_messageB
Read one message by Graph ID, immutable ID, or internetMessageId with an explicit body mode.
| Name | Required | Description | Default |
|---|---|---|---|
| locator | Yes | Message identifier returned by search, or an internetMessageId. | |
| bodyMode | No | Message content to return: none omits body and preview, preview returns bodyPreview, and text/html returns body in the requested format. | preview |
| mailboxScope | No | ||
| includeHeaders | No | Include internetMessageHeaders in addition to the selected body content. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must convey behavioral traits. It states 'read', which implies a non-mutating operation, but does not disclose authentication requirements, potential rate limits, error behavior, or that it returns only one message. The body mode does hint at output content, but the description is too terse to fully disclose behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, efficient sentence that leads with the action and essential qualifiers. It is front-loaded and contains no filler. It could be improved by adding usage context, but as a concise statement of purpose it is well-structured and easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with four parameters, three identifier types, and a body-mode option, the description is too sparse. It does not explain the different locator kinds (Graph ID, immutable ID, internetMessageId) or when to use each, nor does it mention the mailboxScope parameter. Without an output schema, it should at least state that the response includes the requested body or headers, but it only hints with 'body mode'. An agent would need to rely heavily on the schema to understand the full behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers 75% of parameters with descriptions (locator, bodyMode, includeHeaders). The description adds minimal value—it mentions 'explicit body mode' but does not elaborate beyond the schema. Since coverage is high, the baseline of 3 is appropriate; the description does not compensate for the uncovered mailboxScope parameter, but that is not its role given the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is precise: it names the verb 'read', the resource 'one message', and specifies three identifier types plus an explicit body mode. This clearly distinguishes it from sibling tools like mail_search_messages or mail_get_thread, which handle different operations. There is no ambiguity about what the tool accomplishes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance on when to use this tool versus alternatives. It does not mention that it is for retrieving a single message by ID, that it should be used after a search, or that get_thread is for a whole conversation. The schema's locator description hints at IDs from search, but the description itself offers no such context or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_get_threadA
Reconstruct a bounded mailbox-local conversation with complete per-message headers and the requested body mode. Embedded forwarded chains are not separate messages.
| Name | Required | Description | Default |
|---|---|---|---|
| seed | Yes | A seed message used to resolve the mailbox-local conversationId. | |
| bodyMode | No | Message content to return: none omits body and preview, preview returns bodyPreview, and text/html returns body in the requested format. | preview |
| folderIds | No | Optional Graph folder IDs that bound thread reconstruction. | |
| maxMessages | No | Maximum messages reconstructed from the mailbox-local conversation. | |
| mailboxScope | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does a good job in parts: 'Reconstruct' signals a non-mutating, read-style operation, and the statement 'Embedded forwarded chains are not separate messages' surfaces a genuinely surprising behavior. It stops short of details like errors or response envelope, but the core behavioral trait is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences, with the central operation and scope front-loaded and the important exception stated after. Every clause contributes new operational information rather than restating the tool name or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description together with the very detailed input schema provides enough to select and configure the tool: the required seed, optional folder and message bounds, and body modes are all recoverable from the schema, while the prose covers the returned content concept ('headers', 'body mode'). Without an output schema or annotations, one could still appreciate the thread-level return and non-mutating nature, though response shape details are left open.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents 80% of the parameters with meaningful descriptions, so the description need not repeat them. The prose only echoes the concepts behind bodyMode and bounded reconstruction without adding new semantic details about seed, mailboxScope, or maxMessages beyond what the schema gives.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource ('mailbox-local conversation') and a clear operation ('Reconstruct'), and it goes beyond a tautology by specifying 'complete per-message headers' and the 'requested body mode'. The clause about embedded forwarded chains not being separate messages distinguishes this threaded behavior from a simple message fetch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The wording implies this is the tool to use when a conversation-level reconstruction is needed, and the sibling mail_get_message reinforces that contrast. However, there is no explicit statement of when to prefer this over mail_get_message or when not to use it, so the guidance remains mostly implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_list_attachmentsC
List metadata for a message attachments collection.
| Name | Required | Description | Default |
|---|---|---|---|
| messageId | Yes | ||
| mailboxScope | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations available, the description carries the full burden of behavioral disclosure. It only implies a read operation via 'List' and 'metadata', but does not disclose return format, pagination, ordering, size limits, or authentication needs. It does not contradict any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence with no filler. It is concise and readable, though the brevity sacrifices useful context. For what is actually written, the structure is clean.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema and no annotation metadata, so an agent does not know what fields the metadata list contains or how the results are scoped or paginated. The required messageId is evident from the schema, but a list tool needs more context about its return shape and behavior to be fully callable without guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description needed to compensate for the parameters, but it mentions neither messageId nor mailboxScope. The input schema itself documents the parameters well, but the description adds no meaning beyond that structured data.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear action ('List'), resource ('attachments collection'), and the nature of the output ('metadata'), which distinguishes it from the sibling mail_get_attachment. It does not explicitly say the scope is a single message, but the required messageId parameter makes that clear enough.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided about when to use this tool instead of siblings such as mail_get_attachment or mail_get_message. There are no stated prerequisites, exclusions, or conditions that would help an agent choose between tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_list_foldersD
List folders in the selected mailbox scope.
| Name | Required | Description | Default |
|---|---|---|---|
| cursor | No | ||
| pageSize | No | ||
| mailboxScope | No | ||
| includeHidden | No | ||
| parentFolderId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for disclosing behavior. It only states the listing action and omits any information about pagination, hidden folders, required permissions, or effects. There is no mention of what happens when optional parameters are omitted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very short, which is good for conciseness, but it is under-specified rather than efficiently detailed. It conveys no additional information beyond the tool name and a slightly expanded phrase, so it fails to earn its place through useful content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has five optional parameters, no output schema, and no annotations, the description is far too minimal. It does not explain return format, pagination behavior, or the meaning of mailboxScope variants. An agent cannot reliably invoke this tool correctly with just this description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, meaning the schema itself provides no parameter descriptions. The description adds no meaning to any of the five parameters (cursor, pageSize, mailboxScope, includeHidden, parentFolderId). The agent is left without any guidance on parameter format, purpose, or relationships.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb and resource ('List folders'), but 'selected mailbox scope' is vague and does not clarify what scopes exist or how they are chosen. It does not distinguish this tool from siblings like mail_list_attachments, but it does identify the primary action and target.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given on when to use this tool versus alternatives. There is no mention of prerequisites, typical use cases, or conditions that would route an agent to a sibling tool like mail_search_messages or mail_get_thread.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_search_messagesA
Search messages safely. Prefer subject for a known title; query is full-text and may match message bodies.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | auto selects safe KQL search for text and safe OData filter for structured-only criteria. | auto |
| query | No | Quoted full-text phrase that may match subject, body, or indexed message content. Use subject instead when the subject is known. | |
| cursor | No | Opaque signed cursor returned by a previous call. | |
| sentUtc | No | Inclusive UTC sent-time bounds. | |
| subject | No | One or more subject phrases. Prefer this over query when locating a known email title. | |
| bodyMode | No | Message content to return: none omits body and preview, preview returns bodyPreview, and text/html returns body in the requested format. | preview |
| pageSize | No | Microsoft Graph page size. | |
| folderIds | No | Optional Graph folder IDs that bound the search. | |
| maxResults | No | Maximum messages returned across fetched pages. | |
| receivedUtc | No | Inclusive UTC received-time bounds. | |
| mailboxScope | No | ||
| participants | No | Sender or recipient email-address filters. | |
| conversationId | No | Mailbox-local conversation ID, normally used internally for thread reconstruction. | |
| hasAttachments | No | Filter by Graph hasAttachments state. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of behavioral disclosure. It mentions 'safely' and explains the difference between query and subject, and it hints at mode selection (safe KQL vs OData) but does not disclose details about rate limits, authentication needs, or potential side effects. It doesn't describe what happens on errors or how results are paginated. This is a moderate gap, so a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise with two sentences, front-loading the core purpose ('Search messages safely') and then immediately providing practical guidance on parameter choice. There is no wasted wording, and it efficiently covers the most important usage hint. It earns a 4 for clarity and minimalism, though it could be slightly more structured with explicit sections.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (14 parameters, no output schema, no annotations), the description could be more comprehensive. However, the schema itself is rich with descriptions, and the description covers the key decision point (subject vs query). It does not explain return format or error behavior, but since the schema is detailed, the description is adequate for an agent to call it correctly. A 4 is fitting.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is high at 93%, so the baseline is 3. The description adds some value by clarifying that subject should be preferred over query for known titles, which is a semantic nuance beyond the schema. It does not detail the exact behavior of mode, cursor, or other parameters, but the schema descriptions are already comprehensive. Thus, a 3 is justified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Search messages safely.' It specifies the verb 'search' and the resource 'messages', and it distinguishes between subject and query usage, differentiating it from sibling tools like mail_get_message or mail_list_folders. The emphasis on safety and the guidance on when to use subject vs query make the tool's function unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: it says to prefer subject for a known title and warns that query is full-text and may match message bodies. This helps an agent choose between parameters. However, it does not explicitly mention alternatives among siblings (e.g., when to use mail_search_messages vs mail_get_message), though the distinction is implied. There is no explicit 'use this when' or 'avoid this if' statement, but the guidance is strong enough for a 4.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_update_draftB
Update an MCP-owned draft after registry and Graph preflight.
| Name | Required | Description | Default |
|---|---|---|---|
| patch | Yes | ||
| draftId | Yes | ||
| expectedEtag | No | ||
| mailboxScope | No | ||
| expectedChangeKey | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are absent, so the description must carry the behavioral burden. It mentions a preflight step implying some safety checks, but doesn't state whether the update requires draft ownership, whether it's reversible, or what the response is. For a mutation tool, this is lacking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, which is concise, but it's under-specified. It refers to 'registry and Graph preflight' that isn't elaborated, adding jargon without clarity, so it trades completeness for brevity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (nested objects, 5 params, no output schema, no annotations), the description is insufficient. It doesn't explain the preflight conditions, the effect of expectedEtag, or what happens on conflict. An agent needs more to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensateason. It doesn't explain the semantics of 'patch' or 'expectedEtag' or what the preflight checks. The description adds no information about parameters beyond the schema, leaving the agent to infer meaning from types and names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the specific verb 'update' and resource 'an MCP-owned draft', distinguishing it from create draft and other mail tools. It also mentions a preflight step, but the phrase 'after registry and Graph preflight' is somewhat vague and doesn't fully clarify what the preflight entails.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies a workflow where preflight passes before updating, but doesn't give explicit when-to-use or when-not-to-use guidance. It doesn't compare to siblings like mail_create_draft or mail_get_message, so an agent has to infer usage from the name and context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v0.1.0- First observed
mail_auth_status - First observed
mail_create_draft - First observed
mail_get_attachment - First observed
mail_get_message - First observed
mail_get_thread - First observed
mail_list_attachments - First observed
mail_list_folders - First observed
mail_search_messages - First observed
mail_update_draft
TDQS
Scored across 9 tools
Most tools are clearly distinct by resource and action: folders, auth, messages, threads, attachments, drafts. There is slight potential confusion between mail_get_message and mail_get_thread (both retrieve messages), but descriptions clarify the difference (single message vs conversation). mail_list_attachments and mail_get_attachment are distinct (list vs get). Overall, boundaries are clear.
All tools follow the exact pattern mail_<verb>_<noun>, using snake_case throughout. Verbs are consistent: list, auth, search, get, create, update. This is a highly predictable and consistent naming convention.
Nine tools is well within the ideal range (3-15). Each tool serves a distinct purpose in the email domain: folder navigation, auth, search, retrieval, thread handling, attachments, and draft management. No redundant or trivial tools.
The tool set covers core email operations: listing folders, searching/reading messages, handling threads, attachments, and creating/updating drafts. A notable missing operation is sending or deleting messages/drafts, which would be expected for a full lifecycle. However, the focus on drafts (without send) may be intentional. Thus, a minor gap.
Maintenance
Related MCP Connectors
Read email/chat conversations, messages, contacts and teams; draft, send and update threads.
Manage Microsoft 365 email, calendar, contacts and inbox rules via the Graph API with OAuth 2.0.
Manage Gmail end-to-end: search, read, send, draft, label, and organize threads. Automate workflow…
Permissioned access to Outlook, OneDrive and Teams via the user's own Microsoft account
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables to manage your Outlook mailbox via Microsoft Graph (write access) with delegated permissions, providing MCP tools to create, modify, and send emails, folders, categories, rules, and automatic replies.-
- AlicenseAqualityBmaintenanceEnables reading and searching personal Outlook/Hotmail mail, filing into folders, deleting, and unsubscribing from newsletters via Microsoft Graph.911 npmMIT
- AlicenseAqualityCmaintenanceGives an AI assistant read- and draft-only access to a single personal Microsoft mailbox, letting it search, list and read mail, browse folders, save attachments to a jailed directory, and create new or reply drafts. It requests no send scope, so mail can never be sent, deleted, moved or marked, and calendar, contacts and files are untouched.6MIT
- AlicenseBqualityBmaintenanceEnables AI agents to access Microsoft Outlook mail and calendar, plus read-only Microsoft Teams message history, acting as the signed-in user via delegated permissions.50331 PyPI1MIT