Your Notion workspace, fully accessible through AI.
A Model Context Protocol (MCP) server that exposes Notion's API for managing pages, databases, blocks, and users across your workspace.
The Notion MCP Server provides a complete interface to your Notion workspace:
- Search, read, and write pages with full block-level content control
- Create and query databases (data sources) with filters, sorts, and pagination
- Manage workspace users and retrieve bot/workspace identity
Perfect for:
- AI assistants that need to read or update Notion pages and databases
- Automating content creation, knowledge base updates, and task management
- Building tools that integrate Notion with other services
search_notion — Search pages and databases by title
Search all pages and databases by title or list all pages
Inputs:
- `query` (string, optional, default: "") — Search query string, keep it empty to list all pages
- `filter_type` (string, optional) — Filter by 'page' or 'data_source'.
- `page_size` (int, optional, default: 20) — Number of pages to return (max 100)
- `start_cursor` (string, optional) — Cursor from a previous response to page through results.
Output data schema:
{
pages: {
id: string | null;
title: string;
url: string | null;
last_edited_time: string | null;
}[];
has_more: boolean;
next_cursor: string | null;
}get_page — Retrieve a page by ID
Retrieve a Notion page by ID with properties and metadata
Inputs:
- `page_id` (string, required) — Notion page ID (UUID) to retrieve.
Output data schema:
{
id: string | null;
object: string | null;
url: string | null;
public_url: string | null;
created_time: string | null;
last_edited_time: string | null;
archived: boolean | null;
in_trash: boolean | null;
parent: object | null;
properties: object | null;
icon: object | null;
cover: object | null;
}fetch_page_content — Retrieve a page with full block content
Retrieve a Notion page with its full content including all child blocks and properties
Inputs:
- `page_id` (string, required) — Notion page ID (UUID) to fetch content for.
- `include_children` (bool, optional, default: true) — Whether to fetch and include the page's child blocks.
- `recursive` (bool, optional, default: false) — Recursively fetch nested children of child blocks, up to max_depth.
- `max_depth` (int, optional, default: 3) — Maximum recursion depth when recursive=True.
- `page_size` (int, optional, default: 100) — Number of child blocks to fetch per page when recursive=False (max 100).
- `start_cursor` (string, optional) — Cursor from a previous response to page through child blocks (non-recursive only).
Output data schema:
{
page_id: string | null;
title: string;
content: string;
url: string | null;
has_more_children: boolean | null;
next_cursor: string | null;
children_count: number | null;
}create_page_under_page — Create a new page under a parent page
Create a new page under a parent page
Inputs:
- `parent_page_id` (string, required) — The ID of the parent page this new page will be created under.
- `title` (string, optional, default: "Untitled New page Created") — The title for the new page. Defaults to 'Untitled New page Created' if omitted.
- `position` (object, optional) — Insert postion. strict Format:{"type": "page_end"} or {"type": "page_start"}
Output data schema:
{
id: string | null;
object: string | null;
url: string | null;
public_url: string | null;
created_time: string | null;
last_edited_time: string | null;
archived: boolean | null;
in_trash: boolean | null;
parent: object | null;
properties: object | null;
icon: object | null;
cover: object | null;
}create_workspace_page — Create a top-level workspace page
Create a new page at a workspace level (without parent page)
Inputs:
- `title` (string, optional, default: "Untitled New page Created") — The title for the new page. Defaults to 'Untitled New page Created' if omitted.
Output data schema:
{
id: string | null;
object: string | null;
url: string | null;
public_url: string | null;
created_time: string | null;
last_edited_time: string | null;
archived: boolean | null;
in_trash: boolean | null;
parent: object | null;
properties: object | null;
icon: object | null;
cover: object | null;
}update_page — Update a page's properties and metadata
Update an existing Notion page's properties and metadata. Providing properties, icon, cover, or other fields replaces the corresponding current values rather than merging with them — the original state is not stored by the API after the call. Call get_page first to see current property values before updating. The response includes both the before and after state so you have a full record of what changed.
Inputs:
- `page_id` (string, required) — The ID of the Notion page to update.
- `properties` (object, optional) — A dict of Notion page property updates keyed by property name; replaces the corresponding existing property values rather than merging with them. Omit to leave properties unchanged.
- `icon` (object, optional) — A Notion file, emoji, or external object to set as the page icon. Omit to leave the icon unchanged.
- `cover` (object, optional) — A Notion file or external object to set as the page cover image. Omit to leave the cover unchanged.
- `archived` (bool, optional) — Whether to archive (true) or restore (false) the page. Omit to leave archival state unchanged.
- `in_trash` (bool, optional) — Whether to move the page to (true) or restore it from (false) the trash. Omit to leave trash state unchanged.
- `is_locked` (bool, optional) — Whether to lock (true) or unlock (false) the page to prevent further edits. Omit to leave the lock state unchanged.
- `template` (object, optional) — A Notion page template object to reapply to the page. Omit to leave the current template unchanged.
- `erase_content` (bool, optional) — Whether to clear the page's existing block content before applying the update. Omit to leave existing content in place.
Output data schema:
{
before: {
id: string | null;
object: string | null;
url: string | null;
public_url: string | null;
created_time: string | null;
last_edited_time: string | null;
archived: boolean | null;
in_trash: boolean | null;
parent: object | null;
properties: object | null;
icon: object | null;
cover: object | null;
};
after: {
id: string | null;
object: string | null;
url: string | null;
public_url: string | null;
created_time: string | null;
last_edited_time: string | null;
archived: boolean | null;
in_trash: boolean | null;
parent: object | null;
properties: object | null;
icon: object | null;
cover: object | null;
};
}append_text_block — Append a text block to a page
Append a text block to a page
Inputs:
- `block_id` (string, required) — The ID could be page ID or parent block ID
- `type` (string, required, one of: paragraph | heading_1 | heading_2 | heading_3 | bulleted_list_item | numbered_list_item | to_do | toggle | quote | callout) — The type of text block to create
- `content` (string, required) — The text content for the block
- `checked` (bool, optional) — For to_do blocks only - whether the item is checked
- `color` (string, optional) — text color or background color. available colors : [ 'default', 'gray', 'brown', 'orange', 'yellow', 'green', 'blue', 'purple', 'pink', 'red'] background color format : eg. red_background or blue_background
- `position` (string, optional, one of: end | start) — Position to insert the new block;
Output data schema:
{
blocks: {
id: string | null;
type: string | null;
created_time: string | null;
}[];
}get_database — Retrieve a database by ID
Retrieve a database object by ID with title, parent, and data sources
Inputs:
- `database_id` (string, required) — The ID of the database to retrieve
Output data schema:
{
id: string | null;
title: object[] | null;
parent: object | null;
data_sources: object[] | null;
url: string | null;
archived: boolean | null;
created_time: string | null;
last_edited_time: string | null;
icon: object | null;
cover: object | null;
}get_data_source — Retrieve a data source schema
Retrieve a data source (database schema/properties) by ID
Inputs:
- `data_source_id` (string, required) — The ID of the data source to retrieve
Output data schema:
{
id: string | null;
properties: object | null;
parent: object | null;
}query_data_source — Query a data source with filters and sorts
Query a data source to get pages with optional filtering and sorting
Inputs:
- `data_source_id` (string, required) — The ID of the data source to query
- `filter` (object, optional) — Notion filter object to restrict which pages are returned
- `sorts` (list, optional) — List of Notion sort objects controlling result order
- `page_size` (int, optional, default: 100) — Maximum number of results per page (silently capped at 100)
- `start_cursor` (string, optional) — Cursor from a previous response's next_cursor to page through results
Output data schema:
{
results: object[];
has_more: boolean;
next_cursor: string | null;
}create_database — Create a new database
Create a new database as a child of an existing page
Inputs:
- `parent_id` (string, required) — The ID of the parent page to create the database under
- `title` (string, optional, default: "Untitled Database") — Title of the new database
- `description` (string, optional) — Plain-text description of the database
- `properties` (object, optional) — Database schema properties keyed by column name (defaults to a single 'Name' title property)
- `is_inline` (bool, optional, default: false) — Whether the database should render inline within its parent page
- `icon` (object, optional) — Icon object to set on the database
- `cover` (object, optional) — Cover object to set on the database
Output data schema:
{
id: string | null;
title: object[] | null;
parent: object | null;
data_sources: object[] | null;
url: string | null;
archived: boolean | null;
created_time: string | null;
last_edited_time: string | null;
icon: object | null;
cover: object | null;
}list_users — List workspace users
List all users in the workspace (guests not included)
Inputs:
- `page_size` (int, optional, default: 100) — Maximum number of users to return per page (values above 100 are clamped).
- `start_cursor` (string, optional) — Cursor from a previous response's next_cursor, used to page through results.
Output data schema:
{
results: {
id: string | null;
name: string | null;
avatar_url: string | null;
type: string | null;
person: object | null;
bot: object | null;
}[];
has_more: boolean;
next_cursor: string | null;
}get_user — Retrieve a specific user
Retrieve a specific user by their ID
Inputs:
- `user_id` (string, required) — ID of the user to retrieve.
Output data schema:
{
id: string | null;
name: string | null;
avatar_url: string | null;
type: string | null;
person: object | null;
bot: object | null;
}get_self — Retrieve the bot user for your token
Retrieve the bot user associated with your API token, including owner and workspace info
Inputs:
None
Output data schema:
{
id: string | null;
name: string | null;
avatar_url: string | null;
type: string | null;
person: object | null;
bot: object | null;
owner: object | null;
workspace_name: string | null;
workspace_limits: object | null;
}Response Envelope
Every tool returns the same top-level envelope. Only data varies per tool.
// Success
{
"success": true,
"statusCode": 200,
"retriable": false,
"retry_after_seconds": null,
"error": null,
"data": { ... }
}
// Error
{
"success": false,
"statusCode": 400,
"retriable": false,
"retry_after_seconds": null,
"error": { "code": "VALIDATION_ERROR", "message": "At least one update parameter must be provided", "details": {} },
"data": null
}retriable—truewhen it is safe to retry (rate limit, network error, 503).falsefor validation and auth errors.retry_after_seconds— seconds to wait before retrying; present only whenretriableistrueand the upstream specifies a delay.error.code— machine-readable string:VALIDATION_ERROR,AUTH_ERROR,UPSTREAM_ERROR,SERVER_ERROR.
Common Parameters
page_size— Maximum number of results per page. Accepted bysearch_notion,fetch_page_content,query_data_source, andlist_users; each tool silently caps it at 100.start_cursor— Cursor value from a previous response'snext_cursorfield, used to page through results. Omit for the first page.filter— Restricts which results are returned. Shape differs by tool:search_notiontakesfilter_type('page' or 'data_source'), whilequery_data_sourcetakes a full Notion filter object.
Resource Formats
Notion ID:
UUID, dashes optional
Example: 8f9b3c2d-1a2b-3c4d-5e6f-7a8b9c0d1e2f
Block Types (append_text_block):
paragraph | heading_1 | heading_2 | heading_3 | bulleted_list_item | numbered_list_item | to_do | toggle | quote | callout
Colors (append_text_block):
default | gray | brown | orange | yellow | green | blue | purple | pink | red
Background variant: append "_background", e.g. red_background, blue_background
Missing or Invalid Headers
- Cause: OAuth access token not provided in request headers or incorrect format
- Solution:
- Verify
Authorization: Bearer YOUR_ACCESS_TOKENandX-Mewcp-Credential-Id: CREDENTIAL-IDheaders are present - Check the OAuth token has not expired — reconnect your Notion account in your MewCP account if needed
- Verify
Insufficient Credits
- Cause: API calls have exceeded your request limits
- Solution:
- Check credit usage in your Curious Layer dashboard
- Upgrade to a paid plan or add credits for higher limits
- Contact support for credit adjustments
Credential Not Connected
- Cause: No Notion credential linked to your account
- Solution:
- Go to Credentials in your MewCP dashboard
- Connect your Notion account (OAuth)
- Retry the request with the correct
X-Mewcp-Credential-Idheader
Malformed Request Payload
- Cause: JSON payload is invalid or missing required fields
- Solution:
- Validate JSON syntax before sending
- Ensure all required tool parameters are included
- Check parameter types match expected values (e.g.
filterandsortsmust match Notion's expected object schema)
Server Not Found
- Cause: Incorrect server name in the API endpoint
- Solution:
- Verify endpoint format:
{server-name}/mcp/{tool-name} - Use correct server name from documentation
- Check available servers in your Curious Layer account
- Verify endpoint format:
Notion API Error
- Cause: Upstream Notion API returned an error
- Solution:
- Check Notion service status at Notion Status Page
- Verify your integration has access to the target page or database (share it with the integration in Notion)
- Review the error message for specific details
Resources
- Notion API Documentation — Official API reference
- Notion API Reference — Complete endpoint reference
- FastMCP Docs — FastMCP specification
- FastMCP Credentials — FastMCP Credentials package for credential handling