Cross-platform CLI for Microsoft Outlook via the Microsoft Graph API, with dual implementations in Node.js and .NET NativeAOT.
- 📧 Email — inbox, read, search, draft, reply, forward, send, move, flag, mark-read
- 📅 Calendar — today, week, date range, view, create events, list calendars
- 👥 Contacts — search contacts and GAL, manage aliases (
fred→freddie@outlook.com) - 👤 Multi-account — personal + work accounts, parent chains, per-account permissions
- 👥 Delegate access — read and send on behalf of other users (
--as fred) - 👀 Watch mode — real-time change tracking via delta queries, rules engine for channel delivery
- 🔐 Security — AES-256-GCM encrypted token cache, configurable permissions, forbidden scopes, send_to whitelist, defense-in-depth token validation
- 🖥️ Cross-platform — Mac, Linux, Windows; device code flow for headless VMs
- 📊 Multiple output formats — text, json, markdown, html
- 🤖 AI agent integration — OpenClaw / NanoClaw dual-mode (Skill + Channel Factory)
- 🌉 COM Bridge — For enterprise environments where Graph API is blocked, connect via Outlook Desktop COM automation on Windows
git clone https://github.com/jeffstall/outlook-cli.git
cd outlook-cli
.\build.ps1 all
build.ps1runsnpm installautomatically and builds both Node.js and .NET NativeAOT for your current OS/architecture. Both implementations share the same config directory (~/.outlook-cli/) and can be used interchangeably. See docs/architecture.md for details.
You need an Azure App Registration (free). See docs/AZURE-SETUP.md for step-by-step instructions. Both accounts below can share the same client ID — each user authenticates independently.
# Add a read/write account
node bin/outlook-cli.js account add work --client-id YOUR_CLIENT_ID
node bin/outlook-cli.js auth login --account work
# Add a read-only account (can read mail but cannot send, draft, move, or delete)
node bin/outlook-cli.js account add monitor --client-id YOUR_CLIENT_ID --read-only
node bin/outlook-cli.js auth login --account monitorEach auth login opens a browser to sign in with a different Microsoft account. Read-only is enforced at two layers: the token only contains read scopes, and the CLI blocks write commands before they reach the API. See docs/usage/account.md for details.
# List recent messages for the read/write account
node bin/outlook-cli.js mail inbox --account work
# Read message #1 (short IDs are assigned by inbox/search commands)
node bin/outlook-cli.js mail read 1 --account work# Compose and send a message from "work" to "monitor"
node bin/outlook-cli.js mail draft --to "monitor@example.com" --subject "Hello from CLI" --body "Testing outlook-cli!" --send --yes --account workReplace
monitor@example.comwith the actual email address of the monitor account. Use--sendto compose and send in one step, or omit it to create a draft you can review first.
# Check the read-only account's inbox
node bin/outlook-cli.js mail inbox --account monitor# List all accounts
node bin/outlook-cli.js account list
# Remove a single account (deletes config and cached tokens)
node bin/outlook-cli.js account remove monitor
# Reset to clean state — remove the entire config directory
# (Windows)
rmdir /s /q "%USERPROFILE%\.outlook-cli"
# (Mac/Linux)
rm -rf ~/.outlook-cliSee docs/usage/account.md for the full account command reference.
| Document | Description |
|---|---|
| docs/usage.md | Complete command-line reference with examples |
| docs/architecture.md | Architecture, config formats, security model, integration |
| docs/src-node.md | Building and testing the Node.js implementation |
| docs/src-dotnet.md | Building and testing the .NET implementation |
| docs/src-tests.md | Test architecture and conventions |
| docs/AZURE-SETUP.md | Azure App Registration setup guide |
| docs/SECURITY.md | Security model and threat analysis |
| docs/MULTI-ACCOUNT.md | Multi-account configuration guide |
| docs/DELEGATE-ACCESS.md | Delegate access setup and usage |
| docs/WATCH-MODE.md | Watch mode and rules engine |
| docs/JSON-INPUT.md | JSON input file schemas for all commands |
| docs/DIRECTORY-STRUCTURE.md | Project directory layout |
| docs/self-hosting.md | Self-hosting guide including COM Bridge deployment |
┌──────────────────────────────────────────────────────┐
│ outlook-cli │
├──────────────────────┬───────────────────────────────┤
│ Node.js (ESM) │ .NET 8 (NativeAOT) │
│ Commander.js CLI │ System.CommandLine CLI │
│ @azure/msal-node │ MSAL.NET │
│ Direct HTTP Graph │ Direct HTTP Graph │
├──────────────────────┴───────────────────────────────┤
│ Shared Configuration │
│ ~/.outlook-cli/accounts.json │
│ ~/.outlook-cli/aliases.json │
│ ~/.outlook-cli/cache-*.enc (AES-256-GCM) │
│ ~/.outlook-cli/delta-*.json │
└──────────────┬───────────────────────────────────────┘
│ (provider = "bridge" in accounts.json)
┌──────────────▼───────────────────────────────────────┐
│ COM Bridge (optional) │
│ src/bridge/ — Windows-only ASP.NET Core server │
│ COM automation → Outlook Desktop → Exchange │
│ HMAC-SHA256 authenticated HTTP API │
└──────────────────────────────────────────────────────┘
Both implementations produce identical CLI behavior and share all configuration files. You can switch between them freely.
You need a free App Registration in Microsoft Entra ID. See docs/AZURE-SETUP.md for step-by-step instructions.
Required permissions:
| Permission | Purpose |
|---|---|
User.Read |
Read user profile |
Mail.Read |
Read email messages |
Mail.ReadWrite |
Create drafts, move, flag, mark-read |
Calendars.Read |
Read calendar events |
Calendars.ReadWrite |
Create calendar events |
offline_access |
Refresh tokens for persistent sessions |
Optional permissions:
| Permission | Purpose |
|---|---|
Mail.Send |
Send email from own account |
Mail.Send.Shared |
Send on behalf of delegate mailbox owner |
Mail.Read.Shared |
Read delegate mailbox |
Mail.ReadWrite.Shared |
Write to delegate mailbox |
Calendars.Read.Shared |
Read delegate calendar |
Security note: Permissions are configurable per account via
accounts.json. The security model supports forbidden scopes and send_to recipient whitelists. See docs/SECURITY.md.
- Read-only accounts — Restrict accounts to read-only operations with
--read-only(setup guide) - Configurable permissions — Each account defines allowed/forbidden scopes in
accounts.json - Forbidden scopes — Block specific permissions (e.g.,
Mail.Send) at the config level - send_to whitelist — Restrict which recipients can receive mail from a given account
- Defense-in-depth token validation — Tokens are inspected at runtime to enforce permission policy
- Encrypted token cache — AES-256-GCM encryption, PBKDF2 SHA-512 with 310,000 iterations
- Confirmation prompts — Write operations require confirmation (bypass with
--yesfor scripting)
See docs/SECURITY.md for the full threat model.
outlook-cli operates in two modes for AI agent integration:
- Skill mode — Agents invoke CLI commands and parse JSON output
- Channel Factory mode — Watch rules engine normalizes incoming emails to channel messages, delivered via stdout / webhook / exec / file
# OpenClaw — symlink as a skill
ln -s /path/to/outlook-cli/skill ~/.openclaw/skills/outlook
# NanoClaw — install in container
git clone https://github.com/jeffstall/outlook-cli.git /opt/outlook-cli
cd /opt/outlook-cli && npm install && npm linkSee skill/SKILL.md and skill/NANOCLAW.md.
| Variable | Description | Default |
|---|---|---|
OUTLOOK_CLI_CLIENT_ID |
Azure app client ID | (none) |
OUTLOOK_CLI_TENANT_ID |
Azure AD tenant ID | common |
OUTLOOK_CLI_PASSPHRASE |
Token cache encryption passphrase | Machine-derived |
OUTLOOK_CLI_LOG_LEVEL |
Logging level | warn |
All configuration is stored in ~/.outlook-cli/:
| File | Purpose |
|---|---|
accounts.json |
Account definitions, permissions, defaults |
aliases.json |
Contact aliases |
cache-*.enc |
Encrypted MSAL token cache |
delta-*.json |
Watch mode delta tokens |
config.json |
Optional global configuration |
# Node.js
npm install
npm test # 160+ tests via vitest
node bin/outlook-cli.js --help
# .NET
dotnet build src/dotnet/OutlookCli.csproj
dotnet run --project src/dotnet -- --help
# Both (unified build script)
.\build.ps1 allSee docs/src-node.md, docs/src-dotnet.md, and docs/src-tests.md.
MIT — see LICENSE