Skip to main content

OutNote Logo

OutNote

Headless Microsoft 365 reader - mail, calendar, Teams, OneDrive, SharePoint and OneNote over Graph, without an app registration.

visits Python 3.10+ Latest release MIT license Built on Entrance

About The Project

Every "read my mailbox over Graph" tutorial makes you register an Azure app and get admin consent - which a locked-down tenant (a school, a company) simply refuses. OutNote sidesteps that entirely.

It signs in with entrance using the pre-consented OfficeHome first-party client, which hands back a FOCI family refresh token. A tiny broker then trades that token for whichever first-party client's Graph token a given call needs - all without another sign-in. Each of those clients is already consented in the tenant, so there is nothing to register and nobody to ask.

The result: one headless login (password + TOTP MFA, no browser), then clean Graph reads across your whole Microsoft 365 account. Built to be the base layer for an MCP server.

Features

  • 🔓 No app registration, no admin consent - rides pre-consented Microsoft first-party clients
  • 🤖 Fully headless - password + TOTP MFA over pure HTTP, no browser (via entrance)
  • 🧩 One login, every service - a FOCI family token is brokered into the right client per call
  • 📬 Broad coverage - mail, calendar, Teams chats & channels, channel files, OneDrive, SharePoint, OneNote
  • 🍪 Silent reuse - a persisted session jar means password-free reruns
  • 📦 Tiny - stdlib urllib Graph client, one dependency (ms-entrance)

Installation

pip install outnote
# or
uv add outnote

Requires Python 3.10+; ms-entrance is pulled in automatically.

Quickstart

from outnote import authenticate

note = authenticate(
    "you@school.ch",
    "your-password",
    totp_secret="JBSWY3DPEHPK3PXP",  # Base32 TOTP, if the account has MFA
)

# mail & calendar
note.emails(top=5)
note.events(top=5)

# Teams chats
for chat in note.chats():
    note.chat_messages(chat["id"])

# Teams channels + files
for team in note.teams():
    for ch in note.channels(team["id"]):
        note.channel_messages(team["id"], ch["id"])
        note.channel_files(team["id"], ch["id"])  # files posted in the channel

# OneDrive & SharePoint
note.files()  # OneDrive items
note.download_url(item_id)  # direct, pre-authed download link
note.sites()  # SharePoint sites

# search across the account
note.search_emails("invoice")
note.search_files("budget.xlsx")
note.search_messages("standup")  # Teams chat & channel messages
note.search_people("Fischer")
note.search("project kickoff", entity="event")  # any entity type

# OneNote - list and read page content as HTML
for nb in note.notebooks():
    for sec in note.sections(nb["id"]):
        for page in note.pages(sec["id"]):
            html = note.page_content(page["id"])

note.get("me/drive/root/children")  # any Graph endpoint, raw

The first call saves .ms_session.json; every call after that skips the password and MFA.

Already have a Graph token (MSAL, a browser session, ...)? Skip auth entirely. A single static token only carries its own scopes, so OneNote/SharePoint calls may 403 if it lacks them:

from outnote import OutNote

OutNote(access_token).emails()

What it does

Verified end-to-end against a live locked-down EDU tenant:

Area Methods Graph endpoint
Mail emails(), email(id) /me/mailFolders/{f}/messages
Calendar events(start, end) /me/events, /me/calendarView
Teams chats chats(), chat_messages(id) /me/chats, /me/chats/{id}/messages
Teams channels teams(), channels(t), channel_messages(t, c) /me/joinedTeams, /teams/{t}/channels[/{c}/messages]
Channel files channel_files(t, c) filesFolder → /drives/{d}/items/{i}/children
OneDrive files(path), download_url(id[, drive_id]) /me/drive/...
SharePoint sites(search), site_drive_items(id) /sites, /sites/{id}/drive
OneNote notebooks(), sections(nb), pages(sec), page_content(id) /me/onenote/...
Contacts / Tasks contacts(), task_lists(), tasks(list) /me/contacts, /me/todo/lists
Search search_emails(q), search_files(q), search_messages(q), search_people(q), search(q, entity) /search/query (message, driveItem, chatMessage, event, person, site)
Anything get(path), list(path) any endpoint, follows @odata.nextLink

How It Works

authenticate runs a PKCE authorization-code sign-in for the OfficeHome client through entrance (which drives Microsoft's sign-in - including TOTP MFA - over pure HTTP and intercepts the code at the native-client redirect), then redeems it for a FOCI family refresh token. The OutNote client holds a broker that, per call, refreshes that token into the first-party client whose pre-consented scopes cover the request, caching each token until it expires:

Service FOCI client Key scope it carries
Mail, Calendar, Chats, Channels, Files, Teams, Contacts, Tasks Office d3590ed6-... Mail.ReadWrite, Chat.ReadWrite, ChannelMessage.Read.All, Files.ReadWrite.All, Calendars.ReadWrite
OneNote (read page content) Teams-classic 1fec8e78-... Notes.ReadWrite.All
SharePoint sites OfficeHome 4765445b-... Sites.Read.All

Which first-party clients are FOCI-family and pre-consented is tenant-dependent; the three above work on a standard Microsoft 365 / EDU tenant.

Caveats

  • Works against managed Entra tenants (login.microsoftonline.com) with these first-party clients pre-consented - the common case for Microsoft 365 / EDU.
  • Push / SMS MFA can't be done headless - only TOTP (authenticator app). Enroll one and pass its Base32 secret.
  • Microsoft tweaks its login pages and client registrations; if a flow breaks, a user-agent, field name or client ID is usually the culprit.

Disclaimer

OutNote is published for educational and research purposes only - to document how Microsoft 365's first-party OAuth2 clients and the Graph API behave for a browserless client.

Use it only against accounts you own, or with the explicit authorization of the account or tenant owner. You alone are responsible for your use of it and for complying with Microsoft's terms, your identity provider's terms, your institution's acceptable-use policy, and applicable law. The software is provided "as is", without warranty of any kind, and the author accepts no liability for misuse. Not affiliated with or endorsed by Microsoft.

License

MIT (c) PianoNic

Metadata

Release files for outnote 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for outnote 0.1.0
File Size Uploaded Provenance
outnote-0.1.0.tar.gz 11.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for outnote 0.1.0
File Interpreter ABI Platform
outnote-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 23.2 kB

Release files / outnote-0.1.0.tar.gz

Download URL outnote-0.1.0.tar.gz
Size 11.1 kB
Tags Source
SHA-256 checksum
How to use checksums
8b9efb04646537eb013d469b9555be24a1fc2679db95e13452a772b4e5b3bb8b
BLAKE2b-256 checksum
How to use checksums
1df9b02867e12e3cd258ccaf8843eba446bb2b4c60101883d2eb76a6f8357edc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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}

Release files / outnote-0.1.0-py3-none-any.whl

Download URL outnote-0.1.0-py3-none-any.whl
Size 12.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42e037b95c9e0e43ff442d2a5c88d0802603a36f9ae0ba3ce5861c9317f3e480
BLAKE2b-256 checksum
How to use checksums
71a7e204493d880277adab944a8239ca0881311307e8a16f3337e6e98b8be9c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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}

Security for outnote 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page