mcp-outlook

Sofias-ai/mcp-outlook

3.3

If you are the rightful owner of mcp-outlook and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

A lightweight MCP Server for seamless integration with Microsoft Outlook, enabling MCP clients to interact with emails for specific users.

MCP Outlook Server

An MCP (Model Context Protocol) server for interacting with Microsoft Outlook through Microsoft Graph API. This server provides advanced tools for searching, managing, and cleaning emails with HTML and text content processing capabilities.

Key Features

🔍 Advanced Email Search

  • Dual search methods: OData filters + KQL (Keyword Query Language)
  • Performance optimization: Body/No-Body variants for all search functions
  • Unified search across multiple folders (Inbox, SentItems, Drafts)
  • Natural language searches with KQL support ("from:john meeting today")
  • Precise filtering with OData ("isRead eq false and importance eq 'high'")
  • Automatic pagination for extensive results
  • Structured result formatting

📧 Email Management

  • Draft creation with local attachments
  • Updating existing drafts
  • Email deletion
  • Support for categories and multiple recipients (TO, CC, BCC)

🧹 Advanced Content Cleaning

  • Intelligent HTML to plain text conversion
  • Automatic noise and disclaimer removal
  • Character and format normalization
  • Boilerplate detection and truncation
  • Optional lightweight mode to skip deep HTML cleaning (clean_body=False)

📊 Structured Formatting

  • Consistent dictionary format output
  • Automatic metadata extraction (sender, date, subject, etc.)
  • Automatic content summaries
  • Robust error handling

Installation

Prerequisites

  • Python 3.8+
  • Azure AD application registration with Microsoft Graph permissions
  • Configured environment variables

Dependencies

pip install mcp fastmcp office365-rest-python-client python-dotenv beautifulsoup4 html2text

Azure AD Configuration

  1. Register a new application in Azure AD

  2. Configure the following Microsoft Graph permissions:

    • Mail.Read - Read user emails
    • Mail.ReadWrite - Create, update and delete emails
    • Mail.Send - Send emails (if needed)
    • User.Read - Read basic user profile
  3. Generate a client secret

Environment Variables

Create a .env file in the project root:

CLIENT_ID=your_azure_client_id
CLIENT_SECRET=your_azure_client_secret
TENANT_ID=your_azure_tenant_id
# Optional: comma separated folders to search by default
OUTLOOK_DEFAULT_FOLDERS=Inbox,SentItems,Drafts

Note: Make sure to use exactly these variable names (CLIENT_ID, CLIENT_SECRET, TENANT_ID) as they are required by the server.

Usage

Starting the Server

python -m mcp_outlook_server.server

🎯 Function Selection Guide

Choose the right function for your use case:

Use CaseRecommended FunctionReason
Email listing/previewsSearch_Outlook_Emails_No_Body or Search_Outlook_Emails_No_Body_By_Search_QueryFaster, reduced bandwidth, optimized for UI
Reading full email contentGet_Outlook_EmailComplete email with formatted body
Natural language searchSearch_Outlook_Emails_By_Search_QueryKQL supports content-based searches
Precise filteringSearch_Outlook_EmailsOData filters for exact criteria
Performance-critical appsFunctions ending with _No_BodyOptimized for speed, uses native Graph bodyPreview
Content analysisSearch_Outlook_Emails or Search_Outlook_Emails_By_Search_QueryFull body content included
Building email clientsSearch_Outlook_Emails_No_Body + Get_Outlook_EmailList view + detail view pattern
Complex business logicOData functions (Search_Outlook_Emails*)Precise boolean logic and date ranges
User-friendly searchKQL functions (*_By_Search_Query)Natural language and keyword searches

🔍 Search Method Comparison

FeatureOData ($filter)KQL ($search)
PrecisionExact field matchingSemantic/fuzzy matching
SyntaxSQL-like operatorsNatural language keywords
PerformanceHighly optimizedGood for content search
Use CasesBusiness logic, exact filteringUser search, content discovery
Date RangesISO format requiredRelative dates supported
Boolean LogicFull AND/OR/NOT supportKeyword combinations

Recommendation: Use OData for precise business logic, KQL for user-facing search interfaces.

Available Tools

1. Get_Outlook_Email

Retrieves a specific email by its unique ID.

Parameters:

  • message_id (str): Unique message ID
  • user_email (str): Email of the mailbox owner

Example response:

{
  "success": true,
  "data": {
    "sender": "John Doe <john@example.com>",
    "date": "2024-01-15 10:30:00",
    "cc": ["maria@example.com"],
    "subject": "Project meeting",
    "summary": "Meeting confirmation to discuss project progress...",
    "body": "Complete email content clean and formatted",
    "id": "AAMkAGE1M2IyNGNmLWI4MjktNDUyZi1iMzA4LTViNDI3NzhlOGM2NgBGAAAAAADUuTiuQqVlSKDGAz"
  }
}
2. Search_Outlook_Emails

Advanced email search with OData filter support and optional field selection.

Parameters:

  • user_email (str): User email
  • query_filter (str, optional): OData filter
  • top (int, optional): Maximum number of results (default: 10)
  • folders (List[str], optional): Folders to search (default: ["Inbox", "SentItems", "Drafts"])
  • fields (List[str], optional): NEW - Specific fields to return (e.g., ['subject', 'sender', 'date', 'id'])

🎯 Field Selection Examples:

// Get only essential metadata
fields: ['subject', 'sender', 'date', 'id']

// Get summary information without full body
fields: ['subject', 'sender', 'summary', 'date']

// Get recipients and categorization
fields: ['subject', 'sender', 'cc', 'date', 'id']

// All available fields
fields: ['subject', 'sender', 'date', 'id', 'body', 'summary', 'cc']

OData filter examples (Validated ✅):

// Unread emails
"isRead eq false"

// Emails from specific sender
"from/emailAddress/address eq 'user@example.com'"

// Emails containing keywords in subject
"contains(subject, 'invoice')"

// Emails starting with specific text
"startswith(subject, 'Newsletter')"

// Date range filtering
"receivedDateTime ge 2025-01-01T00:00:00Z and receivedDateTime le 2025-12-31T23:59:59Z"

// High importance unread emails
"isRead eq false and importance eq 'high'"

// Emails with attachments from last month
"hasAttachments eq true and receivedDateTime ge 2025-06-01T00:00:00Z"

// Complex OR conditions
"startswith(subject, 'Invoice') or startswith(subject, 'Receipt')"

// Domain-based filtering
"contains(from/emailAddress/address, 'gmail.com')"

Performance Notes:

  • All OData operators validated: eq, ne, gt, ge, lt, le, contains, startswith
  • Boolean logic fully supported: and, or, not
  • Date filtering requires ISO 8601 format with timezone
  • Use _No_Body variants for 2-3x faster performance when full content isn't needed
2b. Search_Outlook_Emails_No_Body (Performance Optimized)

Performance-optimized email search that excludes email body content for faster processing and reduced data transfer.

Parameters:

  • user_email (str): User email
  • query_filter (str, optional): OData filter (same syntax as Search_Outlook_Emails)
  • top (int, optional): Maximum number of results (default: 10)
  • folders (List[str], optional): Folders to search (default: ["Inbox", "SentItems", "Drafts"])
  • fields (List[str], optional): NEW - Specific fields to return (optimized for no-body searches)

Key Benefits:

  • Faster performance: Excludes body content processing
  • Reduced bandwidth: Smaller response payload
  • Uses native Microsoft Graph bodyPreview field for the summary, which is more efficient and consistent than manual summary generation
  • Ideal for listings: Perfect for email previews and list views
  • Same filtering: Full OData filter support maintained

Use Cases:

  • Quick email listing and previews
  • Performance-critical searches when body content is not needed
  • Bandwidth-constrained environments
  • Building email selection interfaces
3. Search_Outlook_Emails_By_Search_Query (KQL Search)

Advanced search using Microsoft Graph's KQL (Keyword Query Language) search parameter. More powerful for natural language and content-based searches.

Parameters:

  • user_email (str): User email
  • search_query (str): KQL search query
  • top (int, optional): Maximum number of results (default: 10)
  • folders (List[str], optional): Folders to search (default: ["Inbox", "SentItems", "Drafts"])
  • fields (List[str], optional): NEW - Specific fields to return with intelligent KQL/OData mapping

KQL Search Examples (Validated ✅):

// Search by sender (multiple formats supported)
"from:john@example.com"
"from:John Doe" 

// Content and subject searches
"meeting agenda"
"subject:urgent"

// Recipient-based searches  
"to:maria@company.com"

// Date-based searches (natural language)
"received:today"
"received:last week"
"sent:2025-07-01..2025-07-07"

// Attachment searches
"hasattachment:true"

// Complex keyword combinations
"AI AND newsletter"
"project AND deadline NOT completed"
"invoice AND (Hetzner OR Google)"

// Mixed field and content searches
"from:boss@company.com urgent meeting"

KQL Advantages:

  • Natural language date expressions
  • Fuzzy matching and semantic search
  • Content-based discovery across subject and body
  • User-friendly syntax for end-user interfaces
  • Excellent for exploratory searches

When to use KQL vs OData:

  • Use KQL (Search_Outlook_Emails_By_Search_Query) for:

    • Natural language searches
    • Content-based searches (searching within email body)
    • Complex keyword combinations
    • Searches involving attachments
    • Date-relative searches ("today", "last week")
  • Use OData (Search_Outlook_Emails) for:

    • Precise property-based filtering
    • Boolean logic on specific fields
    • Exact date/time ranges
    • Flag-based searches (isRead, importance)
3b. Search_Outlook_Emails_No_Body_By_Search_Query (KQL + Performance)

Combines the power of KQL search with performance optimization by excluding email body content.

Parameters:

  • user_email (str): User email
  • search_query (str): KQL search query (same syntax as Search_Outlook_Emails_By_Search_Query)
  • top (int, optional): Maximum number of results (default: 10)
  • folders (List[str], optional): Folders to search (default: ["Inbox", "SentItems", "Drafts"])
  • fields (List[str], optional): NEW - Specific fields to return (optimized for KQL no-body searches)

Best for:

  • Fast KQL-based searches when you only need email metadata
  • Building search result previews with KQL capabilities
  • Performance-critical applications using natural language search

Example response for all search functions:

{
  "success": true,
  "data": [
    {
      "sender": "John Doe <john@example.com>",
      "date": "2024-01-15 10:30:00",
      "cc": ["maria@example.com"],
      "subject": "Project meeting",
      "summary": "Hi team, I wanted to schedule our weekly project review for Thursday at 2pm. Please confirm your availability and we'll send out the meeting invite. The agenda will cover...",
      "id": "AAMkAGE1M2IyNGNmLWI4MjktNDUyZi1iMzA4LTViNDI3NzhlOGM2NgBGAAAAAADUuTiuQqVlSKDGAz"
      // Note: 'body' field only included in full search functions unless specifically requested in 'fields'
    }
  ]
}

🎯 Field Selection Response Examples:

// With fields: ['subject', 'sender', 'id']
{
  "data": [
    {
      "asunto": "Project meeting",        // OData results use Spanish keys
      "remitente": "John Doe <john@example.com>",
      "id": "AAMkAGE1M2IyNGNmLWI4..."
    }
  ]
}

// KQL search with fields: ['subject', 'sender', 'date']
{
  "data": [
    {
      "subject": "Project meeting",       // KQL results use English keys
      "from": "John Doe <john@example.com>",
      "receivedDateTime": "2024-01-15 10:30:00"
    }
  ]
}

Field Mapping Reference:

Standard FieldOData Key (Spanish)KQL Key (English)
subjectasuntosubject
senderremitentefrom
datefechareceivedDateTime
cccopiadosccRecipients
bodycuerpobody
summaryresumenbodyPreview
ididid
4. Create_Outlook_Draft_Email

Creates a new draft email with support for attachments and categories.

Parameters:

  • subject (str): Email subject
  • body (str): Message body
  • to_recipients (List[str]): List of primary recipients
  • user_email (str): User email
  • cc_recipients (List[str], optional): CC recipients
  • bcc_recipients (List[str], optional): BCC recipients
  • body_type (str, optional): Body type ("HTML" or "Text")
  • category (str, optional): Email category
  • file_paths (List[str], optional): File paths to attach

Example:

create_draft_email_tool(
    subject="Project report",
    body="<p>Please find the requested report attached.</p>",
    to_recipients=["client@example.com"],
    cc_recipients=["supervisor@company.com"],
    user_email="my.email@company.com",
    category="Work",
    file_paths=["C:/documents/report.pdf", "C:/documents/data.xlsx"]
)
5. Update_Outlook_Draft_Email

Updates an existing draft, including adding new attachments.

Parameters:

  • message_id (str): ID of the draft to update
  • user_email (str): User email
  • All optional parameters from Create_Outlook_Draft_Email
6. Delete_Outlook_Email

Deletes an email by its ID.

Parameters:

  • message_id (str): ID of the message to delete
  • user_email (str): User email

🆕 Latest Updates (v0.1.15+)

Major Refactoring & Optimization

  • Unified Resource Architecture: Complete refactoring of resources.py with unified search logic and pagination
  • Eliminated Code Duplication: Removed legacy functions and consolidated helpers for better maintainability
  • Enhanced Performance: Optimized data fetching with configurable include_body parameter across all search functions
  • Improved Error Handling: Robust exception handling and fallback strategies
  • Attachment Management: Streamlined attachment handling with unified API approach

Advanced Search Capabilities

  • Comprehensive $filter Support: Full OData operator support validated (eq, ne, gt, ge, lt, le, contains, startswith)
  • Complete KQL Integration: Natural language search with Microsoft's Keyword Query Language
  • Combined Logic Operations: Complex AND/OR filtering with multiple conditions
  • Performance Optimized: Smart body inclusion/exclusion based on use case
  • Extensive Testing: Thoroughly validated against real mailbox data (sss@sofias.ai)

🎯 NEW: Field Selection Feature

  • Selective Data Retrieval: All search tools now support a fields parameter for returning only specific email fields
  • Bandwidth Optimization: Reduce response size by selecting only needed fields (e.g., ['subject', 'sender', 'date', 'id'])
  • Dual Mapping Support: Intelligent field mapping for both OData (Spanish keys) and KQL (English keys) search results
  • Autogen Compatible: Returns arrays optimized for AI agent frameworks and automated processing
  • Flexible Field Names: Supports standardized field names (subject, sender, date, id, body, summary, cc) across all search methods

Reply Drafts with Preserved Conversation History

  • The create_draft_email_tool function supports a reply_to_id parameter
  • When replying, drafts automatically include full conversation history with original formatting
  • HTML and plain text preservation for maximum fidelity
  • Seamless integration with Outlook's native reply behavior
Example usage
create_draft_email_tool(
    subject="Re: Project Update",
    body="Thank you for the update!",
    to_recipients=["user@example.com"],
    user_email="me@example.com",
    reply_to_id="AAMkAGI2...",
    category="Work"  # Now supports categories in replies
)

Validated Search Examples

All search patterns have been extensively tested and validated:

# Advanced OData filtering
"receivedDateTime ge 2025-07-01T00:00:00Z and isRead eq false"
"contains(subject, 'invoice') and importance eq 'high'"
"startswith(subject, 'Newsletter') or endswith(from/emailAddress/address, 'gmail.com')"

# KQL natural language searches  
"AI newsletter", "from:user@domain.com sent:2025-07-01..2025-07-07"
"subject:urgent AND hasattachment:true"

💡 Practical Examples

Example 1: Building an Email Client Interface

# Step 1: Get email list for preview (fast)
emails = search_emails_no_body_tool(
    user_email="user@company.com",
    query_filter="isRead eq false",
    top=20
)

# Step 2: When user selects an email, get full content
full_email = get_email_tool(
    message_id=selected_email_id,
    user_email="user@company.com"
)

Example 2: Smart Content Search

# Natural language search using KQL
meetings = search_emails_by_search_query_tool(
    user_email="user@company.com", 
    search_query="meeting AND (today OR tomorrow)",
    top=10
)

# Precise filtering using OData
urgent_unread = search_emails_tool(
    user_email="user@company.com",
    query_filter="isRead eq false and importance eq 'high'",
    top=5
)

Example 3: Performance-Optimized Searches with Field Selection

# Fast search for email previews without body processing
preview_results = search_emails_no_body_by_search_query_tool(
    user_email="user@company.com",
    search_query="from:boss@company.com project",
    top=50
)

# Get only essential fields for UI display
essential_data = search_emails_tool(
    user_email="user@company.com",
    query_filter="isRead eq false",
    fields=['subject', 'sender', 'date', 'id'],
    top=100
)

# Bandwidth-optimized search for autogen/AI processing
ai_optimized = search_emails_by_search_query_tool(
    user_email="user@company.com",
    search_query="meeting AND urgent",
    fields=['subject', 'sender', 'summary'],
    top=20
)

Example 4: Draft Management

# Create draft with attachments
draft = create_draft_email_tool(
    subject="Weekly Report",
    body="<h1>Weekly Status</h1><p>Please find attached...</p>",
    to_recipients=["team@company.com"],
    user_email="user@company.com",
    category="Work",
    file_paths=["C:/reports/weekly.pdf"]
)

# Update the draft
update_draft_email_tool(
    message_id=draft['id'],
    user_email="user@company.com",
    subject="Weekly Report - Updated",
    cc_recipients=["manager@company.com"]
)

Technical Features

Field Selection System (NEW in v0.1.15+)

The server now supports selective field retrieval across all search functions:

  1. Standardized Field Names: Use consistent field names (subject, sender, date, id, body, summary, cc) across all search methods
  2. Intelligent Mapping: Automatic detection and mapping between OData (Spanish keys) and KQL (English keys) result formats
  3. Bandwidth Optimization: Return only needed fields to reduce response size and improve performance
  4. Autogen Compatibility: Optimized array responses for AI agent frameworks and automated processing
  5. Flexible Implementation: Works with both full and no-body search variants

Unified Architecture (Latest)

The server features a completely refactored architecture with:

  1. Unified Resource Management: Single source of truth for all email operations in resources.py
  2. Optimized Data Fetching: Configurable body inclusion with include_body parameter
  3. Consolidated Pagination: Unified pagination logic across all search functions
  4. Eliminated Duplication: Removed legacy functions and consolidated helper methods
  5. Enhanced Error Handling: Robust exception handling with detailed logging

Content Cleaning System

The server includes an advanced cleaning system that:

  1. Converts HTML to plain text using html2text with optimized configuration
  2. Removes unwanted elements: scripts, styles, metadata, etc.
  3. Normalizes text: special characters, spaces, line breaks
  4. Detects and truncates disclaimers: identifies legal/boilerplate text patterns
  5. Generates automatic summaries: extracts first 150 relevant characters
  6. Smart Body Processing: Uses native Graph bodyPreview for _No_Body functions for optimal performance

Robust Error Handling

  • Exception handling decorators on all critical operations
  • Fallback strategies for content processing
  • Detailed logging for debugging
  • Input parameter validation
  • Graceful degradation for malformed content

Performance Optimizations

  • Smart Body Inclusion: include_body=False for 2-3x performance improvement
  • Field Selection: NEW - Return only specific fields to reduce bandwidth and processing time
  • Native Graph Features: Leverages bodyPreview for summaries when available
  • Unified Pagination: Consistent page limits to avoid timeouts
  • Optimized Queries: Reduced API calls through intelligent batching
  • Memory Efficient: Processes large result sets without memory bloat
  • Dual Mapping System: Efficient field mapping for both OData and KQL result formats

Project Structure

mcp-outlook/
├── src/
│   └── mcp_outlook_server/
│       ├── __init__.py
│       ├── server.py             # Server entry point
│       ├── common.py             # Configuration and Graph client setup
│       ├── tools.py              # MCP tool definitions (refactored & optimized)
│       ├── resources.py          # Unified resource access logic (major refactor)
│       ├── format_utils.py       # Email formatting and extraction utilities
│       ├── format_utils_search.py # Search result formatting
│       └── clean_utils.py        # Advanced content cleaning system
├── .env                          # Environment variables (don't include in git)
├── pyproject.toml               # Modern Python project configuration
└── README.md                    # This file

Recent Architecture Changes

  • resources.py: Completely refactored with unified search logic, eliminated duplicate functions, added field selection system
  • tools.py: Streamlined tool definitions, improved attachment handling, consolidated helpers, added fields parameter support
  • Performance: Added configurable include_body parameter and field selection across all search functions
  • Field Mapping: Implemented dual mapping system for OData (Spanish) and KQL (English) result compatibility
  • Testing: Comprehensive validation documented in conversation history, including field selection scenarios

Logging

The server generates detailed logs in:

  • File: mcp_outlook.log
  • Console: Standard output

Logging configuration in common.py with INFO level by default.

Troubleshooting

Authentication Error

  • Verify environment variables are correctly configured in .env file:
    • CLIENT_ID: Your Azure application (client) ID
    • CLIENT_SECRET: Your Azure application client secret
    • TENANT_ID: Your Azure tenant (directory) ID
  • Confirm Azure AD application has necessary permissions
  • Check that tenant ID is correct
  • Ensure the .env file is in the project root directory

Common Environment Variable Issues

  • Error: "Missing required environment variables": Make sure variable names match exactly (CLIENT_ID, CLIENT_SECRET, TENANT_ID)
  • Error: "Authentication failed": Verify the client secret hasn't expired and values are correct

Search Errors

  • Validate OData filter syntax
  • Verify user has access to specified folders
  • Check logs for specific Graph API errors

Attachment Issues

  • Confirm file paths exist and are accessible
  • Verify files don't exceed Outlook size limits (25MB per attachment, 150MB total)
  • Check file read permissions
  • Ensure attachment functions use the unified API approach (implemented in latest version)

Performance Issues

  • Use _No_Body functions for listings and previews (2-3x faster)
  • Use fields parameter to return only needed data (reduces bandwidth and processing time)
  • Limit top parameter to reasonable values (10-50 for UI, 100+ for batch processing)
  • Consider search scope - searching specific folders is faster than all folders
  • For large datasets, implement pagination using multiple calls
  • Field selection examples: ['subject', 'sender', 'id'] for lists, ['subject', 'sender', 'summary', 'date'] for previews

Search Issues

  • OData syntax: Ensure proper quoting and escaping of string values
  • Date formats: Use ISO 8601 format with timezone (e.g., 2025-07-01T00:00:00Z)
  • Field names: Use exact field names (from/emailAddress/address, not from.email)
  • KQL queries: Don't mix OData and KQL syntax in the same query
  • Field selection: Use standardized field names (subject, sender, date, id, body, summary, cc)
  • Result format: OData returns Spanish keys, KQL returns English keys - field mapping handles this automatically
  • Check the validation results in conversation history for confirmed working examples

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/new-functionality)
  3. Commit changes (git commit -am 'Add new functionality')
  4. Push to branch (git push origin feature/new-functionality)
  5. Create a Pull Request

License

MIT License

Copyright (c) 2024

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Support

To report bugs or request new features, please open an issue in the repository.