Give Claude (or any AI assistant) full access to your Outlook mailbox — read, search, send, reply, forward, organize, and manage drafts — all through the Model Context Protocol.
git clone https://github.com/ajaya/outlook-mcp-ruby.git
cd outlook-mcp-ruby
bundle install
cp .env.example .env # edit with your Azure credentials
bin/outlook-mcp auth # one-time OAuth login
bin/outlook-mcp config claude-code # prints config you can paste17 mail tools exposed over MCP stdio transport:
| Tools | |
|---|---|
| Read | list_emails search_emails read_email list_folders list_attachments get_attachment |
| Compose | send_email reply_to_email reply_all_to_email forward_email create_draft send_draft |
| Manage | mark_as_read move_emails copy_email delete_email create_folder |
- Ruby 4.0+ — pinned in
.ruby-version; works with rv, rbenv, mise, asdf, rvm, chruby - Azure AD app registration with Microsoft Graph delegated permissions
- Azure Portal > App registrations > New registration
- Redirect URI:
http://localhost:3333/auth/callback(Web platform) - Certificates & secrets > new client secret
- API permissions > add Microsoft Graph delegated:
User.Read,Mail.Read,Mail.ReadWrite,Mail.Send - Note your Client ID, Client secret, and Tenant ID
git clone https://github.com/ajaya/outlook-mcp-ruby.git
cd outlook-mcp-ruby
bundle installCreate .env from the example:
cp .env.example .envOUTLOOK_CLIENT_ID=your-client-id
OUTLOOK_CLIENT_SECRET=your-client-secret
OUTLOOK_TENANT_ID=commonAuthenticate (one-time):
bin/outlook-mcp authThis opens your browser for Azure AD consent and saves tokens to ~/.outlook-mcp-tokens.json. Tokens auto-refresh when expired.
Generate a ready-to-paste config snippet (reads your .env credentials):
bin/outlook-mcp config claude-desktop
bin/outlook-mcp config claude-codeOr add manually:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"outlook": {
"command": "/path/to/outlook-mcp-ruby/bin/outlook-mcp",
"args": ["server"],
"env": {
"OUTLOOK_CLIENT_ID": "...",
"OUTLOOK_CLIENT_SECRET": "...",
"OUTLOOK_TENANT_ID": "..."
}
}
}
}Claude Code: same, without env (reads from .env file directly).
The bin/outlook-mcp wrapper auto-detects your Ruby version manager so MCP clients (which spawn with a minimal PATH) find the right Ruby. Supported: rv, rbenv, mise, asdf, rvm, chruby.
If auto-detection picks the wrong one, pin it in .env:
RUBY_VERSION_MANAGER=rv| Variable | Default | Description |
|---|---|---|
OUTLOOK_CLIENT_ID |
required | Azure app client ID |
OUTLOOK_CLIENT_SECRET |
required | Azure app client secret |
OUTLOOK_TENANT_ID |
common |
Azure tenant ID |
OUTLOOK_TOKEN_PATH |
~/.outlook-mcp-tokens.json |
Token storage location |
OUTLOOK_REDIRECT_URI |
http://localhost:3333/auth/callback |
OAuth redirect URI |
OUTLOOK_SCOPES |
offline_access User.Read Mail.Read Mail.ReadWrite Mail.Send |
OAuth scopes |
RUBY_VERSION_MANAGER |
auto |
auto, rv, rbenv, mise, asdf, rvm, chruby |
bin/outlook-mcp auth # OAuth login
bin/outlook-mcp server # Start MCP server
bin/outlook-mcp config TARGET # Generate config (claude-desktop, claude-code)
bin/outlook-mcp version # Print version
bin/outlook-mcp debug # Config, token status, registered tools
bin/outlook-mcp tools # List all MCP tools
bin/outlook-mcp tool NAME # Tool details and input schema
bin/outlook-mcp token # Token status
# Direct mail operations
bin/outlook-mcp me # Authenticated user profile
bin/outlook-mcp inbox [COUNT] # Recent emails (--folder=ID)
bin/outlook-mcp read ID # Read email
bin/outlook-mcp search QUERY # Search (--count=N)
bin/outlook-mcp folders # List folders
bin/outlook-mcp attachments ID # List attachmentslib/outlook_mcp/
├── config.rb # Env var loading
├── auth/
│ ├── token_store.rb # JSON token persistence + auto-refresh
│ ├── oauth_client.rb # Azure AD OAuth2 flow
│ └── callback_server.rb # WEBrick server for OAuth callback
├── graph/
│ ├── sanitize_ids.rb # Automatic ID validation via prepend callbacks
│ ├── client.rb # Faraday HTTP client with Bearer auth + 401 retry
│ ├── email.rb # Mail API methods (mixed into Client)
│ └── folder.rb # Folder API methods (mixed into Client)
├── tools/ # 17 MCP tool classes
├── server.rb # MCP::Server factory
└── cli.rb # Thor CLI
Key design decisions:
- Dependency injection — tools receive
server_context[:graph]instead of globals - Graph mixins —
Graph::EmailandGraph::Foldermixed intoGraph::Client - Auto token refresh — catches 401, refreshes, retries once
- ID sanitization —
SanitizeIds.wrap(mod)auto-validates ID parameters viaprepend - No OAuth2 gem — Azure AD OAuth2 is two HTTP calls; Faraday is enough
bundle exec rake test
bundle exec ruby -Itest test/outlook_mcp/graph/email_test.rb
bundle exec rubocopSee TOOLS.md for Graph API endpoints and methods available for future tools.
MIT