Skip to main content

Local-first Microsoft 365 email, calendar, and Teams toolkit with MCP support and guarded write flows

Project description

ms365-toolkit

ms365-toolkit is a local-first Microsoft 365 email, calendar, and Teams toolkit for AI agents and maintainers. It provides:

  • device-code auth
  • Graph read clients and guarded email write flows
  • local mailbox indexing
  • triage and briefing workflows
  • a local labeling workflow for improving per-user email intelligence

Public beta note: this is local-first tooling for delegated Microsoft 365 access. Each installation uses its own Microsoft Entra public-client app and grants the delegated Graph permissions required by the features in use.

See CHANGELOG.md for release notes.

Security and Privacy

  • Tokens, local profile config, mailbox indexes, labels, audit logs, and usage analytics stay on the user's machine.
  • Device-code auth uses a public-client Entra app registration; no client secret is required.
  • Usage analytics record coarse command/tool metadata, durations, sizes, and sanitized errors, not email bodies, recipients, subjects, transcript text, raw Graph IDs, or tokens.
  • Guarded email writes require a write token, user.endpoint = "me", configured allowlists, rate limits, confirmation/audit checks, and a content hash for send.
  • Microsoft Graph access is delegated and tenant-controlled. Some Teams channel-message, file, transcript, and Copilot notes features require additional scopes, admin consent, licenses, or available tenant data.

Agent Setup

Use this path when an AI agent needs to operate Microsoft 365 through Codex or Claude. A repository clone is not required for normal MCP use.

Runtime Model

  • The AI client launches a local stdio MCP server named ms365.
  • The MCP server runs on the local machine through uvx --refresh-package ms365-toolkit-mcp ms365-toolkit-mcp@latest.
  • The MCP server uses your Microsoft 365 delegated login to call Microsoft Graph.
  • Tokens are stored in your OS keychain and profile config stays under ~/.ms365-toolkit.
  • No client secret is used or stored.

Before You Start

Required before registration:

  • Python 3.11 or newer.
  • uv, verified with uv --version.
  • Codex CLI or Claude CLI, verified with codex --version or claude --version.
  • A Microsoft 365 work or school account with access to the mailbox, calendar, and Teams data the agent should operate on.
  • Permission to create or update a Microsoft Entra app registration. If you cannot access app registrations or admin consent, ask your Microsoft 365 tenant admin.

Install uv if it is missing:

curl -LsSf https://astral.sh/uv/install.sh | sh

1. Create the Microsoft Entra App

Open the Microsoft Entra admin center:

https://entra.microsoft.com

Create the app registration:

  • Go to Identity -> Applications -> App registrations -> New registration.
  • Name it something recognizable, such as ms365-toolkit-local.
  • Choose Accounts in this organizational directory only unless you intentionally need a multi-tenant app.
  • Leave redirect URI blank.
  • Select Register.

Copy these two values from the app Overview page:

  • Application (client) ID: this becomes client_id.
  • Directory (tenant) ID: this becomes tenant_id.

These are IDs, not passwords or API keys.

Enable device-code login:

  • Go to Authentication.
  • Under Advanced settings, set Allow public client flows to Yes.
  • Select Save.

Do not create a client secret for this toolkit. Device-code auth uses a public client app registration. No client secret is required.

Microsoft references:

2. Add Microsoft Graph Permissions

In the app registration:

  • Go to API permissions.
  • Select Add a permission.
  • Choose Microsoft Graph.
  • Choose Delegated permissions.
  • Add the permissions for the features you want.
  • If your tenant requires admin consent, have an admin select Grant admin consent.

Start with the base read-only permissions:

Feature Delegated permissions Login command
Email, calendar, basic Teams reads Mail.Read, Calendars.Read, MailboxSettings.Read, Chat.Read, Team.ReadBasic.All, Channel.ReadBasic.All auth login
Teams channel messages ChannelMessage.Read.All auth login --teams-channel-messages
Teams file downloads Files.Read auth login --teams-files
Email inline SharePoint/OneDrive link downloads Files.Read.All, Sites.Read.All auth login --share-links
Meeting transcripts OnlineMeetings.Read, OnlineMeetingTranscript.Read.All auth login --meeting-transcripts
Meeting notes and Copilot insights OnlineMeetings.Read, OnlineMeetingAiInsight.Read.All auth login --meeting-notes
Directory org-tree reads User.Read.All auth login --directory
Guarded email and calendar writes Mail.ReadWrite, Mail.Send, Calendars.ReadWrite, MailboxSettings.ReadWrite auth login --write

Only add optional permissions for features you plan to use. Channel messages, meeting transcripts, meeting notes, directory org-tree reads, inline SharePoint/OneDrive link downloads, and some tenant-wide Teams data commonly require admin consent, tenant policy support, the right license, and actual available data.

Microsoft references:

3. Create Local Config

Create the default profile config:

mkdir -p ~/.ms365-toolkit/profiles/default
cat > ~/.ms365-toolkit/profiles/default/config.toml <<'EOF'
[auth]
tenant_id = "<paste-directory-tenant-id-here>"
client_id = "<paste-application-client-id-here>"

[user]
endpoint = "me"
user_principal_name = ""
timezone = "America/Chicago"

[safety]
domain_allowlist = []
folder_allowlist = ["Archive", "Follow Up"]
send_email_per_hour = 5
send_email_per_day = 20
draft_email_per_hour = 10
draft_email_per_day = 30
reply_email_per_hour = 5
reply_email_per_day = 20
forward_email_per_hour = 5
forward_email_per_day = 20
move_email_per_hour = 20
flag_email_per_hour = 50
create_event_per_hour = 5
create_event_per_day = 20
update_event_per_hour = 10
respond_event_per_hour = 10

[vip]
emails = []
domains = []

[intelligence]
critical_keywords = ["exec", "leadership", "decision", "board"]
noise_keywords = ["ooo", "focus", "block"]
action_keywords = ["urgent", "P1", "approve", "deadline"]
EOF

Edit the file and replace:

  • <paste-directory-tenant-id-here> with the Directory (tenant) ID.
  • <paste-application-client-id-here> with the Application (client) ID.
  • timezone with your timezone if America/Chicago is not correct.

Keep endpoint = "me" for normal personal mailbox, calendar, and Teams access. endpoint = "users" is only for delegated/shared mailbox scenarios and requires user_principal_name.

domain_allowlist = [] is safe for read-only use. Guarded email writes will stay blocked until you explicitly add allowed recipient domains.

4. Authenticate

Run the base read-only login:

uvx --from ms365-toolkit@latest ms365-toolkit auth login

The CLI prints a verification URL and user code. Open the URL in a browser, enter the code, and sign in with your Microsoft 365 account.

Run one login command with the optional flags you configured. For example, you can combine flags:

uvx --from ms365-toolkit@latest ms365-toolkit auth login --teams-channel-messages --teams-files --share-links --meeting-transcripts --meeting-notes --directory
uvx --from ms365-toolkit@latest ms365-toolkit auth login --write

Use --write separately because write tokens are stored separately from read tokens.

Check status:

uvx --from ms365-toolkit@latest ms365-toolkit auth status
uvx --from ms365-toolkit@latest ms365-toolkit doctor

5. Add the MCP to Codex or Claude

Recommended Codex registration:

codex mcp add ms365 --env MS365_TOOLKIT_PROFILE=default --env MS365_TOOLKIT_CLIENT=codex --env MS365_TOOLKIT_WARN_STALE=1 --env UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache --env UV_TOOL_DIR=/tmp/ms365-toolkit-uv-tools -- sh -lc 'cd /tmp && exec uvx --refresh-package ms365-toolkit-mcp ms365-toolkit-mcp@latest'

Recommended Claude registration:

claude mcp add -s user ms365 -e MS365_TOOLKIT_PROFILE=default -e MS365_TOOLKIT_CLIENT=claude -e MS365_TOOLKIT_WARN_STALE=1 -e UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache -e UV_TOOL_DIR=/tmp/ms365-toolkit-uv-tools -- sh -lc 'cd /tmp && exec uvx --refresh-package ms365-toolkit-mcp ms365-toolkit-mcp@latest'

Restart any open Codex or Claude sessions after registration. Existing sessions usually keep using the MCP processes they started with.

Verify the saved registration:

codex mcp get ms365
claude mcp get ms365

Both should show a command ending with:

uvx --refresh-package ms365-toolkit-mcp ms365-toolkit-mcp@latest

The --refresh-package flag refreshes cached package metadata, and the @latest suffix resolves the latest published MCP wrapper when a new MCP process starts. The MS365_TOOLKIT_WARN_STALE=1 environment variable enables startup and response warnings if a process is still running an older version.

6. Verify Inside Your AI Session

After restarting Codex or Claude, ask the AI to run these MCP checks:

Use the ms365 MCP tool get_toolkit_status with include_latest=true.
Use the ms365 MCP tool list_inbox with top 3 and max_chars 12000.

A healthy setup should return toolkit status and a small inbox summary. If the tool is missing, the AI session probably started before MCP registration or is not using the profile where ms365 was added.

You can also verify from a terminal:

UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache uvx --from ms365-toolkit@latest ms365-toolkit inbox --top 5
UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache uvx --from ms365-toolkit@latest ms365-toolkit version --check

Example Use Cases

After setup, ask Codex or Claude to use the ms365 MCP with prompts like these.

Check Toolkit Health

Use the ms365 MCP tool get_toolkit_status with include_latest=true and tell me whether the toolkit is connected, current, and ready to use.

Needs: base setup.

Triage Recent Inbox

Use ms365 to list my latest inbox messages, then summarize the top actionable items, deadlines, and who I need to reply to.

Needs: base read-only login.

Understand an Email Thread

Search my email for "Q3 Vendor Planning", read the conversation thread, and tell me what is going on, open decisions, blockers, and next actions.

Needs: base read-only login.

Prepare for a Meeting

List my calendar for tomorrow, identify the highest-priority meeting, then search related email and Teams context to create a short prep brief.

Needs: base read-only login. Teams context requires the relevant Teams permissions.

Pull a Meeting Transcript

Find yesterday's "Pricing Review Knowledge Transfer" meeting and pull the transcript if it was recorded and transcribed. Summarize decisions, risks, and action items.

Needs: auth login --meeting-transcripts.

Pull Meeting Notes

Find the meeting notes or Copilot insights for my latest project meeting and summarize key decisions and follow-ups.

Needs: auth login --meeting-notes.

Search Teams Context

Search Teams messages for "forecast model" and summarize the relevant threads, who said what, and what needs follow-up.

Needs: base Teams read permissions. Channel messages require auth login --teams-channel-messages.

Read Teams-Shared Files

Find files attached to this Teams message or thread, read the supported documents, and summarize what they contain.

Needs: auth login --teams-files.

Download Files Linked Inside Email

Read this email, list any SharePoint or OneDrive links embedded in the body, download the linked documents, and summarize the extracted text.

CLI equivalent:

ms365-toolkit download-email-share-links MESSAGE_ID --output-dir ./downloads

Needs: auth login --share-links.

Draft a Guarded Reply

Draft a reply to this email thread that confirms the next steps. Do not send it. Create a draft only and show me the content hash.

Needs: auth login --write and domain_allowlist configured.

Find Availability

Find 30-minute windows this week when these attendees are free: person1@example.com and person2@example.com.

Needs: base calendar read permissions.

Common Problems

  • config.toml not found: rerun the config creation command and confirm the file exists at ~/.ms365-toolkit/profiles/default/config.toml.
  • invalid_client or login says the client cannot be found: check that client_id is the Application (client) ID and tenant_id is the Directory (tenant) ID.
  • Device-code login is blocked: confirm Authentication -> Advanced settings -> Allow public client flows is set to Yes.
  • Permission or consent error: add the missing Microsoft Graph delegated permission and ask a tenant admin to grant admin consent if required.
  • Teams channel messages fail: confirm ChannelMessage.Read.All was added and login was run with --teams-channel-messages.
  • Teams file downloads fail: confirm Files.Read was added and login was run with --teams-files.
  • Email-linked SharePoint/OneDrive downloads fail: confirm Files.Read.All and Sites.Read.All were added and login was run with --share-links.
  • Meeting transcripts are empty or skipped: the meeting must have been recorded/transcribed, the signed-in user must have access, and login must include --meeting-transcripts.
  • Meeting notes are empty or skipped: Copilot notes must exist, the signed-in user must have access, and login must include --meeting-notes.
  • MCP is not visible in Codex or Claude: run codex mcp get ms365 or claude mcp get ms365, then restart the AI session.
  • MCP version is stale: use the @latest registration command again, restart the AI session, and run get_toolkit_status.
  • Email writes are blocked: run auth login --write, keep endpoint = "me", and add allowed recipient domains to domain_allowlist.

MCP Tool Behavior

Email, Teams, and calendar list/search MCP tools return compact summaries by default and accept max_chars to cap response size. Use the corresponding read/get tool when you need the full message, event, thread, attachment, transcript, or meeting-notes payload. Email list_inbox and search_emails return short message_ref values by default so agents can call read_email, attachment, share-link, and reply tools without copying long Graph IDs. Pass include_ids=true only when you need raw message IDs. read_email already returns body share links. Use inspect_email_assets when you need the email body, downloadable attachments, and body share links together without separate MCP calls.

When a compact MCP response returns truncated: true with next_offset, repeat the same tool call with offset=<next_offset> to continue through the current ordered result set. If next_offset is null, increase max_chars because the next item did not fit.

Graph-backed MCP collection tools can also return next_cursor. Repeat the same tool with cursor=<next_cursor> to continue with Microsoft Graph's next page. If both next_offset and next_cursor are possible, consume next_offset first; next_cursor is only returned after the current response page fits inside max_chars.

Cursor support covers list_inbox, search_emails, list_events, search_events, list_calendars, list_chats, list_chat_messages, search_teams_messages, list_teams, list_channels, list_channel_messages, and list_channel_replies.

Local derived searches such as search_chats, search_chat_messages, and search_channel_messages remain bounded first-page convenience filters. Use search_teams_messages for paged global Teams message search.

Supported v1 tools include guarded email writes:

  • get_toolkit_status
  • get_capability_status
  • list_inbox
  • search_emails
  • read_email
  • inspect_email_assets
  • read_conversation_thread
  • get_org_tree
  • list_email_attachments
  • read_email_attachment
  • list_email_share_links
  • download_share_link
  • download_email_share_links
  • create_email_draft
  • create_reply_draft
  • create_reply_all_draft
  • send_email_draft
  • flag_email
  • unflag_email
  • move_email
  • archive_email
  • list_folders
  • list_events
  • get_event
  • search_events
  • list_calendars
  • find_free_slots
  • create_event
  • update_event
  • cancel_event
  • respond_event
  • list_chats
  • search_chats
  • list_chat_messages
  • search_chat_messages
  • search_teams_messages
  • read_chat_message
  • list_teams
  • list_channels
  • list_channel_messages
  • search_channel_messages
  • list_channel_replies
  • read_channel_message
  • read_channel_thread
  • list_teams_message_files
  • read_teams_message_file
  • list_meeting_transcripts
  • read_meeting_transcript
  • read_meeting_notes

All MCP datetime inputs must be timezone-aware ISO 8601 strings. Email write operations require a write token, user.endpoint = "me", and a configured domain allowlist. Calendar write operations support both endpoint = "me" and delegated/shared endpoint = "users" calendars when the profile has write scopes. Event create checks new attendees against domain_allowlist; event update/cancel/respond require the current content_hash from get_event or list_events before mutating the calendar. Move/archive actions also require the destination folder to match folder_allowlist. Use create_email_draft for standalone new messages, create_reply_draft for sender-only threaded replies, and create_reply_all_draft for threaded reply-all drafts. Reply drafts are created through Graph reply actions and verified against the source conversation before they are returned. Reply and reply-all drafts preserve Outlook's generated original message chain by default; pass quote_mode="replace" in MCP or --quote-mode replace in the CLI to replace the generated quoted body. Reply and reply-all drafts may include external recipients who were already original thread participants; newly added external recipients are still blocked unless they match domain_allowlist. Use flag_email, unflag_email, move_email, and archive_email for guarded mailbox state changes.

CLI Verification and Useful Commands

Try a basic Teams read:

uvx --from ms365-toolkit@latest ms365-toolkit list-chats --top 5
uvx --from ms365-toolkit@latest ms365-toolkit search-chats "Alex Rivera" --top 3
uvx --from ms365-toolkit@latest ms365-toolkit list-teams
uvx --from ms365-toolkit@latest ms365-toolkit search-teams-messages "budget owner" --top 5
uvx --from ms365-toolkit@latest ms365-toolkit search-channel-messages <team_id> <channel_id> "approval rate" --top 5
uvx --from ms365-toolkit@latest ms365-toolkit read-channel-thread <team_id> <channel_id> <message_id> --top 10

Global Teams search returns ranked search hits and snippets, not hydrated full message bodies.

Try attachment download:

uvx --from ms365-toolkit@latest ms365-toolkit download-email-attachments <message_id> --output-dir ./downloads
uvx --from ms365-toolkit@latest ms365-toolkit list-email-attachments <message_id>
uvx --from ms365-toolkit@latest ms365-toolkit read-email-attachment <message_id> <attachment_id> --max-chars 50000
uvx --from ms365-toolkit@latest ms365-toolkit list-email-share-links <message_id>
uvx --from ms365-toolkit@latest ms365-toolkit download-share-link "https://contoso.sharepoint.com/sites/example/Shared%20Documents/brief.docx" --output-dir ./downloads

Meeting transcript reads preserve VTT cue timestamps in MCP segments and print timestamped speaker blocks in the CLI while keeping flattened text_content for compatibility.

Inspect a directory reporting tree:

uvx --from ms365-toolkit@latest ms365-toolkit org-tree "person@example.com"
uvx --from ms365-toolkit@latest ms365-toolkit org-tree "Alex Rivera" --report-depth 2

Try the guarded email write flow:

uvx --from ms365-toolkit@latest ms365-toolkit create-email-draft --to user@example.com --subject "Hello" --body "<p>Hello</p>"
uvx --from ms365-toolkit@latest ms365-toolkit create-reply-draft <message_id> --body "<p>Reply</p>"
uvx --from ms365-toolkit@latest ms365-toolkit create-reply-all-draft <message_id> --body "<p>Reply all</p>"
uvx --from ms365-toolkit@latest ms365-toolkit send-email-draft <draft_id> <content_hash>
uvx --from ms365-toolkit@latest ms365-toolkit flag-email <message_id>
uvx --from ms365-toolkit@latest ms365-toolkit unflag-email <message_id>
uvx --from ms365-toolkit@latest ms365-toolkit move-email <message_id> <folder_id>
uvx --from ms365-toolkit@latest ms365-toolkit archive-email <message_id>

Try the guarded EA calendar write flow:

uvx --from ms365-toolkit@latest ms365-toolkit create-event --subject "Planning" --start 2026-04-03T09:00:00-05:00 --end 2026-04-03T09:30:00-05:00 --required-attendees person@example.com --online-meeting
uvx --from ms365-toolkit@latest ms365-toolkit update-event <event_id> <content_hash> --start 2026-04-03T10:00:00-05:00 --end 2026-04-03T10:30:00-05:00
uvx --from ms365-toolkit@latest ms365-toolkit respond-event <event_id> <content_hash> accept --comment "Confirmed"
uvx --from ms365-toolkit@latest ms365-toolkit cancel-event <event_id> <content_hash> --comment "Rescheduling"

Inspect local usage analytics:

uvx --from ms365-toolkit@latest ms365-toolkit usage summary --days 7
uvx --from ms365-toolkit@latest ms365-toolkit usage tools --days 7
uvx --from ms365-toolkit@latest ms365-toolkit usage clients --days 7
uvx --from ms365-toolkit@latest ms365-toolkit usage sessions --days 7 --top 10
uvx --from ms365-toolkit@latest ms365-toolkit usage errors --days 7
uvx --from ms365-toolkit@latest ms365-toolkit usage slow --days 7 --top 10
uvx --from ms365-toolkit@latest ms365-toolkit usage health --days 7 --top 5
uvx --from ms365-toolkit@latest ms365-toolkit usage health --version current
uvx --from ms365-toolkit@latest ms365-toolkit usage summary --client codex --surface mcp

Usage analytics are local-only JSONL files under the active profile. They record coarse tool/command metadata, client/session/request IDs, sanitized input shape metrics, durations, result sizes, sanitized Graph endpoint templates, retry counts, and sanitized error categories. They do not record email bodies, subjects, recipients, message IDs, full URLs, query strings, transcript text, raw Graph IDs, or tokens. Use usage health to identify failure hotspots, slow tools, large responses, Graph endpoint hotspots, and recommended fixes. By default, usage health focuses on the current toolkit version when current events exist; use --scope all to include older versions. Usage reports also support --version, --client, --surface, and --name filters.

Inspect currently running local MCP processes from a checkout:

uv run python scripts/inspect_mcp_sessions.py

Pinned/reproducible MCP registration:

codex mcp add ms365 --env MS365_TOOLKIT_PROFILE=default --env MS365_TOOLKIT_CLIENT=codex --env MS365_TOOLKIT_WARN_STALE=1 --env UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache --env UV_TOOL_DIR=/tmp/ms365-toolkit-uv-tools -- sh -lc 'cd /tmp && exec uvx --from ms365-toolkit-mcp==0.2.0 ms365-toolkit-mcp'
claude mcp add -s user ms365 -e MS365_TOOLKIT_PROFILE=default -e MS365_TOOLKIT_CLIENT=claude -e MS365_TOOLKIT_WARN_STALE=1 -e UV_CACHE_DIR=/tmp/ms365-toolkit-uv-cache -e UV_TOOL_DIR=/tmp/ms365-toolkit-uv-tools -- sh -lc 'cd /tmp && exec uvx --from ms365-toolkit-mcp==0.2.0 ms365-toolkit-mcp'

Pinned registrations are deterministic but can become stale. Use them only when you need an exactly reproducible MCP version. To inspect MCP version state in a session, call get_toolkit_status or run ms365-toolkit doctor. MS365_TOOLKIT_WARN_STALE=1 performs a short live version check when the MCP process starts, prints a startup warning if stale, caches the result, and adds a version_warning field to MCP tool responses while the cached status is stale.

Developer Workflow

Repo-Local MCP Fallback

Install the MCP extra:

uv sync --extra mcp

Run the local stdio MCP server:

uv run --extra mcp ms365-toolkit-mcp

Or run it directly from the module entrypoint:

uv run --extra mcp python -m ms365_toolkit.mcp

Real MCP Smoke Test

Run the opt-in real-world MCP smoke test from the local checkout:

uv run --extra mcp python scripts/smoke_mcp.py --profile default

The smoke test launches the local stdio MCP server and calls read-only tools against real Microsoft Graph data. Email, calendar, toolkit-status, compact response, and continuation-shape checks are required. Teams, channel messages, files, transcripts, and meeting notes are tiered optional checks and report SKIP when tenant data or optional delegated scopes are unavailable.

The output is redacted by design. It prints only tool names, pass/fail/skip status, counts, booleans, and hashed ID prefixes. It does not print subjects, senders, recipients, message previews, event bodies, Teams message text, transcript text, meeting notes, tokens, or raw Graph IDs.

Useful variants:

uv run --extra mcp python scripts/smoke_mcp.py --profile work
uv run --extra mcp python scripts/smoke_mcp.py --timeout 60

Repo-Local One-Command Setup

Use the installer script to wire everything up for both tools:

./scripts/setup_mcp.sh

This is the developer fallback, not the primary public install path. It runs the MCP server from the local checkout and does not use the published ms365-toolkit-mcp@latest package.

What it does:

  • installs the MCP extra with uv
  • checks that your selected MS365 profile is already authenticated
  • registers the ms365 server with Codex
  • registers the ms365 server with Claude using user scope so it is available across sessions
  • enables stale-version warnings for both registered MCP sessions

Useful variants:

./scripts/setup_mcp.sh --target codex
./scripts/setup_mcp.sh --target claude
./scripts/setup_mcp.sh --profile default
./scripts/setup_mcp.sh --dry-run

Equivalent Make targets:

make mcp-setup
make mcp-setup-codex
make mcp-setup-claude

Run unit tests:

uv run --with '.[dev]' pytest tests/unit/ -q

Claude Review Workflow

This repo includes project-local Claude review commands and a shell workflow.

In an interactive claude session, use:

/review-critical
/review-critical teams changes
/review-commit f9c3079
/consult Should we expose participant names in 1:1 chats?

For terminal or Codex-driven runs, use:

./scripts/claude_workflow.sh review
./scripts/claude_workflow.sh review-commit f9c3079
./scripts/claude_workflow.sh consult "Should Teams support user.endpoint=users?"

Equivalent Make targets:

make claude-review
make claude-review-commit COMMIT=f9c3079
make claude-consult PROMPT="Should Teams support user.endpoint=users?"

Where Secrets and Local State Live

Do not put secrets or mailbox data into the repo.

Local config:

  • ~/.ms365-toolkit/profiles/<profile>/config.toml

Token caches:

  • stored in the OS keychain via keyring
  • read cache key: ms365-read-token-<profile>
  • write cache key: ms365-write-token-<profile>

Mailbox-derived local state:

  • ~/.ms365-toolkit/profiles/<profile>/mail_index.db
  • ~/.ms365-toolkit/profiles/<profile>/thread_labels.jsonl

Shareable Code vs Private Local State

The repository is intended to be generic and shareable.

Generic, shareable parts:

  • source code under src/
  • tests under tests/
  • build and dependency config
  • generic CLI commands and labeling UI

Private, per-user local state:

  • ~/.ms365-toolkit/profiles/<profile>/config.toml
  • token caches
  • local mailbox index databases
  • exported label datasets such as thread_labels.jsonl
  • any mailbox-derived training or evaluation data

Do not commit or share profile directories, token caches, mailbox indexes, or label datasets unless you explicitly want to share mailbox-derived data.

Local Workflow

  1. Configure a local profile under ~/.ms365-toolkit/profiles/<profile>/config.toml
  2. Authenticate with:
uvx --from ms365-toolkit@latest ms365-toolkit auth login
  1. Build a local mailbox index:
uv run --with '.[dev]' ms365-toolkit sync-mail-index
  1. Export thread candidates for labeling:
uv run --with '.[dev]' ms365-toolkit export-label-candidates --top 30 --sync-index
  1. Review and label locally:
uv run --with '.[dev]' ms365-toolkit label-ui

The learned behavior from triage is driven by each user’s local labeled dataset, not by checked-in project state.

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

ms365_toolkit-0.2.0.tar.gz (331.1 kB view details)

Uploaded Source

Built Distribution

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

ms365_toolkit-0.2.0-py3-none-any.whl (142.3 kB view details)

Uploaded Python 3

File details

Details for the file ms365_toolkit-0.2.0.tar.gz.

File metadata

  • Download URL: ms365_toolkit-0.2.0.tar.gz
  • Upload date:
  • Size: 331.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","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 ms365_toolkit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 d54f43945cfecff557fe577bb8fd3d98340fef639dfe27a0ef2e1459fea099ef
MD5 72c597abda1592581908d93d01825eb9
BLAKE2b-256 f90ae9df6b8dc43bf62bcf895efdafc2fadbd3339c97f378b757d24f022bc04d

See more details on using hashes here.

File details

Details for the file ms365_toolkit-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: ms365_toolkit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 142.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","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 ms365_toolkit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09d8964b7dc5ecbdae51ac01421da6cda6567cf0db2d7a41e16f1270efae2209
MD5 9b3c391d2ca7a02695ac4b4f09aa321a
BLAKE2b-256 3fa4941e22dcdd65c148d663518be92e89e0b14ae4c4f7b3d4baf52bfd8c1033

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