Notion MCP Server
Pricing
Pay per usage
Notion MCP Server
Notion MCP Server for AI agents (Claude, GPT, Cursor). 20 Notion API operations: search pages, query databases, create content, manage blocks, comments, users. Model Context Protocol server with full CRUD support.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
CQ
Maintained by CommunityActor stats
0
Bookmarked
16
Total users
5
Monthly active users
3 days ago
Last modified
Categories
Share
A cloud-hosted Notion tool server for AI agents (Claude, GPT, Cursor). Perform 20 Notion API operations across pages, databases, blocks, comments, and users. Each run executes one operation and returns structured JSON.
Features
- 20 Operations - Pages, databases, blocks, comments, and users
- Demo Mode - Test the actor without a Notion token
- URL or ID Input - Accepts Notion page/database/block URLs or raw IDs
- Pagination - Cursor-based paging via
startCursor/pageSize - Graceful Errors - Failed operations return a structured error object (with the Notion error code) instead of crashing the run
Quick Start
Demo Mode (No Setup)
demo is the default operation and needs no token:
{"operation": "demo"}
Search Your Workspace
{"notionToken": "ntn_your_token_here","operation": "search","query": "Meeting Notes"}
Setup
1. Create a Notion Integration
- Go to notion.so/profile/integrations
- Click New integration
- Name it (e.g., "Apify MCP")
- Copy the token (starts with
ntn_orsecret_)
2. Share Pages with the Integration
For each page/database you want to access:
- Open the page in Notion
- Click ... menu -> Connections
- Select your integration
Without this step, requests to that page/database return object_not_found.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
notionToken | string (secret) | - | Notion integration token (ntn_ or secret_). Required for every operation except demo. |
operation | string (enum) | demo | The operation to perform (see Operations below). |
query | string | - | Search query text (for search). |
pageId | string | - | Page ID or Notion URL. |
databaseId | string | - | Database ID or Notion URL. |
blockId | string | - | Block ID or Notion URL. |
userId | string | - | User ID (for get_user). |
parentPageId | string | - | Parent page for new pages/databases. |
parentDatabaseId | string | - | Parent database for new database entries. |
title | string | - | Title for new pages/databases. |
properties | string (JSON) | - | JSON properties object for create/update. |
children | string (JSON) | - | JSON array of block children to append. |
filter | string (JSON) | - | JSON filter for search/query. |
sorts | string (JSON) | - | JSON sort configuration. |
richText | string (JSON) | - | JSON rich-text array (for create_comment). |
propertyId | string | - | Property ID (for get_page_property). |
discussionId | string | - | Discussion ID (for replying with create_comment). |
startCursor | string | - | Pagination cursor for the next page of results. |
pageSize | integer | 100 | Results per page (1-100). |
archived | boolean | - | Archived flag for update operations. |
icon | string (JSON) | - | JSON icon configuration. |
cover | string (JSON) | - | JSON cover image configuration. |
persistResults | boolean | true | Save the result to the default dataset. |
Operations
Search & Discovery
| Operation | Description | Required |
|---|---|---|
demo | Return example output without a token | - |
search | Find pages/databases | - (query optional) |
Pages
| Operation | Description | Required |
|---|---|---|
get_page | Get page details | pageId |
create_page | Create a page | parentPageId or parentDatabaseId |
update_page | Update page properties/icon/cover/archived | pageId |
archive_page | Archive a page | pageId |
get_page_property | Get a single property value | pageId, propertyId |
Databases
| Operation | Description | Required |
|---|---|---|
get_database | Get database schema | databaseId |
query_database | Query database rows | databaseId |
create_database | Create a database | parentPageId |
update_database | Update a database | databaseId |
Blocks
| Operation | Description | Required |
|---|---|---|
get_block | Get a block | blockId |
get_block_children | List a block's children | blockId |
append_block_children | Append child blocks | blockId, children |
update_block | Update/archive a block | blockId |
delete_block | Delete a block | blockId |
Comments & Users
| Operation | Description | Required |
|---|---|---|
get_comments | List comments on a block | blockId |
create_comment | Add a comment | pageId or discussionId, plus richText |
list_users | List workspace users | - |
get_user | Get user details | userId |
get_bot_user | Get the integration's bot user | - |
Examples
Search Pages
{"notionToken": "ntn_xxx","operation": "search","query": "Project Plan"}
Get Page by URL
{"notionToken": "ntn_xxx","operation": "get_page","pageId": "https://notion.so/My-Page-abc123def456"}
Query Database with Filter
{"notionToken": "ntn_xxx","operation": "query_database","databaseId": "abc123","filter": "{\"property\":\"Status\",\"select\":{\"equals\":\"Done\"}}"}
Create Page with Content
{"notionToken": "ntn_xxx","operation": "create_page","parentPageId": "parent-id","title": "New Page","children": "[{\"object\":\"block\",\"type\":\"paragraph\",\"paragraph\":{\"rich_text\":[{\"type\":\"text\",\"text\":{\"content\":\"Hello world!\"}}]}}]"}
Output
Every run sets the result on the default key-value store under the OUTPUT key, and (when persistResults is true, the default) also pushes it as one item to the default dataset.
| Field | Type | Description |
|---|---|---|
operation | string | Operation performed |
success | boolean | Whether the operation succeeded |
objectType | string | Notion object type returned (page, database, block, search_results, etc.) |
objectId | string | Primary object ID (when applicable) |
content | object/array | Returned Notion data (for list/child operations) |
resultCount | integer | Number of results (for list/search operations) |
hasMore | boolean | Whether more results are available |
nextCursor | string | Cursor for the next page of results |
processedAt | string | ISO timestamp when the operation completed |
error | string | Error message (only on failure) |
errorCode | string | Notion API error code (only on failure) |
The demo operation additionally returns message, availableOperations, exampleOutput, and setupInstructions.
Success Response
{"operation": "search","success": true,"objectType": "search_results","content": [],"resultCount": 10,"hasMore": false}
Error Response
Errors do not fail the run - they resolve to an object with success: false:
{"operation": "get_page","success": false,"error": "[object_not_found] Could not find page","errorCode": "object_not_found"}
Using with AI Agents / MCP
Each run of this actor performs one Notion operation and returns structured JSON, so the 20 operations act as the tools it serves. You can invoke it in three ways:
- Apify API / client libraries - call the actor directly (see the JavaScript and Python examples below).
- Apify MCP Server - any Apify actor can be surfaced as an MCP tool to MCP-compatible clients (Claude Desktop, Cursor, etc.) through Apify's MCP server. See the Apify MCP documentation at docs.apify.com for the current configuration; a typical Claude Desktop entry looks like:
{"mcpServers": {"apify": {"command": "npx","args": ["-y", "@apify/actors-mcp-server", "--actors", "YOUR_USERNAME/notion-mcp-server"],"env": { "APIFY_TOKEN": "your-apify-token" }}}}
Replace YOUR_USERNAME/notion-mcp-server with this actor's full ID on the Apify platform.
Apify Client (JavaScript)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('YOUR_USERNAME/notion-mcp-server').call({notionToken: 'ntn_xxx',operation: 'search',query: 'Meeting Notes'});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Apify Client (Python)
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("YOUR_USERNAME/notion-mcp-server").call(run_input={"notionToken": "ntn_xxx","operation": "search","query": "Meeting Notes"})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(items)
Limitations
- Notion token required. Every operation except
demorequires a Notion internal integration token. - Integration must be connected per resource. The integration has to be explicitly connected to each page/database (Notion ... -> Connections), or requests return
object_not_found. - One operation per run. This is a request/response actor - each run executes a single operation. It is not a persistent or streaming MCP connection.
- Notion rate limits. Notion enforces an average of ~3 requests/second. Use
startCursor/pageSize(max 100 per page) to paginate large result sets. - Write capabilities depend on the integration. Create/update/archive/delete operations only work if the integration has the matching content capabilities enabled in Notion.
- JSON-string inputs must be valid JSON.
properties,filter,sorts,children,richText,icon, andcoverare passed as JSON strings and must match the Notion API schema, or the run returns a validation error. - Scope. Coverage is limited to the 20 listed Notion API operations; file uploads, OAuth flows, and webhooks are not included.
- Check
success, not just run status. Notion API and network errors (including transient outages) are caught and returned assuccess: falsewith an error code, and the run still finishes successfully. Inspect thesuccessfield to detect failures. - Node.js >= 18 (provided by the actor's Docker image).
Pricing
This actor runs on the Apify platform, so you pay for the platform compute/usage it consumes; see the actor's Apify Store page for current pricing. Notion API usage under your own integration is free.
Troubleshooting
| Issue | Solution |
|---|---|
| "API token is invalid" | Token must start with ntn_ or secret_. Regenerate at notion.so/profile/integrations |
| "Could not find object" | Connect the integration to the page: ... -> Connections -> select your integration |
| Rate limiting | Notion allows ~3 req/sec. Paginate with startCursor / pageSize for large datasets |
Resources
License
MIT
