OneNote MCP Server
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
OneNote MCP Server
An MCP server for Microsoft OneNote, using the Graph API. Pages are read and written as Markdown. It uses the public Graph Explorer client id, so no Azure app registration is needed.
Derived from danosb/onenote-mcp and azure-onenote-mcp-server. See Credits.
Requirements
Node.js 20 or newer. marked@18 requires it, and the repo has an .nvmrc.
A Microsoft account with OneNote. Personal accounts work. See Account limitations.
Related MCP server: OneNote MCP Server
Install
git clone https://github.com/violet10B/1note-mcp.git
cd 1note-mcp
npm install
npm run authClient registration
Claude Desktop config lives at %APPDATA%\Claude\claude_desktop_config.json on
Windows, and ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS. Merge this in and restart the app:
{
"mcpServers": {
"onenote": {
"command": "node",
"args": ["/absolute/path/to/1note-mcp/onenote-mcp.mjs"]
}
}
}On Windows command has to be the full path to the executable, for example
C:\\Program Files\\nodejs\\node.exe. On macOS, command -v node prints it.
Cursor uses the same mcpServers object.
Authentication
npm run auth starts the device-code flow. Open microsoft.com/devicelogin,
enter the code, sign in. The token cache goes to .msal-cache.json with mode
0600. It refreshes for about 90 days.
The authenticate tool runs the same flow from a client.
Configuration
Copy .env.example to .env if you need any of these. .env is read from the
repo root, not the working directory.
Variable | Default | Purpose |
|
| Heading preset: |
| none | Raw token, used only without an MSAL session. Expires in about an hour and does not refresh. |
| Graph Explorer client id | Use a private app registration, or survive Microsoft retiring the public one. |
Content model
getPage and all writing tools default to Markdown. The converter emits only
the supported HTML/CSS subset:
Markdown | Emitted as |
|
|
GFM table |
|
|
|
|
|
|
|
| an empty paragraph |
OneNote drops <th>, <pre>, <code>, <blockquote>, <hr>, <style>, CSS
classes and table borders on input. Raw HTML passes through, so <math> and
<sup>/<sub> work as written.
Two Markdown errors fail without an error message. Writing tools return
warnings for both: a table with no | --- | separator row, and any cell in a
row wider than its header. Both checks run against the Markdown source.
format: "html" skips the converter. format: "text" escapes the content and
wraps it in paragraphs.
Style presets
Set in code or with ONENOTE_STYLE. Not per call.
Preset | Headings |
| every level set explicitly |
| no style attribute, so OneNote applies its own look |
native sends fewer bytes. It skips the wrapping <span> OneNote adds when an
h3 to h6 element carries its own style.
Table borders come back as border:0px, so shading is the only way to separate
rows. Both presets use it.
MathML
Every write validates MathML first: well-formedness plus child counts for
mfrac, msub, msup and similar. Invalid markup is rejected.
Two placements corrupt an equation after OneNote stores it. This server does not cause either, and validation does not catch either:
Two equations with nothing between them merge into one. A blank line does not stop this. Put text between them.
An equation that is the only content of a list item gets duplicated outside the list. Reading the page and writing it back adds another copy. Write
a) <equation>instead.
OneNote promotes a standalone equation to block on import whatever display
says, so both apply to inline math as well. Both are checked before the write and
reported in warnings.
getPage(format:"text") flattens equations to characters.
Tools
Guide
Tool | Description |
| Long-form authoring reference. Optional |
Holds what the instructions field cannot, since instructions is re-sent on
every call.
Notebooks and sections
Tool | Description |
| List notebooks, optionally filtered by name |
| Notebook / section-group / section hierarchy in one call |
| Sections with notebook and group attribution |
| Async. Polls up to 60 s. |
Also: createNotebook, createSection, createSectionGroup.
Reading
Tool | Description |
| List pages in a section; |
| Read a page as |
| Read the content under one heading |
| Read the |
| Match titles, or bodies with |
Writing
Use the narrowest tool that fits.
Tool | Description |
| Set cells, add or delete rows and columns |
| Replace, append, prepend or delete one heading's content |
| Tick or untick check boxes by text |
| Swap an exact HTML fragment or a run of visible text |
| Insert before or after the paragraph matching a fragment |
| Replace one block element by id |
| Append or prepend content |
| Replace the whole body. Widest edit. |
| Optional |
| From a local path or URL |
| One change applied to many pages |
Also: setPageTitle.
Managing and auth
Tool | Description |
| Copy to another section, or move |
| Start the device-code flow |
| Raw token. About an hour, no refresh. |
Also: deletePage. Pages are the only thing Graph lets this server delete.
Addressing by text
Headings, table rows, columns and check boxes take a text fragment instead of an
index (pickOne in src/dom.js).
An exact match beats a substring match.
"Tasks"selects the headingTasks, notTasks archive.An ambiguous fragment returns an error listing the candidates. Nothing is written.
Rows and columns also take a 0-based index.
Table editing
editTable finds a table by nearText or tableIndex, then rewrites that one
<table>:
{ "pageId": "p1ar6", "nearText": "Inventory",
"setCells": [{ "row": "Widgets", "col": "Stock", "value": "12" }],
"addColumns": [["Reorder point", "5", "10", "20"]],
"deleteRows": ["Fasteners"] }row takes an index or text in the row. col takes an index or a header name.
References resolve against the table as read, so setCells and deleteRows in
one call both land where intended.
OneNote cannot PATCH a <tr> or <td>, only a whole <table> by id, so every
cell edit rewrites the table.
Untouched cells keep their markup. Cells carry across as HTML.
Cell values are inline Markdown:
**bold**, links,<math>.Row 0 counts as a header only if it already looks like one.
atColanddeleteColumnspair withatRowanddeleteRows.The result is rectangular.
Section editing
editPageSection finds a heading, splices content into that section on the
server, and writes it back:
{ "pageId": "p1ar6", "heading": "Tasks", "mode": "append",
"content": "- [ ] Write the report" }mode is replace (default), append, prepend or delete. delete removes
the heading and its content, and needs no content. The other modes keep the
heading. OneNote has no delete action in its PATCH API, so delete splices the
nodes and writes the body back.
Images get the same guard as replacePage. Pass allowImageLoss: true to
override.
Check boxes
- [ ] and - [x] become <p data-tag="to-do">. setCheckbox ticks by text
in one PATCH, however many boxes:
{ "pageId": "p1ar6", "text": ["Write the report", "Review the draft"] }checked: false unticks. Nothing is written until every named box resolves.
OneNote has about 20 other note tags: important, question, idea, critical and so
on. None has Markdown syntax and no tool sets one, but they can be read.
getPage emits [tag: important] paragraph text, and
getPage(format:"outline") counts them per kind. Writing the marker back does
not attach a tag.
A raw tag passes through as a CommonMark HTML block. A raw tag inside a list breaks list parsing, so keep tagged lines between blocks.
Formula editing
getMathExpressions(pageId)returns each<math>block with its storedmathmland the containing block's element id.findAndReplace(pageId, find, replace)swaps that fragment.
OneNote normalises MathML on write. <mi>dy</mi> comes back as
<mrow><mi>d</mi><mi>y</mi></mrow>. Take find from getMathExpressions, not
from the string you sent.
findAndReplace tries an exact HTML fragment first, then a run of visible text.
A replacement containing markup is refused. The result reports mode,
occurrences and remaining.
The PATCH scopes to the smallest replaceable block: <p>, heading, list, table
or nested <div>. The page's top-level positioned <div> is excluded because
OneNote has no replace for it, and targeting it corrupts the page. If nothing
narrower contains the fragment, the tool falls back to a whole-body replace and
reports scope: "body".
data-id survives a PATCH. id from includeIDs=true does not: it is
snapshot-bound and needs a re-read after each edit. OneNote discards any id you
send. The converter stamps data-id on every heading and table.
Images
addImageToPage(pageId, { filePath | url, nearText?, position?, width?, height?, altText? }).
OneNote needs raw binary in a multipart/form-data part, and does not fetch URLs
during a PATCH. The server reads the bytes and uploads them.
Types: PNG, JPEG, GIF, BMP, TIFF. Limit 3.5 MB.
A public
<img src="https://…">works only increatePage.Images already on a page cannot be re-submitted. A body rewrite that would drop them is refused unless you pass
allowImageLoss: true.urlmust behttporhttpsand must not resolve to a loopback, private or link-local host (src/images.js).
Token efficiency
Tools return short handles (
p1ar6,s1wdi,nb1n8s) instead of 85-character Graph ids, kept in.id-map.json. Raw ids work as input.Page content and section listings cache for 60 s and clear on write.
getPage(format:"outline")lists headings, table headers and row labels without the body, which is enough to address a cell.getPageSection,getMathExpressions,listPages(previewChars)andsearchPages(previewChars)narrow further.editPageSection,editTableandfindAndReplaceread, modify and write on the server. Only the new content crosses the wire.Tool schemas are re-sent on every request, so test/server.test.mjs enforces a byte budget.
Graph errors reduce to
codeandmessage.Reading tools carry
readOnlyHint.deletePagecarriesdestructiveHint.
Account limitations
These do not work on personal Microsoft accounts:
Full-text page search.
searchPages(inContent: true)reads pages instead, capped at 150 per call. Scope it withsectionIdornotebookId.Deleting sections, notebooks or section groups. Only pages can be deleted.
Copy and move.
copyToSectionandcopyToNotebookreturn501.copyPageToSectionfalls back to re-creating the page from its HTML and reportsre-created. Images cannot be re-submitted, so a page with any is refused unless you passallowImageLoss: true.copySectionToNotebookcannot be emulated, and tells the caller to copy its pages instead..Allscopes. This server asks forNotes.ReadWriteandNotes.Create.
Development
npm testnode --test with no arguments; a glob would need shell-specific quoting. The
suite runs offline. Layout:
onenote-mcp.mjsholds the caches, content preparation and tool registrations.src/content.jsconverts Markdown to OneNote XHTML and back.src/dom.jshandles text matching, section spans, splicing and check boxes.src/ids.js,src/mathml.js,src/graph.js,src/images.jsmint handles, validate MathML, make Graph requests, and guard the image URL.
CI runs the suite on Node 20, 22 and 24.
Local files
.msal-cache.json holds the refresh token and lives about 90 days.
.access-token.txt only appears if you used saveAccessToken, and lasts an
hour. .id-map.json is the handle lookup and is safe to delete. All three are
gitignored. The two token files are written 0600.
Troubleshooting
Symptom | Cause |
| Token expired. Run |
Server missing in Claude Desktop | The app was closed, not quit. Quit from the tray and reopen. |
| Use the full path in |
| Node 18 or older. Run |
Credits
danosb/onenote-mcp. Direct ancestor. The device-code flow, the Graph request layer and the client setup come from here.
ZubeidHendricks/azure-onenote-mcp-server. What danosb's work builds on.
Particular to this repository: the Markdown content model, style presets, table
addressing by row and column name, MathML validation, check-box handling, the
authoring guide, and the src/ and test/ trees.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP-native open-source Notion alternative: read & write pages, databases and kanban boards.
Search, read, and safely update Markdown notes in your connected Phasoric knowledge vaults.
Encrypted Markdown notes and team knowledge base. Search, read and write notes in vaults you allow.
Personal context for every AI: search, read, and write back to your private Markdown library.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Microsoft OneNote via the Microsoft Graph API, allowing users to list notebooks and retrieve page content. It supports both personal and organization notebooks with credential caching for efficient authentication.32 npm3MIT
- AlicenseNot gradedqualityDmaintenanceConnects to OneNote via Microsoft Graph and exposes tools to list notebooks, sections, pages, and read page content.1MIT
- AlicenseAqualityDmaintenanceEnables natural language interaction with Microsoft OneNote to manage notebooks, sections, and pages, including creating and searching pages via the Microsoft Graph API.922 npm1MIT
- AlicenseNot gradedqualityDmaintenanceA secure MCP server for Microsoft OneNote via the Graph API, enabling listing/reading notebooks, sections, and pages, with optional gated write tools. Read-only by default and deployable to Azure Container Apps.17 npmMIT