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.0). 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

This package is currently distributed from a private Git repository, not PyPI.

# pinned to a tag (recommended)
pip install "git+ssh://git@github.com/goetzcj/m365-cli.git@v0.1.0"

# or HTTPS with a GitHub token that has read access to the repo
pip install "git+https://<token>@github.com/goetzcj/m365-cli.git@v0.1.0"

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.0.tar.gz (35.1 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.0-py3-none-any.whl (39.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: m365_cli-0.1.0.tar.gz
  • Upload date:
  • Size: 35.1 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.0.tar.gz
Algorithm Hash digest
SHA256 93623aeb35a52d7f945dff60d034fab42e583170ec8553876a2333a214754934
MD5 051ed4ca7143b64e76491bb78d80f561
BLAKE2b-256 dc1e70acac91785f2a5f41e610819eb0f7e1d461ee81cff553860edd625006d8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: m365_cli-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.0 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3d4a005fd98ccba19a7021ef3b30ec9da1c3455984f0b1cfb41545cee784881c
MD5 74b5b91ca1579e7438413e35f30d6436
BLAKE2b-256 6fbc9452e128331885ce4571ec13d2e394367162a63deb0af41770eb6330a42e

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