Skip to main content

Microsoft 365 superpowers for Python and AI agents

Project description

office-connect

Python 3.14+ License: MIT PyPI version Code style: ruff

Microsoft 365 superpowers for Python and AI agents.

office-connect is a Python library and stdio MCP server that gives both human developers and AI agents structured access to Microsoft 365 — mail, calendar, Teams, chats, files, directory, profile — through the Microsoft Graph API, behind a simple token-based authentication flow.

Features

  • MCP Server -- Stdio-based MCP server for seamless integration with AI assistants
  • Mail -- List messages, read bodies and attachments, send, draft, reply, manage categories
  • Calendar -- List calendars, query events, create events, check free/busy schedules
  • Teams -- List joined teams, channels, channel messages, and team members
  • Chat -- List recent 1:1, group, and meeting chats with message history
  • Files / OneDrive -- Browse drives, list folders, download file content, search by name
  • SharePoint -- Search sites and list document libraries
  • Directory -- List organization users, resolve managers, fetch profile photos
  • Profile -- Retrieve the authenticated user's profile details
  • Mock Transport -- Full synthetic data layer for testing without a real O365 account

Quick Start

Installation

poetry add office-connect

MCP Server

office-connect --keyfile path/to/token.json

Token File Format

{
  "app": "MyApp",
  "email": "user@example.com",
  "access_token": "eyJ...",
  "refresh_token": "1.AUs...",
  "client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "client_secret": "...",
  "tenant_id": "common"
}

Tokens are automatically refreshed (proactively when within 15 min of expiry, reactively on a 401 from Graph) and persisted back to the file (0600 permissions).

Signing in for the first time (office-connect login)

When you don't have any tokens yet, use the device-code login. The first run takes your Azure AD app credentials once and saves them; every subsequent re-auth needs no arguments at all.

# Cold start — supply the Azure AD app once
office-connect login --client-id <APP_ID> --tenant-id <TENANT_ID> [--client-secret <SECRET>]

# From there on — re-auth with zero arguments
office-connect login

# Narrow scopes (default is all eight groups)
office-connect login --scope mail --scope calendar

office-connect login prints a microsoft.com/devicelogin URL plus a short code, blocks polling Microsoft until you finish signing in, then writes two files:

  • ~/.config/office-connect/token.json — the keyfile (access + refresh tokens), 0600 permissions
  • ~/.config/office-connect/config.json — the app config: client_id / tenant_id / optional client_secret, 0600 permissions

Both paths are configurable (--keyfile and --app-config, or env var OFFICE_CONNECT_APP_CONFIG). When login resolves credentials it tries, in order: explicit CLI args → existing keyfile → O365_CLIENT_ID etc. → app-config file. You can also pre-create the app-config file by hand — it's just a JSON object with client_id, tenant_id, and optional client_secret.

Available scope groups: profile, directory, mail, calendar, chat, teams, drive, tasks.

Your Azure AD app registration must have "Allow public client flows" enabled in its authentication manifest for device-code flow to work.

Refreshing tokens during a running session

You don't have to. The MCP keeps tokens fresh on its own — within a session via in-process refresh, across restarts via the keyfile. Manual top-ups only matter if even the refresh token has been invalidated (typically after ~90 days of inactivity, or by tenant policy).

If a host application exports tokens through an admin endpoint, drop the exported JSON at the canonical keyfile location with:

office-connect import-token ~/Downloads/token_export.json
# override destination with --dest /custom/path/token.json

Both login and import-token write atomically with 0600 permissions. The MCP server compares the keyfile's mtime before each tool call; once the file changes, the next tool invocation rebuilds the Graph instance from the new contents — Claude Desktop, Cursor, etc. do not need to be restarted.

Permission tiers — what Claude is allowed to do

Three tiers, from most to least restrictive:

tier what it allows
read_only list/get/search/peek — no mutation of Microsoft 365 state
drafts (default) everything read_only does, plus creating and updating draft emails. No sending.
all everything drafts does, plus sending mail, moving/deleting mail, flagging read, creating calendar events

Three places can set the tier — the most restrictive of the ones that are set wins:

  1. MCP launcher CLI flag: --permission-level read_only|drafts|all in the args list passed by Claude Desktop / Cursor / etc.

  2. Environment variable: OFFICE_CONNECT_PERMISSION_LEVEL=read_only

  3. Global policy file: a JSON object at ~/.config/office-connect/policy.json (overridable via --policy-file PATH or $OFFICE_CONNECT_POLICY):

    { "permission_level": "drafts" }
    

The policy file acts as a host-wide ceiling: regardless of how any individual MCP launcher is configured, the resolver clamps the effective tier down to the most restrictive value found. A launcher can always tighten further on top.

Tools above the effective tier are removed from list_tools and refused by call_tool (defense in depth, fail-closed for unknown tool names).

Mock Transport

A full mock layer for development and testing — no real O365 account needed.

from office_con.msgraph.ms_graph_handler import MsGraphInstance
from office_con.testing.fixtures import default_mock_profile

profile = default_mock_profile()
graph = MsGraphInstance(endpoint="https://graph.microsoft.com/v1.0/")
graph.enable_mock(profile)

# Now use graph exactly like the real thing
mail = graph.get_mail()
inbox = await mail.email_index_async(limit=10)

The mock provides:

  • 18+ inbox messages (rich HTML with signatures, newsletters, notifications)
  • 9 mail folders with subfolders, fake downloadable attachments
  • 127 calendar events across 3 months (OOF, Teams calls, tentative, free blocks)
  • 25 directory users with org hierarchy, departments, and profile photos
  • Teams, chats, categories, OneDrive stubs
  • Synthetic JWT tokens

Face photos can be loaded from JPEG files via set_faces_dir() or the FACES_DIR env var. Falls back to generated SVG initials.

Safety: mock is automatically blocked on Azure App Service and production URLs.

Project Structure

office-connect/
├── office_con/
│   ├── auth/                # Azure AD OAuth, scopes, background refresh
│   ├── db/                  # Company directory builder and storage
│   ├── msgraph/             # MS Graph API handlers
│   │   ├── ms_graph_handler.py   # Central class: MsGraphInstance
│   │   ├── mail_handler.py       # Send, draft, reply, list, categories
│   │   ├── calendar_handler.py   # Events, schedules, timezones
│   │   ├── directory_handler.py  # Users, managers, photos
│   │   ├── teams_handler.py      # Teams, channels, messages
│   │   ├── chat_handler.py       # 1:1, group, meeting chats
│   │   ├── files_handler.py      # OneDrive, SharePoint
│   │   └── profile_handler.py    # /me profile
│   ├── testing/             # Mock transport, fixtures, synthetic tokens
│   ├── utils/               # Excel parser, health check
│   └── mcp_server.py        # MCP server entry point + CLI
├── tests/
├── docs/
├── pyproject.toml
└── LICENSE

Development

poetry install
poetry run pytest          # tests (skips integration tests without token file)
poetry run ruff check      # lint

MCP Tools

Tool Description
o365_get_profile Current user's profile
o365_list_mail List recent inbox emails
o365_get_mail Single email with full body and attachments
o365_get_mail_categories Outlook mail categories
o365_list_calendars User's calendars
o365_get_events Calendar events in a date range
o365_get_schedule Free/busy availability
o365_list_teams Joined Microsoft Teams
o365_list_channels Channels in a team
o365_get_channel_messages Channel messages
o365_get_team_members Team members
o365_list_chats Recent chats
o365_get_chat_messages Chat messages
o365_get_chat_members Chat members
o365_get_my_drive Default OneDrive info
o365_list_drive_items Files and folders
o365_get_file_content Download file content
o365_search_files Search OneDrive
o365_search_sites Search SharePoint sites
o365_get_site_drives Site document libraries
o365_list_users Organization directory
o365_get_user_manager User's manager

License

MIT -- Copyright (c) 2026 Michael Ikemann

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

office_connect-0.1.8.tar.gz (140.6 kB view details)

Uploaded Source

Built Distribution

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

office_connect-0.1.8-py3-none-any.whl (156.8 kB view details)

Uploaded Python 3

File details

Details for the file office_connect-0.1.8.tar.gz.

File metadata

  • Download URL: office_connect-0.1.8.tar.gz
  • Upload date:
  • Size: 140.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.1 CPython/3.13.11 Darwin/25.2.0

File hashes

Hashes for office_connect-0.1.8.tar.gz
Algorithm Hash digest
SHA256 08438243a95ace02a1056212bdce2ce7c1eb436e018af32dbb5151bc5bb8aadf
MD5 c6dd5dfaf84ea00f109d37059429c3cb
BLAKE2b-256 e69527471be726aca2da64a88e8fd13cad772ae748cf9c305e24b9aaaf2ca669

See more details on using hashes here.

File details

Details for the file office_connect-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: office_connect-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 156.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.1 CPython/3.13.11 Darwin/25.2.0

File hashes

Hashes for office_connect-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 ea76147545c12ea1351e2867c5db1a19e528e5ff432b8260ab94a45b6c40632f
MD5 17338fe2a6476c238de80f697e9c6099
BLAKE2b-256 44ebd74fad455c11ad67f80d276591831ac541b02a02be2892a624eab3642556

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