Office 365 Email MCP
by cybrlou
README.md
# Office 365 Email MCP
An [MCP](https://modelcontextprotocol.io) server that lets an AI agent **send email through Microsoft 365 / Office 365** using the OAuth2 **client-credentials (app-only)** flow and the **Microsoft Graph** API.
No interactive login at runtime — register an app once, grant it `Mail.Send`, and the server sends mail headlessly. Ideal for automation, notifications, and agent workflows.
## Features
- `office365_send_email` — send plain-text or HTML email, with CC/BCC and local file attachments.
- `office365_test_connection` — verify your OAuth2 config by acquiring a token (sends nothing).
- App-only auth (no user sign-in), tokens cached and refreshed automatically by MSAL.
- Clear, actionable error messages for the common auth/permission/mailbox mistakes.
## How it works
```
agent → MCP tool → MSAL (client credentials) → Entra ID → access token
→ POST https://graph.microsoft.com/v1.0/users/{sender}/sendMail
```
## Prerequisites
- Python 3.10+
- A Microsoft 365 tenant where you can register an app (or an admin who can).
- A licensed mailbox to send from.
## 1. Register the app in Microsoft Entra ID
1. Go to **[Entra admin center](https://entra.microsoft.com)** → **Identity → Applications → App registrations → New registration**.
2. Name it (e.g. `office365-email-mcp`), leave redirect URI blank, **Register**.
3. Copy the **Application (client) ID** and **Directory (tenant) ID** from the Overview page.
4. **Certificates & secrets → New client secret** → copy the secret **Value** (shown once).
5. **API permissions → Add a permission → Microsoft Graph → Application permissions → `Mail.Send`** → **Add**.
6. Click **Grant admin consent** for your tenant. (Requires an admin; the green check must appear.)
> **Scope it down (recommended).** `Mail.Send` (Application) lets the app send as *any* mailbox in the tenant. Restrict it to specific mailboxes with an [Application Access Policy](https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access) in Exchange Online:
>
> ```powershell
> New-ApplicationAccessPolicy -AppId <CLIENT_ID> `
> -PolicyScopeGroupId mcp-senders@yourdomain.com `
> -AccessRight RestrictAccess `
> -Description "Restrict office365-email-mcp to the mcp-senders group"
> ```
## 2. Install
```bash
# from a clone of this repo
pip install .
# or run without installing, using uv
uvx --from . office365-email-mcp
```
## 3. Configure
Set these environment variables (see [.env.example](.env.example)):
| Variable | Required | Description |
|---|---|---|
| `O365_TENANT_ID` | yes | Directory (tenant) ID |
| `O365_CLIENT_ID` | yes | Application (client) ID |
| `O365_CLIENT_SECRET` | yes | Client secret **value** |
| `O365_SENDER` | no | Default sender mailbox (overridable per call) |
## 4. Add to your MCP client
**Claude Desktop / Claude Code** (`claude_desktop_config.json` or `.mcp.json`):
```json
{
"mcpServers": {
"office365-email": {
"command": "office365-email-mcp",
"env": {
"O365_TENANT_ID": "00000000-0000-0000-0000-000000000000",
"O365_CLIENT_ID": "00000000-0000-0000-0000-000000000000",
"O365_CLIENT_SECRET": "your-secret-value",
"O365_SENDER": "noreply@yourdomain.com"
}
}
}
}
```
If you didn't `pip install`, use `"command": "uvx"` with `"args": ["--from", "/path/to/repo", "office365-email-mcp"]`.
## Tools
### `office365_send_email`
| Parameter | Type | Required | Notes |
|---|---|---|---|
| `to` | string[] | yes | Primary recipients |
| `subject` | string | yes | |
| `body` | string | yes | Plain text or HTML |
| `is_html` | bool | no | Treat `body` as HTML (default false) |
| `cc` / `bcc` | string[] | no | |
| `sender` | string | no | Overrides `O365_SENDER` |
| `attachments` | string[] | no | Local file paths, < ~3 MB total |
| `save_to_sent_items` | bool | no | Default true |
### `office365_test_connection`
No parameters. Acquires a token and reports success/failure — run this first when troubleshooting.
## Troubleshooting
| Error | Fix |
|---|---|
| `401 Authentication failed` | Client secret expired or wrong tenant/client ID. |
| `403 Permission denied` | Missing `Mail.Send` **Application** permission or admin consent not granted. |
| `404 Sender mailbox not found` | `sender` isn't a real, licensed mailbox in the tenant. |
| `Application Access Policy` blocks send | The sender isn't in the allowed group from your access policy. |
## Security notes
- The client secret is a credential — keep it out of source control (`.env` is gitignored). Rotate it periodically.
- Prefer an Application Access Policy so the app can only send as intended mailboxes.
- Attachments over ~3 MB exceed Graph's single-request `sendMail` limit; share a link instead.
## License
[MIT](LICENSE)
TDQS
A4.6/5.0
Scored across 2 tools
Disambiguation5/5
The two tools serve entirely distinct purposes: one sends emails, the other verifies OAuth2 connectivity. There is no overlap or ambiguity.
Naming Consistency5/5
Both tools follow a consistent 'office365_verb_noun' pattern (send_email, test_connection), with no mixing of conventions.
Tool Count4/5
With only two tools, the server is extremely focused on sending emails and testing authentication. While this is appropriate for a minimal integration, it feels slightly under-scoped for a full Office 365 Email MCP.
Completeness2/5
The server only covers sending emails and connection testing, missing essential email operations like reading, searching, or managing folders. This is a significant gap for a service branded as 'Email MCP'.
Maintenance
ActivityInactive
ResponsivenessNo issues