Why I Built a OneDrive Writeback MCP Server

I run a Claude “chat” project I mostly use from the Claude mobile app, and it needs to keep certain files up to date. I’d already run into this same problem with mail and calendar, and wrote about building a narrow, write-only MCP server for those after neither Anthropic’s Microsoft 365 connector nor any workaround gave me real write access there. This time it was files: neither of the connectors I had access to could actually write. Anthropic’s Microsoft 365 connector was read-only from the start — no create, no update, nothing — so that was a non-starter before I even tried. Google Drive’s connector at least let the agent create files, so that’s where my workaround actually played out: agent creates a new file with the updated content, I go delete the old one myself. That held up fine until Google Drive’s file list started showing two files with the same name, and the same YYYY-MM-DD hh:mm timestamp, and neither the mobile app nor the web UI surfaces the file ID the agent actually used, so there’s no “check details” step to tell them apart before deleting. I’ve pulled the wrong version out of the trash more than once, then spent a few rounds of “is this the right one?” with the agent to confirm that only the file with the right ID is left.

The read-only wall

Anthropic’s Microsoft 365 connector setup docs explain why: OneDrive/SharePoint file writes sit in the same admin-gated bucket as mail and calendar writes, activating only once a Microsoft Entra admin consents and an org enables them under Organization settings → Connectors — a Team/Enterprise-only surface. Free, Pro, and Max plans have no admin surface to grant it from, so the connector stays read-only for files, same as it was for mail.

What I built

onedrive-writeback follows the same design premise as the mail/calendar server I mentioned above — narrow, write-only, self-hosted on Azure Functions in your own subscription and Entra tenant, independently deployable and revocable from the other servers in the monorepo.

Based on the live tool set, the server currently exposes:

  • create_file — writes a new file at a given drive-relative path with inline UTF-8 content. Parents aren’t auto-created (a typo’d path fails loudly instead of silently creating a folder tree), and conflict_behavior controls what happens if the target already exists: fail (default), rename with a numeric suffix, or replace.
  • create_folder — mkdir -p semantics, idempotent.
  • update_file_content — replaces a file’s full content in place. Requires an if_match value (the file’s current ETag/cTag) pulled from get_item first — this is the concurrency guard: if the file changed since you read it, the write is refused rather than silently clobbering someone else’s edit.
  • get_item — metadata lookup (size, file/folder type, web URL) and the source of the if_match token needed before any update. Not a directory-listing tool.
  • move_item — moves a file or folder to a different parent, same drive only.
  • delete_item — two-step, HMAC-signed confirmation before it runs: the first call returns the file’s details plus a signed token and does nothing else; the actual delete only happens on a second call that echoes the token back. Even confirmed, it only moves the file to the recycle bin — permanent deletion is left to the user, not something the server does.
  • rename_item — renames in place, same parent.

That update_file_content / if_match pairing is the direct fix for the specific failure mode described above — no more create-a-new-file-and-hope-you-delete-the-right-old-one, because there’s now an actual in-place update with a concurrency check attached to it.

One naming wrinkle if you go looking at the source: the monorepo folder is still called drive-writeback, a holdover from before I settled on onedrive-writeback as the server name. Didn’t seem worth a rename just to make the repo match the blog post.

Open source, contributions welcome

As a reminder, sapidus-writeback-mcp is open source — fork it, deploy your own instance, or open an issue or PR.