20 October 2026

Notion Has No File System Mount, So We Engineered One With AI

A customer asked if Sasha could see their Notion. Sasha already shows Google Drive, OneDrive and SharePoint as one file tree, and we wanted Notion in the same tree. No Notion mount exists: no rclone backend, no FUSE driver, no WebDAV provider. We built one from Notion's MCP server and its new Markdown API. AI wrote most of the code. The engineering was in the process around it: test every claim before you build on it, get a second model to attack the design, and look for the failures that don't throw errors.

Our customers' knowledge lives in Notion, and Notion has no mount

Sasha is our AI knowledge system. A team connects its drives, and Sasha shows them in one file tree. People browse the files, the agent reads them, and the files stay where they are.

A customer's marketing team asked: "we're also using Notion in the marketing team as a communication and task planning platform - is this something you could also integrate in some way?" I hear "our team lives in Notion" a lot, and it's a common reason a team won't adopt a knowledge tool.

For a cloud drive, the normal answer is a mount. You point rclone or a FUSE driver at the service, and it looks like a folder. Our first step was a prior-art search, and the agent came back with:

  • rclone has no Notion backend. The only one it found is a stub marked "planned".
  • WsgiDAV takes plug-in WebDAV providers. It has no Notion provider.
  • The rest are exports, sync scripts and an Obsidian plugin. They give you a copy, and the copy goes stale.

Notion's own MCP server covers part of the job. Through it, our agent can search Notion, move a card or set a field. But MCP gives the agent tools. A person can't browse a tool, and the agent can't treat a tool like the other drives.

The opening came from Notion: it added Markdown to its public API this year. One call reads a whole page as Markdown. Another edits a page by search and replace, and it refuses unless the old text appears once on the page. A file system needs a read and a write, and those two calls give you both.

How Notion becomes a file system

Notion is a tree of pages and databases, so we had to decide what each one becomes. We took the layout from a small open-source project, notionfs:

  • A page becomes a file, Playbooks.md.
  • A page with sub-pages becomes a folder, and its own text goes in _index.md inside it.
  • A database (a board or a table) becomes a folder with one file per row, so each card is a file.
  • A row's fields (stage, owner, renewal date) sit in a read-only header above the text.

Notion lets you have two pages called "Notes" side by side, and people rename pages all the time. So the path is only a label, and Notion's page id is the identity. Sasha keeps an index from path to id. Duplicate titles become "Notes" and "Notes (2)", and after a rename the old path still opens the page.

Sasha doesn't mount any of its drives. There's no kernel driver and no network share. Every drive sits behind one contract (list a folder, read a file, write a file), and Notion got an adapter that speaks it.

Two channels into Notion came out of this. The agent's work in Notion ("move Acme to Proposal sent") goes through Notion's MCP server, added to Sasha as one of its connected tools. The file tree goes through the REST API, because our server has to check every edit, and it can't check calls the agent makes to someone else's server.

A claim isn't a fact until you've tested it

The prior-art search and Notion's docs gave us a long list of claims: what the Markdown looks like, what an edit does, how the sign-in works. Before we built anything, we turned each claim into a test against a sandbox Notion workspace. Nothing went into the design as a fact until it had passed.

About thirty tests later, several claims had changed:

  • A popular open-source project says Notion's MCP server reads pages block by block. It doesn't any more. It returns whole pages as Markdown.
  • Notion rounds a page's "last edited" time to the minute, so two edits in the same minute look the same. Sasha compares a hash of the text.
  • A sub-page tag written inside a code block comes back word for word, so the parser has to skip code blocks or it invents pages.
  • Webhooks turned out simpler than we feared, which removed a whole relay service from the plan.

Each of those would have been a bug in production. They were cheap to find in a sandbox.

A second model to attack the design

Then we gave the design to a second AI model, from a different lab, and asked it to find what was wrong. Its first finding: our claim that Notion needed "one new adapter, and nothing else changes" was false. It listed what else had to change: the database schema, the connection lifecycle, search, the preview and the permissions.

It also found:

  • Our first idea for unique paths, a short id suffix on each file name, wasn't unique and couldn't find a page on its own. That's where the path-to-id index came from.
  • Mixing a card's fields into the same text as its body made edits ambiguous. That's why each file has a read-only header.
  • The web server and each agent process would each keep their own cache and rate limit, and they wouldn't add up. So every Notion call now goes through one broker in the web server, and the agent's process never holds the Notion key.

The model that writes a design is a poor judge of it. A second model reads it cold and has nothing to defend.

The hard parts

The worst failures in this build were silent ones. They returned success, and they lost data or showed nothing.

Dropping a tag deletes the block. Notion's Markdown isn't the whole page. Embeds, bookmarks and synced blocks come back as placeholders such as <unknown url="..." alt="bookmark"/>. In the sandbox, we sent a page back without one placeholder. Notion reported success and deleted the bookmark. An agent tidying up a page would do this to any page with an embed. So Sasha never replaces a whole page. It counts every tag before and after an edit, the counts must match, and removing one on purpose needs a person to press Confirm.

A valid key that can see nothing. Notion starts a new connection with "No page access". On our first live connection, the admin pasted the key and didn't pick any pages. Sasha accepted the key, which was right, and showed an empty folder, which was no use to anyone. Now Sasha runs a search when you save the key. If it finds nothing, it says how to give access, in settings, in the file tree and to the agent.

Two keys that don't cross. We hoped one Notion sign-in would cover both channels. Notion's MCP server refused the REST key, and the REST API refused the MCP key. The MCP sign-in also sees everything the person who signed in can see, while the REST key sees only the pages the admin picked. So settings show two steps under one Notion card, and each step says what it can reach.

A side door around the safe edit. The safe edit only protects you if it's the only way in. Notion's MCP server can also rewrite page text. Sasha's proxy lets the agent change a card's fields through MCP and refuses the commands that rewrite text.

It worked at 1,000 pages and broke at 20,000. Real Notion workspaces are big. We had the agent walk the code through a 20,000-page workspace. Root discovery looked only at the first 1,000 search results, so the top of the tree came out wrong. Rows past 1,000 couldn't be opened. A person's click could wait behind an agent's long read and give up. None of this shows in a small sandbox. We fixed the discovery pass, followed every page of results, and gave people's clicks their own lane ahead of the agent.

What it looks like in Sasha

The admin of a small consultancy opens settings, connects Notion and picks a Clients board, a tree of meeting notes and a Playbooks page in Notion's own page picker. Sasha sees only those.

A Notion drive appears in the file tree next to Google Drive. A consultant asks, "What did we agree with Acme last month, and is it in their playbook?" Sasha reads the notes and the playbook from Notion and answers. Ask it to fix a date in the Acme notes and it changes that one sentence, and Notion's page history shows the edit. Ask it to move Acme to Proposal sent and it moves the card.

Notion stays the only copy. If the admin disconnects, Sasha's access stops at once and Notion loses nothing. The Notion set-up guide shows each step.

Six weeks then, days now

When I was a developer and typed every line of code myself, this would have been a six-week project, with a lot of risk in it. This time it took days, and the agents worked overnight.

Some parts of the process have collapsed to near zero. The prior-art search, the sandbox tests, the code and the second opinion cost almost nothing now. Other parts take what they always took. Making the right architecture decisions is one of them: what a page becomes, what identifies it, which calls Sasha must never send. I spent the same thinking on those as I did when I typed the code.

We also didn't start from a blank page. Sasha already had Google Drive and SharePoint in the file tree, behind the same contract, with the same agent tools. Notion had working models to follow, so the new decisions were the ones Notion forced on us.

Why this matters

AI wrote most of this code, and the decisions stayed with us. AI helped with those too, in roles we gave it: one model searched for prior art, the sandbox tests checked its claims, and a second model attacked the design. A small team can run that process now. A few years ago, it took a department.

made with bernard

Cookie settings