Labsco
awkoy logo

Notion

β˜… 160

from awkoy

Interact with Notion's API to read, create, and modify content using natural language.

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedAccount requiredAdvanced setup

Notion MCP Server β€” Connect Claude, Cursor & VS Code to Notion

npm version NPM Downloads License Model Context Protocol notion-mcp-server on Smithery Stars

Give your AI full read/write access to Notion with one token and one paste. This is an agent-first Notion MCP server: your AI client (Claude Code, Claude Desktop, Cursor, VS Code, Cline, Zed β€” anything that speaks MCP) can create pages, query databases, append blocks, apply templates, comment, and upload files in natural language.

Three reasons it exists when Notion ships its own MCP:

  • Built for agents, not humans-in-the-loop. Notion's hosted MCP is OAuth-only β€” it cannot run headless. This server authenticates with a token, so it works in CI, cron jobs, background agents, and self-hosted deployments where nobody can click "Authorize".
  • 97% smaller tool footprint at connection. Two MCP tools (422 tokens) instead of one schema per endpoint β€” the official open-source server loads 17,163 tokens of tool schemas before you do anything. Operation schemas load on demand via notion_describe, so even a typical multi-operation task stays 85–95% lighter. Measured, reproducible β†’
  • The operational stuff is built in. Batched mutations with atomic rollback, idempotency keys, automatic retry on rate limits, slim token-efficient responses, full markdown round-trip, and self-healing validation errors that let the model fix its own bad payloads in one turn.
<a href="https://glama.ai/mcp/servers/zrh07hteaa"> <img width="380" height="200" src="https://glama.ai/mcp/servers/zrh07hteaa/badge" alt="Notion MCP Server on Glama" /> </a>

πŸ’‘ What your AI can do with it

  • "Find every row in my Tasks database where Status is 'Doing' and tell me which are overdue." β€” typed where filters, flattened rows
  • "Rename these 50 pages to the new convention." β€” one batched call, 10-way parallel, idempotent retry
  • "Create a page from my 'Weekly review' template and fill in this summary." β€” Notion templates support
  • "Rewrite that spec page: fix the headings and add a code sample." β€” full markdown round-trip (get_page_markdown β†’ edit β†’ update_page_markdown)
  • "Comment on yesterday's meeting notes with a one-paragraph summary."
  • "Upload this diagram to the design page." β€” single- and multi-part file uploads

Full capability list in Features; the complete operation catalog (43 ops) is in the Operations menu.

🧭 Which Notion MCP should you use?

Three options exist. Honest guidance:

Best forAuthHeadless / CINotes
Notion hosted MCP (mcp.notion.com)Interactive chat in claude.ai, ChatGPT, CursorOAuth (human must click)❌First-party, 18 markdown tools, some plan-gated
Official open-source serverβ€”Tokenβœ…Notion has soft-deprecated it (β€œmay sunset this repository… issues and PRs not actively monitored”)
This serverAgents, automation, CI, self-hosting, token-sensitive workloadsToken (PAT)βœ…Actively maintained, agent-first design below

If you just want to chat with your Notion in claude.ai's web UI, use Notion's hosted connector β€” it's one click. Use this server when your agent runs unattended, when context/token cost matters, or when you want batch/idempotent semantics and self-hosting.

<details> <summary><b>Detailed comparison vs. the official open-source server</b></summary>
CapabilityOfficial Notion MCP (open source)This server
Tool surface24 tools (one per endpoint), 17,163 tokens loaded into context2 tools, 422 tokens β€” 97% less schema at connection
Operations covered~24 endpoints43 operations (plus a trash_page alias) across pages, blocks, databases, data sources, views, templates, comments, users, files
Batch mutationsNot documentedβœ… Universal { items: [...] } envelope; up to 10 in parallel
Atomic batches + rollbackNot documentedβœ… atomic: true aborts on first failure, best-effort archives entities created earlier
IdempotencyNot documentedβœ… idempotency_key β€” same key + op returns the cached result for 5 minutes
Rate-limit handling429s bubble upβœ… Token-bucket limiter (3 req/s default) + exponential backoff, honors Retry-After
Response shapesRaw Notion SDK JSONSlim shapers drop noise by default; verbose: true opts out
Database queriesRaw properties bag per rowFlattened name β†’ primitive map (all 20+ property types)
Wire formatDefault SDK serializationCompact JSON β€” ~30% smaller payloads
MarkdownPage-level markdown toolsβœ… Markdown accepted by create_page / append_blocks / update_block / comments + full round-trip (get_page_markdown / update_page_markdown), full GFM
Templatesβ€”βœ… create_page from a Notion template + list_data_source_templates discovery
File uploadsNot in the documented tool surfaceβœ… Single- and multi-part (5 MB chunks), MIME inferred
Validation errorsPlain error stringSelf-healing: { code, message, path, issues, schema, example, fix } β€” corrected in one round-trip
Notion API versionβ€”Pinned 2026-03-11 (data sources, views, templates)

Real-world impact:

  • Renaming 50 pages β€” one notion_execute call with { items: [...], concurrency: 10 } instead of 50 separate tool calls through the agent's reasoning loop: roughly an order of magnitude faster, and the prompt-token savings are the bigger win.
  • Tool list in context β€” 2 schema blobs per conversation instead of ~24, no matter which of the 43 operations get called.
  • Reading a 100-row database β€” flattened rows are typically 5–10Γ— fewer tokens than the raw properties bag, with no information loss.
</details>

πŸͺ„ Complete walkthrough (no coding required)

<details> <summary><b>Set up Claude Desktop + Notion in 5 minutes β€” assumes you've never seen a config file</b></summary>

What you'll need

  1. A Notion account.
  2. The Claude Desktop app.
  3. About 5 minutes.

Step 1 β€” Get your Notion token

A Personal Access Token (PAT) is like a key that lets the AI act as you inside Notion β€” it sees every page you can see, with no per-page setup.

  1. Open app.notion.com/developers/tokens while logged into Notion β€” that's the Personal access tokens page of Notion's developer portal (also reachable from the app via Settings β†’ Connections β†’ Develop or manage integrations β†’ Personal access tokens in the sidebar).

  2. Click + New token.

  3. Name it (e.g. Claude), pick the workspace, leave the default Notion API capability checked, click Create token.

    <img src="https://raw.githubusercontent.com/awkoy/notion-mcp-server/main/assets/notion-new-token-modal.png" width="460" alt="The New personal access token dialog: enter a token name, pick the workspace it has access to, keep the Notion API capability checked, then press Create token">
  4. Copy the token now β€” Notion shows it only once. It starts with ntn_. Treat it like a password.

PATs expire 1 year after creation β€” set a reminder to rotate. No "Personal access tokens" tab? Your admin disabled them; use the Internal Integration alternative.

Step 2 β€” Tell Claude Desktop about the server

  1. Open Claude Desktop β†’ Claude menu (top-left on Mac, hamburger on Windows) β†’ Settings β†’ Developer β†’ Edit Config.
  2. A file named claude_desktop_config.json opens. Don't panic at the curly braces β€” it's just text.
  3. Select all (Cmd+A / Ctrl+A), delete, and paste:
Copy & paste β€” that's it
{
  "mcpServers": {
    "notion": {
      "command": "npx",
      "args": ["-y", "notion-mcp-server"],
      "env": {
        "NOTION_TOKEN": "ntn_paste_your_token_here"
      }
    }
  }
}

This block tells Claude Desktop how to launch the connector. npx downloads and runs it automatically the first time. The only thing you change is the token.

  1. Replace ntn_paste_your_token_here with your token β€” keep the quotation marks.
  2. Save (Cmd+S / Ctrl+S).
  3. Quit Claude Desktop completely (Mac: Cmd+Q; Windows: tray icon β†’ Quit) and reopen it.

Step 3 β€” Check and try

Type / in a new chat β€” you should see notion_execute and notion_describe in the list. Then ask:

"Use Notion to make a new page called 'Hello from Claude' and add a checklist of three things I want to try today."

Claude calls the tool and replies with a page link. If something's off, it's almost always a token typo or Claude Desktop not fully quit β€” see Troubleshooting.

</details>

🌐 Remote / HTTP transport

By default the server speaks stdio (the local path above). To run it as a remote/hosted endpoint β€” web clients, networked agents, shared deployments β€” set MCP_TRANSPORT=http:

Copy & paste β€” that's it
MCP_TRANSPORT=http PORT=3000 NOTION_TOKEN=ntn_xxx npx -y notion-mcp-server
# -> notion-mcp-server vX.Y.Z running on http://127.0.0.1:3000/mcp

It serves MCP Streamable HTTP at POST/GET/DELETE /mcp (stateful sessions via the mcp-session-id header) plus an unauthenticated GET /health. It's single-tenant β€” every request acts as the one NOTION_TOKEN the process started with.

envdefaultmeaning
MCP_TRANSPORTstdioset to http to enable HTTP
PORT3000listen port (0 = OS-assigned)
HOST127.0.0.1bind address; set 0.0.0.0 to expose externally (only with MCP_AUTH_TOKEN)
MCP_AUTH_TOKENβ€”when set, every /mcp request must send Authorization: Bearer <token>
MCP_ALLOWED_HOSTSlocalhost + bound hostcomma-list for DNS-rebinding Host allowlist
MCP_ALLOWED_ORIGINSlocalhost originscomma-list for browser Origin allowlist

⚠️ Whoever reaches /mcp acts as your NOTION_TOKEN. On loopback (the default) that's just local processes. Before binding a non-loopback HOST, set MCP_AUTH_TOKEN (the server warns if you don't) and/or front it with an authenticating reverse proxy.

Connect from clients that support headers (Claude Code, Cursor, VS Code):

Copy & paste β€” that's it
claude mcp add --transport http notion https://your-host/mcp \
  --header "Authorization: Bearer <MCP_AUTH_TOKEN>"

Try it locally:

Copy & paste β€” that's it
curl http://127.0.0.1:3000/health
# -> {"status":"healthy","transport":"http","port":3000}
npx @modelcontextprotocol/inspector --transport http --server-url http://127.0.0.1:3000/mcp

In Docker:

Copy & paste β€” that's it
docker run --rm -e NOTION_TOKEN=ntn_xxx -e MCP_TRANSPORT=http -p 3000:3000 ghcr.io/awkoy/notion-mcp-server

🌟 Features: what this Notion MCP server does

  • Two-tool surface β€” notion_execute (do it) + notion_describe (learn the shape). The whole API is one schema deep.
  • Universal batch envelope β€” every mutating op accepts { items: [...], atomic?, idempotency_key?, concurrency? } with per-item validation and results.
  • Atomic batches with best-effort rollback β€” atomic: true aborts on first failure and archives anything created earlier in the batch.
  • Idempotency keys β€” same (operation, idempotency_key) returns the cached result for 5 minutes. Safe to retry on flaky networks.
  • Rate-limit + retry baked in β€” token-bucket limiter (3 req/s default, NOTION_RATE_LIMIT to change) with exponential backoff on 429/5xx/timeouts, honoring Retry-After.
  • Self-healing validation errors β€” failures return { schema, example, fix } so the model corrects bad payloads in one round-trip.
  • Markdown everywhere β€” create_page / append_blocks / update_block / comment bodies accept a markdown string (full GFM: headings 1–4, lists, nested to-dos, blockquotes, fenced code with language detection, images, dividers, inline formatting), plus full round-trip via get_page_markdown / update_page_markdown.
  • Notion templates β€” create_page can apply a data source's template (template: { type: "template_id" | "default" }), with list_data_source_templates to discover template IDs.
  • Database views β€” list/get/query/create/update/delete views; query_view runs a view's stored filters/sorts and returns hydrated rows.
  • Typed where filter shorthand β€” query_database takes {Status: {equals: "Done"}, AND: [...]} and compiles it to Notion filter JSON (raw filter still accepted for edge cases).
  • Slim responses + flattened rows β€” noisy fields dropped by default, query_database rows flattened to name β†’ primitive maps, compact JSON wire format (~30% smaller). verbose: true opts out per call.
  • File uploads β€” single-part and multi-part (5 MB chunks) transparently; MIME inferred from filename.
  • Opt-in auto-pagination β€” paginate: true on search_pages / list_comments / query_database walks next_cursor for you (default cap β‰ˆ 1000 items).
  • HTTP(S) proxy support β€” standard HTTPS_PROXY / HTTP_PROXY env vars for corporate networks.
  • Access control β€” NOTION_READ_ONLY one-switch read-only mode plus per-operation allow/block lists.

πŸ“š MCP tools (notion_execute & notion_describe)

The server exposes exactly two MCP tools β€” your client loads two schemas regardless of which of the 43 operations gets called.

notion_execute

Run any operation: { operation, payload }, where payload is a single object or { items: [...] } for batch mode.

Copy & paste β€” that's it
// single call
{
  "operation": "set_page_title",
  "payload": { "page_id": "<page-id>", "title": "Q3 plan" }
}
Copy & paste β€” that's it
// batch
{
  "operation": "set_page_title",
  "payload": {
    "items": [
      { "page_id": "<p1>", "title": "First" },
      { "page_id": "<p2>", "title": "Second" }
    ],
    "atomic": false,
    "concurrency": 3,
    "idempotency_key": "rename-pass-2026-07-02"
  }
}
Copy & paste β€” that's it
// markdown shortcut (create_page, append_blocks, update_block, update_page_markdown)
{
  "operation": "create_page",
  "payload": {
    "parent": { "type": "page_id", "page_id": "<parent>" },
    "title": "Notes",
    "markdown": "# Heading\n\n- [ ] todo\n- [x] done\n\n```ts\nconst x = 1;\n```"
  }
}

If a payload doesn't validate, the error response includes the operation's full JSON Schema, a working example, and a fix hint β€” the next call can be corrected without a notion_describe round-trip.

notion_describe

Returns the JSON Schema + working example for one operation β€” useful before complex calls (filter expressions, mixed block batches, database property definitions).

Copy & paste β€” that's it
{ "operation": "query_database" }

Operations menu (43 ops, plus one alias)

AreaOperations
Pagescreate_page, get_page, set_page_title, set_page_property, set_page_properties, archive_page (alias: trash_page), restore_page, search_pages, move_page, get_page_markdown, update_page_markdown
Blocksappend_blocks, get_block, get_block_children, update_block, delete_block, batch_mixed_blocks
Databasescreate_database, query_database, update_database
Data sourceslist_data_sources, get_data_source, update_data_source, list_data_source_templates
Viewslist_views, get_view, query_view, create_view, update_view, delete_view
Commentslist_comments, add_page_comment, add_discussion_comment, get_comment, update_comment, delete_comment
Userslist_users, get_user, get_bot_user, get_self
Filesupload_file, list_file_uploads, get_file_upload

The authoritative list (with batchability) is served as an MCP resource at notion://operations.

MCP resources

Clients that support resource attachment (@-mention) can pull Notion content into context without a tool call:

Resource URIReturns
notion://operationsMarkdown cheat sheet of every enabled operation
notion://page/<page_id>Page body as markdown
notion://database/<data_source_id>Data source schema as JSON

Dynamic resources route through the same auth, rate limiting, and access gating as tool calls.

πŸ›  Development

Copy & paste β€” that's it
git clone https://github.com/awkoy/notion-mcp-server.git
cd notion-mcp-server
npm install
echo "NOTION_TOKEN=ntn_xxx" > .env

npm run build       # tsc -> build/
npm test            # vitest suite
npm run inspector   # MCP inspector against the built binary

Local build instead of npx:

Copy & paste β€” that's it
claude mcp add notion -s user \
  -e NOTION_TOKEN=ntn_xxx \
  -- node "$(pwd)/build/index.js"
<details> <summary><b>Technical details: how it's built</b></summary>
  • TypeScript + MCP SDK (^1.29.0); stdio + Streamable HTTP transports
  • Notion SDK @notionhq/client@^5.22.0, pinned Notion-Version: 2026-03-11
  • Zod 4 payload validation; emits draft-7 JSON Schema with $defs deduplication for error envelopes
  • Markdown β†’ Notion blocks via remark / remark-gfm
  • Bounded-concurrency batch worker (default 3, max 10); shared token-bucket rate limiter; withRetry with exponential backoff around every dispatched call
  • In-memory idempotency cache (5-minute TTL, 512 entries)
  • Slim shapers per entity type with verbose: true opt-out
  • Vitest suite covering the markdown parser, shapers, schema emitter, dispatcher, batch semantics (partial success / atomic rollback / idempotency), access control, and HTTP transport
</details>

🀝 Contributing

PRs welcome. Fork β†’ branch β†’ commit β†’ push β†’ PR. Run npm test before submitting.

πŸ“„ License

MIT β€” see LICENSE.


mcp-name: io.github.awkoy/notion-mcp-server