{
  "markdown": "<!-- mcp-name: io.github.mpalermiti/outlook-mcp -->\n\n# outlook-mcp\n\nMCP server for Microsoft Outlook personal accounts via Microsoft Graph API.\n\n[![PyPI](https://img.shields.io/pypi/v/outlook-graph-mcp.svg)](https://pypi.org/project/outlook-graph-mcp/)\n[![Python](https://img.shields.io/pypi/pyversions/outlook-graph-mcp.svg)](https://pypi.org/project/outlook-graph-mcp/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-green)](https://registry.modelcontextprotocol.io/v0/servers?search=mpalermiti)\n\n> **Personal Microsoft accounts only** — `@outlook.com`, `@hotmail.com`, `@live.com`. Work/school accounts (Entra ID) are not supported in v1.\n\n> **Disclaimer:** Independent open-source project. Not affiliated with, endorsed by, or supported by Microsoft Corporation. \"Outlook\" and \"Microsoft Graph\" are trademarks of Microsoft.\n\n---\n\n## Who this is for\n\nYou'll like this if you're:\n\n- An **agent builder** wiring Outlook into your own infra (OpenClaw, Claude Code, Cursor, custom MCP host) and want a typed tool surface — not stdout you have to parse\n- Building on **personal Microsoft accounts** (Outlook.com / Hotmail / Live) and want full control: BYO Azure app, no enterprise consent flow, no shared client ID\n- Looking for **real coverage** — mail, calendar, contacts, to-do, drafts, folders, batch ops, threading — instead of a mail-only or calendar-only wrapper\n- Security-conscious: tokens in the OS keyring (Keychain on macOS), granular `allow_categories`, optional `read_only` mode, zero telemetry\n\nThis **isn't for you** if you need work/school M365 accounts (use Microsoft's official tooling — Entra ID auth and admin-consent flows are out of scope here), or if a basic mail-only client would suffice (this has 62 tools — way more than you need for \"read my inbox\").\n\n### How it differs from other Outlook tools you'll find\n\nThis is the only **first-class MCP server** in the personal-Outlook space — most alternatives are bash scripts or skill-shaped CLI wrappers the agent shells out to. That distinction matters: the agent gets typed tool schemas with structured args/returns, not stdout it has to parse. Other things you won't find elsewhere: `/$batch`-optimized triage (10-20× faster on bulk ops), recursive folder ops with name resolution, granular per-category permissions, multi-account support, and full attachment write paths including >3MB upload sessions for drafts.\n\n---\n\n## What This Enables\n\nGive your AI agent full Outlook access. Example prompts that just work:\n\n- *\"Summarize my unread email from the past 24 hours and flag anything time-sensitive.\"*\n- *\"What's in my Focused Inbox right now? Anything in Other that looks like it belongs up top?\"*\n- *\"Any shipping updates in my inbox? Track what I'm waiting on and when it's supposed to arrive.\"*\n- *\"Scan my email for upcoming subscription renewals — what's about to auto-charge in the next two weeks?\"*\n- *\"I've got a trip to Seattle next week — check my calendar for the itinerary and create a To Do task with a packing checklist.\"*\n- *\"Draft a reply to the last message from my sister saying I'll call her this weekend.\"*\n- *\"Move all newsletter and promotional email from this week to a 'Read Later' folder — batch 20 at a time.\"*\n\nThe server exposes 62 discrete tools so the agent can compose its own workflow — read, triage, write, schedule, track tasks — without hardcoded macros.\n\n## Works With\n\n- **[OpenClaw](https://openclaw.ai)** — native MCP support, available via [ClawHub](https://clawhub.ai/skills?q=outlook-mcp)\n- **[Claude Code](https://claude.com/claude-code)** — add to `~/.claude/settings.json` under `mcpServers`\n- **[Cursor](https://cursor.com)** — MCP-compatible\n- **Any MCP client** — it's a standard stdio MCP server\n\nListed on the [official MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=mpalermiti) as `io.github.mpalermiti/outlook-mcp`.\n\n---\n\n## Features\n\n**62 tools** across 13 categories:\n\n- **Auth (1)** -- auth status check (login is via CLI)\n- **Mail Read (7)** -- list inbox (with Focused Inbox and uncategorized filters), read message, bulk read by ID via `$batch`, search (KQL), list folders, delta-sync inbox changes, composed \"since last call\" digest across mail/events/contacts\n- **Mail Write (3)** -- send, reply/reply-all, forward\n- **Mail Triage (9)** -- move, delete (soft by default), flag, categorize, mark read/unread, reclassify (Focused Inbox), list/set/delete per-sender Focused Inbox overrides\n- **Calendar Read (3)** -- list events (with recurring expansion), get event details, delta-sync event changes\n- **Calendar Write (4)** -- create, update, delete, RSVP (accept/decline/tentative)\n- **Contacts (7)** -- list, search, get, create, update, delete, delta-sync changes\n- **To Do (6)** -- list task lists, list/create/update/complete/delete tasks\n- **Drafts (5)** -- list, create, update, send, delete\n- **Attachments (5)** -- list, download, send-with-attachments, attach-to-draft, remove-draft-attachment\n- **Folder Management (3)** -- create, rename, delete mail folders\n- **Threading and Batch (3)** -- list thread, copy message, batch triage\n- **User and Admin (6)** -- whoami, list calendars, list categories, mail tips, accounts\n\n**Design principles:**\n\n- **BYOID** -- Bring Your Own ID. You register your own Azure AD app. No shared client ID.\n- **Zero telemetry** -- no analytics, no local caching, no third-party calls.\n- **Token storage** -- OS keyring via `azure-identity` (macOS Keychain, Windows Credential Store, Linux Secret Service).\n- **Input validation** -- all inputs validated (email, Graph IDs, OData, KQL, datetimes) before any API call.\n- **Read-only mode** -- set `read_only: true` in config to block all write operations.\n- **Soft delete** -- delete moves to Deleted Items by default. Hard delete requires explicit `permanent: true`.\n- **Timezone-aware** -- calendar operations respect your configured IANA timezone.\n\n### Agent-friendly shape (1.8.0)\n\nTwo pure-code upgrades that make the same 57 tools cheaper and more recoverable for AI agents:\n\n- **Concise mode** — pass `concise=True` to the five high-volume read tools (`outlook_list_inbox`, `outlook_read_message`, `outlook_search_mail`, `outlook_list_events`, `outlook_list_thread`) to drop bulky fields: full message bodies, per-event attendee lists, quoted prior-message text in threads, body previews/categories on inbox listings. Typical payload reduction ~10×. Default `concise=False` preserves the existing response shape — strict backward compat.\n\n- **Structured Graph errors** — every tool wraps msgraph SDK exceptions into `{code, message, action}` responses with operator-friendly recovery hints: re-auth on 401, a link to the repo's [ROADMAP dead-ends list](https://github.com/mpalermiti/outlook-mcp/blob/main/ROADMAP.md#investigated-and-not-viable) on 403/`ErrorAccessDenied`, re-list on 404/`ErrorItemNotFound`, back-off on 429, retry on 503. `OutlookMCPError` subclasses and validation errors pass through unchanged.\n\n---\n\n## Azure AD App Registration\n\nYou need to register a free Azure AD app to get a client ID.\n\n### Prerequisites (Personal Microsoft Accounts)\n\nMicrosoft has deprecated app registration for personal accounts without an Azure AD tenant. You need to create a free Azure account first:\n\n1. Go to [azure.microsoft.com/free](https://azure.microsoft.com/free) and sign up with your personal `@outlook.com` account. Requires a credit card for identity verification but **won't charge you**. This creates a proper Azure AD tenant.\n\n### Register the App\n\n1. Go to [App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) and sign in with your `@outlook.com` account.\n\n2. Click **\"+ New registration\"** and fill in:\n   - **Name:** anything except Microsoft-branded terms (e.g. `mp-outlook-mcp` — names like \"Outlook MCP\" will be rejected)\n   - **Supported account types:** select **\"Personal Microsoft accounts only\"**\n   - **Redirect URI:** leave blank\n\n3. Click **Register**. Copy the **Application (client) ID** from the overview page.\n\n4. Go to **Authentication (Preview)** → **Settings** tab → toggle **\"Allow public client flows\"** to **Yes** → **Save**.\n\n5. Go to **API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions** → add:\n   - `Mail.ReadWrite`, `Mail.Send`\n   - `Calendars.ReadWrite`\n   - `Contacts.ReadWrite`, `Tasks.ReadWrite`\n   - `User.Read`, `offline_access`\n\nNo client secret is needed. The device code flow uses public client auth.\n\n---\n\n## Quick Start\n\n### Install\n\n**Option A — from PyPI (recommended):**\n\n```bash\nuv tool install outlook-graph-mcp\n# or: pipx install outlook-graph-mcp\n# or: pip install outlook-graph-mcp\n```\n\n**Option B — from source:**\n\n```bash\ngit clone https://github.com/mpalermiti/outlook-mcp.git\ncd outlook-mcp\nuv sync\n```\n\n### Configure\n\nCreate `~/.outlook-mcp/config.json`:\n\n```json\n{\n  \"client_id\": \"YOUR_APPLICATION_CLIENT_ID\",\n  \"tenant_id\": \"consumers\",\n  \"timezone\": \"America/Los_Angeles\",\n  \"read_only\": true\n}\n```\n\nThe only required field is `client_id`. Everything else has sensible defaults. Start with `read_only: true` — flip to `false` when you're comfortable.\n\n### Register with your MCP client\n\n**If installed from PyPI:**\n\n```json\n{\n  \"mcpServers\": {\n    \"outlook\": {\n      \"command\": \"outlook-mcp\"\n    }\n  }\n}\n```\n\n**If installed from source:**\n\n```json\n{\n  \"mcpServers\": {\n    \"outlook\": {\n      \"command\": \"uv\",\n      \"args\": [\"--directory\", \"/path/to/outlook-mcp\", \"run\", \"outlook-mcp\"]\n    }\n  }\n}\n```\n\n**For OpenClaw**, use the `openclaw mcp` CLI — it writes to `mcp.servers` in `~/.openclaw/openclaw.json` for you:\n\n```bash\n# If installed from PyPI:\nopenclaw mcp set outlook '{\"command\":\"outlook-mcp\"}'\n\n# If installed from source:\nopenclaw mcp set outlook '{\"command\":\"uv\",\"args\":[\"--directory\",\"/path/to/outlook-mcp\",\"run\",\"outlook-mcp\"]}'\n\n# Verify:\nopenclaw mcp list\nopenclaw mcp show outlook --json\n```\n\nRestart the OpenClaw gateway after registering. See the [OpenClaw MCP docs](https://docs.openclaw.ai/cli/mcp) for SSE/HTTP transport variants.\n\n### Authenticate\n\nRun this once on the machine where the MCP server will run:\n\n```bash\nuv run outlook-mcp auth\n```\n\nYou'll get a URL and a code. Open the URL in any browser, enter the code, and sign in with your Microsoft account. Tokens are cached in the OS keyring — the MCP server picks them up automatically.\n\nOther CLI commands:\n\n```bash\nuv run outlook-mcp status   # Check auth status\nuv run outlook-mcp logout   # Clear credentials\nuv run outlook-mcp serve    # Start MCP server (default, used by OpenClaw/Claude)\n```\n\n---\n\n## Troubleshooting\n\n### `SSL: CERTIFICATE_VERIFY_FAILED` on Linux\n\nIf auth fails with `[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate`, your Python environment can't find the system CA bundle. This is common on minimal/container Linux images and with the isolated venv from `uv tool install`.\n\nPoint Python at your system CA bundle. Set **both** variables — auth (via `azure-identity` → `requests`) reads `REQUESTS_CA_BUNDLE`, while the delta/`$batch` paths (via `httpx`) read `SSL_CERT_FILE`:\n\n```bash\nexport SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt      # httpx + Python ssl\nexport REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt  # azure-identity auth\n```\n\nThe path varies by distro: Debian/Ubuntu use `/etc/ssl/certs/ca-certificates.crt`; RHEL/Fedora use `/etc/pki/tls/certs/ca-bundle.crt`. If the file is missing, install your distro's CA package (`ca-certificates`). Set these in the same environment your MCP client launches the server from so they apply at runtime, not just to the one-time `auth` command.\n\n### Token cache stored unencrypted (Linux)\n\nA one-time startup warning about the token cache falling back to plaintext means `libsecret`/PyGObject isn't importable — see [Privacy and Security](#privacy-and-security) for the fix.\n\n---\n\n## Tool Reference\n\n### Auth\n\n| Tool | Description |\n|------|-------------|\n| `outlook_auth_status` | Check if authenticated and whether read-only mode is active. |\n\n> **Note:** Authentication is handled via the CLI (`outlook-mcp auth`), not through MCP tools. See [Authenticate](#authenticate) above.\n\n### Mail Read\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_inbox` | List messages in a folder. `folder` accepts display names, well-known names, or Graph IDs. Filter by read status, sender, date range, Focused Inbox classification. Pagination via `skip`. |\n| `outlook_read_message` | Get full message by ID. Format: `text`, `html`, or `full` (both). Pass `include_deferred_send=True` to also surface the draft's scheduled delivery time. |\n| `outlook_read_messages` | Bulk read up to 20 messages by ID via Graph `$batch` in one round-trip. Per-message shape matches `outlook_read_message` byte-for-byte for the same `(format, concise, include_deferred_send)`. Partial-failure tolerant: 404s on some IDs surface in `failures[]` without failing the whole call. Use NOT N `outlook_read_message` calls. |\n| `outlook_search_mail` | Search mail using KQL query. Optionally scope to a folder by name or ID. |\n| `outlook_list_folders` | List mail folders with counts, `parent_id`, and `child_count`. Pass `recursive=true` to walk the full folder tree (subfolders included). |\n| `outlook_list_inbox_delta` | List only inbox changes since the last call. First call returns a full snapshot plus a `delta_token`; subsequent calls (token passed back) return only added/updated/deleted items. Deletes come back as `{id, is_deleted: True}`. Cursor is stateless — agent persists and replays. |\n| `outlook_changes_since` | One structured \"since last call\" digest composing mail/events/contacts deltas. Returns counts + `urgent_flagged` mail + top-5 `by_sender` + new/cancelled events. Each resource has an independent `delta_token`; stale-token recovery (HTTP 410) auto-resyncs that resource and surfaces `_meta.resync`. First-call snapshot is filtered to `fallback_window_hours` (default 24). Designed for recurring agent loops. |\n\n### Mail Write\n\n| Tool | Description |\n|------|-------------|\n| `outlook_send_message` | Send email. Supports TO/CC/BCC, HTML body, importance level. |\n| `outlook_reply` | Reply or reply-all to a message. |\n| `outlook_forward` | Forward a message to one or more recipients with optional comment. |\n\n### Mail Triage\n\n| Tool | Description |\n|------|-------------|\n| `outlook_move_message` | Move a message to a folder by name or ID. |\n| `outlook_delete_message` | Delete a message. Soft delete (Deleted Items) by default. `permanent: true` for hard delete. |\n| `outlook_flag_message` | Set follow-up flag: `flagged`, `complete`, or `notFlagged`. |\n| `outlook_categorize_message` | Set categories on a message. |\n| `outlook_mark_read` | Mark a message as read or unread. |\n| `outlook_reclassify_message` | Move a message between Focused Inbox and Other (`focused` / `other`). |\n| `outlook_list_inbox_overrides` | List Focused Inbox per-sender override rules. |\n| `outlook_set_inbox_override` | Upsert a per-sender Focused Inbox override (`focused` / `other`). Case-insensitive sender matching; PATCH-if-exists, else POST. |\n| `outlook_delete_inbox_override` | Delete a Focused Inbox override by ID. |\n\n### Calendar Read\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_events` | List events in a date range. Expands recurring events. Each event carries `type`, so a series master is distinguishable from a one-off. Configurable via `days`, `after`, `before`. |\n| `outlook_get_event` | Get full event details: attendees, body, online meeting URL, recurrence, `type` (`singleInstance` / `seriesMaster` / `occurrence` / `exception`). |\n| `outlook_list_events_delta` | List only event changes inside a window since the last call. `start` and `end` (ISO 8601) required on the first call (Graph constraint — no whole-calendar sync). Deletes come back as `{id, is_deleted: True}`. Cursor is stateless. |\n\n### Calendar Write\n\n| Tool | Description |\n|------|-------------|\n| `outlook_create_event` | Create event with location and attendees. (`is_online` has no effect on personal accounts — Graph ignores `isOnlineMeeting` for consumer mailboxes.) Pass `recurrence` to create a **series**: a shorthand (`daily`, `weekdays`, `weekly`, `monthly`, `yearly`, anchored on `start`) or a full [Graph recurrence object](https://learn.microsoft.com/graph/api/resources/patternedrecurrence) for anything else. `range.startDate` defaults to the event's start date. |\n| `outlook_update_event` | Update event fields (subject, time, location, body, attendees, all-day). Only patches changed fields. Pass `recurrence` to turn a single event into a series, or `remove_recurrence=True` to turn a series back into a single event. `attendees` **replaces** the whole guest list and emails invitations/cancellations; `is_all_day` needs `start`+`end` in the same call. |\n| `outlook_delete_event` | Delete a calendar event. |\n| `outlook_rsvp` | RSVP to an event: `accept`, `decline`, or `tentative`. Optionally include a message. |\n\n### Contacts\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_contacts` | List contacts with cursor pagination. |\n| `outlook_search_contacts` | Search contacts by name or email. |\n| `outlook_get_contact` | Get full contact details by ID. |\n| `outlook_create_contact` | Create a new contact. |\n| `outlook_update_contact` | Update contact fields. |\n| `outlook_delete_contact` | Delete a contact. |\n| `outlook_list_contacts_delta` | List only contact changes since the last call. Deletes come back as `{id, is_deleted: True}`. Cursor is stateless. |\n\n### To Do\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_task_lists` | List To Do lists. |\n| `outlook_list_tasks` | List tasks with status filter and pagination. |\n| `outlook_create_task` | Create task with due date, importance, recurrence. |\n| `outlook_update_task` | Update task fields. |\n| `outlook_complete_task` | Mark task as completed. |\n| `outlook_delete_task` | Delete a task. |\n\n### Drafts\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_drafts` | List draft messages with pagination. |\n| `outlook_create_draft` | Create a draft. Supports scheduled delivery via `deferred_send_datetime` (server-side, Outlook-desktop-compatible \"Delay Delivery\"). |\n| `outlook_update_draft` | Update draft fields. Accepts `is_html=True` for HTML bodies and `deferred_send_datetime` to set or clear the scheduled delivery time. |\n| `outlook_send_draft` | Send an existing draft. |\n| `outlook_delete_draft` | Delete a draft. |\n\n### Attachments\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_attachments` | List attachments on a message. |\n| `outlook_download_attachment` | Download attachment and save decoded bytes to a file. |\n| `outlook_send_with_attachments` | Send message with file attachments (auto upload session for >3MB). |\n| `outlook_attach_to_draft` | Add attachments to an existing draft (auto upload session for >3MB). |\n| `outlook_remove_draft_attachment` | Remove a single attachment from a draft. |\n\n### Folder Management\n\n| Tool | Description |\n|------|-------------|\n| `outlook_create_folder` | Create mail folder (top-level or nested). |\n| `outlook_rename_folder` | Rename a mail folder. |\n| `outlook_delete_folder` | Delete a mail folder (refuses well-known folders). |\n\n### Threading and Batch\n\n| Tool | Description |\n|------|-------------|\n| `outlook_list_thread` | Get all messages in a conversation thread. |\n| `outlook_copy_message` | Copy a message to another folder. |\n| `outlook_batch_triage` | Batch move/flag/categorize/mark_read (max 20 per call). Single Graph `/$batch` round-trip — 10-20× faster than per-message calls for large triage. |\n\n### User and Admin\n\n| Tool | Description |\n|------|-------------|\n| `outlook_whoami` | Get current user profile. |\n| `outlook_list_calendars` | List available calendars. |\n| `outlook_list_categories` | List category definitions with colors. |\n| `outlook_get_mail_tips` | Pre-send check (OOF, delivery restrictions). |\n| `outlook_list_accounts` | List configured accounts. |\n| `outlook_switch_account` | Switch active account. |\n\n---\n\n## Configuration\n\nConfig lives at `~/.outlook-mcp/config.json` (created with `0600` permissions).\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `client_id` | `string` | `null` | Azure AD application (client) ID. Required for auth. |\n| `tenant_id` | `string` | `\"consumers\"` | Azure AD tenant. Use `\"consumers\"` for personal Microsoft accounts. |\n| `timezone` | `string` | `\"UTC\"` | IANA timezone (e.g. `\"America/New_York\"`). Used for relative date computations in calendar tools. |\n| `read_only` | `bool` | `false` | When `true`, all write tools (send, reply, move, delete, create, update, RSVP) return an error. |\n| `allow_categories` | `list[string]` | `[]` | Optional. Restrict write tools to specific categories (see below). Empty list = all writes allowed when `read_only: false`. |\n\n### Toolset selection (optional) — `OUTLOOK_MCP_TOOLSETS`\n\nAll 62 tool schemas load into the client's context every turn (~8.6k tokens). A client that only needs part of the surface can set the `OUTLOOK_MCP_TOOLSETS` environment variable to a comma-separated list of tool groups, and only those load. The `account` group (auth / identity) is always available.\n\n```bash\n# e.g. a recurring mail + calendar agent: ~30 tools instead of 62 (~52% fewer tool tokens/turn)\nOUTLOOK_MCP_TOOLSETS=\"mail,calendar,digest,delta\"\n```\n\nGroups: `mail`, `drafts`, `attachments`, `calendar`, `contacts`, `todo`, `folders`, `digest`, `delta`, `admin`. Unset (the default) loads everything — fully backward compatible. This only affects which tools are advertised; enabled tools behave identically.\n\n### Granular Write Permissions (optional)\n\nBy default, `read_only: false` unlocks **all** write tools. For finer control, set `allow_categories` to restrict write access to specific categories. Read tools (list, search, get) are always allowed — `allow_categories` only narrows the write surface.\n\n**Available categories:**\n\n| Category | Tools | Risk |\n|---|---|---|\n| `mail_drafts` | create/update/delete draft | Safe — drafts only, no send |\n| `mail_triage` | move, delete (soft), flag, categorize, mark read, copy, batch | Moderate — reversible except hard delete |\n| `mail_folders` | create/rename/delete folder | Moderate |\n| `mail_send` | send, reply, forward, send_draft, send_with_attachments | **Dangerous** — sends email on your behalf |\n| `calendar_write` | create/update/delete event, RSVP | Moderate — creates calendar entries |\n| `contacts_write` | create/update/delete contact | Moderate |\n| `todo_write` | create/update/complete/delete task | Safe — your own task list |\n\n**Example policies:**\n\n**Draft-only assistant** (agent can compose drafts, you review and send):\n\n```json\n{ \"read_only\": false, \"allow_categories\": [\"mail_drafts\", \"mail_triage\", \"todo_write\"] }\n```\n\n**Calendar-only** (agent can manage your schedule, nothing else):\n\n```json\n{ \"read_only\": false, \"allow_categories\": [\"calendar_write\"] }\n```\n\n**Full write access** (agent can do everything):\n\n```json\n{ \"read_only\": false }\n```\n\n**Read-only** (safest default, no writes):\n\n```json\n{ \"read_only\": true }\n```\n\nWhen `allow_categories` is set, any tool in a non-allowed category returns a permission-denied error (`PermissionDeniedError`) naming the blocked category. When `allow_categories` is empty (or unset) and `read_only` is false, all write tools are permitted. `read_only: true` always takes precedence — if set, all writes are blocked regardless of `allow_categories`. Unknown category names are rejected at config load time with a validation error; only the seven names above are accepted.\n\n---\n\n## Privacy and Security\n\n- **Zero telemetry.** No analytics, no tracking, no usage data collected.\n- **Zero local caching.** Every call goes directly to Microsoft Graph. No local email/calendar storage.\n- **Zero third-party calls.** The server only talks to `graph.microsoft.com` and `login.microsoftonline.com`.\n- **Token storage.** OAuth tokens are persisted via `azure-identity`'s `TokenCachePersistenceOptions`. On macOS the OS Keychain is used; on Windows, DPAPI; on Linux with PyGObject/libsecret available, gnome-keyring. On Linux *without* libsecret (e.g. the isolated venv created by `uv tool install`), tokens fall back to a `0600` plaintext file at `~/.IdentityService/` and the MCP logs a one-time warning at startup. For encrypted storage on Linux, install `python3-gi gnome-keyring libsecret-1-0` and re-create the venv with `--system-site-packages`.\n- **No logging of sensitive data.** Message bodies, recipient addresses, and tokens are never logged.\n- **Config permissions.** Config directory is `0700`, config file is `0600`. Symlinked configs are rejected.\n- **Input validation.** All user inputs (email addresses, Graph IDs, OData filters, KQL queries, datetimes) are validated and sanitized before reaching the Graph API.\n\n---\n\n## Development\n\n```bash\n# Install dev dependencies\nuv sync --extra dev\n\n# Run tests\nuv run pytest\n\n# Lint\nuv run ruff check src/ tests/\n\n# Format\nuv run ruff format src/ tests/\n\n# Run server locally (stdio)\nuv run outlook-mcp\n```\n\n**Requirements:** Python 3.10+\n\n---\n\n## Roadmap\n\n- **Inbox Rules** -- list, create, delete rules\n- **Advanced mail** -- raw MIME export, internet message headers\n- **Calendar** -- cancel event (with attendee notification)\n- **Checklists** -- checklist items on To Do tasks\n- **Enterprise (Entra ID)** -- work/school account support\n\n---\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n",
  "bytes": 25696,
  "sha": "9fd062bf4e0a28e5730ab78cdba2f42774e0a02d1fbf16244fe0bbbfa550624e",
  "repo_slug": "mpalermiti/outlook-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mpalermiti_outlook_mcp_3c5741c9/readme"
}