outlook-evidence-mcp
# outlook-evidence-mcp
A **read-only** [Model Context Protocol](https://modelcontextprotocol.io) server that lets an MCP client (Claude Code, Claude Desktop, etc.) **search a Microsoft 365 / Outlook mailbox and export messages + attachments to a local folder**, recording a **chain-of-custody manifest** (SHA-256 per file) so the collection is defensible as evidence.
Built for the case where you need to pull a specific set of email threads and their attachments out of Outlook — for litigation, compliance, or analysis — **without** granting broad, writeable, or tenant-wide access.
## Design principles
- **Read-only.** Requests only the delegated `User.Read` + `Mail.Read` Graph scopes. There are **no** send / delete / modify tools — none exist in the code.
- **Your mailbox only.** Delegated auth (device-code flow) acts as the signed-in user. No application (app-only) permissions, so it can never read other people's mailboxes.
- **No secrets in the repo.** You supply your own Entra app IDs via env. Device-code flow means there's no client secret at all. The OAuth token cache and the exported evidence are git-ignored.
- **Auditable collection.** Every export appends a `manifest.jsonl` record: message id, internet message id, sender, subject, received time, and the SHA-256 + size of each saved file, plus the retrieval timestamp.
## Tools
| Tool | What it does |
|------|--------------|
| `outlook_authenticate` | One-time device-code sign-in; returns a URL + code to complete in a browser |
| `outlook_auth_status` | Report whether the server is signed in |
| `outlook_search` | Search mail by free-text KQL (`query`) or structured `from`/`since`/`until`/`folder` |
| `outlook_get_message` | Fetch one message's metadata + body |
| `outlook_list_attachments` | List a message's attachments |
| `outlook_save_message` | Export one message as `.eml` + download its attachments + manifest entry |
| `outlook_save_thread` | Save every message in a conversation (+ attachments) |
Messages are exported as **`.eml`** (full MIME via Graph `/$value`) to preserve headers and fidelity.
## Prerequisites
- **Node.js 18+**
- A **Microsoft 365 business tenant** (personal `@outlook.com` accounts are not supported by Graph delegated mail here)
- Rights to **register an app** and **grant admin consent** in your tenant (or an admin who will)
## Setup
### 1. Register an Entra application (~5 min, one-time)
In [portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID → App registrations → New registration**:
1. **Name**: e.g. `outlook-evidence-mcp`. **Supported account types**: *Accounts in this organizational directory only* (single tenant).
2. **API permissions → Add a permission → Microsoft Graph → Delegated permissions** → add `User.Read` and `Mail.Read` → **Grant admin consent**.
3. **Authentication → Advanced settings → Allow public client flows → Yes** (enables device-code auth — no client secret, no redirect URI needed).
4. Copy the **Application (client) ID** and **Directory (tenant) ID** from the app's Overview.
> **Conditional Access note:** some tenants block the device-code flow via a CA policy. If sign-in is blocked, scope a CA exception for this app registration (an auth-code + `localhost` redirect variant isn't bundled yet — open an issue if you need it).
### 2. Install & build
```bash
npm install
npm run build # compiles to dist/
```
### 3. Configure
Copy `.env.example` to `.env` and fill in your IDs (or pass the same vars via your MCP client config):
```
AZURE_CLIENT_ID=<application-client-id>
AZURE_TENANT_ID=<directory-tenant-id>
OUTPUT_DIR=./evidence
TOKEN_CACHE_PATH=./.token-cache.json
```
### 4. Register with your MCP client
**Claude Code:**
```bash
claude mcp add outlook-evidence \
-e AZURE_CLIENT_ID=<client-id> \
-e AZURE_TENANT_ID=<tenant-id> \
-e OUTPUT_DIR=/absolute/path/to/evidence \
-- node /absolute/path/to/outlook-evidence-mcp/dist/index.js
```
Or add it to your client's MCP config JSON:
```json
{
"mcpServers": {
"outlook-evidence": {
"command": "node",
"args": ["/absolute/path/to/outlook-evidence-mcp/dist/index.js"],
"env": {
"AZURE_CLIENT_ID": "<client-id>",
"AZURE_TENANT_ID": "<tenant-id>",
"OUTPUT_DIR": "/absolute/path/to/evidence"
}
}
}
}
```
### 5. Authenticate (one-time, interactive)
In an **interactive** session, call the `outlook_authenticate` tool. It returns something like:
> To sign in, use a web browser to open https://microsoft.com/devicelogin and enter the code `ABCD-EFGH`.
Complete that in your browser. The refreshable token is cached locally; subsequent (including headless) runs won't prompt again until it expires.
## Usage
Once authenticated, ask your MCP client to, e.g.:
- *"Search Outlook for mail from planet.com about the ISO 8583 spec."* → `outlook_search`
- *"Save that whole thread and its attachments to the evidence folder."* → `outlook_save_thread`
Everything lands in `OUTPUT_DIR` as `.eml` files + attachments, with an appended `manifest.jsonl`.
## Security & privacy
- Read-only, delegated, single-user. The **Entra app registration is the hard boundary** — even if you don't trust this code, you consent only to `Mail.Read` on your own mailbox.
- The **token cache** (`TOKEN_CACHE_PATH`) holds live tokens — it is git-ignored and written `0600`. Treat it like a credential.
- The **evidence folder** is git-ignored. Do not commit collected mail.
- The only network egress is `https://graph.microsoft.com`. No telemetry.
## Development
```bash
npm run dev # run from source (tsx)
npm run typecheck # tsc --noEmit
npm test # vitest (offline; Graph is mocked — no live calls)
```
## License
MIT — see [LICENSE](./LICENSE).
TDQS
Scored across 7 tools
Each tool has a clear and distinct purpose: authentication, status checking, fetching a message, listing attachments, saving a message, saving a thread, and searching. No overlapping functionality.
Most tools follow a consistent 'outlook_verb_noun' pattern (e.g., get_message, save_message, search). However, outlook_auth_status is a noun-noun combination and outlook_authenticate is just a verb, causing minor inconsistency.
With 7 tools, the set is well-scoped for the purpose of reading and saving Outlook emails as evidence. It covers authentication, retrieval, attachment handling, and search without being excessive.
The tool set covers the full evidence-gathering workflow: authenticate, search, retrieve messages, list/save attachments, and save entire threads. No obvious gaps for the stated read-only, chain-of-custody purpose.