Ten99.Aria.Mcp.Email.O365
1.0.742
dotnet tool install --global Ten99.Aria.Mcp.Email.O365 --version 1.0.742
dotnet new tool-manifest
dotnet tool install --local Ten99.Aria.Mcp.Email.O365 --version 1.0.742
#tool dotnet:?package=Ten99.Aria.Mcp.Email.O365&version=1.0.742
nuke :add-package Ten99.Aria.Mcp.Email.O365 --version 1.0.742
Ten99.Aria.Mcp.Email.O365
ARIA's Microsoft 365 (Outlook / Microsoft Graph) email MCP server. A read/triage surface over a mailbox for Claude Desktop/Code and ARIA agents — the tooling half; triage logic belongs in an agent.
Runs via dnx (packaged as a dotnet tool).
Running it
Requires the .NET 10 SDK; the first run fetches the package from NuGet (--yes accepts the prompt). Configure it with the O365:Auth settings (see Auth below) — here via env vars on the MCP-client entry.
- macOS / Linux:
dotnet dnx Ten99.Aria.Mcp.Email.O365 --yes - Windows:
dnx Ten99.Aria.Mcp.Email.O365 --yes(the standalonednxcommand ships on the PATH with the SDK on Windows; on macOS/Linux usedotnet dnx)
{
"mcpServers": {
"email-o365": {
"command": "dotnet",
"args": ["dnx", "Ten99.Aria.Mcp.Email.O365", "--yes"],
"env": {
"O365__Auth__AuthMode": "interactive",
"O365__Auth__ClientId": "<your-entra-app-client-id>",
"O365__Auth__DefaultMailbox": "you@your-org.com"
}
}
}
}
(On Windows you can set "command": "dnx" and drop the leading "dnx" arg.)
Tools
email_health— connection + mailbox access checkemail_read_mailboxes— list the configured mailboxes and probe each for access (reachable? unread/total + displayName, or the error), plus the active auth mode — so a session knows which mailboxes it can act on up front instead of discovering by failing. Candidates =O365:Auth:DefaultMailbox+ operator-declaredO365:Auth:Mailboxes(Graph has no reliable "shared mailboxes I can access" enumeration, so the estate is operator-declared)email_read_folders— folders with unread/total counts; top-level by default,parentFolderIdto drill down one level,recursive:truefor the whole treeemail_read_list— general list: scope to afolder(well-known name or id) or whole mailbox; filter byisRead/isFlagged/from/subject/date-window/withCategories/excludeCategories/conversationId(walk a thread across all folders incl. Sent);oldestFirstfor backlog order;includeCcto add CC.email_read_unread/email_read_flaggedare sugar over this. Rows are lean by default (drops the long base64 ids —webLink/conversationId/internetMessageId/parentFolderId); passverbose:truefor the full shape, or fetch detail viaemail_read_body. Unread-Junk audit =folder:"junkemail", isRead:false.email_read_delta— folder-scoped incremental sync:changed+removedIdssincedeltaToken, plusnextDeltaToken. First call baselines (page whilehasMore); the caller owns the token (keyed by mailbox+folder). Stale token → transparent resync (resynced=true).email_read_unread— unread mail across all folders, newest first; excludes Deleted Items + Junk by default (includeSpamAndTrashto include them). OptionalfromContains/subjectContains, asince/beforedate window (ISO-8601), andexcludeCategories(comma-separated). Lean list + paging.email_read_flagged— flagged (follow-up) mail; same Junk/Deleted exclusion +since/before+excludeCategories; lean list + pagingemail_read_body— one message in full (metadata incl.ccRecipients+ body) by id;bodyFormattext(default) orhtml. PasspropertySetGuid+propertyNameto read back a named extended property aspropertyValue.email_read_attachments— list a message's attachments (metadata only: id, name, contentType, size, isInline)email_read_attachment— fetch one attachment by id; returns base64 up to the inline cap (O365:Auth:MaxInlineAttachmentBytes, default 30 MB), or passsaveToDirectoryto write it to disk and get back asavedPath(any size)email_edit_categorize— merge categories onto messages (batched; the queryable read-path state)email_edit_property— write a named extended property onto messages (batched; the caller's ledger:propertySetGuid,name,value)email_edit_move— move messages to a folder (batched); returns each message's new id + fromFolderId (the undo record)email_edit_read— set read/unread on messages (batched, both directions)email_delete— soft-delete messages → Deleted Items, 30-day recovery (batched). No hard-delete exists.email_send— send a new outbound email (to/cc/bcc,subject,body,bodyFormat). Named alone so it's gated separately. Mechanism only — the caller owns whether/what to send; the operator can hard-disable withO365:Auth:AllowSend=false. NeedsMail.Send. Attach files two ways (combinable):attachmentPaths(comma/semicolon-separated local file paths, read from disk — the inverse ofemail_read_attachment'ssaveToDirectory) andinlineAttachments(a JSON array[{name, contentType?, contentBase64}]for in-context bytes). Small sends go via one-shotsendMail; larger ones (total > ~3 MB) via a draft + chunked upload session, so files well past the ~4 MBsendMailceiling send fine. Capped byO365:Auth:MaxOutboundAttachmentBytes(default 150 MB, Graph's per-message maximum) and rejected over the cap before the Graph call. The large-file path always keeps a Sent-Items copy (saveToSentItems:falsecan't be honoured there).
Write tools are batched: messageIds is comma-separated and the result is a per-item {success, error} envelope ({count, succeeded, failed, results}), so a partial failure reports which ids failed rather than all-or-nothing.
Tool naming & grouped permissions
Tools are class-segmented so a client can grant by group (Claude Code globs after mcp__<server>__):
email_read_*— reads (no side effects) ·email_edit_*— mutations ·email_delete— destructive (soft-delete only), named alone so it's gated separately ·email_send— outbound send, named alone (also killable viaO365:Auth:AllowSend=false) ·email_health— diagnostic.
allow mcp__email-o365__email_read_* # all reads
allow mcp__email-o365__email_edit_* # all safe writes
ask mcp__email-o365__email_delete # gate the destructive one
ask mcp__email-o365__email_send # gate outbound send
Vocabulary-agnostic
The MCP holds no vocabulary of its own — no ARIA-specific categories or property schema. Categories and the extended property are pure mechanism; the caller (agent) supplies all tokens. Filter/exclude by any category, and write any property. ARIA's own scheme lives in the agent (see the wiki Email Triage: Categorisation & Ledger Convention), so other consumers can use this MCP without inheriting ARIA's conventions.
Mailbox targeting
Every data tool takes an optional mailbox (a UPN), defaulting to O365:Auth:DefaultMailbox when omitted. The credential is the same regardless of mailbox (it's just the /users/{mailbox} segment); reaching another user's or a shared mailbox needs Full Access + Mail.ReadWrite.Shared (in the delegated scope set), so one interactive login covers the signed-in mailbox plus every in-tenant shared mailbox it can access. List results carry the resolved mailbox on the response envelope, plus internetMessageId (stable dedup key across mailbox copies) and parentFolderId per message.
Auth (authMode)
Three modes (credentials come from the shared Ten99.Aria.Identity.EntraId factory):
servicePrincipal(default;clientcredentialaccepted as an alias) — app-only. NeedstenantId,clientId, a credential, anddefaultMailbox(admin-consented app). The credential is either aclientSecretor a client certificate —clientCertThumbprint(CurrentUser/LocalMachineMystore) orclientCertPath(PFX/PKCS#12, or a PEM with cert+key;clientCertPasswordfor an encrypted PFX). A certificate wins over a secret when both are set. Uses application permissions (Mail.ReadWriteapplication role) via/.default.managedIdentity— app-only via Azure Managed Identity, no secret to manage. System-assigned needs onlydefaultMailbox(a silent login from the runtime's identity — noclientId/tenantId/secret); user-assigned adds the identity'sclientId. For unattended hosting on Azure.interactive— desktop browser sign-in as the signed-in user (delegatedMail.ReadWrite), with a persistent token cache so it only prompts once. NeedsclientId(a multi-tenant app) anddefaultMailbox(your UPN) — no secret, and notenantId.defaultMailboxis the sign-in login hint: Azure AD does home-realm discovery on its domain and routes to the right tenant, so the UPN alone selects the correct account/mailbox (that's what makes multi-tenancy work without atenantId). It also keys a per-account token cache, so several instances for different mailboxes run side by side without colliding. Pass atenantIdonly to pin a specific/guest tenant.
Scopes (delegated): Mail.ReadWrite, Mail.ReadWrite.Shared, Mail.Send, and User.Read — all
no admin consent. User.Read reads the signed-in user's own display name via /me; other mailboxes'
display names fall back to the address (resolving those needs the directory scope User.ReadBasic.All,
deferred to the estate name-resolution feature that actually needs it).
Config lives under the O365:Auth section (like the ADO MCP's Ado:Auth). Supply it via:
- env —
O365__Auth__ClientId,O365__Auth__TenantId,O365__Auth__DefaultMailbox,O365__Auth__Mailboxes(estate list foremail_read_mailboxes),O365__Auth__AuthMode(+O365__Auth__ClientSecretorO365__Auth__ClientCertThumbprint/ClientCertPath/ClientCertPasswordfor app-only) - CLI — friendly switches map to those keys:
--clientId,--tenantId,--defaultMailbox,--mailboxes,--authMode,--clientSecret,--clientCertThumbprint,--clientCertPath,--clientCertPassword - user secrets — the
O365:Authsection
Requested scopes (O365__Auth__Scopes)
Interactive requests Mail.ReadWrite, Mail.ReadWrite.Shared, Mail.Send, User.Read by default.
Override with O365__Auth__Scopes (space- or comma-separated; short names or full URLs) to request a
leaner set — useful in restricted client tenants where an admin must consent and a smaller ask is
easier to approve. For triaging only your own mailbox, O365__Auth__Scopes="Mail.ReadWrite User.Read"
drops the shared-mailbox and send permissions. Omit it to keep the full default set.
Secrets are never returned by any tool. BodyPreview on list results is scrubbed of bearer-shaped
secrets (magic/reset links, one-time codes) by default — previews flow to a model on every page. Disable
with O365__Auth__RedactBodyPreview=false (operator toggle; not a per-call, model-controlled flag).
Sending (email_send) is off by default — a deliberately safe default for an irreversible,
outward action. An operator must opt in with O365__Auth__AllowSend=true. Once enabled the MCP stays a
mechanism and does not itself decide whether to send — that policy belongs to the agent; this is the
operator's on/off gate, not a per-call/model-reachable flag.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.742 | 0 | 11/5/2026 |
| 1.0.741 | 0 | 11/5/2026 |
| 1.0.740 | 0 | 11/5/2026 |
| 1.0.739 | 104 | 10/25/2026 |
| 1.0.738 | 101 | 10/25/2026 |
| 1.0.737 | 99 | 10/25/2026 |
| 1.0.736 | 117 | 10/24/2026 |
| 1.0.735 | 99 | 10/24/2026 |
| 1.0.734 | 97 | 10/23/2026 |
| 1.0.733 | 92 | 10/23/2026 |
| 1.0.726 | 152 | 10/20/2026 |
| 1.0.722 | 108 | 10/20/2026 |
| 1.0.721 | 96 | 10/20/2026 |
| 1.0.718 | 98 | 10/16/2026 |
| 1.0.717 | 92 | 10/16/2026 |
| 1.0.716 | 97 | 10/16/2026 |
| 1.0.715 | 89 | 10/16/2026 |
| 1.0.701 | 89 | 10/14/2026 |
| 1.0.700 | 97 | 10/12/2026 |
| 1.0.693 | 108 | 10/6/2026 |