Skip to content
Agentic Control Plane

The Notion MCP server control model, explained

The Notion MCP server is first-party and about as default as MCP gets: hosted at https://mcp.notion.com/mcp (Streamable HTTP, with /sse for older clients), OAuth-authorized, and preinstalled as a connector in claude.ai, ChatGPT, and Cursor. There is also an open-source server — makenotion/notion-mcp-server, npm @notionhq/notion-mcp-server — which Notion’s own docs now describe as “no longer actively maintained”; the README says Notion is “prioritizing, and only providing active support for, Notion MCP (remote)” and may sunset the repo. That split is the whole story of this page: the maintained path has the adoption and almost none of the controls; the controls live on the path being wound down. Sources: the developer docs, the getting-started guide, and the Notion MCP help page.

This page covers the server’s own controls. For the cross-server picture, see the MCP server controls comparison; for what a control layer adds on top, the coverage matrix.

What’s at stake on this surface

A Notion workspace is not a code repository — it’s the company’s soft interior. Meeting notes, strategy docs, HR pages, customer lists, and, in practice, credentials people pasted somewhere convenient. The tool surface covers search across the workspace and connected sources, page and database reads, page/database create and update, moves and duplication, and comments.

Two things make the read side as consequential as the write side here. The content is dense with exactly what an attacker wants exfiltrated. And the content is shared — pages arrive from other people, comments arrive from outside collaborators, PDFs get uploaded — so the workspace doubles as an inbound channel for instructions the agent was never meant to follow. On most surfaces in this series the question is what the agent might do; on this one, what the agent might read is half the incident.

The hosted path: identity is the only lever

The load-bearing fact, in Notion’s own words from the help docs: “MCP tools act with your full Notion permissions—they can access everything you can access.” The developer docs say the same from the other side: after OAuth, the client can “read and update content that you can access.” The grant is the user, whole.

Below that identity, the native control surface is empty:

  • No read-only mode — in any spelling. No flag, no header, no URL parameter.
  • No tool filtering or toolsets — the tool surface is what it is, for every session.
  • No scoping — no way to pin the connection to a teamspace, a page tree, or a database.
  • No per-call anything — no policy, no approval hold, no rate limit.

Compare GitHub’s three spellings of read-only and per-tool selection, or Supabase’s read-only Postgres role — Notion’s hosted server ships none of the equivalent levers. This is the honest zero of the series: the one decision the native model lets you make is which human logs in. It’s a real decision — a dedicated account with deliberately narrow page access is the only scoping mechanism that works on this path — but it’s account administration standing in for controls the server doesn’t have.

To be precise about what does hold: Notion’s permission system itself. The docs are consistent that “MCP does not bypass Notion permissions” — the agent can’t reach a page its user can’t. That’s the floor, and it’s enforced on Notion’s side. The problem is that for most users the floor is the entire workspace.

The Enterprise plane: what Notion calls “MCP Governance”

Enterprise-plan admins get a set of controls Notion markets as “MCP Governance” (help docs): approve specific AI apps and MCP clients, block any tool not explicitly approved, route connections through an identity provider (an Okta admin can set up an enterprise-managed connection for the whole team), and a “Disconnect All Users” switch. Connection events land in the audit log — including tool renames, which the docs note can be traced by connection URL. One caveat they state themselves: removing a previously approved tool doesn’t revoke its tokens; instead “Notion blocks every call from any AI app or MCP client that is not on the approved list.”

Grade it honestly: these are real controls, competently built, at the wrong altitude. Every question they answer is about connections — which apps, which users, connected or not. No question they answer is about calls. An approved app, connected by an approved user, can still search the whole workspace and update any page that user can touch, and the admin plane neither sees the individual call nor offers anywhere to say “reads yes, writes with approval.” Notion says as much about visibility: per-user detail on tool usage is “not yet available.” The allowlist decides who may walk through the door; nothing native decides what they do inside.

The OSS path: the real grain, on the unmaintained server

The open-source server authenticates with an internal integration token (ntn_…), and internal integrations carry the two scoping mechanisms the hosted path lacks:

  1. Capabilities. Set at integration creation (reference): read content, update content, and insert content are independent grants, alongside comment and user-info capabilities. The README spells out the useful configuration: “you can create a read-only integration token by giving only ‘Read content’ access.” Capabilities are enforced by the API and never exceed the underlying user’s permissions.
  2. Page connections. An internal integration reaches nothing by default — pages and databases must be explicitly connected, per page or via the integration’s Access tab. That’s genuine blast-radius control: the agent sees the pages you connected, not the workspace.

So a read-only, three-pages-only Notion agent is perfectly expressible — on the server whose README says issues and PRs “are not actively monitored.” The demand hasn’t gone away with the maintenance: community forks like notion-readonly-mcp-server exist specifically to package the read-only posture (and to shrink the tool count). When users maintain forks to get a control the vendor’s maintained path doesn’t offer, that’s the market stating a requirement.

Audit: connections, not calls

Neither path writes an operator-queryable record of what an agent actually did. The hosted server keeps no MCP-level trail of which tools fired with what arguments; the OSS server keeps none either. The Enterprise audit log records connection events — apps connecting, tools being renamed (traceable by connection URL, per the docs) — and Notion states that per-user visibility into tool usage is “not yet available.” So the best available record answers “which apps were attached to the workspace,” never “what did the agent read, and what did it change” — the general MCP audit gap, at its widest. On a surface where the damage case is a quiet bulk read, the absence of a read trail is not a paperwork problem.

Injection: the workspace is an inbound channel

The reference incident is adjacent rather than on this exact server, so state it precisely: in September 2025, CodeIntegrity researchers showed that a PDF with hidden instructions could drive Notion 3.0’s built-in agent to exfiltrate workspace data through its web-search tool — Simon Willison’s write-up frames it as the lethal trifecta on a Notion workspace. That attack targeted Notion’s in-product agent, not the MCP server. But the ingredient it proved is the one that transfers: Notion content is an injection carrier. Any agent wired to this server reads shared pages, uploaded files, and outsiders’ comments as a matter of course, holds full-permission read access to everything else, and — in any realistic deployment — has other tools to write through. The hosted server has no equivalent of GitHub’s lockdown mode, no filtering of externally authored content, and no native way to break the read-untrusted-then-act chain. On a full-permission connection, one poisoned page shares a session with the entire workspace.

Where the native model ends

On the hosted path, it ends at login. Laid out the way we grade every server in this series:

  • The maintained path has no controls below identity. No surface reduction, no read-only, no scoping, no per-call policy, no holds. The one enforced line is Notion’s own permission system — the floor — and for most users the floor is the whole workspace.
  • The Enterprise plane operates one level too high. A real allowlist, IdP-managed connections, a disconnect-all switch — controls over which apps connect, silent on what any connected app does.
  • The real grain is stranded. Capabilities and page connections — Notion’s own API model — express read-only and page-scoped precisely, and are reachable only through the server Notion says it no longer actively maintains.
  • Nothing is written down. No call-level record on either path; connection events are the whole audit story.
  • The injection chain is invisible by construction. A poisoned read here and an action anywhere else crosses server boundaries no single server sees — same as GitHub, same as Supabase, sharper here because the reads are the treasure.

The practical posture: on the hosted path, connect a dedicated account whose page access is the scope you actually intend, and treat every session as read-write across all of it, because it is. On Enterprise, turn on the allowlist and IdP-managed connections — they’re worth having — without mistaking which-apps for which-calls. If you need read-only or page-scoped and can accept the maintenance risk, the OSS server with a “Read content” token and explicit page connections is the only native way to say it. And since the maintained path offers no seam between the OAuth grant and the workspace, per-call policy, approval on writes, and a record of what actually ran have to come from a layer the agent can’t edit — on this surface, that layer isn’t hardening the native controls; it’s the first place they exist.