Content
# MS365 MCP Mini
> A **local MCP server** that lets an AI agent directly interact with your **Microsoft 365** (Outlook mail, OneDrive, Excel).
> Once you log in with your browser, you're done — tokens are encrypted and stored **only on your computer**. No server, no database, no shared passwords.
## At a Glance
| | |
|---|---|
| **What** | MCP server wrapped around Microsoft Graph API (37 tools) |
| **How** | Local installation via stdio, TypeScript, and `npx` |
| **Authentication** | OAuth2 (Authorization Code + PKCE, public client, no secret) |
| **Token Storage** | Only on your machine (`~/.ms365-mcp-mini`, OS keychain encryption) |
| **Installation** | `claude mcp add ms365-mcp-mini -- npx -y github:krafton-playground/ms365-mcp-mini` |
```bash
# 1) Installation (Claude Code)
claude mcp add ms365-mcp-mini -- npx -y github:krafton-playground/ms365-mcp-mini
# 2) Ask the agent: "Give me the titles of my 5 unread emails" → One-time browser login → You're good to go
```
## How it Works
```mermaid
flowchart LR
subgraph local["🖥️ Your Machine (All Local)"]
Agent["AI Agent<br/>Claude Code · Desktop · Codex"]
MCP["ms365-mcp-mini<br/>(stdio MCP Server)"]
Cache[("🔒 Encrypted Token Cache<br/>~/.ms365-mcp-mini")]
Agent <-->|"stdio JSON-RPC"| MCP
MCP <--> Cache
end
Browser["🌐 Browser"]
Entra["Microsoft Entra ID<br/>OAuth2 + PKCE"]
Graph["Microsoft Graph<br/>graph.microsoft.com/v1.0"]
MCP -.->|"One-time: Login URL"| Browser
Browser -->|"Login + Consent"| Entra
Entra -.->|"code → token<br/>(localhost redirect)"| MCP
MCP -->|"Bearer token"| Graph
Graph -->|"Mail · Files · Excel"| MCP
```
```mermaid
sequenceDiagram
participant A as Agent
participant M as MCP Server
participant B as Browser
participant E as Entra ID
participant G as Graph API
A->>M: Tool call (e.g., list_messages)
alt No token (first time)
M->>B: Open browser (PKCE auth URL)
B->>E: Login + consent
E-->>M: authorization code → http://localhost:port
M->>E: code + PKCE verifier exchange
E-->>M: access + refresh token
M->>M: 🔒 refresh token encryption and storage
else Token exists (subsequent calls)
M->>E: silent refresh (only when expired)
E-->>M: new access token
end
M->>G: GET /me/messages (Bearer)
G-->>M: Email list
M-->>A: Result (JSON)
```
## Allowed Scopes (4 Delegated Permissions)
This app has **exactly 4 delegated scopes**, and the MCP exposes only what's possible with these scopes.
| Scope | Capabilities |
|---|---|
| `User.Read` | Read your profile, profile photo |
| `Mail.Read` | **Read-only** mail (list, search, body, MIME, attachments, delta sync) |
| `Files.ReadWrite` | OneDrive file **read/write** (upload, download, copy, move, delete, share) + **Excel** read/write |
| `offline_access` | Refresh token issuance (silent renewal without re-login) |
**❌ Not allowed (out of scope — intentionally not exposed):** Sending, composing, or deleting emails; calendar, contacts, Teams, organizational chart, or searching other people's emails or files. Company account profile photos might not be visible depending on tenant settings (`get_my_photo` returns `available:false`).
## Installation
> **Prerequisites:** Node.js 20+ (`node -v`). Client ID, tenant, and `http://localhost` redirect are already set up in the corporate Entra app — no additional setup required.
**Claude Code**
```bash
claude mcp add ms365-mcp-mini -- npx -y github:krafton-playground/ms365-mcp-mini
```
**Claude Desktop** — Add to `claude_desktop_config.json` under `mcpServers`:
```json
{
"mcpServers": {
"ms365-mcp-mini": {
"command": "npx",
"args": ["-y", "github:krafton-playground/ms365-mcp-mini"]
}
}
}
```
**Codex CLI**
```bash
codex mcp add ms365-mcp-mini -- npx -y github:krafton-playground/ms365-mcp-mini
```
**First Login** — A browser will open automatically on the first tool call. For a head start:
```bash
npx -y github:krafton-playground/ms365-mcp-mini login
```
Subsequent calls will use the cached token and **work without a browser**. Logout: `… logout`.
## Try it Out (Test Cases)
After installation, ask the agent in natural language (e.g., actual tools in parentheses):
**Profile**
- "Show me my profile info" — `get_my_profile`
- "Get my profile photo" — `get_my_photo`
**Mail (Read-only)**
- "Give me the titles and senders of my 10 unread emails" — `list_messages`
- "Find emails from last week with 'budget' in them" — `search_messages`
- "Show me the full body of that email / as an .eml file" — `get_message` / `get_message_mime`
- "List attachments for this email and give me the first one" — `list_attachments` / `get_attachment`
- "What folders do I have in my inbox?" — `list_mail_folders`
**OneDrive Files**
- "What's in my OneDrive root?" — `list_drive_items`
- "Show me recent files / shared files" — `list_recent_files` / `list_shared_with_me`
- "Upload this base64 content as report.txt to OneDrive" — `upload_file`
- "Show me the content of report.txt / download it" — `download_file`
- "Create a sharing link for this file" — `create_sharing_link`
- "Create a 'backup' folder and move that file there" — `create_folder` / `rename_move_item`
- "Copy / delete / show version history / permissions for this file" — `copy_item` / `delete_item` / `list_versions` / `list_permissions`
- "Search for 'contract' in my drive" — `search_drive`
**Excel** (`.xlsx`, business OneDrive)
- "List worksheets in q1.xlsx" — `excel_list_worksheets`
- "Read A1:C10 in Sheet1" — `excel_get_range` / full range is `excel_get_used_range`
- "Write these values to Sheet1 A1:B2" — `excel_update_range`
- "Add a new worksheet / list tables / add a row to the table" — `excel_add_worksheet` / `excel_list_tables` / `excel_add_table_row`
**Authentication**
- "Check my login status / re-login / logout" — `auth_status` / `auth_login` / `auth_logout`
> All 37 tools: auth 3 · profile 2 · mail 8 · files 15 · excel 8.
## Configuration (Environment Variables, All Optional)
| Variable | Default Value | Purpose |
|---|---|---|
| `MS365_MCP_CLIENT_ID` | (built-in) | Entra Application (client) ID — public client, no secret |
| `MS365_MCP_TENANT` | (built-in) | Tenant ID/domain or `organizations`/`common` |
| `MS365_MCP_CACHE_DIR` | `~/.ms365-mcp-mini` | Token cache location |
| `MS365_MCP_FILE_ROOT` | Disabled | If set, restricts `upload_file`'s `localPath` and `download_file`'s `savePath` to this folder |
| `MS365_MCP_REDIRECT_HOST` | `localhost` | OAuth redirect loopback host (`127.0.0.1` also works) |
| `MS365_MCP_DEBUG` | off | Debug logs to **stderr** if `1` |
## Security Model
- **Public client + PKCE** — No client secret in code or package.
- **Per-user, local** — Your refresh token never leaves your machine. OS keychain encryption (`@azure/msal-node-extensions`) or falls back to `0600` file permissions.
- **Read-only mail** (structurally — only `Mail.Read` requested).
- **Local file access restricted by default** — Enable `MS365_MCP_FILE_ROOT` to allow `localPath`/`savePath` usage. Prevents prompt injection that could upload/overwrite sensitive files like `~/.ssh/id_rsa`.
- **stdio protocol** — stdout is for MCP protocol only, all logs go to stderr.
## Troubleshooting
- **`AADSTS50011` (redirect mismatch)** — Ensure Entra app Authentication has `http://localhost` (or `http://127.0.0.1`) registered under *Mobile and desktop applications*. Adjust with `MS365_MCP_REDIRECT_HOST` if different.
- **Browser doesn't open** — Open the login URL printed to your terminal (stderr).
- **`get_my_photo` returns `available:false`** — Corporate accounts might require additional permissions (normal).
- **Tool returns 403** — Operations not allowed within the 4 scopes (e.g., writing emails). Intentional behavior.
- **429 (throttling)** — Graph client respects `Retry-After` and retries automatically.
## Development
```bash
npm install # Dependencies (prepare hook builds automatically)
npm run build # tsc → dist/
npm test # vitest (35 tests)
npm run dev login # Run from source (tsx)
```
Design and implementation documents: [`docs/PRD.md`](docs/PRD.md) · [`docs/plan.md`](docs/plan.md) · [`docs/research/graph-capability-matrix.md`](docs/research/graph-capability-matrix.md)
---
🤖 [Claude Code](https://claude.com/claude-code) implementation · Adversarial multi-lens review + live E2E validation on a real tenant completed.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
Python tool for converting files and office documents to Markdown.
awesome-claude-skills
A curated list of awesome Claude Skills, resources, and tools for...
antigravity-awesome-skills
The Ultimate Collection of 130+ Agentic Skills for Claude...
claude-context-mode
claude-context-mode plugin reduces MCP context bloat, saving up to 99% of tokens.
context-mode
MCP is the protocol for tool access. We're the virtualization layer for context.