Skip to main content
Glama
wesleyclarkgit

Outlook MCP Server

README.md
# Outlook MCP Server

A Model Context Protocol (MCP) server that gives AI assistants (Claude, Cursor, etc.) full access to your Microsoft Outlook — emails, calendar, and contacts — via the Microsoft Graph API.

## Features

| Category | Operations |
|---|---|
| **Email** | List, read, search, send, reply, reply-all, forward, create draft, send draft, delete, move, mark read/unread, flag |
| **Mail Folders** | List folders, create folder |
| **Calendar** | List calendars, list events (with date range), get event, search, create, update, delete, accept/decline/tentative |
| **Contacts** | List, get, search, create, update, delete, list folders |
| **Auth** | Device code flow with persistent token cache, sign out |

---

## Prerequisites

- Node.js 18+ (tested with v22)
- A Microsoft 365 or Outlook account
- An Azure AD (Entra ID) app registration (free, takes ~5 minutes)

---

## Step 1: Register an Azure AD Application

1. Go to the [Azure Portal](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**

2. Fill in:
   - **Name**: `Outlook MCP` (or any name)
   - **Supported account types**: Choose based on your setup:
     - *Personal Microsoft accounts only* → for @outlook.com / @hotmail.com
     - *Accounts in any organizational directory and personal accounts* → for work + personal
   - **Redirect URI**: Leave blank (we use device code flow)

3. Click **Register**. Copy the **Application (client) ID** — this is your `OUTLOOK_CLIENT_ID`.

4. Copy the **Directory (tenant) ID** if you're using a work/school account — this is your `OUTLOOK_TENANT_ID`. For personal accounts use `consumers`.

5. Go to **API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions**. Add:
   - `Mail.ReadWrite`
   - `Mail.Send`
   - `Calendars.ReadWrite`
   - `Contacts.ReadWrite`
   - `MailboxSettings.ReadWrite`
   - `User.Read`
   - `offline_access`

6. Click **Grant admin consent** (if you're an admin) or ask your admin to do so.

7. Go to **Authentication** → Enable **Allow public client flows** → Yes → Save.

---

## Step 2: Build the Server

```bash
cd outlook-mcp
npm install
npm run build
```

---

## Step 3: Configure in Cursor (or Claude Desktop)

### Cursor

Add to your Cursor MCP config (`~/.cursor/mcp.json` or via Settings → MCP):

```json
{
  "mcpServers": {
    "outlook": {
      "command": "node",
      "args": ["/absolute/path/to/outlook-mcp/dist/index.js"],
      "env": {
        "OUTLOOK_CLIENT_ID": "YOUR_CLIENT_ID_HERE",
        "OUTLOOK_TENANT_ID": "common"
      }
    }
  }
}
```

> Use `"common"` for multi-tenant, `"consumers"` for personal accounts (@outlook.com / @hotmail.com), or your specific tenant ID for work/school accounts.

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "outlook": {
      "command": "node",
      "args": ["/absolute/path/to/outlook-mcp/dist/index.js"],
      "env": {
        "OUTLOOK_CLIENT_ID": "YOUR_CLIENT_ID_HERE",
        "OUTLOOK_TENANT_ID": "common"
      }
    }
  }
}
```

---

## Step 4: First-Time Authentication

The first time a tool is called, the server uses **Device Code Flow**:

1. A message will appear in the MCP client (or stderr) like:
   ```
   To sign in, use a web browser to open the page https://microsoft.com/devicelogin
   and enter the code ABCD1234 to authenticate.
   ```
2. Open that URL in your browser, enter the code, and sign in with your Microsoft account.
3. The token is cached at `~/.outlook-mcp-token-cache.json` (permissions 600) and refreshed automatically.
4. You won't need to authenticate again until the refresh token expires (~90 days of inactivity).

---

## Available Tools

### Auth
| Tool | Description |
|---|---|
| `outlook_get_profile` | Get your account name, email, and mailbox settings |
| `outlook_sign_out` | Clear cached tokens |

### Email
| Tool | Key Parameters |
|---|---|
| `email_list` | `folder`, `top`, `skip`, `filter`, `orderBy` |
| `email_get` | `messageId` |
| `email_search` | `query` (KQL), `top` |
| `email_send` | `to[]`, `subject`, `body`, `cc[]`, `bcc[]`, `importance` |
| `email_reply` | `messageId`, `comment`, `replyAll` |
| `email_forward` | `messageId`, `to[]`, `comment` |
| `email_create_draft` | `to[]`, `subject`, `body`, `cc[]`, `bcc[]` |
| `email_send_draft` | `messageId` |
| `email_delete` | `messageId` |
| `email_move` | `messageId`, `destinationFolderId` |
| `email_mark_read` | `messageId`, `isRead` |
| `email_flag` | `messageId`, `flagStatus` (flagged/complete/notFlagged) |
| `email_list_folders` | — |
| `email_create_folder` | `displayName`, `parentFolderId` |

### Calendar
| Tool | Key Parameters |
|---|---|
| `calendar_list_calendars` | — |
| `calendar_list_events` | `calendarId`, `startDateTime`, `endDateTime`, `top` |
| `calendar_get_event` | `eventId` |
| `calendar_search_events` | `query`, `top` |
| `calendar_create_event` | `subject`, `startDateTime`, `endDateTime`, `timeZone`, `attendees[]`, `isOnlineMeeting` |
| `calendar_update_event` | `eventId`, + any fields |
| `calendar_delete_event` | `eventId` |
| `calendar_accept_event` | `eventId`, `sendResponse`, `comment` |
| `calendar_decline_event` | `eventId`, `sendResponse`, `comment` |
| `calendar_tentative_event` | `eventId`, `sendResponse`, `comment` |

### Contacts
| Tool | Key Parameters |
|---|---|
| `contacts_list` | `top`, `skip`, `filter`, `orderBy` |
| `contacts_get` | `contactId` |
| `contacts_search` | `query` |
| `contacts_create` | `displayName`, `emailAddresses[]`, `businessPhones[]`, etc. |
| `contacts_update` | `contactId`, + any fields |
| `contacts_delete` | `contactId` |
| `contacts_list_folders` | — |

---

## Development

```bash
# Run directly with tsx (no build step)
OUTLOOK_CLIENT_ID=xxx npm run dev

# Rebuild after changes
npm run build
```

---

## Troubleshooting

**"OUTLOOK_CLIENT_ID environment variable is required"**
→ Make sure you've set the env var in your MCP config.

**"AADSTS50011: The redirect URI ... was not found"**
→ Enable *Allow public client flows* in Azure Portal → App Registration → Authentication.

**"Insufficient privileges"**
→ Add the missing Graph API permissions and re-grant admin consent. Then run `outlook_sign_out` and re-authenticate.

**Token keeps expiring**
→ Make sure `offline_access` is in your API permissions.