@atomiclabs97/onenote-mcp
v0.1.1
Published
MCP server for Microsoft OneNote — read, search, create, and delete pages via Microsoft Graph.
Downloads
32
Maintainers
Readme
@atomiclabs97/onenote-mcp
An MCP server for Microsoft OneNote. Bring your notebooks into Claude, Cursor, and any MCP-compatible client — list notebooks and sections, full-text search across pages, read individual pages, and create or delete pages from natural language. Authentication uses Microsoft's device-code flow against your own Entra ID app registration, so your data and credentials never leave your machine.
Screenshot/demo coming soon — drop a GIF in
docs/images/demo.gifand reference it here.
Quick start
1. Register a Microsoft Entra app
The server talks to Microsoft Graph using a Microsoft Entra (Azure AD) app registration that you own. This takes about 2 minutes.
Screenshots referenced below live in
docs/images/. They are placeholders today — contributions welcome.
a. Create the registration
Go to the Microsoft Entra admin center → App registrations and click + New registration.
Name: anything (e.g.
onenote-mcp).Supported account types: choose "Accounts in any organizational directory and personal Microsoft accounts" if you want both work and personal OneNote to work; otherwise pick what matches your tenant.
Redirect URI: leave blank — the device-code flow doesn't need one.
Click Register.

b. Add the API permissions
In the new app's left nav, click API permissions → + Add a permission → Microsoft Graph → Delegated permissions.
Search for and check both:
Notes.ReadWriteoffline_access
Click Add permissions. (No admin consent is required for personal Microsoft accounts. Work/school tenants may need an admin to grant consent for the directory.)

c. Allow the public client flow
The device-code flow is a "public client" flow — it doesn't use a client secret.
Go to Authentication in the left nav.
Scroll to Advanced settings → Allow public client flows and toggle it Yes.
Click Save.

d. Grab the client ID
Back on the app's Overview page, copy the Application (client) ID. You'll pass it to the server via the ONENOTE_MCP_CLIENT_ID environment variable.

2. Sign in
ONENOTE_MCP_CLIENT_ID=<your-app-client-id> npx @atomiclabs97/onenote-mcp loginThis prints a code and a URL like:
To sign in, use a web browser to open https://microsoft.com/devicelogin
and enter the code ABCD-1234 to authenticate.Open the URL, paste the code, sign in with the Microsoft account whose OneNote you want to access, and approve the requested permissions. The refresh token is then cached at ~/.config/onenote-mcp/tokens.json (mode 600) so the MCP server can run silently.
To sign out:
npx @atomiclabs97/onenote-mcp logout3. Wire it into your MCP client
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"onenote": {
"command": "npx",
"args": ["-y", "@atomiclabs97/onenote-mcp"],
"env": {
"ONENOTE_MCP_CLIENT_ID": "your-app-client-id"
}
}
}
}Restart Claude Desktop. The OneNote tools should appear in the tool picker.
Cursor
Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"onenote": {
"command": "npx",
"args": ["-y", "@atomiclabs97/onenote-mcp"],
"env": {
"ONENOTE_MCP_CLIENT_ID": "your-app-client-id"
}
}
}
}Anything else
Any MCP-compatible client that supports stdio servers will work. Run npx @atomiclabs97/onenote-mcp with ONENOTE_MCP_CLIENT_ID set in the environment.
Tools
| Tool | Description | Key inputs |
| ---------------- | --------------------------------------------------------------- | ------------------------------------------------ |
| list_notebooks | Lists all OneNote notebooks for the signed-in user. | (none) |
| list_sections | Lists sections, optionally scoped to a single notebook. | notebookId? |
| list_section_groups | Lists section groups (folders), optionally scoped to a notebook. | notebookId? |
| search_pages | Full-text search across pages (title + content). | query, limit? |
| read_page | Returns page metadata + content (HTML or Markdown). | pageId, format? (html | markdown) |
| create_notebook | Creates a new top-level notebook. | name |
| create_section | Creates a section inside a notebook or section group. | notebookId? | sectionGroupId?, name |
| create_section_group | Creates a section group inside a notebook or another section group. | notebookId? | sectionGroupId?, name |
| create_page | Creates a page in a section. Accepts Markdown (default) or HTML, plus optional binary attachments. | sectionId, title, content, format?, attachments? |
| update_page | Applies edits to a page: append/prepend/insert/replace/delete elements. | pageId, operations[] |
| delete_page | Permanently deletes a page. Irreversible. | pageId |
Configuration
| Env var | Required | Description |
| -------------------------- | -------- | --------------------------------------------------------------------------------- |
| ONENOTE_MCP_CLIENT_ID | yes | Application (client) ID of your Microsoft Entra app registration. |
| ONENOTE_MCP_TENANT_ID | no | Tenant ID. Defaults to common, which works for both personal and work accounts. |
| XDG_CONFIG_HOME | no | Override the config directory. Tokens are stored at <dir>/onenote-mcp/tokens.json. |
Known limitations
- Attachments are sent in-memory.
create_pagereads attachment files synchronously before posting; very large files (~150 MB+) may strain Node's heap. Streamed uploads are a future enhancement. update_pagetargets are rawdata-idselectors. To edit a specific element, read the page first and pull thedata-idattribute out of the returned HTML. Higher-level selectors (e.g. "the section under heading X") are tracked for a follow-up.- Search latency. Microsoft Graph's
$searchagainst/me/onenote/pagescan take a few seconds against large notebooks; the server retries on 429s with exponential backoff.
Contributing
PRs welcome. The codebase aims to stay small and focused.
git clone https://github.com/ahmadAlMezaal/onenote-mcp
cd onenote-mcp
yarn install
yarn build
yarn testyarn typecheck—tsc --noEmit(coverssrc/+scripts/)yarn lint— ESLintyarn test— Vitest (unit tests, mocked Graph)yarn dev— incremental rebuild on save (tsc --watch+tsc-alias --watch);dist/stays runnable
Use Yarn (Classic), not npm. The repo's lockfile is
yarn.lock—package-lock.jsonshould not be committed.
End-to-end smoke test
scripts/smoke.ts exercises every shipped tool against a real OneNote account. It's not in CI — needs real credentials.
# One-time: register an Entra app (see Quick start above) and sign in
ONENOTE_MCP_CLIENT_ID=<your-client-id> yarn build
ONENOTE_MCP_CLIENT_ID=<your-client-id> node dist/cli.js login
# Then run the smoke test
ONENOTE_MCP_CLIENT_ID=<your-client-id> yarn smokeIt's idempotent: reuses (or creates) a notebook called OneNote MCP Smoke Test, walks through 12 steps covering auth, list/search/read/create/update/delete and the multipart attachment path, and cleans up the pages it creates. Sections and section groups are left behind for the next run.
If you're using an AI coding assistant (Claude Code, Cursor, etc.), see CLAUDE.md for project conventions, the arrow-function rule, and the tool-authoring checklist.
Commits follow Conventional Commits (feat:, fix:, chore:, etc.). CI runs typecheck + lint + tests on every push and PR.
Roadmap
- [x]
update_page— in-place edits via Graph'sPATCHsyntax - [x]
create_section/create_notebook - [x] Image and attachment upload (multipart create_page)
- [x] Section group support (list + create + section-in-group targeting)
- [ ] Streamed attachment uploads (avoid in-memory buffering for large files)
- [ ] Resource-style page browsing (alongside the tool surface)
License
MIT © Ahmad Al Mezaal
