Skip to main content
Glama
acangialosi

outlook-mcp-server

by acangialosi
README.md
# outlook-mcp-server

A local [MCP](https://modelcontextprotocol.io) server that gives Claude
(Desktop or Code) read/write access to a **personal** Hotmail / Outlook.com
mailbox via the Microsoft Graph API, using the OAuth 2.0 authorization code
flow (with PKCE) against the Microsoft identity platform.

It exposes six tools: `list_messages`, `get_message`, `search_messages`,
`send_message`, `create_draft`, and `list_folders`.

Everything runs locally over stdio — there is no hosted service, and your
mail never passes through anything but your machine and Microsoft's own
Graph API.

## How it works

- **Auth**: [MSAL Node](https://www.npmjs.com/package/@azure/msal-node)
  runs an authorization-code + PKCE flow against
  `https://login.microsoftonline.com/consumers` (personal accounts only —
  see [Tenant choice](#tenant-choice)), using a short-lived local HTTP
  server as the redirect target. Tokens (including the `offline_access`
  refresh token) are cached and silently refreshed on future runs.
- **Storage**: the token cache is serialized by MSAL, encrypted with
  AES-256-GCM using a locally-generated key, and written to
  `~/.outlook-mcp-server/token-cache.enc` (mode `0600`). The key itself
  lives in `~/.outlook-mcp-server/cache.key` (also `0600`). See
  [Security notes](#security-notes) for the threat model this does (and
  doesn't) cover.
- **Graph calls**: a thin `fetch`-based client calls
  `https://graph.microsoft.com/v1.0/...` with the current access token.
- **MCP server**: built on `@modelcontextprotocol/sdk`, speaking stdio, so
  it can be launched directly by Claude Desktop / Claude Code as a child
  process.

## Prerequisites

- Node.js 18+
- A Microsoft account (Hotmail, Outlook.com, or Live) — the mailbox you
  want Claude to access.
- A free Azure account to register the app (any Microsoft account can do
  this — it does not need to be a paid Azure subscription).

## 1. Install

```bash
git clone <this repo>
cd outlook-mcp-server
npm install
```

## 2. Register an app in the Azure Portal

This registration is what issues the client ID this server uses to talk to
Microsoft Graph on your behalf. `npm run setup` (below) walks you through
this interactively, but the steps are:

1. Go to [portal.azure.com](https://portal.azure.com) and sign in with any
   Microsoft account.
2. Search for **App registrations** → **+ New registration**.
3. Fill in the form:
   - **Name**: anything, e.g. `outlook-mcp-server`.
   - **Supported account types**: **"Personal Microsoft accounts only"**.
     This is what restricts the app to Hotmail/Outlook.com/Live accounts
     rather than a work/school (Azure AD) tenant.
   - **Redirect URI**: platform **"Public client/native (mobile & desktop)"**,
     value `http://localhost:8765/callback` (or another port — just be
     consistent when the setup script asks).
4. Click **Register**, then copy the **Application (client) ID** from the
   Overview page.
5. Go to **API permissions** → **+ Add a permission** → **Microsoft Graph**
   → **Delegated permissions**, and add:
   - `Mail.Read`
   - `Mail.ReadWrite`
   - `Mail.Send`
   - `offline_access` (often present by default)

   Personal Microsoft account delegated permissions like these don't need
   admin consent — you consent yourself during sign-in in step 3 below.
6. *(Optional, advanced)* If you'd rather use a confidential client with a
   client secret instead of the public-client PKCE flow, add a **Web**
   platform redirect URI and create a secret under **Certificates &
   secrets**. Most people should skip this.

## 3. Run setup (auth + config)

```bash
npm run setup
```

This will:

1. Print the walkthrough above.
2. Prompt for the client ID (and optional secret / tenant / redirect URI),
   and save it to `~/.outlook-mcp-server/config.json`.
3. Open your browser to sign in and consent.
4. Verify the token works by calling `GET /me`, printing your name/email.
5. Print the JSON snippet to add to your Claude config (see below).

To re-authenticate later (revoked token, switching accounts, etc.) without
re-entering the app registration details:

```bash
npm run login
```

## 4. Build and register with Claude

```bash
npm run build
```

**Claude Desktop** — add to `claude_desktop_config.json`
(`~/Library/Application Support/Claude/claude_desktop_config.json` on
macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):

```json
{
  "mcpServers": {
    "outlook": {
      "command": "node",
      "args": ["/absolute/path/to/outlook-mcp-server/dist/src/index.js"]
    }
  }
}
```

**Claude Code**:

```bash
claude mcp add outlook -- node /absolute/path/to/outlook-mcp-server/dist/src/index.js
```

Restart Claude Desktop / Claude Code. The tools below should now be
available.

## Tools

| Tool | Description |
| --- | --- |
| `list_messages` | List messages from a folder (default `inbox`), with `since`/`until` date filters, `unreadOnly`, sorting, and pagination. |
| `get_message` | Fetch the full content (body, all recipients) of one message by ID. |
| `search_messages` | Free-text search (`$search`) across mail, optionally scoped to a folder. |
| `send_message` | Send an email immediately (`to`/`cc`/`bcc`, subject, text or HTML body). |
| `create_draft` | Create a draft in the Drafts folder without sending. |
| `list_folders` | List mail folders and their IDs, for use with the `folder` parameter above. |

All tools return JSON (as MCP text content) and surface Graph API errors as
tool errors rather than crashing the server.

## Tenant choice

By default this uses the `consumers` tenant
(`https://login.microsoftonline.com/consumers`), which **only** accepts
personal Microsoft accounts (Hotmail/Outlook.com/Live) — a work/school
account will be rejected at sign-in. If you need to support both personal
and Azure AD accounts, set the tenant to `common` during `npm run setup`
(or via `OUTLOOK_MCP_TENANT=common`). This project is designed and tested
for the personal-account (`consumers`) case.

## Configuration reference

Everything can be set via `npm run setup` (written to
`~/.outlook-mcp-server/config.json`) or via environment variables, which
take precedence — see `.env.example`:

| Variable | Purpose |
| --- | --- |
| `OUTLOOK_MCP_CLIENT_ID` | Azure app registration's client ID. |
| `OUTLOOK_MCP_CLIENT_SECRET` | Only if using a confidential client (Web platform). |
| `OUTLOOK_MCP_TENANT` | `consumers` (default) or `common`. |
| `OUTLOOK_MCP_REDIRECT_URI` | Must match the Azure app registration. |
| `OUTLOOK_MCP_CONFIG_DIR` | Where config/token cache are stored. Defaults to `~/.outlook-mcp-server`. |

## Security notes

- The token cache is encrypted at rest with a locally-generated AES-256-GCM
  key (`~/.outlook-mcp-server/cache.key`, mode `0600`). This protects
  against casual disclosure — accidental commits, backups, other
  unprivileged users on a shared machine — but **not** against an attacker
  who already has read access to your user account's files, since the key
  sits next to the encrypted cache. For stronger protection, swap the
  `ICachePlugin` in `src/auth/tokenCache.ts` for one backed by your OS
  keychain (e.g. via `keytar`) — the plugin interface is intentionally
  isolated to that one file.
- Never commit `~/.outlook-mcp-server/` (it's outside the repo by default)
  or a `.env` file containing `OUTLOOK_MCP_CLIENT_SECRET`.
- `send_message` sends immediately with no confirmation step inside this
  server — Claude is expected to confirm intent with you before calling it
  for anything sensitive. Prefer `create_draft` when you want a review step.
- Requested scopes are limited to `Mail.Read`, `Mail.ReadWrite`,
  `Mail.Send`, and `offline_access` — no calendar, contacts, or broader
  `Mail.*` application-level access.

## Troubleshooting

- **`AADSTS50020` / "user account ... does not exist in tenant"** — you're
  hitting a tenant that doesn't accept personal accounts, or you're signing
  in with a work/school account against `consumers`. Confirm the app
  registration's "Supported account types" is "Personal Microsoft accounts
  only" and that `OUTLOOK_MCP_TENANT` is `consumers` (or `common` if you
  intentionally want both).
- **`AADSTS50011` / redirect URI mismatch** — the `redirectUri` in
  `~/.outlook-mcp-server/config.json` must exactly match a redirect URI
  configured on the Azure app registration, including the port.
- **"Not signed in"** tool errors — run `npm run login`.
- **Port already in use during setup/login** — another process is using
  the redirect URI's port; stop it, or reconfigure the app registration and
  `npm run setup` with a different port.

## Development

```bash
npm run dev     # run the MCP server directly from TypeScript (stdio)
npm run build   # compile to dist/
npm run clean   # remove dist/
```

## Project structure

```
src/
  index.ts            MCP server entrypoint (stdio transport)
  config.ts            Config loading (env + config file)
  auth/
    crypto.ts           AES-256-GCM file encryption helpers
    tokenCache.ts        MSAL ICachePlugin backed by crypto.ts
    msalClient.ts        MSAL app factory + silent token acquisition
    loginFlow.ts          Interactive loopback OAuth flow
  graph/
    client.ts            Generic Microsoft Graph fetch wrapper
    mail.ts               Mail-specific Graph calls
    types.ts              Graph response types
  tools/                 One file per MCP tool, registered in index.ts
scripts/
  setup.ts              Interactive one-time (and re-runnable) setup
```

TDQS

A4.3/5.0

Scored across 6 tools

Disambiguation4/5

Most tools have clearly distinct purposes: listing, fetching, searching, folder enumeration, sending, and drafting. The only possible confusion is between list_messages and search_messages, but their descriptions clearly differentiate structured filtering from full-text relevance search.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern: list_messages, get_message, search_messages, list_folders, send_message, create_draft. There are no style mixups or vague verbs.

Tool Count5/5

Six tools is a well-scoped set for an Outlook mail server. Each tool covers a distinct core email operation without unnecessary bloat or missing essentials.

Completeness4/5

The tool surface covers the primary mail workflows: listing, searching, reading, sending, drafting, and folder navigation. Some common operations like reply, forward, delete, move, or updating drafts are absent, but the core read/send workflow is solid.

Maintenance

ActivitySlowing
ResponsivenessNo issues