Skip to content
Automation
Skill

/ms365-connect

Connect to Microsoft 365 (OneDrive, Outlook mail, Calendar, Teams) via Microsoft Graph using the open-source python-o365 (O365) library. Use when the user wants to list/download OneDrive files, read or send Outlook email, list calendar events, or post a message to a Teams

From plugin
sutando
41087 skills20 hooks

Claude Code

$ npx -y skills add sonichi/sutando --skill ms365-connect --agent claude-code

SKILL.md

ms365-connect.SKILL.md
name: ms365-connect
description: >-
  Connect to Microsoft 365 (OneDrive, Outlook mail, Calendar, Teams) via
  Microsoft Graph using the open-source python-o365 (O365) library. Use when
  the user wants to list/download OneDrive files, read or send Outlook email,
  list calendar events, or post a message to a Teams channel from a Sutando
  workflow.

ms365-connect

Microsoft 365 connectivity for Sutando, built on the Apache-2.0 [`python-o365`](https://github.com/O365/python-o365) library (`O365` on PyPI), which is a thin wrapper over Microsoft Graph.

What it does

All operations go through Microsoft Graph via python-o365:

  • **OneDrive** — list a folder's contents (`onedrive-list`) and download a file

to a local path (`onedrive-get`).

  • **Outlook mail** — list recent inbox messages (`outlook-list`) and send an

email (`outlook-send`).

  • **Calendar** — list upcoming events over a window of days (`calendar-list`).
  • **Teams** — post a message to a channel in a team (`teams-post`).

Authentication is a one-time delegated OAuth2 consent flow (`auth`); the refresh token is cached on disk so subsequent commands run non-interactively.

Usage

Run the CLI with `python3 scripts/ms365.py <subcommand>`:

# One-time: run the OAuth consent flow and cache the token
python3 scripts/ms365.py auth

# OneDrive
python3 scripts/ms365.py onedrive-list                 # list root
python3 scripts/ms365.py onedrive-list "/Projects/Q3"  # list a folder by path
python3 scripts/ms365.py onedrive-get "/Reports/x.pdf" ./x.pdf

# Outlook mail
python3 scripts/ms365.py outlook-list --n 10
python3 scripts/ms365.py outlook-send --to a@b.com --subject "Hi" --body "Hello"

# Calendar
python3 scripts/ms365.py calendar-list --days 7

# Teams
python3 scripts/ms365.py teams-post --team "Engineering" --channel "General" \
  --message "Deploy is green"

Every subcommand except `auth` requires that `auth` has already been run and a cached token exists.

Configuration

Secrets are read from the environment (populate them from the Sutando vault; **never hardcode**):

| Variable | Purpose | |----------|---------| | `MS365_CLIENT_ID` | Azure AD application (client) ID | | `MS365_CLIENT_SECRET` | Azure AD client secret value | | `MS365_TENANT_ID` | Directory (tenant) ID, or `common` / `organizations` | | `MS365_AUTH_FLOW` | `credentials` (default; confidential client, id+secret) or `public` (native client, id only — required when the app's redirect is registered under "Mobile and desktop applications"; symptom of the mismatch: `AADSTS700025` on token exchange). In `public` flow `MS365_CLIENT_SECRET` is not required. |

If any is unset, the CLI exits with a clear message naming the missing variable.

**Token cache:** the OAuth token is stored under the workspace state dir at `state/ms365-token/o365_token.txt` via python-o365's `FileSystemTokenBackend`. Override the base with `MS365_STATE_DIR` if the workspace state dir is elsewhere. Treat this file as a live credential (owner-only permissions).

Azure AD app registration (one-time, owner does this)

Delegated Graph access requires an app registration. The owner performs this once in the Azure Portal:

1. **Register the app** — Azure Portal → **Microsoft Entra ID** (Azure AD) → **App registrations** → **New registration**. Name it (e.g. `Sutando M365`). For "Supported account types", pick single-tenant unless multi-tenant is required. Copy the **Application (client) ID** and **Directory (tenant) ID**.

2. **Add a redirect URI** — in the app's **Authentication** blade → **Add a platform** → **Mobile and desktop applications**, add python-o365's default native-client redirect: `https://login.microsoftonline.com/common/oauth2/nativeclient` (A `http://localhost` redirect also works if you prefer a loopback flow.)

3. **Grant delegated Microsoft Graph scopes** — **API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions**, add:

| Scope | Why | |-------|-----| | `offline_access` | Issue a refresh token so the token cache survives | | `User.Read` | Read the signed-in user's basic profile | | `Files.ReadWrite.All` | List and download (and write) OneDrive files | | `Mail.Read` | Read Outlook inbox messages | | `Mail.Send` | Send Outlook email as the user | | `Calendars.ReadWrite` | Read (and create) calendar events | | `Team.ReadBasic.All` | Look up the team by name (`GET /me/joinedTeams`) — required before posting | | `Channel.ReadBasic.All` | Look up the channel by name (`GET /teams/{id}/channels`) — required before posting | | `ChannelMessage.Send` | Post messages to Teams channels | | `Chat.Read` | Read Teams chat/channel context |

> `teams-post` resolves the team and channel **by display name** before it > can send, so the two `*.ReadBasic.All` lookup scopes are mandatory — without > them the lookups return empty and the CLI reports `Team not found` even > though the team exists.

Click **Grant admin consent** for the tenant if your org requires it.

4. **Create a client secret** — **Certificates & secrets** → **New client secret**. Copy the secret **Value** immediately (shown once).

5. **Store the 3 credentials in the vault, not in code** — put the Application (client) ID, the client secret Value, and the Directory (tenant) ID in the Sutando vault, exposed to this skill as `MS365_CLIENT_ID`, `MS365_CLIENT_SECRET`, and `MS365_TENANT_ID`.

Then run `python3 scripts/ms365.py auth` once to complete the consent flow and cache the token.

Requirements

Install the dependency: `pip install -r requirements.txt` (`O365`).

Read more
@sonichi
Ships withsutando

My AI Stand — Realtime by Day, Rewriting Itself by Night. Summon my AI superpower. Voice, vision, screen, meetings, calls when I'm engaged. Learns my patterns, ships its own code when I'm not. Runs across my Macs, interacts with people & their Stands.

Stars on GitHub

Momentum

stars in total
410
Star history of sonichi/sutando on GitHub, a chart by star-history.com

Other skills on sutando.

ag2-space-mcp

ag2-space-mcp

Connect this Sutando install to AG2 Space's hosted MCP as its own agent, without the desktop app, and know which room and Commons…

@sonichi@sonichiInstall Skill
ag2space-voice

ag2space-voice

s.space voice surface — binds the room the s.space client announces to the voice session's origin, so work delegated by voice in…

@sonichi@sonichiInstall Skill
agent-activity

agent-activity

Streams what the agent is doing into the room, as rows the desktop client renders in an **events drawer** above the composer…

@sonichi@sonichiInstall Skill
agent-registry

agent-registry

Local Agent Registry — a standalone, dependency-free service that tracks running Claude Code (and other) agent instances. Agents…

@sonichi@sonichiInstall Skill
agent-room-ops

agent-room-ops

For an all-room analysis sweep, use the tested window collector and coverage contract in…

@sonichi@sonichiInstall Skill
answer-format

answer-format

Deterministic final-answer normalizer — a last-step pass for any task that ends in a *precise* answer (a number, a short string,…

@sonichi@sonichiInstall Skill

More skills from other plugins.

agent-builder

agent-builder

Load immediately after an Agent intent, before the first agent_builder_* call. Governs how you create, configure, test, and…

n8n2w209k
@n8n-io@n8n-ioInstall Skill
browser-use

browser-use

Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…

@browser-use@browser-useInstall Skill
add-anydoc

add-anydoc

Add local office-document-to-Markdown conversion to NanoClaw agent containers with the pinned Firecrawl AnyDoc CLI. Use when…

@nanocoai@nanocoaiInstall Skill

Questions about ms365-connect.

Which coding agents can use ms365-connect?
ms365-connect installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.