WinMCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@WinMCP ServerWhat's on my calendar for tomorrow?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
WinMCP Server (MVP)
A minimal MCP server that exposes
thirteen tools over stdio for reading your Outlook calendar's "note"
appointments (subject + body), your Outlook Tasks / Microsoft To Do items,
your Outlook Inbox/Sent Items mail, via Outlook COM, local/OneDrive files
indexed by Windows Search, and OneNote pages via a OneNote.Application
COM bridge:
calendar_search— search the default Outlook calendar folder by date range and/or subject substring.calendar_get_event— fetch full detail (including body) for a single event by its OutlookentryId.calendar_get_notes— resolve the single note-appointment matching adate+subjectand return its full detail in one call.task_search— search the default Outlook Tasks folder (synced with Microsoft To Do) by due-date range, subject substring, and/or status; all filters are optional.task_get_task— fetch full detail (including body) for a single task by its OutlookentryId.mail_search— search the default Outlook Inbox or Sent Items folder by date range, subject substring, and/or sender substring; at least one filter is required.mail_get_message— fetch full detail (including body) for a single Inbox/Sent Items message by its OutlookentryId.file_search— search the Windows Search index by a case-insensitivefilenamesubstring and/or a full-textphrasematch, optionally restricted to an absolutescopesubtree; at least one offilename/phraseis required, and anyscopegiven must fall within an allowed search root.file_get_info— fetch full indexed metadata (size, timestamps, kind, extension, content snippet) for a single file by its native path or thefile:///-style URL previously returned byfile_search; the path must also fall within an allowed search root.onenote_search— full-text search over OneNote page content (FindPages);queryis required,limitis optional (default 50, hard max 200).onenote_get_page— fetch full, read-only text detail (title + body) for a single OneNote page by itspageId.onenote_create_page— create a new page in a givensectionId; restricted to a configurable writable-notebook allowlist (default only"z - Test Notebook").onenote_update_page— update an existing page's body bypageId, guarded by required optimistic concurrency (dateExpectedLastModified) and the same writable-notebook allowlist asonenote_create_page.
No authentication, no network listener — the server is launched as a local subprocess by an MCP client (e.g. Claude Desktop) and speaks stdio only.
Platform requirement: Windows + Outlook (runtime), WSL2/Linux (dev only)
The real adapter talks to Outlook via COM (pywin32/win32com), which is
Windows-only and requires a working Outlook installation/profile. This
project is therefore developed and tested on WSL2 Linux using a
FakeCalendarAdapter (in-memory, no Outlook needed), but it runs
only on Windows, under a Windows Python 3.12 interpreter — not inside WSL2.
win32comis never imported at module load time anywhere in this codebase (seetools/outlook_adapter.py); it is imported lazily, insideOutlookCalendarAdapter's own methods, the first time a tool actually needs Outlook. This is what lets the full test suite import and run on Linux with zero Windows dependencies.Do not
pip install pywin32on Linux/WSL2 — it is a Windows-only package and is declared inpyproject.tomlwith an environment marker (pywin32; sys_platform == 'win32') so it is skipped automatically on non-Windows installs.The four OneNote tools take a different route entirely: Windows Search's index has zero
onenote:items, so there is no ADO/Windows Search fallback the wayfile_searchhas one —OneNoteAdapter(tools/onenote_adapter.py) is the only path, and it never importswin32comat all. It spawns a pinned Windows PowerShell 5.1 (powershell.exe, neverpwsh) child runningtools/ps_bridge_onenote.ps1, which drivesNew-Object -ComObject OneNote.Application— this requires the classic desktop OneNote application to be installed on the Windows host (the "OneNote for Windows 10"/Microsoft Store app does not expose this COM object model, same caveat as the Outlook tools). Dev/CI on WSL2 usesFakeOneNoteAdapter(in-memory) instead — nopowershell.exe, no COM, needed to run the test suite.
Related MCP server: Outlook MCP Server
Install (on the Windows host)
The supported install path uses a prebuilt, self-contained distribution zip
(WinMCP-<date>.zip) that bundles the app, the launcher scripts, and every
dependency wheel (including pywin32) — no internet access is needed on the
Windows machine and no Windows Python packages are required beforehand
beyond Python 3.12/3.13 itself.
Get
WinMCP-<date>.zip(built via./make-deploy-package.sh— see "Building the package" below, or ask whoever built it for a copy) onto the Windows machine.Right-click the zip → Properties → tick "Unblock" → OK. This clears Windows' "Mark of the Web" flag for the whole package in one gesture, so the launcher scripts inside aren't blocked from running.
Extract it, e.g. to
C:\WinMCP.Double-click
install.batinside the extracted folder. It will:locate a Windows Python 3.12/3.13 interpreter (
py -3.12, orpythonon PATH),create a private
.venvnext to itself,install WinMCP and all dependencies from the bundled
wheels\folder (fully offline),ask which tools to enable — a short prompt walks the tool families (calendar, tasks, mail, files, OneNote) with the package's recommended set pre-selected; press Enter to accept the defaults, or toggle individual tools on/off. Your choices are saved to
config\installed-tools.yamland only the enabled tools will appear in Claude Desktop. (Scripted installs can skip the prompt withinstall.bat -Preset <file>; see "Selective tool deployment" below. You can re-runinstall.batany time to change the selection.) If you received a curated--sharepackage rather than the default build, this prompt — and the installed copy as a whole — only ever lists the families/tools that package actually shipped; a tool the builder excluded never appears here to toggle on, no matter what you pick.smoke-check that
server.pyandwin32com.clientboth import cleanly, andprint a ready-to-paste JSON snippet for Claude Desktop. The window stays open (press Enter to close it) so you can read any error before it disappears.
Test before configuring Claude Desktop: double-click
test.batinside the extracted folder. It launchesWinMCP.batexactly the way Claude Desktop will and runs the real MCP handshake against it (initialize,tools/list, and one live call per installed tool family — families you didn't enable in step 4 are skipped, not failed), so a broken install shows up here instead of as a silent "server disconnected" inside Claude Desktop. The window stays open (press Enter to close it) so you can read the result:SMOKE TEST PASSED— the server starts, speaks MCP correctly, and found yourcalendar_searchresults. You're ready for step 6.SMOKE TEST PASSED WITH WARNINGS— the MCP plumbing itself is fine (steps 1-3 passed), but Outlook wasn't reachable when the calendar call ran (not installed, not running, or no profile configured). Make sure Outlook is installed and running, then re-runtest.bat; it's still safe to proceed to step 6 in the meantime.SMOKE TEST FAILED— something is actually broken (bad.venv, corrupted stdout, missing tools). Fix the issue described in the output (re-runninginstall.batfixes most causes) before configuring Claude Desktop.
Paste the printed JSON snippet into Claude Desktop's
claude_desktop_config.json, under the top-levelmcpServerskey (merge it in if the file already has other servers configured). It pointscommandat the absolute path ofWinMCP.batin your install folder, e.g.:{ "mcpServers": { "win-mcp": { "command": "C:\\WinMCP\\WinMCP.bat" } } }Restart Claude Desktop; it will launch
WinMCP.bat(which execs the bundled.venv'sserver.pyover stdio) and discovercalendar_search,calendar_get_event,calendar_get_notes,task_search,task_get_task,mail_search,mail_get_message,file_search,file_get_info,onenote_search,onenote_get_page,onenote_create_page, andonenote_update_page— the full default-build list. From a hard-excluded--sharepackage, only the tools that package actually shipped (and that you enabled in step 4) can ever appear; there is no way to enable an excluded tool after the fact.
If you ever need to reinstall or repair the .venv (e.g. after a Windows
Python upgrade), just re-run install.bat — it recreates .venv from the
bundled wheels each time.
Alternative: manual / dev install (from source, on Windows)
If you're working from a source checkout on Windows instead of the
packaged zip (e.g. to develop against a live win32com/Outlook install),
you can install directly with uv or pip. Run these from a Windows
terminal (PowerShell/cmd), not from WSL2, using a Python 3.12 interpreter
that has Outlook available on the same machine:
# from the project root, using uv:
uv sync
# or, using plain pip:
python -m venv .venv
.venv\Scripts\activate
pip install .Either install path pulls in fastmcp, pydantic, pyyaml, and (on
Windows only) pywin32.
Then configure Claude Desktop by hand, pointing at the Windows Python
interpreter you installed into and this project's server.py:
{
"mcpServers": {
"win-mcp": {
"command": "C:\\path\\to\\WinMCP\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\WinMCP\\server.py"]
}
}
}(If you installed with uv sync, you can instead use
"command": "uv", "args": ["--directory", "C:\\path\\to\\WinMCP", "run", "server.py"].)
Restart Claude Desktop; it will launch server.py as a subprocess over
stdio and discover the same thirteen tools.
Configuration
config/settings.yaml controls:
lookback_days— default lookback window used bycalendar_searchwhenfrom/toare omitted (subject-only search).mail_lookback_days— default lookback window used bymail_searchwhendateFrom/dateToare omitted or only one is given; a distinct, live setting fromlookback_days(mail is typically searched much further back than calendar notes, so it defaults to90days rather than7).calendar_folder_id— OutlookGetDefaultFolder()constant used bycalendar_search/calendar_get_event;9isolFolderCalendar(the default calendar folder).tasks_folder_id— OutlookGetDefaultFolder()constant used bytask_search/task_get_task;13isolFolderTasks(synced with Microsoft To Do).inbox_folder_id— OutlookGetDefaultFolder()constant used bymail_search/mail_get_messageonfolder=inbox;6isolFolderInbox.sent_folder_id— OutlookGetDefaultFolder()constant used bymail_search/mail_get_messageonfolder=sent;5isolFolderSentMail.drafts_folder_id— OutlookGetDefaultFolder()constant used bymail_search/mail_get_messageonfolder=drafts;16isolFolderDrafts.timezone_override— optional IANA timezone name (e.g."Europe/Madrid") used instead of the host's local timezone when converting Outlook's naive local-time datetimes to timezone-aware datetimes. Leavenullto use the Windows host's local timezone.file_search_allowed_roots— list of absolute pathsfile_search/file_get_infoare restricted to; ascope/pathoutside every entry is rejected before any Windows Search query runs, and any result row that somehow falls outside these roots is dropped as well. Default[](empty/unconfigured): in that case,tools/settings.py'sdefault_search_roots()resolves the roots live from the environment instead, trying%USERPROFILE%, then whichever of%OneDrive%,%OneDriveCommercial%,%OneDriveConsumer%are set, in that order, dropping any candidate nested inside (or identical to) one already kept (e.g. a plain OneDrive-under-profile setup collapses to just%USERPROFILE%, while a KFM-redirected OneDrive on another drive stays as an extra root).file_search_max_results— cap on the number of rowsfile_searchreturns, passed to the Windows Search query as aTOP nbound (results are never fetched unbounded and then truncated). Default200.onenote_writable_notebooks— list of OneNote notebook namesonenote_create_page/onenote_update_pageare allowed to write to, checked in Python (tools/onenote.py) before any adapter/COM call. When absent, the default is exactly["z - Test Notebook"]— every other live Informa notebook stays read-only until this list is widened.onenote_search_max_results— default row cap foronenote_searchwhen the caller omitslimit; a caller-suppliedlimitover200is still clamped to that fixed ceiling regardless of this setting. Default50.onenote_ps_bridge_timeout_seconds— overall wall-clock deadline (in seconds) for eachpowershell.exechild spawned byOneNoteAdapter. Default20.
Every key above is live: each is read from config/settings.yaml at
COM-access/index-access time by its adapter (tools/outlook_adapter.py,
tools/task_adapter.py, tools/mail_adapter.py, tools/onenote_adapter.py)
or tool layer (tools/file_search.py, tools/onenote.py), falling back to
the documented default only when the key is absent or the file is
unreadable.
Development (WSL2 / Linux)
All development and the automated test suite run entirely against
FakeCalendarAdapter (tools/fake_adapter.py), FakeTaskAdapter
(tools/fake_task_adapter.py), FakeMailAdapter
(tools/fake_mail_adapter.py), FakeFileSearchAdapter
(tools/fake_file_search_adapter.py), and FakeOneNoteAdapter
(tools/fake_onenote_adapter.py) — no Outlook, no Windows, no win32com,
no powershell.exe, no COM of any kind needed:
python3.12 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python3.12 -m pytest -qThis exercises tool-layer logic (tools/calendar.py, tools/tasks.py,
tools/mail.py, tools/file_search.py, tools/onenote.py), schema
validation (models/schemas.py), the error taxonomy (tools/errors.py),
the real adapters' date/tz/error-mapping/status-mapping/SQL-building logic
with win32com/pythoncom/ADODB mocked via pytest-mock
(tests/test_outlook_adapter.py, tests/test_task_adapter.py,
tests/test_mail_adapter.py, tests/test_file_search_adapter.py), the
shared PsBridgeTransport's spawn/deadline/JSON-Lines-parse logic and
OneNoteAdapter's request-shape/XML-extraction logic with
subprocess.Popen mocked, never a real powershell.exe/COM
(tests/test_ps_bridge_transport.py, tests/test_onenote_adapter.py,
tests/test_fake_onenote_adapter.py, tests/test_onenote_tools.py), and
FastMCP tool registration/wiring against all five fake adapters
(tests/test_server.py). win32com itself is never installed and never
touched in this environment — every test that would need it injects a fake
win32com.client/pythoncom module into sys.modules instead; OneNote's
tests never need to, since OneNoteAdapter never imports win32com in
the first place.
Manual smoke test (on Windows, with Outlook running)
Automated CI/dev testing (WSL2) intentionally never exercises real Outlook
COM or real OneNote COM/PowerShell. The packaged test.bat/
deploy/smoke_test.py (see "Test before configuring Claude Desktop" above)
automates most of this against the real MCP stdio handshake and a live
calendar_search call - run it first (it deliberately does not cover
OneNote — see its own note in "Known limitations" below). The steps below
are the fully manual, Claude-Desktop-in-the-loop version, useful for
confirming calendar_get_notes, task_search/task_get_task,
mail_search/mail_get_message, file_search/file_get_info,
onenote_search/onenote_get_page/onenote_create_page/
onenote_update_page, and the Outlook-down/Windows-Search-down/
OneNote-bridge-down error paths specifically. Before relying on this
server, do the following once on the actual Windows host:
Make sure Outlook is installed, configured with a working profile, and running (or launchable) on the same Windows machine.
From the Windows
.venv, run the server directly to confirm it starts without error:.venv\Scripts\python.exe server.pyIt should sit idle waiting on stdio (no printed errors, no crash). Stop it with Ctrl+C.
Point Claude Desktop at it (see "Install (on the Windows host)" above), restart Claude Desktop, and confirm all thirteen tools (
calendar_search,calendar_get_event,calendar_get_notes,task_search,task_get_task,mail_search,mail_get_message,file_search,file_get_info,onenote_search,onenote_get_page,onenote_create_page,onenote_update_page) appear in its tool list.Ask it to search: e.g. "search my calendar for events with subject 'Tareas' in the last 7 days" — confirm it returns real entries from your Outlook calendar (or an empty list if none match, not an error).
Ask it to fetch one event's notes by date+subject (
calendar_get_notes) and confirm the returned body matches what's actually in that Outlook appointment.Ask it to list your open tasks/To Do items (
task_search), then fetch one by name to see its full body (task_get_task) — confirm both match what's actually in Outlook Tasks / Microsoft To Do for that account.Ask it to search your inbox: e.g. "search my inbox for mail with subject 'Factura' in the last 30 days" (
mail_search), then fetch one byentryIdto see its full body (mail_get_message) — confirm both match what's actually in Outlook, and repeat withfolder="sent"to confirm the sender-filter-matches-recipient behavior on Sent Items.To confirm error handling, temporarily quit Outlook entirely and repeat step 4 — the tool call should surface a clear
outlook_unavailableerror, not an unhandled crash of the server process.Ask it to search for a file you know exists under your profile or OneDrive, e.g. "search my files for anything named 'report'" (
file_search), then fetch its full metadata by path (file_get_info) — confirm both match what's actually indexed (size, timestamps, kind/extension). Also try ascopeoutside your allowed roots (e.g.C:\Windows) and confirm it's refused with asearch_root_not_allowederror rather than silently searching anyway. (deploy/smoke_test.py'sfilesfamily already does a scripted version of both halves of this — the out-of-root refusal, and a tolerantfile_search/file_get_infochain — but does not check the actual returned content, which this manual step does.)To confirm error handling for the index itself, temporarily stop the "Windows Search" service (
services.msc) and repeat step 9's search — the tool call should surface a clearwindows_search_unavailableerror, not an unhandled crash.Ask it to search OneNote: e.g. "search my OneNote for pages mentioning 'reunión'" (
onenote_search), then fetch one bypageIdto see its full title/body (onenote_get_page) — confirm both match what's actually in OneNote (or an empty list/""body if none match/the page is blank, not an error).Ask it to create a page in the notebook named exactly
"z - Test Notebook"(onenote_create_page) — confirm the new page actually appears there in the OneNote desktop app, then ask it to update that same page's body (onenote_update_page), passing back thelastModifiedDateTimethe create call returned — confirm the update succeeds and the new body is visible in OneNote.Ask it to create or update a page targeting one of your other, live notebooks (any notebook not in
onenote_writable_notebooks) — confirm the call is refused with a clearonenote_notebook_not_allowederror, and that nothing was actually written to that notebook.Ask it to update the same page from step 12 again, but pass a
dateExpectedLastModifiedfrom before that update (a stale value) — confirm the call is refused with a clearonenote_page_conflicterror, and that the page's real content in OneNote still shows step 12's body, not silently overwritten.To confirm error handling for the bridge itself, temporarily rename or move
tools/ps_bridge_onenote.ps1out of the install folder (or blockpowershell.exefrom launching) and repeat step 11 — the tool call should surface a clearonenote_unavailableerror, not an unhandled crash. Restore the file afterward.
Known limitations (MVP scope)
Only the default Calendar folder (
GetDefaultFolder(9)) is searched by the calendar tools, and only the default Tasks folder (GetDefaultFolder(13)) by the task tools — no other calendar/task folders are searched.mail_search/mail_get_messagecover the default Inbox (GetDefaultFolder(6)), Sent Items (GetDefaultFolder(5)), and Drafts (GetDefaultFolder(16)) folders, plus an arbitrary custom folder viafolderPath(a/-delimited path resolved from the default mail store's root folder).folderPathreaches only the default store's folder tree — shared/delegated mailboxes and other stores/PSTs are not reachable, since resolution starts fromDefaultStore.GetRootFolder().mail_get_messagecan also return each message's attachment file names (attachmentNames, always populated) and, opt-in viaincludeHtmlBody=true, the message'sHTMLBody(htmlBody) — attachment content is not downloadable, and folder discovery (listing whatfolderPathvalues exist) is not exposed.folderPathandincludeHtmlBodyare covered by the automated test suite (fake + real-adapter layers) but have no smoke-test coverage — thedeploy/smoke_test.pylive check only exercisesfolder="inbox",folder="sent", andfolder="drafts"with default (noincludeHtmlBody) detail calls.Recurring-appointment expansion is a documented limitation, not solved:
Items.Restrict()is used withIncludeRecurrences = Truepaired with a boundedSort("[Start]"), which is the safe pattern for a bounded date range, but recurring-series edge cases beyond typical single "note-appointment" use are out of scope for this MVP.task_search/task_get_taskare read-only: creating, completing, or updating tasks is out of scope for this MVP, as are To Do-only features with no COM equivalent (My Day, steps/subtasks).mail_search/mail_get_messageare strictly read-only: no send, move, delete, or read-flag change is ever issued. Composing/sending mail is out of scope for this MVP.file_search/file_get_infodepend entirely on the Windows Search index (Search.CollatorDSO) already having indexed the relevant location — a folder excluded from indexing, or one that hasn't finished being crawled yet, simply won't produce results, with no distinct error from "genuinely no matches." Only metadata is returned; file content itself is out of scope (thesnippetfield is a short indexer-provided preview, not the full file body), and there is no way to read, write, or delete a file through these tools.file_search_allowed_roots/file_search_max_resultsare covered by the automated test suite (fakereal-adapter layers); the
deploy/smoke_test.pylive check now also exercisesfile_search/file_get_infovia afilesfamily (a deterministic check that a fixed synthetic out-of-rootscope(C:\winmcp-smoke-denied-probe) is refused withsearch_root_not_allowed, plus a tolerantfile_search/file_get_infochain that passes on 0+ hits), but that live check does not verify actual result content or thewindows_search_unavailablepath — see step 9/10 of the manual smoke test below for that.
onenote_search/onenote_get_page/onenote_create_page/onenote_update_pagedepend entirely onOneNote.ApplicationCOM, reached through a pinnedpowershell.exe5.1 bridge — the classic desktop OneNote app must be installed (the Store app does not expose COM), and each call spawns a fresh PowerShell child (no persistent daemon), bounded byonenote_ps_bridge_timeout_seconds(default20s). Writes are restricted toonenote_writable_notebooks(default only["z - Test Notebook"]) — every other live notebook stays read-only until that list is widened, a deliberate MVP guardrail against an LLM-driven write landing somewhere unintended.onenote_update_pageappends a new body paragraph rather than replacing the page's existing content wholesale (repeated updates accumulate, they don't overwrite) — a documented limitation of the underlyingUpdatePageContentpatch semantics, not a bug. Only plain text is extracted/written; ink, images, and other rich content are out of scope, and atitle/bodyTextcontaining the literal sequence]]>will break the page's CDATA construction (a rare edge case, not chunk-split in this MVP).onenote_search/onenote_get_pageare covered by the automated test suite (fake + mocked-transport real- adapter layers) but have no smoke-test coverage — see "Manual smoke test" steps 11-15 above for the fully manual verification instead.The
onenote_update_pageconflict check (dateExpectedLastModified) has a live-confirmed blind spot: OneNote stamps a page's last-modified time lazily, at its own internal background save, not synchronously with the write call returning — the COM-visible timestamp was observed unchanged for 15+ seconds after a real write landed. A second write issued within that save-latency window can slip past the conflict check undetected, since there is no timestamp-based way (including OneNote's own native check) to see a change that hasn't been stamped yet. The guard is reliable for genuinely stale timestamps (seconds-to-minutes old — the realistic case for an LLM-driven caller), just not for two writes racing within that short window.No authentication/authorization — the process boundary (your Windows session) is the only trust boundary, per this MVP's stdio-only, zero-network design.
Possible extensions: other services this server could expose
The calendar, task, and mail tools are built on a generic pattern — a
lazily-imported Outlook COM adapter opening a folder via
GetDefaultFolder(<constant>) — and that same pattern reaches everything
the local Outlook profile stores. Each of these could be added as new
*_search / *_get tools with no new dependencies, no network access, and
no authentication, exactly like the calendar, Tasks/To Do, and mail tools:
Sending mail — the mail tools are strictly read-only (Inbox, Sent Items, Drafts, and arbitrary
folderPathfolders); sending mail is possible via COM, but that's a write operation with real-world side effects, so it deserves its own confirmation-oriented design.Attachment content — the mail tools already expose
attachmentNames(file names), but not attachment content/download; that would be a natural follow-on tomail_get_message.Folder discovery —
folderPathrequires the caller to already know the exact/-delimited path; amail_list_folders-style tool walkingFoldersunderDefaultStore.GetRootFolder()would let a caller discover valid paths instead of guessing.Shared/delegated mailboxes —
folderPathresolves only against the default store (DefaultStore.GetRootFolder()); reaching another mailbox/store the user has delegate access to would need walkingnamespace.Foldersinstead, plus a way to name the target store.Contacts —
olFolderContacts = 10: lookup by name/company, return email addresses and phone numbers.Sticky Notes (classic Outlook Notes) —
olFolderNotes = 12.More calendars — non-default and shared calendars, by walking the
Folderscollection instead of onlyGetDefaultFolder(9); plus write support (creating/updating appointments) on the existing calendar.
The OneNote tools follow a related but distinct pattern — a
lazily-imported, PowerShell-bridged OneNote.Application COM adapter,
since OneNote never appears in an Outlook profile at all. Natural
follow-ons, all still zero-network/no-auth:
Widen or make the writable-notebook allowlist configurable per-call — today
onenote_writable_notebooksis one global, operator-set list; a future version could accept it as a request-time parameter (with its own confirmation-oriented design, mirroring the "sending mail" caveat above).True overwrite semantics for
onenote_update_page— replacing a page's body wholesale instead of appending, once a safe "fetch full page XML, apply the change, rewrite" pattern is designed (see "Known limitations").Delete/move pages, sections, or notebooks — explicitly out of scope for this MVP (see the change proposal for
add-onenote-adapter).Rich content — ink, images, and file attachments embedded in a page are not read or written today; only plain extracted text.
Two boundaries to keep in mind when picking from this list:
Every Outlook-profile extension above requires classic Outlook — the "new Outlook" app does not expose the COM object model at all, same as for the current calendar tools. The OneNote tools have the same caveat for the classic desktop OneNote app specifically (see "Platform requirement" above).
Anything outside what a local COM object model exposes — full- fidelity Microsoft To Do, Teams, OneDrive files/sharing — lives behind the Microsoft Graph API, which means network access and OAuth. That's a deliberate break from this project's zero-network, no-auth design and would be a different kind of server. (OneNote itself is reachable locally via
OneNote.ApplicationCOM — see above — so it does not need Graph, unlike these.)
Building the package
The distributable zip described in "Install (on the Windows host)" above is built from this repo on the WSL2/Linux dev host with:
./make-deploy-package.shThis runs the full test suite as a gate, checks that win32com is never
imported at module level, checks that the launcher scripts are pure ASCII
and that install.ps1 parses cleanly, then stages server.py, tools/,
models/, config/settings.yaml, pyproject.toml, README.md, and the
five launcher scripts - install.bat, install.ps1, WinMCP.bat,
test.bat, smoke_test.py (flattened from deploy/) - into a WinMCP/
folder.
It builds this project's own wheel and downloads every Windows dependency
wheel (fastmcp, pydantic, pyyaml, pywin32, and their transitive
deps) for Python 3.12 and, best-effort, 3.13, into WinMCP/wheels/ — this
step needs network access on the machine running the script. The result is
written to dist/WinMCP-<YYYYMMDD>.zip, along with its sha256 and an
unzip -l listing printed at the end. dist/ is a build output and is
never itself included in the package.
Selective tool deployment: choosing which tools ship enabled
tools/catalog.yaml is the source of truth for every tool this server can
ship: which family it belongs to (calendar, task, mail, file,
onenote), its maturity (onenote's 4 tools are beta; the other 9
calendar/task/mail/file tools are alpha), and the files it depends on
(Python modules, PowerShell bridge scripts, config/settings.yaml keys).
It is never read at runtime by server.py or smoke_test.py — it only
drives two generated, downstream artifacts: tools/shipped-tools.json
(written by make-deploy-package.sh, read by install.ps1) and
config/installed-tools.yaml (written by install.ps1, read by
server.py/smoke_test.py). Maturity seeds the --share build's default
pre-selection only — it never excludes a tool from the default build,
and the installer never re-derives it.
make-deploy-package.sh supports two build modes:
Default (no flags) — today's behavior, unchanged: all 13 tools ship with
default_enabled=true. This is the only modedeploy-qa.sh/promote-pro.shuse, and the file-selection/staging pipeline is byte-identical to before this feature existed.--share— curates a default selection for a package you hand to someone else, without physically removing any tool's files (every tool is always staged in both modes — "shipped-but-disabled" — so no import ever breaks regardless of what's enabled). At an interactive terminal withwhiptailonPATH(present on this dev host), tool selection is a singlewhiptail --checklistscreen — one row per tool, labeled[family] tool_name, pre-checked from catalog maturity (beta/stablepre-checked,alphaunchecked) with the tool's maturity shown in the row description; toggle with Space, confirm with Enter, or Cancel to abort the build cleanly (nonzero exit, no zip). Pass--no-tuito force the older plain per-family/per-tooly/nread -ploop instead (also the automatic fallback whenwhiptailisn't found). Either way, your answer always wins over the maturity seed, in either direction. Add--tools=a,b,cto give the exact tool list up front (validated againsttools/catalog.yaml's names) and skip the prompt entirely — this works with or without a terminal attached. Running--sharewithout a terminal and without--tools=fails loudly and writes no package, rather than silently guessing a selection from maturity.Share package output is isolated from the pipeline zip. A share build writes to
dist/share/WinMCP-share-<YYYYMMDD>-<HHMMSS>.zip, neverdist/WinMCP-<YYYYMMDD>.zip—deploy-qa.sh/promote-pro.shresolve their zip via a non-recursivedist/WinMCP-*.zipglob (or an exact marker filename), which never matches anything underdist/share/, so a share build can never collide with or be auto-picked-up as the pipeline's own zip. The default build's output is unaffected:dist/WinMCP-<YYYYMMDD>.zip, exactly as before.The final report states the exact package path prominently in share mode, and step 1 of "Next steps" names the file (e.g. "Copy
WinMCP-share-20260828-131723.zipto the target machine") rather than the generic "Copy the zip". At an interactive console (TTY, and only when the genuine interactive picker ran — not--tools=), the report is followed by an offer to copy the package now: default destination/mnt/c/usr/tmp(created if missing), or type an alternate directory. On copy, both the WSL path (/mnt/c/...) and the equivalent Windows path (C:\...) are printed. Declining just exits, as before. A non-TTY--share --tools=...build never prompts — it prints the path and stops.
Either mode writes tools/shipped-tools.json into the package, recording
each of the 13 tools' maturity and default_enabled flag — the default
build sets default_enabled: true for all 13; a --share build mirrors
exactly the resolved selection (never a blanket flag). A build-time Gate 7
checks name-set equality across tools/catalog.yaml, server.py's
registered @app.tool names, and shipped-tools.json, and confirms every
enabled tool's catalog dependencies were actually staged — the build fails
before zipping if any of that drifts.
Hard exclusion: unchecked in a --share build means physically absent
The selective-deploy picker above only ever controlled default
enablement — every tool's files always shipped, "shipped-but-disabled"
for anything left unchecked. Hard tool exclusion makes an unchecked tool
in a --share selection a two-tier guarantee instead of one:
Build-time physical omission.
tools/catalog.py::excluded_files()computes the owner-set union of every tool's declareddeps.modules/deps.ps1files across the entire catalog, then omits from staging every file whose owners are all unselected. A file shared by two tools (in the same family or different ones — e.g.tools/ps_bridge_transport.py, used by bothfileandonenote) is kept as long as either owner was selected; only a file with zero selected owners is ever dropped.--share --tools=onenote_searchtherefore still ships all ofonenote's shared files (one selected tool keeps the whole family's code present), but a--tools=selection with zeromail/calendar/task/filetools omits every one of those families' files entirely.Runtime registration ceiling.
tools/shipped-tools.json's tool-name set becomes a hard ceilingserver.py's_tool_enabled()enforces alongside the existingconfig/installed-tools.yamlcheck: a tool must be in both the shipped set (or the ceiling is absent — legacy/full packages) and the installed set (or absent) to register. This closes the hand-edit hole the first tier alone would leave open: even if someone editedconfig/installed-tools.yamlon a deployed copy to add back a tool name that was never shipped,server.pystill refuses to register it, because that tool's own code was never staged in the first place and its name never appears in the ceiling either way.
A full/default build always selects every tool, so excluded_files()
returns nothing and both tiers are no-ops — file-for-file, byte-identical
output to a pre-hard-exclusion build.
What this does not protect against. Hard exclusion is a build-time staging choice plus a runtime allow-list — not a security or DRM boundary: it does nothing to stop someone who received a curated package from separately obtaining (or being handed) a fuller one and redistributing that instead, and it does nothing to stop anyone from reading the plain Python/PowerShell source of whatever did ship (no signing, obfuscation, or sandboxing here). Its actual purpose is narrower and more mundane: let you hand a colleague a package that never even carries the code for tool families that touch mail/calendar/file content they have no need to see, so an idle read of the extracted folder — or an idle re-enable attempt — can't expose PII or confidential data that was never there to expose.
Choosing which tools install enabled
At install time, install.bat/install.ps1 reads the package's
tools/shipped-tools.json and resolves the enabled-tool set with this
priority:
-Preset <path-to-json-file>— passinstall.bat -Preset C:\path\to\preset.json(orinstall.ps1 -Preset ...directly), where the file is{"tools": ["tool_a", "tool_b"]}. Explicit and scripted; an unknown tool name fails the install loudly, naming the offending tool.Non-interactive (no
-Preset, stdin redirected/no console) — enables exactly the manifest'sdefault_enabled=trueset, no prompt, never blocks. This is whatdeploy-qa.sh/promote-pro.shrely on for their unattendedinstall.bat < /dev/nullruns.Interactive — a per-family
y/n/sprompt (same three-way choice as the--sharebuild picker above), pre-checked per each tool'sdefault_enabledflag, ending in a selection summary you can accept or redo.
Whichever path resolves, install.ps1 writes config/installed-tools.yaml
(a flat tools: list) into the installed copy. If the package predates
this feature (no tools/shipped-tools.json staged), this whole step is
skipped and no installed-tools.yaml is written at all — the exact
back-compat path below.
Runtime effect of the installed-tools selection
server.py's create_server(installed=..., shipped=...) gates each of the
13 @app.tool registrations individually against both sets — a tool
registers only if it's in installed (or installed is absent/None)
and in shipped (or shipped is absent/None) and its family's
files are actually present on disk (per-family importlib.util.find_spec
guard, so a hard-excluded family's absent modules never even attempt an
import, let alone raise). shipped comes from tools/shipped-tools.json
(hard-tool-exclusion's build-time ceiling, absent = legacy package, no
ceiling); installed comes from config/installed-tools.yaml exactly as
before. Every tool module for a family whose files are present is still
imported unconditionally regardless of the installed selection, so a
merely-disabled (but shipped) tool's code is present but simply never
registered with the MCP client:
config/installed-tools.yamlabsent — every shipped tool registers, byte for byte the same as before this feature existed on a full/default package (exact back-compat: nothing installed by this project'sinstall.ps1before this change ever wrote that file).tools:present and non-empty — only the listed tool names register, and only if they're also shipped; an unrecognized name, or a name absent fromshipped-tools.jsonon a hard-excluded package, is silently ignored either way — there is no way to hand-edit this file back into a tool that was never shipped.tools: [](empty list) — zero tools register.
deploy/smoke_test.py derives its own expectations from the same file (a
small stdlib re scrape, no yaml import needed): a fully-disabled
family (none of its tools enabled) reports verdict "skipped" rather than
running any live check against it — skips are verdict-neutral, so a
selective install can still finish with an overall SMOKE TEST PASSED.
Building a share package end-to-end
./make-deploy-package.sh --share # interactive whiptail checklist (or plain y/n if whiptail is absent)
./make-deploy-package.sh --share --no-tui # interactive, but force the plain y/n picker
./make-deploy-package.sh --share --tools=onenote_search,onenote_get_page # explicit, no prompt
./make-deploy-package.sh --share --tools=onenote_search # single tool: still ships onenote's whole shared file set
# (onenote.py/onenote_adapter.py/ps_bridge_transport.py/
# ps_bridge_onenote.ps1), but shipped-tools.json's manifest
# lists ONLY onenote_search — the other 3 onenote tools are
# hard-excluded (physically absent from the ceiling, not
# merely default_enabled=false) even though their code
# rides along as a shared-file side effect.Any selection that leaves zero tools of a family checked physically omits
that family's files from the zip entirely — e.g. --tools=onenote_search
alone still ships every mail/calendar/task/file module you'd see
in a default build, but --tools=onenote_search,file_search omits
tools/calendar.py, tools/outlook_adapter.py, tools/tasks.py,
tools/task_adapter.py, tools/mail.py, and tools/mail_adapter.py
outright (verify with unzip -l on the resulting dist/share/*.zip).
Manual verification: selective build/install (Windows host)
The automated suite covers the catalog, the registration gate, and the smoke-test derivation logic (via fakes/stubs), but the actual interactive prompts and a real install only get exercised by hand, on a Windows host with an attached console:
Build with
--shareand install at an interactive console: confirm thewhiptailchecklist arrives withonenote's 4 tools pre-checked and the 9 alpha tools unchecked (labels[family] tool_name, maturity in the description); toggle one alpha tool on and one onenote tool off, confirm, and check the resultinginstalled-tools.yamlmatches your override, not the maturity seed. Also confirm Cancel aborts the build with a nonzero exit and no zip, and that--share --no-tuifalls back to the plain per-familyy/nread -ploop instead. After a successful build, confirm the post-report copy offer: accept the default/mnt/c/usr/tmpdestination, then repeat and type an alternate directory, checking both the/mnt/c/...andC:\...paths it prints; then decline and confirm it just exits.Build with
--shareand no TTY, no--tools=: confirm the build fails loudly and produces no package. Then build with--share --tools=a,band no TTY: confirm it succeeds unattended, staging exactly the named tools withshipped-tools.jsonmarking exactly thosedefault_enabled=true.Install a
--sharesubset (e.g.onenoteonly), then runsmoke_test.pyagainst it: confirm live checks run only foronenotewhile every other family reports"skipped", with the overall verdict unaffected by the skips.
Deploying from this dev machine (QA → PRO)
The instructions above are for a third party installing a zip they were
handed. On this dev machine (WSL2 + a WSL2-mounted Windows host under
/mnt/c), two root scripts automate getting a freshly built zip in front
of a human validator and then, once approved, onto the live install —
without ever touching Claude Desktop's config:
Build:
./make-deploy-package.sh(see "Building the package" above) producesdist/WinMCP-<YYYYMMDD>.zip.Deploy to QA:
./deploy-qa.sh(no argument needed — it picks the newestdist/WinMCP-*.zipby modification time; pass a path explicitly to deploy an older one). It wipes any prior QA install, extracts the zip into a disposable sandbox atC:\usr\WinMCP-qa, runsinstall.batnon-interactively, and writes aQA-VALIDATED.txtmarker (zip name, sha256, UTC timestamp) inside that sandbox. This never touchesC:\usr\WinMCP(the live PRO install) or Claude Desktop's config.Manual validation (the human gate): on the Windows host, double-click
test.batinsideC:\usr\WinMCP-qa. This is the sametest.bat/deploy/smoke_test.pydescribed in "Install (on the Windows host)" above, except it now exercises every registered tool family (calendar, tasks, mail-inbox, mail-sent, mail-drafts, files), not just calendar, and prints one line per family plus a final overall verdict:SMOKE TEST PASSED— every family's search+detail chain succeeded. Safe to promote.SMOKE TEST PASSED WITH WARNINGS— the MCP plumbing is fine, but one or more families couldn't reach Outlook (not installed, not running, or no profile). Still safe to promote once you're satisfied the warning is environmental, not a real regression.SMOKE TEST FAILED— something is actually broken. Do not promote; fix the issue (re-runninginstall.batinWinMCP-qafixes most causes) and re-deploy to QA before trying again.
Promote to PRO: quit Claude Desktop first — it keeps the PRO server's
python.exe(underC:\usr\WinMCP\.venv) alive as a subprocess for as long as it's running, andpromote-pro.shhas a hard lock gate that refuses to run while that process is alive (this is unconditional; it is the one thing--forcedoes not override). Once Claude Desktop is closed, run./promote-pro.sh. With no argument it resolves the zip named inQA-VALIDATED.txtand refuses to promote if that zip's sha256 no longer matches what was actually validated (pass--forceto override only the sha256 check, e.g. if you rebuilt an identical package after QA passed). On a clean run it wipeswheels/and unzips ontoC:\usr\WinMCPin place (the same overwrite-preserving-.venvmechanics the olddist/deploy.shused), runsinstall.batnon-interactively, copies the zip to the OneDrive_DEV\WinMCP\_OUTaudit folder, and writes aDEPLOYED.txtmarker (same schema asQA-VALIDATED.txt, withdeployed_utcinstead ofvalidated_utc).Restart Claude Desktop to pick up the new PRO install.
claude_desktop_config.jsonitself never changes across this flow — only the files underC:\usr\WinMCPdo.
Rollback: if a promoted build turns out to be bad, promote an older
zip from dist/ explicitly, e.g. ./promote-pro.sh dist/WinMCP-20260731.zip --force. --force is required here because that older zip's sha256 won't
match the current QA-VALIDATED.txt (which still names whatever was most
recently QA'd) — the lock gate and the actual install/copy/marker steps
all behave identically to a normal promotion.
Available Tools
15 toolscalendar_get_eventCalendar Get EventA
Fetch full detail (including body) for a single event by its Outlook entryId.
| Name | Required | Description | Default |
|---|---|---|---|
| entryId | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| end | Yes | |
| body | Yes | |
| start | Yes | |
| subject | Yes | |
| entry_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the burden of behavioral disclosure. It does reveal the read-only nature ('Fetch') and the fact that the body is included, which is useful. However, it does not describe error behavior, missing events, authentication requirements, or response shape beyond what an output schema might imply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, compact sentence that front-loads the core action and key parameter with no filler. Every word contributes value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter retrieval tool with an output schema present, the description covers the essential behavior and parameter semantics. It does not explicitly mention when to use the tool relative to siblings, but that gap is already separately accounted for in usage guidelines.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It adds meaningful context by explaining that the entryId is an Outlook entryId and that it identifies a single event. This is more informative than the bare string type in the schema, though it could specify where to obtain the entryId.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('Fetch'), resource ('single event'), scope ('full detail including body'), and the lookup key ('Outlook entryId'). This distinguishes it from sibling tools like calendar_search and calendar_get_notes without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The usage context is implied: use this when you need full event details by a single Outlook entryId. However, it does not explicitly state when to prefer this over calendar_search or other siblings, nor does it mention any exclusions or alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_get_notesCalendar Get NotesA
Resolve the single note-appointment matching date+subject and return its full detail (subject + body).
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| subject | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| end | Yes | |
| body | Yes | |
| start | Yes | |
| subject | Yes | |
| entry_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the behavioral disclosure burden. It clearly indicates a read operation that returns subject and body, but it does not describe behavior when no match is found, when multiple matches exist, or whether matching is exact.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler. Every phrase adds useful information: the operation, the lookup keys, and the return contents.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter lookup with an output schema present, the description covers the core calling contract. However, it does not disambiguate this tool from closely related siblings like calendar_get_event or calendar_search, and it omits edge-case behavior that an agent might need to know before invoking it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description compensates by explaining both parameters as the matching criteria ('date+subject') and identifying the output fields ('subject + body'). It adds real meaning beyond the raw schema, though it stops short of defining exact-match semantics or date-normalization behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific operation—resolving a single note-appointment by date and subject—and mentions what is returned ('full detail (subject + body)'). It is clear enough to be useful, though the compound term 'note-appointment' is unusual and the description does not explicitly distinguish this from calendar_get_event.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'matching date+subject' implies this tool is for retrieving a specific existing note-appointment when both lookup values are known. However, it does not explicitly state when to prefer this over siblings like calendar_search or calendar_get_event, nor does it mention any exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_searchCalendar SearchA
Search the default Outlook calendar folder by date range and/or
subject substring. At least one of from/to/subject is required.
A subject-only query (no explicit from/to) auto-applies a default
window — 90 days back, 365 days forward from now, configurable via
calendar_subject_search_lookback_days/..._lookahead_days —
since a calendar's value is mostly ahead of today; the window
actually used is echoed back as windowApplied in the response.
Supplying explicit from/to overrides this window entirely (they
can widen or narrow the search — caller-controlled). limit
bounds the number of rows returned (optional, default 50, hard
max 200 — over-max is clamped, not rejected; <= 0 is
rejected).
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| from | No | ||
| limit | No | ||
| subject | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| window_applied | No | |
| results_truncated | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it delivers: it discloses the default 90/365-day window, configuration via lookback/lookahead settings, the windowApplied echo, override semantics, and limit clamping/rejection behavior. This is unusually transparent about side effects and edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence carries information an agent needs: purpose, requirement, default-window behavior, override semantics, and limit rules. It is well-structured, front-loading the core operation before explaining edge-case behavior, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given four parameters, zero required, no annotations, and non-trivial default-window logic, the description covers all important behavioral aspects. It also explains the configurable nature and response echo (windowApplied) without needing to describe the output schema since one exists. Nothing critical is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must explain the parameters, and it does. It defines from/to as an explicit date range, subject as a substring, and limit with default 50, hard max 200, clamping vs rejection of over-max, and rejection of <=0. Every parameter receives meaningful semantic detail beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Search' and the specific resource 'the default Outlook calendar folder', and it specifies the two search dimensions (date range and subject substring). It is distinguishable from siblings like calendar_get_event and calendar_get_notes, which imply retrieving specific items rather than searching across a folder.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives strong usage context: at least one of from/to/subject is required, explains subject-only search window behavior, and describes how explicit from/to overrides the window. It does not explicitly name alternative tools or say 'use X instead', but the usage conditions are clear enough for an agent to decide when this search tool is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
file_get_infoFile Get InfoA
Fetch full indexed metadata for a single file by its native path
or file:///-style URL form, as previously returned by
file_search. path must fall within a configured or default
allowed search root.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| kind | No | |
| name | Yes | |
| path | Yes | |
| size | Yes | |
| snippet | No | |
| extension | No | |
| created_time | Yes | |
| last_modified | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral disclosure burden. It communicates the path format, the search-root restriction, and that the operation is a metadata fetch, implying read-only behavior. However, it does not disclose error behavior, permission requirements, or what happens when the path is invalid or outside the allowed root.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The core purpose and resource are front-loaded, followed by essential path-format and restriction details. Every sentence contributes useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given this is a simple single-parameter metadata retrieval tool with an output schema present, the description covers the necessary invocation details: what the tool does, acceptable path forms, and a security constraint. Nothing critical is missing for selecting and calling it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description fully compensates by explaining the single parameter 'path' with concrete format guidance: native path or file:/// URL, as returned by file_search, and constrained to an allowed root. This adds substantial meaning beyond the bare string type in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states a specific action ('Fetch full indexed metadata'), a specific resource type ('a single file'), and the accepted path forms. It distinguishes itself from file_search by describing the post-search retrieval use case, making its role clear among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context by indicating that paths come from prior file_search results and must be within an allowed search root. It does not explicitly state when not to use the tool or name alternatives, but the workflow implication is strong and sufficient for an agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
file_searchFile SearchA
Search by a case-insensitive filename substring (answered by a
bounded filesystem walk, independent of the Windows Search index)
and/or a full-text phrase match (answered by the index: ADO,
then a PowerShell bridge on ADO failure), optionally restricted to
an absolute scope subtree. At least one of filename/phrase is
required; scope (if given) must fall within a configured or
default allowed search root. Returns results plus a
resultsTruncated flag.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | ||
| phrase | No | ||
| filename | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| results_truncated | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It reveals important operational traits: the filename search is case-insensitive and uses a bounded filesystem walk independent of the Windows Search index, while phrase search uses ADO with a PowerShell fallback. It also discloses the resultsTruncated flag, though it does not cover potential errors, permissions, or latency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every clause earns its place, covering search modes, backend mechanisms, constraints, and output flags. It is front-loaded with the core action and keeps technical details in parentheticals. It is slightly long due to implementation detail, but it is not redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given there are no annotations and the output schema exists, the description sufficiently covers call requirements, parameter constraints, and return behavior via the resultsTruncated flag. It would benefit from explicit examples of filename/phrase formatting and a direct note about when to prefer this tool over sibling searches, but nothing critical is missing for invoking it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for the bare schema. It does this effectively by assigning semantics to all three parameters: filename as a substring, phrase as a full-text match, and scope as an absolute subtree. It also clarifies the mutual-exclusion-like constraint that at least one of filename/phrase must be provided and that scope is restricted to allowed roots.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise operation: search files by a case-insensitive filename substring and/or a full-text phrase, optionally within an absolute scope subtree. It names the specific resource and mechanism, and clearly distinguishes itself from sibling search tools by focusing on files. Even without naming siblings, the scope and technical detail remove ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear invocation constraints: at least one of filename/phrase is required and scope must be within an allowed root. However, it never explicitly states when to use this tool over alternatives such as file_get_info, mail_search, or onenote_search, nor does it describe when not to use it. Usage is implied by the resource type, not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_get_messageMail Get MessageA
Fetch full detail (including body) for a single Inbox/Sent
Items/Drafts/folderPath message by its Outlook entryId.
attachmentNames is always populated; htmlBody is populated only
when includeHtmlBody=true is passed (default false).
| Name | Required | Description | Default |
|---|---|---|---|
| entryId | Yes | ||
| includeHtmlBody | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| to | Yes | |
| body | Yes | |
| date | Yes | |
| sender | Yes | |
| subject | Yes | |
| entry_id | Yes | |
| html_body | No | |
| sender_address | Yes | |
| has_attachments | Yes | |
| attachment_names | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses conditional response behavior: attachmentNames is always populated while htmlBody is only populated when includeHtmlBody=true (default false). It could add error/not-found behavior but covers the key conditional.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences. The first gives purpose and scope; the second gives the field-availability nuance. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter retrieval tool with an output schema, the description covers the call shape, parameter effect, and important field-population behavior. It could mention what happens when the entryId is not found, but that is a minor omission.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description fully compensates: entryId is identified as the Outlook message identifier, and includeHtmlBody is explained with its effect on htmlBody population and its default. Both parameters are given meaningful semantics beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('Fetch full detail ... for a single ... message by its Outlook entryId'), clearly distinguishing it from search or listing tools. The mention of Inbox/Sent Items/Drafts/folderPath adds scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the use case explicit: use when you have an Outlook entryId and need the full single message, including body and attachments. It does not explicitly name sibling alternatives such as mail_search, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_searchMail SearchA
Search the default Outlook Inbox, Sent Items, or Drafts folder
(folder), or an arbitrary custom folder resolved from the default
mail store's root (folderPath, a /-delimited path) — exactly one
of folder/folderPath is required. Also filters by date range,
subject substring, and/or sender substring; at least one of
dateFrom/dateTo/subject/sender is required. limit bounds the
number of rows returned (optional, default 50, hard max 200 —
over-max is clamped, not rejected; <= 0 is rejected).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| dateTo | No | ||
| folder | No | ||
| sender | No | ||
| subject | No | ||
| dateFrom | No | ||
| folderPath | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| results_truncated | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses limit semantics (default 50, hard max 200, over-max clamped not rejected, <= 0 rejected) and how folderPath resolves from the default mail store's root. It stops short of full transparency — result ordering, error behavior for a nonexistent folderPath, and explicit read-only/auth status are unstated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded: the core purpose leads, and every clause about folders, filters, and limit earns its place with zero filler. The heavy parentheticals and em-dash chains make it harder to scan than a structured list, but nothing is redundant or wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 7 nullable parameters with interdependent constraints and an output schema that documents return values, the description gives an agent everything needed for a correct first call: the XOR folder rule, the at-least-one filter rule, and clamping semantics. Remaining gaps — invalid-folderPath error behavior and result ordering — are minor for successful invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate — and it covers every parameter: folder vs folderPath mutual-exclusivity semantics, '/' delimited path meaning, substring semantics for subject/sender, date-range meaning for dateFrom/dateTo, and the limit clamp/reject behavior. This vastly exceeds what the bare schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Search the default Outlook Inbox, Sent Items, or Drafts folder' — a specific verb (Search), a concrete resource (Outlook mail folders), and a defined scope (folder/folderPath selection plus filters). This clearly distinguishes it from sibling search tools targeting calendar, tasks, files, and OneNote, and from mail_get_message by the search-vs-fetch semantics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear invocation context: 'exactly one of folder/folderPath is required' and 'at least one of dateFrom/dateTo/subject/sender is required,' which tells an agent precisely when the tool is appropriate and what it needs. However, it never names a sibling alternative (e.g., mail_get_message for single-message retrieval), so there is no explicit when-not or exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onenote_create_pageOnenote Create PageA
Create a new page in the OneNote section identified by
sectionId — the canonical {GUID}{1}{B0} form returned by
onenote_list_sections (also on onenote_search rows); a bare
GUID or a section name will never resolve. bodyText is written
as one plain-text paragraph. The section's owning notebook is
resolved and checked against the writable-notebook allowlist
(onenote_writable_notebooks, default ["z - Test Notebook"])
before any write is attempted — refused with a clear
[onenote_notebook_not_allowed] error otherwise.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| bodyText | Yes | ||
| sectionId | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| title | Yes | |
| page_id | Yes | |
| body_text | Yes | |
| section_id | No | |
| notebook_id | No | |
| section_name | Yes | |
| last_modified | No | |
| notebook_name | Yes | |
| body_text_incomplete | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does so well. It discloses that bodyText is written as a single plain-text paragraph, that the owning notebook is checked against an allowlist before any write, and that refusal produces a specific [onenote_notebook_not_allowed] error. This is meaningful behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded, with the core action in the first clause. Every sentence carries useful operational detail: canonical format, source of valid IDs, bodyText behavior, allowlist check, and error outcome. There is no padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write action with no annotations, the description is unusually complete: it covers input sources, format constraints, a security precondition, and error behavior. The main missing piece is the semantics of the required title parameter, which keeps it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It adds strong semantics for sectionId (canonical form, source, non-resolving alternatives) and bodyText (plain-text paragraph), but the required title parameter is never described. This is a clear gap for a required argument.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Create a new page in the OneNote section identified by sectionId.' This clearly differentiates the tool from siblings like onenote_get_page and onenote_update_page, even without explicitly naming them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context: it is for creating a page, and it tells the agent to obtain sectionId from onenote_list_sections or onenote_search rows. It does not explicitly state when not to use this tool or mention onenote_update_page as an alternative, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onenote_get_pageOnenote Get PageA
Fetch full, read-only text detail for a single OneNote page by
its pageId (as returned by onenote_search). Never mutates any
notebook/section/page state.
bodyText is a FLATTENED plain-text reading view of the page —
bullets, indentation, tables and images render as plain lines. It
is not the storage format: onenote_update_page appends and never
writes this flattened text back over the page, so reading a
formatted page is always safe. bodyTextIncomplete: true means
the page contains tables, images or ink that this flattened view
cannot represent — the page holds more than you are seeing;
updating it is still safe. lastModifiedDateTime is the value
onenote_update_page's conflict guard expects — pass it back
exactly as returned.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| title | Yes | |
| page_id | Yes | |
| body_text | Yes | |
| section_id | No | |
| notebook_id | No | |
| section_name | Yes | |
| last_modified | No | |
| notebook_name | Yes | |
| body_text_incomplete | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden, and it delivers thoroughly. It discloses read-only semantics, explains that `bodyText` is a flattened reading view rather than storage format, clarifies what `bodyTextIncomplete: true` means, and details the conflict-guard contract around `lastModifiedDateTime`. This is exemplary transparency for a fetch operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description opens with the core purpose and read-only guarantee, then front-loads the key parameter relationship. The subsequent caveats about flattened text, incomplete content, and the update conflict guard are dense but each earns its place because they prevent realistic misuse. The length is justified and well-organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with an output schema, the description covers the essential semantics: what the output fields mean, what incompleteness signals, and how the result interacts with the sibling update tool. Error cases and authorization are not mentioned, but the behavioral and integration context provided is sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema only defines `pageId` as a required string with no description, so the description must compensate. It does so by stating that `pageId` is the value returned by `onenote_search` and is used to address a single page. This adds useful source-level meaning beyond the raw schema, though it does not elaborate on format or validation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Fetch full, read-only text detail') on a specific resource ('a single OneNote page') and identifies the exact identifier needed (`pageId`). It also explicitly declares that the operation never mutates state, which distinguishes it from write tools like `onenote_create_page` and `onenote_update_page`.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly ties `pageId` to the output of `onenote_search`, signaling where the identifier comes from, and explains how `lastModifiedDateTime` should be passed back to `onenote_update_page`. It does not explicitly state 'use this instead of X' or provide when-not-to-use guidance, but the context strongly implies the intended workflow among the sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onenote_list_sectionsOnenote List SectionsA
List every OneNote notebook/section pair with its canonical
ids. sectionId (the {GUID}{1}{B0} form) is what
onenote_create_page requires — a bare GUID or a section NAME
will never resolve. Read-only; never mutates any state.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It explicitly states 'Read-only; never mutates any state,' which is important and accurate. It also clarifies the exact identifier format returned. Since this is a simple zero-parameter listing tool, this level of transparency is adequate, though it does not discuss pagination or size limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no fluff. The first sentence states the core purpose, and the second adds the crucial canonical-id detail and safety guarantee. Front-loaded and efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only listing operation with an output schema, the description covers everything an agent needs: what the tool returns, why the format matters, the tool it feeds, and the non-mutating guarantee. No missing context blocks correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is nothing to document beyond the schema. The description adds useful meaning about what the returned output contains—canonical ids for every notebook/section pair—and emphasizes the critical id format. This matches the baseline for a no-parameter tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'List every OneNote notebook/section pair with its canonical ids.' It also clearly distinguishes the tool's output by emphasizing the `{GUID}{1}{B0}` sectionId form required by `onenote_create_page`, setting it apart from searching or fetching page content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives actionable context by explaining that this tool yields the canonical sectionId needed by `onenote_create_page`, and warns that a bare GUID or section name will never resolve. It does not explicitly tell when not to use it or name alternatives like `onenote_search`, but the routing information is clear enough for the primary use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onenote_searchOnenote SearchA
Full-text search over OneNote page content (FindPages
via the COM bridge). query must be non-empty — rejected as an
[invalid_request] error before any adapter call. limit bounds
the number of rows returned (optional, default 50, hard max 200 —
over-max is clamped, not rejected; <= 0 is rejected). An empty
result is [], not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does so thoroughly: it documents `[invalid_request]` validation, the default and hard maximum for `limit`, clamping behavior for over-max values, rejection of `<= 0`, and empty-result semantics (`[]`, not an error). This gives an agent concrete expectations before calling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but compact, front-loading the core purpose and then providing only high-value behavioral details. Every sentence earns its place, with no repetition of the tool name or filler content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter search tool with an output schema present, the description is complete: it covers required input, validation errors, default and boundary behavior, and the empty-result case. Even without annotations, it fully prepares an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage, but the description compensates by fully explaining both parameters: `query` must be non-empty, and `limit` is optional with default 50, hard max 200, clamping over-max, and rejection of invalid values. This adds meaning far beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action: 'Full-text search over OneNote page content' and identifies the underlying implementation via `FindPages`. This clearly distinguishes it from siblings like onenote_get_page and onenote_list_sections, so an agent understands the tool's unique purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context: this is for full-text content search, not listing sections or fetching a specific page. It does not explicitly name alternative tools or exclusion conditions, but the search-focused framing is sufficient to guide selection among the OneNote siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onenote_update_pageOnenote Update PageA
APPEND bodyText as one new plain-text paragraph at the end of
the OneNote page identified by pageId. Never replaces or
reformats existing content — the page's original formatting
(bullets, tables, images) is preserved untouched, so updating a
formatted page is safe.
dateExpectedLastModified is a conflict guard, not a write-back
of the read body: pass lastModifiedDateTime exactly as
onenote_get_page returned it, and a mismatch (the page changed
since your read) raises [onenote_page_conflict] — re-read and
retry with the fresh value. OMIT the field for an UNGUARDED
overwrite (no concurrency check at all): the escape hatch for a
page that keeps rejecting a freshly-read value, which OneNote's
lazily-stamped timestamps can cause on pages not written recently.
The page's owning notebook is checked against the
writable-notebook allowlist before any write, same as
onenote_create_page.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | Yes | ||
| bodyText | Yes | ||
| dateExpectedLastModified | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| title | Yes | |
| page_id | Yes | |
| body_text | Yes | |
| section_id | No | |
| notebook_id | No | |
| section_name | Yes | |
| last_modified | No | |
| notebook_name | Yes | |
| body_text_incomplete | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries the behavioral disclosure burden. It explains the append-only guarantee, preservation of original formatting, the conflict guard mechanism, the exact error raised on mismatch, the unguarded overwrite escape hatch, and the notebook allowlist check. This is exceptional transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average, but every sentence adds essential operational detail. It front-loads the core behavior first, then explains the conflict guard, then the allowlist context. There is no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, three parameters, and complete absence of annotations or schema descriptions, the description covers everything an agent needs: what the tool does, how each parameter behaves, failure modes, retry guidance, and permissions context. The output schema exists, so not detailing return values is acceptable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must explain the parameters, and it does. bodyText is defined as a plain-text paragraph, pageId is the page target, and dateExpectedLastModified gets a detailed explanation of its role as a conflict guard, including how to obtain it, when to omit it, and what happens on mismatch.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'APPEND bodyText as one new plain-text paragraph at the end of the OneNote page.' It also explicitly distinguishes the behavior from replacing or reformatting content, which separates it from related page tools like onenote_create_page.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear practical context: when to use, how to handle conflict-guard values, and when to omit the guard for an unguarded overwrite. It references onenote_get_page and onenote_create_page for related behavior, but it does not explicitly state exclusions such as 'create a new page instead' or 'use search instead.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_infoServer InfoA
Identify this deployment: package name, build UTC and build
id from the build stamp (build-info.json; null with a note
for a source checkout or a pre-stamp package), install root
(distinguishes the PRO and QA installs), Python version, and
the tool names this server process actually registered.
Read-only, touches no Outlook/OneNote/file state. Call it
FIRST when verifying that a redeployed build is the one
answering — a server started before a promote keeps running
the old code until its client restarts.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| package | No | |
| build_id | No | |
| built_utc | No | |
| build_mode | No | |
| install_root | Yes | |
| enabled_tools | Yes | |
| python_version | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the behavioral burden. It explicitly states 'Read-only, touches no Outlook/OneNote/file state,' discloses the edge case for source checkouts/pre-stamp packages (null with a note), and explains the deployment verification behavior. This is unusually transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is information-dense without fluff: each sentence contributes a distinct piece of value—output contents, edge-case handling, safety guarantee, and a concrete usage recommendation. It is front-loaded with the core purpose and maintains clear structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter introspection tool, the description is complete: it covers what data is returned, when to call it, why ordering matters, and side-effect guarantees. The presence of an output schema further reduces the need to describe return structure in prose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is nothing for the description to add beyond the empty input schema, which is fully covered. The baseline of 4 applies because no parameter documentation is needed; the description focuses on outputs and behavior instead.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Identify this deployment' and enumerates specific outputs such as package name, build UTC/build id, install root, Python version, and registered tool names. This distinguishes it from sibling data-access tools (calendar, mail, files, OneDrive, tasks) by focusing on server/build introspection rather than domain content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: 'Call it FIRST when verifying that a redeployed build is the one answering' and explains the stale-server scenario. It does not name specific sibling alternatives or provide when-not-to-use conditions, but the read-only, no-state statement implies it is not for Outlook/OneNote/file operations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
task_get_taskTask Get TaskA
Fetch full detail (including body) for a single task by its Outlook entryId.
| Name | Required | Description | Default |
|---|---|---|---|
| entryId | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| body | Yes | |
| status | Yes | Mirrors COM's `OlTaskStatus` 1:1 (see design.md's "Status mapping" decision). |
| subject | Yes | |
| due_date | No | |
| entry_id | Yes | |
| is_complete | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. 'Fetch' clearly signals a non-mutating read operation, and 'including body' adds useful behavior beyond a simple metadata lookup. It does not mention error or authorization cases, but these are not critical for a simple get-by-id tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, efficient sentence with no redundant words. The core action and identifier type are front-loaded, and every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a one-parameter tool with no parameter descriptions, an existing output schema, and a clear explanatory description, the definition is nearly complete. It might benefit from pointing to task_search for discovering entryIds, but the essential information for correct invocation is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only a bare string property with no description. The tool description compensates by identifying the parameter as an 'Outlook entryId', giving the agent the key semantic needed to invoke the tool correctly. It could add format or example details, but this is adequate for a single required parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Fetch'), names the resource ('a single task'), and states the scope ('by its Outlook entryId'). It also adds distinctive detail ('including body') that separates it from sibling tools like task_search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes clear it is for fetching one known task via an entryId, which implies the appropriate context: use it after you have an entryId and need full details. It does not explicitly name alternatives or exclusion conditions, but the intent is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
task_searchTask SearchA
Search the default Outlook Tasks folder. All filters are
optional; a filterless call returns every task in the folder (up
to the effective limit). limit bounds the number of rows
returned (optional, default 50, hard max 200 — over-max is
clamped, not rejected; <= 0 is rejected).
| Name | Required | Description | Default |
|---|---|---|---|
| dueTo | No | ||
| limit | No | ||
| status | No | ||
| dueFrom | No | ||
| subject | No | ||
| includeNoDueDate | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| results_truncated | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden. It discloses the default folder scope, the effect of a filterless call, the limit default (50), the hard maximum (200), clamping behavior for over-max values, and rejection of <= 0. This is substantive transparency beyond a simple 'search tasks' line, though it omits minor details like sort order or date boundary inclusivity.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no wasted words. The first sentence states the purpose immediately, and the second packs the essential filter and limit behavior efficiently. Front-loading is excellent.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, so return values are covered, and the limit behavior is well documented. However, with six optional parameters, no annotations, and zero schema descriptions, the tool still lacks needed clarity on filter semantics for most parameters. It is usable but has clear gaps that an agent would have to resolve through inference or experimentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, but it only explains the `limit` parameter in detail. The other five parameters (`dueTo`, `dueFrom`, `status`, `subject`, `includeNoDueDate`) are left to name-based inference, with no explanation of matching semantics, range inclusivity, or defaults beyond what the parameter names imply. This is insufficient for confident invocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Search') and a specific resource ('default Outlook Tasks folder'), making the tool's purpose unmistakable. It implicitly distinguishes itself from sibling search tools for mail, calendar, OneNote, and files, and from task_get_task by emphasizing folder-wide search rather than single-task retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context: this is the tool for searching the default Outlook Tasks folder, and filters are optional with filterless returning all tasks. It does not explicitly name alternatives or exclusion conditions, so it falls short of a 5, but the usage context is strong enough for an agent to select it appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
15 tool updates
v0.1.0- First observed
calendar_get_event - First observed
calendar_get_notes - First observed
calendar_search - First observed
file_get_info - First observed
file_search - First observed
mail_get_message - First observed
mail_search - First observed
onenote_create_page - First observed
onenote_get_page - First observed
onenote_list_sections - First observed
onenote_search - First observed
onenote_update_page - First observed
server_info - First observed
task_get_task - First observed
task_search
TDQS
Each tool targets a distinct resource/action combination: search, get-by-ID, lookup-by-key, and write operations are clearly separated across calendar, mail, tasks, files, and OneNote. Even close pairs like calendar_search and calendar_get_notes are distinguishable by their lookup key and return shape.
Tool names consistently use a lowercase snake_case {resource}_{action}[_target] pattern, which is predictable and readable. The only mild deviations are server_info, which is not action-based, and calendar_get_notes, which uses 'notes' rather than an event/appointment term.
Fifteen tools is at the upper end of the ideal range but appropriate given the five subdomains covered: calendar, tasks, mail, files, and OneNote, plus a server_info utility. No tool feels redundant; each adds a distinct search, read, or write capability.
The read/search surface is strong across all domains, and OneNote additionally supports create and update operations. However, calendar, task, and mail tools are read-only, file content is not retrievable, and there are no delete operations anywhere, leaving notable lifecycle gaps for a general Windows/Outlook automation server.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Notes, files, GitHub, and Drive through one MCP connection.
Notes, files, GitHub, and Drive through one MCP connection.
Notes, files, GitHub, and Drive through one MCP connection.
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA pure-local Microsoft OneNote MCP server for Windows that controls the OneNote desktop app through the local OneNote COM API without needing Azure, Microsoft Graph, API keys, or OAuth.1MIT
- FlicenseNot gradedqualityDmaintenanceProvides LLMs with access to Microsoft Outlook email functionality, allowing them to read, search, compose, and manage emails through a standardized MCP interface on Windows.-
- AlicenseNot gradedqualityAmaintenanceA CLI and MCP server for Microsoft Outlook and OneDrive via the Microsoft Graph API, enabling AI agents to manage email, calendar, contacts, tasks, and files through the Model Context Protocol.17MIT
- FlicenseAqualityAmaintenanceAn MCP server that lets you talk to your OneNote notebooks, enabling search, reading, creating, and managing pages, sections, and notebooks via natural language. It connects directly to Microsoft Graph from your own computer, so your notes and credentials never leave your machine.13-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/collero/winMCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server