Skip to main content
Glama
uaixo

MCP Server for Multiple Outlook Accounts

by uaixo
README.md
# MCP Server for Multiple Outlook Accounts

A **local, single-connector** [Model Context Protocol](https://modelcontextprotocol.io) server
that lets an AI assistant operate **several Outlook / Microsoft 365 mailboxes at once** โ€”
searching, reading, drafting, sending, and organising mail โ€” through one safety-annotated tool
surface, with OAuth tokens that **never leave your machine**.

This is the **Outlook / Microsoft Graph** provider variant of a provider-neutral specification.

- ๐Ÿ“„ [`doc/business-specification.md`](./doc/business-specification.md) โ€” provider-neutral business + functional spec (the contract).
- ๐Ÿ”Œ [`doc/provider-mapping.md`](./doc/provider-mapping.md) โ€” how neutral requirements bind to Microsoft Graph.
- ๐Ÿ—๏ธ [`doc/architecture.md`](./doc/architecture.md) โ€” how this variant is designed and built.
- โœ… [`doc/traceability-matrix.md`](./doc/traceability-matrix.md) โ€” every requirement โ†’ module โ†’ test.
- ๐Ÿš€ [`doc/ONBOARDING.md`](./doc/ONBOARDING.md) โ€” operator guide: register an Entra app, handle consent, connect a mailbox.
- โœ… [`doc/LIVE-ACCEPTANCE.md`](./doc/LIVE-ACCEPTANCE.md) โ€” live acceptance runbook + `npm run live-smoke` (the remaining work).
- ๐Ÿค [`doc/CONTRIBUTING.md`](./doc/CONTRIBUTING.md) โ€” handoff guide: how we work, workflow, and the per-phase record.
- ๐Ÿ“‹ [`doc/HANDOVER.md`](./doc/HANDOVER.md) โ€” handover snapshot: what's done and the outstanding-work checklist.

---

## Project status: feature-complete (offline build)

Built with **TypeScript 6.0.3**; build, typecheck, tests (179), and format all green. All eight
capabilities are implemented and the offline build is feature-complete. What exists today:

- โœ… Architecture design + requirements traceability matrix (`doc/`).
- โœ… **Auth core:** Entra credential-source discovery, MSAL public-client (consent + silent refresh),
  secure token store (`0600`/`0700`, atomic + cross-process-locked), account-selection registry.
- โœ… **Account-management CLI:** `outlook-mcp-auth connect | list | remove`.
- โœ… **Microsoft Graph client:** thin `fetch` wrapper with a per-request timeout, bounded jittered
  retry (with the no-duplicate-send policy), and actionable error mapping.
- โœ… **Read tools:** `list_accounts` (C1), `search_conversations` (C2), `read_conversation` (C3) โ€” with
  Gmail-style search-operator translation and bounded, truncating output.
- โœ… **Write tools:** `create_draft` (C4) and `send_message` (C5) โ€” recipient parsing
  (`Display Name <addr>`), header-injection stripping, allow-listed/TOCTOU-safe attachments,
  local outgoing-size validation, and reply threading. Small attachments ride inline; large ones
  (> ~3 MB) upload to the draft via a Graph **upload session**, so send becomes
  create-draft โ†’ upload โ†’ send with the final `/send` under the `nonDuplicable` retry policy โ€”
  a retry can never double-deliver.
- โœ… **Organise tools:** `list_labels` (C6), `create_label` (C7), and `organize_mail` (C8) โ€” the
  label-decomposition fan-out that maps one neutral organise request to the right mix of Graph
  category-PATCH / read-state / `move` calls (archive, **trash**, **junk** โ€” mutually exclusive),
  applied per message across a conversation under a bounded concurrency limit, reporting the union
  of resulting labels. `list_labels` enumerates folders recursively (full paths).
- โœ… **Hardening + onboarding:** a secret-redaction boundary so tokens/credentials can never reach
  the logs (NFR-SEC-6), and the operator [onboarding guide](./doc/ONBOARDING.md) (Entra app
  registration, unverified-app consent, the connect CLI).

**Remaining:** the **live** acceptance runs (real browser consent + real Graph calls) that the
operator performs against an Entra app registration + Outlook mailbox. See `doc/architecture.md` ยง13.

> Tests mock Microsoft Graph and MSAL. Live `ยง13` acceptance โ€” real browser consent and Graph calls โ€”
> requires an Entra app registration + Outlook mailboxes and is run locally by the operator.

---

## Capabilities (spec ยง5)

| Tool                   | Purpose                    | Destructive? | Status  |
| ---------------------- | -------------------------- | ------------ | ------- |
| `list_accounts`        | List connected mailboxes   | No           | โœ… live |
| `search_conversations` | Search a mailbox (paged)   | No           | โœ… live |
| `read_conversation`    | Read a full conversation   | No           | โœ… live |
| `create_draft`         | Compose a draft (not sent) | No           | โœ… live |
| `send_message`         | Send immediately           | **Yes**      | โœ… live |
| `list_labels`          | List categories + folders  | No           | โœ… live |
| `create_label`         | Create a category/folder   | No           | โœ… live |
| `organize_mail`        | Tag / move / read-state    | **Yes**      | โœ… live |

Plus an out-of-band account-management CLI (`outlook-mcp-auth connect | list | remove`).

---

## Development

Requires **Node.js โ‰ฅ 18** (developed on Node 22).

```bash
npm install
npm run typecheck      # tsc --noEmit
npm run build          # compile to dist/
npm test               # vitest (Graph/MSAL mocked)
npm run format:check   # prettier
```

### Configuration

All operational knobs are environment variables (see [`.env.example`](./.env.example)):

| Env var                          | Meaning                                       | Default          |
| -------------------------------- | --------------------------------------------- | ---------------- |
| `OUTLOOK_MCP_DATA_DIR`           | tokens + app-registration configs             | `~/.outlook-mcp` |
| `OUTLOOK_OAUTH_CREDENTIALS`      | pin one app registration (disables discovery) | unset            |
| `OUTLOOK_MCP_ATTACHMENTS_DIR`    | allow-list for `path` attachments             | unset (disabled) |
| `OUTLOOK_MCP_LOCK_TIMEOUT_MS`    | token-store lock wait                         | `12000`          |
| `OUTLOOK_MCP_REQUEST_TIMEOUT_MS` | per-Graph-call timeout                        | `30000`          |

### Connecting a mailbox

> For the full walkthrough โ€” Entra app registration, the unverified-app consent policy, and
> troubleshooting โ€” see **[`doc/ONBOARDING.md`](./doc/ONBOARDING.md)**. The short version:

1. Register a **public-client** app in Entra ID with the redirect URI `http://localhost` and the
   delegated scopes `Mail.ReadWrite`, `Mail.Send`, `User.Read`, `offline_access`.
2. Drop a `credentials*.json` into the data dir (or point `OUTLOOK_OAUTH_CREDENTIALS` at it):

   ```json
   { "clientId": "<application-client-id>", "tenant": "common" }
   ```

   Multiple `credentials*.json` files are auto-discovered, so accounts under different app
   registrations each refresh with the client that authorised them.

3. Connect, list, and remove mailboxes:

   ```bash
   outlook-mcp-auth connect            # opens the browser for consent
   outlook-mcp-auth connect --source acme   # pick a specific app registration
   outlook-mcp-auth list
   outlook-mcp-auth remove user@example.com
   ```

### Security posture

OAuth tokens are stored only on the local machine (file mode `600` in a `700` data dir). Reading
local files by path for attachments is **disabled by default** and only allowed from an explicit
allow-list. The server never logs tokens, credentials, or message content. See
`doc/architecture.md` ยง9.

## License

MIT โ€” see [LICENSE](./LICENSE).

TDQS

A4.2/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of ambiguity. Agents will always select list_accounts when any tool is needed.

Naming Consistency5/5

The single tool follows a clear verb_noun pattern (list_accounts), making its purpose immediately understandable.

Tool Count1/5

A single listing tool is far too few for a server purporting to handle multiple Outlook accounts. The user would expect tools for email operations, calendar management, or at least sending/reading messages.

Completeness1/5

The server only lists accounts and provides no way to actually interact with them. There are obvious missing operations like read_email, send_email, search_mailbox, or manage folders, leaving the surface severely incomplete.

Maintenance

ActivityInactive
ResponsivenessNo issues