From snap-skills
Use this skill when the user wants to read, search, send, or draft Stanford Office 365 email from the command line or via an agent. Works via the Microsoft Graph API — does NOT require Stanford IT to enable IMAP on the mailbox. Syncs to a local Maildir for use with notmuch, mutt, or similar tools.
How this skill is triggered — by the user, by Claude, or both
Slash command
/snap-skills:emailThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill lets the agent read, search, send, and draft Stanford email without requiring IMAP to be enabled by Stanford IT. It uses Microsoft Graph API with device-code auth.
This skill lets the agent read, search, send, and draft Stanford email without requiring IMAP to be enabled by Stanford IT. It uses Microsoft Graph API with device-code auth.
TODO: This skill is currently untested end-to-end. The underlying script (
scripts/office365_sync.py) was contributed by Marcel and works in principle, but the full agent workflow (auth flow, cache location, notmuch integration) needs validation. If you hit issues, report them and/or update this skill.
The sync script is bundled with this skill at scripts/office365_sync.py (relative to the skill's base directory, which the Skill runtime injects as Base directory for this skill: <absolute-path> at the top of this file when loaded).
Always invoke it via that absolute path, e.g.:
uv run "<skill-base-dir>/scripts/office365_sync.py" <command>
Do NOT ask the user to copy the script to ~/Mail/ — it should be run in-place from the skill cache. User-specific state (token cache, Maildir, sync state) lives under ~/Mail/ regardless of where the script itself sits.
~/Mail/Stanford/) for indexing with notmuch.$search endpoint.Before the agent can use this skill, the user must:
The script uses uv's inline script metadata (PEP 723), so you can run it directly with uv run and dependencies (msal, requests) install automatically on first run.
Verify uv is installed:
uv --version
If not, install it: https://docs.astral.sh/uv/getting-started/installation/
Run the auth command from the bundled script. This opens a device-code flow: you'll get a URL and a code to paste in a browser (one-time).
mkdir -p ~/Mail
uv run "<skill-base-dir>/scripts/office365_sync.py" auth
The token is cached at ~/Mail/.office365/token_cache.json and auto-refreshed on subsequent runs. No password is ever stored.
After the first sync, point notmuch at the Maildir:
notmuch setup # set database.path = ~/Mail/Stanford
notmuch new
Add a systemd timer or cron job to run uv run "<skill-base-dir>/scripts/office365_sync.py" sync every 10 minutes. For cron, hardcode the resolved absolute path — the skill cache path is stable across skill versions but changes on upgrade, so pin to a specific version or re-install the timer when upgrading.
Privacy principle: This skill should only read/search email when the user explicitly asks. Do NOT proactively scan the mailbox — prompt injection risk is significant.
uv run "<skill-base-dir>/scripts/office365_sync.py" search "grants office"
uv run "<skill-base-dir>/scripts/office365_sync.py" folders
For arbitrary text queries, prefer notmuch search after a sync (faster, richer query syntax):
uv run "<skill-base-dir>/scripts/office365_sync.py" sync
notmuch search "from:[email protected] and date:last-week"
uv run "<skill-base-dir>/scripts/office365_sync.py" sync
Uses the stored timestamp in ~/Mail/.office365/sync_state.json to only fetch new messages. Use --full to ignore the timestamp and re-check all messages (they will not be re-downloaded if the filename hash already exists locally).
uv run "<skill-base-dir>/scripts/office365_sync.py" send \
--to "[email protected]" \
--subject "Subject here" \
--body "Body text"
For HTML emails, add --html. For CC/BCC, use --cc / --bcc.
Confirm with the user before sending. Always show the composed message and ask "Send this?" before running the command.
uv run "<skill-base-dir>/scripts/office365_sync.py" read <message-id>
Message IDs are Graph API IDs (long opaque strings). You typically get them from the search output or from notmuch results.
| Symptom | Likely cause | Fix |
|---|---|---|
Auth failed on auth command | Stanford O365 tenant blocks the default MSAL public client | User needs to register their own app at https://entra.microsoft.com and replace CLIENT_ID in the script |
403 Forbidden on any Graph call | Token expired or lacks scopes | Re-run auth |
Throttled, waiting Xs... | Rate limit (harmless, script handles it) | Wait for retry |
ModuleNotFoundError: msal | uv's inline dep management not picking up the script | Run with uv run --script "<skill-base-dir>/scripts/office365_sync.py" ... explicitly |
Sync runs but notmuch sees no mail | Maildir permissions or notmuch config | Check ~/Mail/Stanford/{new,cur,tmp} exist, and notmuch config |
Send fails with insufficient privileges | User declined Mail.Send scope during auth | Re-run auth and approve all scopes |
scripts/office365_sync.py (resolved against the skill base directory — do not copy out)~/Mail/Stanford/ (created by the script)~/Mail/.office365/token_cache.json~/Mail/.office365/sync_state.json~/Mail/.office365/ to git.set -x or verbose shells.auth.CONFIG_DIR and MAIL_DIR paths.Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub snap-stanford/snap-skills --plugin snap-skills