Skip to main content

Agent-friendly Microsoft 365 CLI — email, calendar, and more via the Graph API

Project description

m365-cli

Agent-friendly Microsoft 365 CLI — read and manage Outlook email, calendar, and people lookups via the Microsoft Graph API. All commands emit structured JSON to stdout; errors emit JSON to stderr with a non-zero exit code, so the tool is usable both by humans and by scripts/agents.

Status: alpha (0.1.1). Surface area and JSON shapes may change.

Requirements

  • Python 3.11+
  • An Azure AD app registration in the tenant you want to access (Client ID + Tenant ID). A client secret is only required for the headless client_credentials mode.

Install

pip install m365-cli

After install you'll have an m365 command on your PATH.

Azure app registration (one-time)

  1. Azure Portal → App registrations → New registration.
  2. Supported account types: pick what matches your tenant (single-tenant for most users; "common" works for personal multi-tenant).
  3. After creation, go to Authentication → Advanced settings and set Allow public client flows = Yes (required for the device-code flow).
  4. Go to API permissions → Add a permission → Microsoft Graph → Delegated and add: User.Read, Mail.Read, Mail.ReadWrite, Mail.Send, Calendars.Read, Calendars.ReadWrite, People.Read. Grant admin consent if your tenant requires it.
  5. Copy the Application (client) ID and Directory (tenant) ID — you'll paste them into the setup wizard.

For the headless client_credentials mode you'll also need a client secret and Application (not Delegated) Graph permissions, with admin consent.

Configure

Run the interactive wizard (writes a .env in the current directory):

m365 setup

Then authenticate:

m365 auth login        # device-code flow; opens a browser
m365 auth status       # confirm who you're signed in as

Configuration is read from the following locations (later overrides earlier):

  1. ~/.m365/.env — global config written by m365 setup (works from any directory)
  2. .env in the current working directory — per-project override

Environment variables set in the shell always override both files. See .env.example for all supported keys.

Common commands

# Email
m365 email list --limit 10 --unread-only
m365 email search --query "invoice" --since 2025-01-01
m365 email read   <message-id>
m365 email send   --to alice@example.com --subject "hi" --body "hello"
m365 email reply  <message-id> --body "thanks"

# Calendar
m365 calendar events       --start 2025-04-25 --end 2025-04-30
m365 calendar create       --subject "Sync" --start ... --end ... --attendee ...
m365 calendar availability --user a@example.com --user b@example.com --start ... --end ...

# People
m365 people search --query "alice"

# Auth
m365 auth login | logout | status | token

m365 --help lists every group; each group and command has its own --help.

Where things live on disk

Path Purpose
~/.m365/tokens.json OAuth tokens (refresh token Fernet-encrypted), chmod 600
~/.m365/.key Fernet encryption key, chmod 600 (auto-generated on first login)
~/.m365/m365.log Rotating log file (5 MB × 3)
./.env Per-project configuration (Client ID, tenant, preferences)

Security notes

  • The refresh token is encrypted at rest with Fernet. The encryption key is stored in ~/.m365/.key next to the ciphertext with the same permissions, so the on-disk encryption is defense-in-depth, not a barrier against a local attacker who already has read access to your home directory. Treat ~/.m365/ as sensitive.
  • To make tokens portable across machines, export the auto-generated key (printed on first login) into TOKEN_ENCRYPTION_KEY in your .env.
  • m365 auth token prints the raw bearer access token to stdout — use it only when you intend to pipe it into another tool.
  • client_credentials mode requires MICROSOFT_CLIENT_SECRET. Never commit it. .env, *.key, *.pem, and *.p12 are gitignored.
  • Logs in ~/.m365/m365.log may contain Graph request metadata (URLs, status codes) but tokens are truncated before being logged.

Exit codes

Code Meaning
0 success
1 error
2 authentication required (run m365 auth login)
3 resource not found

Development

git clone git@github.com:goetzcj/m365-cli.git
cd m365-cli
uv sync --extra dev      # or: pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

m365_cli-0.1.1.tar.gz (34.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

m365_cli-0.1.1-py3-none-any.whl (38.9 kB view details)

Uploaded Python 3

File details

Details for the file m365_cli-0.1.1.tar.gz.

File metadata

  • Download URL: m365_cli-0.1.1.tar.gz
  • Upload date:
  • Size: 34.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for m365_cli-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4a89782cdb73133528b7c947a552c7e87e5080f56e6d814d14211d0aa091dfff
MD5 fe4a7be3f330d3263be26cf29643952b
BLAKE2b-256 132ee460ba9f9e6264adc0bffb8fda183f3b1b60c5607f20283394610133b0fd

See more details on using hashes here.

File details

Details for the file m365_cli-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: m365_cli-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 38.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for m365_cli-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 194f6fa40fc570d20ce64a39fdec07990a00228aa5d26aef822068dc0d934679
MD5 0cc7399b929e94f069f586f050d12d47
BLAKE2b-256 e6b666e5bb68c7c3e3634b80493b6e512a161660907636d04b941d1cb0ef8175

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page