Metadata-Version: 2.4
Name: mcp-outlook-personal
Version: 0.1.1
Summary: MCP server for a personal Outlook.com mailbox and calendar via Microsoft Graph
Author: Salah Awad
License: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp>=1.2.0
Requires-Dist: msal>=1.28.0
Requires-Dist: pydantic>=2.6.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

<!-- mcp-name: io.github.salahawad/outlook-personal-mcp -->
# outlook-personal-mcp

An MCP server that gives Claude Code and Codex full control of a personal Outlook.com mailbox and calendar via the Microsoft Graph API. Written in Python, speaks the MCP stdio transport, and uses per-user device-code OAuth so your credentials never leave your machine. MIT licensed.

---

## Features

- **Mail** — list, search, read, send, reply, forward, move, copy, flag, mark read/unread, delete (soft or hard)
- **Drafts** — create, update, attach local files, send
- **Folders** — list, create, rename, delete
- **Calendar** — list calendars, list/search/get/create/update/delete events, respond to invites (accept/decline/tentative), check free/busy availability
- **Per-user OAuth** — you register your own free Azure app; the server authenticates with your Microsoft account and caches the token locally
- **Local stdio** — runs as a child process of the MCP host; your mailbox data never transits a third party

---

## Prerequisites

- Python 3.10 or later
- [`uv`](https://docs.astral.sh/uv/) (fast Python package and tool runner) — **required at runtime**: the server is launched via `uvx`, and it is not bundled by your MCP host (including Claude Desktop). Install it with `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux) or `powershell -c "irm https://astral.sh/uv/install.ps1 | iex"` (Windows).
- A personal Microsoft account (Outlook.com, Hotmail, Live, etc.)
- A free Azure app registration (see below — takes about three minutes)

---

## Azure App Registration

1. Go to [https://portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**.
2. Name it anything you like (e.g. `outlook-personal-mcp`). Under **Supported account types** choose **Personal Microsoft accounts only**. No redirect URI is needed. Click **Register**.
3. Open the app → **Authentication** → **Advanced settings** → **Allow public client flows** → set to **Yes** → **Save**. (This is required for the device-code login flow used by this server.)
4. Go to **API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions**, then add:
   - `Mail.ReadWrite`
   - `Mail.Send`
   - `Calendars.ReadWrite`

   (`User.Read` is included by default; `offline_access` is requested automatically at runtime — you do not need to add it.)
5. Copy the **Application (client) ID** from the **Overview** page. This is your `OUTLOOK_MCP_CLIENT_ID`.

### Alternatively: create the app with the Azure CLI

If you have the [`az` CLI](https://learn.microsoft.com/cli/azure/install-azure-cli), the whole registration is one command:

```bash
az login --use-device-code --allow-no-subscriptions   # sign in with your personal account

az ad app create \
  --display-name "outlook-personal-mcp" \
  --sign-in-audience PersonalMicrosoftAccount \
  --is-fallback-public-client true \
  --required-resource-accesses '[{"resourceAppId":"00000003-0000-0000-c000-000000000000","resourceAccess":[{"id":"024d486e-b451-40bb-833d-3e66d98c5c73","type":"Scope"},{"id":"e383f46e-2787-4529-855e-0e479a3ffac0","type":"Scope"},{"id":"1ec239c2-d7c9-4623-a91a-a9775856bb36","type":"Scope"}]}]' \
  --query appId -o tsv
```

The printed `appId` is your `OUTLOOK_MCP_CLIENT_ID`. (The GUIDs are the Microsoft Graph delegated scopes `Mail.ReadWrite`, `Mail.Send`, and `Calendars.ReadWrite`.) **Note:** a brand-new app registration takes a few minutes to propagate to Microsoft's consumer login endpoint — if your first login fails with `AADSTS700016` ("application … not found"), wait a few minutes and retry.

---

## Install & First-Time Login

Run the one-time interactive login. It prints a short URL and a code; open the URL in any browser, enter the code, approve the permissions, and you are done. The token is cached at `~/.config/outlook-personal-mcp/token_cache.bin` (mode 600) and refreshed automatically on subsequent runs — you will not be prompted again unless the refresh token expires or is revoked.

```bash
OUTLOOK_MCP_CLIENT_ID=<your-app-client-id> uvx mcp-outlook-personal login
```

> To run the latest unreleased code from source instead of the PyPI release, swap `mcp-outlook-personal` for `--from git+https://github.com/salahawad/outlook-personal-mcp mcp-outlook-personal`.

---

## Configure Claude Code

Add the server to your project's `.mcp.json` (or `~/.claude/.mcp.json` for all projects):

```json
{
  "mcpServers": {
    "outlook": {
      "command": "uvx",
      "args": ["mcp-outlook-personal"],
      "env": { "OUTLOOK_MCP_CLIENT_ID": "<your-app-client-id>" }
    }
  }
}
```

Alternatively, use the CLI: `claude mcp add`.

---

## Configure Codex

Add the server to `~/.codex/config.toml`:

```toml
[mcp_servers.outlook]
command = "uvx"
args = ["mcp-outlook-personal"]
env = { OUTLOOK_MCP_CLIENT_ID = "<your-app-client-id>" }
```

---

## Install as a Claude Desktop extension (.mcpb)

This server is also packaged as a [Claude Desktop extension](https://www.anthropic.com/engineering/desktop-extensions) (`.mcpb`). Build the bundle from a checkout:

```bash
npx @anthropic-ai/mcpb pack . dist/mcp-outlook-personal.mcpb
```

Then in Claude Desktop open **Settings → Extensions**, install the `dist/mcp-outlook-personal.mcpb` file, and enter your **Azure App Client ID** (plus any optional settings) when prompted.

> **Requirement:** the extension launches the server with `uvx mcp-outlook-personal`, so [`uv`](https://docs.astral.sh/uv/) must be installed and on your `PATH` — Claude Desktop does not bundle it (see [Prerequisites](#prerequisites)). Codex and other stdio hosts use the config above instead of the `.mcpb`.

---

## Configuration (Environment Variables)

| Variable | Required | Default | Description |
|---|---|---|---|
| `OUTLOOK_MCP_CLIENT_ID` | **Yes** | — | Azure app's Application (client) ID |
| `OUTLOOK_MCP_AUTHORITY` | No | `https://login.microsoftonline.com/consumers` | MSAL authority URL (change only if you move to a work/school tenant) |
| `OUTLOOK_MCP_TOKEN_CACHE` | No | `~/.config/outlook-personal-mcp/token_cache.bin` | Path to the MSAL token cache file |
| `OUTLOOK_MCP_FILE_ROOT` | No | `~/.local/share/outlook-personal-mcp/files` | Only files under this directory can be read by `add_attachment` or written by `download_attachment` |
| `OUTLOOK_MCP_MAX_FILE_BYTES` | No | `3145728` | Maximum bytes allowed for local attachment reads and attachment downloads |
| `OUTLOOK_MCP_ALLOW_PERMANENT_DELETE` | No | `false` | Set to `true` to enable the `permanent_delete` tool (irreversible — see below) |
| `OUTLOOK_MCP_DEBUG` | No | `false` | Set to `true` to log each Graph request's method, URL, and HTTP status code to stderr. Never logs tokens or message content. |

---

## Tools

### Account

| Tool | Description |
|---|---|
| `whoami` | Return the signed-in user's Microsoft account profile |

### Mail

| Tool | Description |
|---|---|
| `list_messages` | List messages (newest first); optionally filter by folder or unread-only |
| `search_messages` | Full-text search across the entire mailbox (Graph `$search`) |
| `get_message` | Get a single message; optionally include the full body |
| `list_attachments` | List a message's attachments (id, name, size, content type) |
| `download_attachment` | Download an attachment to a path under `OUTLOOK_MCP_FILE_ROOT` (refuses to overwrite an existing file) |
| `send_mail` | Send an email |
| `reply` | Reply to a message (`reply_all` to reply to everyone) |
| `forward` | Forward a message to recipients with an optional comment |
| `move_message` | Move a message to another folder |
| `copy_message` | Copy a message to another folder |
| `mark_read` | Mark a message read or unread |
| `flag_message` | Flag or unflag a message |
| `delete_message` | Delete a message (moves it to Deleted Items; reversible) |
| `permanent_delete` | **Permanently** delete a message (irreversible). Only available when `OUTLOOK_MCP_ALLOW_PERMANENT_DELETE=true` |

### Folders

| Tool | Description |
|---|---|
| `list_folders` | List mail folders with unread and total message counts |
| `create_folder` | Create a mail folder, optionally nested under a parent |
| `rename_folder` | Rename a mail folder |
| `delete_folder` | Delete a mail folder (moves it to Deleted Items) |

### Drafts

| Tool | Description |
|---|---|
| `create_draft` | Create a draft message (not sent) |
| `update_draft` | Update a draft's subject and/or body |
| `add_attachment` | Attach a local file (a regular, non-symlink file under `OUTLOOK_MCP_FILE_ROOT`) to a draft |
| `send_draft` | Send an existing draft |

### Calendar

| Tool | Description |
|---|---|
| `list_calendars` | List the user's calendars |
| `list_events` | List events; if `start`/`end` (ISO 8601) are given, returns that time window |
| `search_events` | Search events by free text |
| `get_event` | Get one event including body, attendees, and online meeting link |
| `create_event` | Create a calendar event with optional attendees and online meeting |
| `update_event` | Update fields on an existing event (only provided fields change) |
| `delete_event` | Delete/cancel a calendar event |
| `respond_event` | Respond to a meeting invite: `accept`, `decline`, or `tentative` |
| `find_availability` | Get free/busy availability for a list of people over a time window |

---

## Permanent Delete

The `permanent_delete` tool bypasses the Deleted Items folder and removes a message irreversibly. It is **disabled by default** — when `OUTLOOK_MCP_ALLOW_PERMANENT_DELETE` is not set (or is `false`), the tool is not registered with the MCP server at all and will not appear in the tool list.

To enable it, set `OUTLOOK_MCP_ALLOW_PERMANENT_DELETE=true` in the server's environment block in your `.mcp.json` / `config.toml`. Only do this if you understand the consequences: there is no undo and no Recoverable Items path for personal accounts.

---

## Security

- **Token cache is a credential.** The file at `~/.config/outlook-personal-mcp/token_cache.bin` contains a long-lived refresh token. It is written with mode `600`, but treat it like a password — never commit it, never share it, and store it on an encrypted volume.
- **Data stays local.** The server runs as a child process of Claude Code / Codex over stdio. Your mailbox content is passed directly between the MCP host and the Microsoft Graph API; no third-party relay is involved.
- **Revocation.** To revoke access, delete the token cache file and/or navigate to [https://account.microsoft.com/permissions](https://account.microsoft.com/permissions) to remove the Azure app's consent. You can also delete the Azure app registration entirely from the portal.
- **File paths.** `download_attachment` writes only under `OUTLOOK_MCP_FILE_ROOT` and refuses to overwrite existing files. `add_attachment` reads only regular, non-symlink files under `OUTLOOK_MCP_FILE_ROOT`. Relative paths are resolved under that root; absolute paths outside it (and any path traversing a symlink) are rejected. Both tools enforce `OUTLOOK_MCP_MAX_FILE_BYTES`. Review these paths before confirming any tool call that touches the filesystem.

---

## Development

```bash
git clone https://github.com/salahawad/outlook-personal-mcp
cd outlook-personal-mcp
uv venv && uv pip install -e ".[dev]"
uv run pytest
uv run ruff check .
```

---

## Privacy Policy

This server runs entirely on your machine and sends data only between your machine and Microsoft's Graph API — no third-party relay, no telemetry, and the maintainer receives nothing. OAuth tokens are cached locally at `~/.config/outlook-personal-mcp/token_cache.bin` (mode 600). See [PRIVACY.md](PRIVACY.md) for the full privacy policy.

---

## License

MIT — see [LICENSE](LICENSE).
