Notion MCP
Access Notion via MCP (Model Context Protocol) with managed authentication.
Quick Start
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': 'meeting notes', 'query_type': 'internal'}).encode()
req = urllib.request.Request('https://gateway.maton.ai/notion/notion-search', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/notion/{tool-name}
Replace {tool-name} with the MCP tool name (e.g., notion-search). The gateway proxies requests to mcp.notion.com and automatically injects your credentials.
Request Headers
MCP requests use the Mcp-Session-Id header for session management. If not specified, the gateway initializes a new session and returns the session ID in the Mcp-Session-Id response header. You can include this session ID in subsequent requests to reuse the same session.
Authentication
All requests require the Maton API key:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Notion MCP connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=notion&method=MCP&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'notion', 'method': 'MCP'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "21fd90f9-5935-43cd-b6c8-bde9d915ca80",
"status": "PENDING",
"creation_time": "2025-12-08T07:20:53.488460Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "notion",
"method": "MCP",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Notion connections (eg. OAuth2, MCP), you must specify which MCP connection to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': 'meeting notes', 'query_type': 'internal'}).encode()
req = urllib.request.Request('https://gateway.maton.ai/notion/notion-search', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
IMPORTANT: If omitted, the gateway uses the default (oldest) active connection, which may fail if it's not an MCP connection.
MCP Reference
All MCP tools use POST method:
| Tool | Description | Schema |
|---|---|---|
notion-search | Search workspace and connected services | schema |
notion-fetch | Retrieve content from pages/databases | schema |
notion-create-pages | Create pages with properties and content | schema |
notion-update-page | Update page properties and content | schema |
notion-move-pages | Relocate pages to new parent | schema |
notion-duplicate-page | Copy pages within workspace | schema |
notion-create-database | Create databases with schema | schema |
notion-update-data-source | Modify data source attributes | schema |
notion-create-comment | Add comments to pages/blocks | schema |
notion-get-comments | Retrieve page comments | schema |
notion-get-teams | List workspace teams | schema |
notion-get-users | List workspace users | schema |
Search
Search for pages and databases:
POST /notion/notion-search
Content-Type: application/json
{
"query": "meeting notes",
"query_type": "internal"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"results\":[{\"id\":\"30702dc5-9a3b-8106-b51b-ed6d1bfeeed4\",\"title\":\"Meeting Summary Report\",\"url\":\"https://www.notion.so/30702dc59a3b8106b51bed6d1bfeeed4\",\"type\":\"page\",\"highlight\":\"Meeting materials\",\"timestamp\":\"2026-02-15T00:07:00.000Z\"}],\"type\":\"workspace_search\"}"
}
],
"isError": false
}
Search for users:
POST /notion/notion-search
Content-Type: application/json
{
"query": "john@example.com",
"query_type": "user"
}
With date filter:
POST /notion/notion-search
Content-Type: application/json
{
"query": "quarterly report",
"query_type": "internal",
"filters": {
"created_date_range": {
"start_date": "2024-01-01",
"end_date": "2025-01-01"
}
}
}
Fetch Content
Fetch page by URL:
POST /notion/notion-fetch
Content-Type: application/json
{
"id": "https://notion.so/workspace/Page-a1b2c3d4e5f67890"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"metadata\":{\"type\":\"page\"},\"title\":\"Project Overview\",\"url\":\"https://www.notion.so/30702dc59a3b8106b51bed6d1bfeeed4\",\"text\":\"Here is the result of \\\"view\\\" for the Page with URL https://www.notion.so/30702dc59a3b8106b51bed6d1bfeeed4 as of 2026-02-14T22:56:21.276Z:\\n<page url=\\\"https://www.notion.so/30702dc59a3b8106b51bed6d1bfeeed4\\\">\\n<properties>\\n{\\\"title\\\":\\\"Project Overview\\\"}\\n</properties>\\n<content>\\n# Project Overview\\n\\nThis document outlines the project goals and milestones.\\n</content>\\n</page>\"}"
}
],
"isError": false
}
Fetch by UUID:
POST /notion/notion-fetch
Content-Type: application/json
{
"id": "12345678-90ab-cdef-1234-567890abcdef"
}
Fetch data source (collection):
POST /notion/notion-fetch
Content-Type: application/json
{
"id": "collection://12345678-90ab-cdef-1234-567890abcdef"
}
Include discussions:
POST /notion/notion-fetch
Content-Type: application/json
{
"id": "page-uuid",
"include_discussions": true
}
Create Pages
Create a simple page:
POST /notion/notion-create-pages
Content-Type: application/json
{
"pages": [
{
"properties": {"title": "My New Page"},
"content": "# Introduction\n\nThis is my new page content."
}
]
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"pages\":[{\"id\":\"31502dc5-9a3b-816d-a2ac-e9b7ec9aece7\",\"url\":\"https://www.notion.so/31502dc59a3b816da2ace9b7ec9aece7\",\"properties\":{\"title\":\"My New Page\"}}]}"
}
],
"isError": false
}
Create page under parent:
POST /notion/notion-create-pages
Content-Type: application/json
{
"parent": {"page_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"},
"pages": [
{
"properties": {"title": "Child Page"},
"content": "# Child Page Content"
}
]
}
Create page in data source (database):
POST /notion/notion-create-pages
Content-Type: application/json
{
"parent": {"data_source_id": "f336d0bc-b841-465b-8045-024475c079dd"},
"pages": [
{
"properties": {
"Task Name": "New Task",
"Status": "In Progress",
"Priority": 5,
"Is Complete": "__NO__",
"date:Due Date:start": "2024-12-25"
}
}
]
}
Update Page
Update properties:
POST /notion/notion-update-page
Content-Type: application/json
{
"page_id": "f336d0bc-b841-465b-8045-024475c079dd",
"command": "update_properties",
"properties": {
"title": "Updated Page Title",
"Status": "Done"
}
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"page_id\":\"f336d0bc-b841-465b-8045-024475c079dd\"}"
}
],
"isError": false
}
Replace entire content:
POST /notion/notion-update-page
Content-Type: application/json
{
"page_id": "f336d0bc-b841-465b-8045-024475c079dd",
"command": "replace_content",
"new_str": "# New Heading\n\nCompletely replaced content."
}
Replace content range:
POST /notion/notion-update-page
Content-Type: application/json
{
"page_id": "f336d0bc-b841-465b-8045-024475c079dd",
"command": "replace_content_range",
"selection_with_ellipsis": "# Old Section...end of section",
"new_str": "# New Section\n\nUpdated section content."
}
Insert content after:
POST /notion/notion-update-page
Content-Type: application/json
{
"page_id": "f336d0bc-b841-465b-8045-024475c079dd",
"command": "insert_content_after",
"selection_with_ellipsis": "## Previous section...",
"new_str": "\n## New Section\n\nInserted content here."
}
Move Pages
Move to page:
POST /notion/notion-move-pages
Content-Type: application/json
{
"page_or_database_ids": ["31502dc5-9a3b-816d-a2ac-e9b7ec9aece7"],
"new_parent": {
"page_id": "31502dc5-9a3b-81e4-b090-c6f705459e38"
}
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"result\":\"Successfully moved 1 item: 31502dc5-9a3b-816d-a2ac-e9b7ec9aece7\"}"
}
],
"isError": false
}
Move to workspace:
POST /notion/notion-move-pages
Content-Type: application/json
{
"page_or_database_ids": ["page-id-1", "page-id-2"],
"new_parent": {
"type": "workspace"
}
}
Move to database:
POST /notion/notion-move-pages
Content-Type: application/json
{
"page_or_database_ids": ["page-id"],
"new_parent": {
"data_source_id": "f336d0bc-b841-465b-8045-024475c079dd"
}
}
Duplicate Page
POST /notion/notion-duplicate-page
Content-Type: application/json
{
"page_id": "31502dc5-9a3b-816d-a2ac-e9b7ec9aece7"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"page_id\":\"31502dc5-9a3b-812d-a865-ccac00a21f72\",\"page_url\":\"https://www.notion.so/31502dc59a3b812da865ccac00a21f72\"}"
}
],
"isError": false
}
Create Database
Create with SQL DDL schema:
POST /notion/notion-create-database
Content-Type: application/json
{
"title": "Task Database",
"schema": "CREATE TABLE (\"Task Name\" TITLE, \"Status\" SELECT('To Do':red, 'In Progress':yellow, 'Done':green), \"Priority\" NUMBER)"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"result\":\"Created database: <database url=\\\"{{https://www.notion.so/2a3cdbb18c1c475b909a84e5615c7b74}}\\\" inline=\\\"false\\\">\\nThe title of this Database is: Task Database\\n<data-sources>\\n<data-source url=\\\"{{collection://c0f0ce51-c470-4e96-8c3f-cafca780f1a0}}\\\">\\n...\"}"
}
],
"isError": false
}
Update Data Source
POST /notion/notion-update-data-source
Content-Type: application/json
{
"data_source_id": "c0f0ce51-c470-4e96-8c3f-cafca780f1a0",
"name": "Updated Database Name"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"result\":\"Updated data source: <database url=\\\"{{https://www.notion.so/2a3cdbb18c1c475b909a84e5615c7b74}}\\\">\\n...\"}"
}
],
"isError": false
}
Get Comments
POST /notion/notion-get-comments
Content-Type: application/json
{
"page_id": "30702dc5-9a3b-8106-b51b-ed6d1bfeeed4"
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"results\":[{\"object\":\"comment\",\"id\":\"31502dc5-9a3b-8164-aa9c-001dfb9cb942\",\"discussion_id\":\"discussion://pageId/blockId/discussionId\",\"created_time\":\"2026-02-28T20:00:00.000Z\",\"last_edited_time\":\"2026-02-28T20:00:00.000Z\",\"created_by\":{\"object\":\"user\",\"id\":\"237d872b-594c-81d6-b88e-000200ac4d04\"},\"rich_text\":[{\"type\":\"text\",\"text\":{\"content\":\"This looks great! Ready for review.\"},\"annotations\":{\"bold\":false,\"italic\":false,\"strikethrough\":false,\"underline\":false,\"code\":false,\"color\":\"default\"}}]}],\"has_more\":false}"
}
],
"isError": false
}
Create Comment
POST /notion/notion-create-comment
Content-Type: application/json
{
"page_id": "f336d0bc-b841-465b-8045-024475c079dd",
"rich_text": [
{
"type": "text",
"text": {
"content": "This looks great! Ready for review."
}
}
]
}
Response:
{
"content": [
{
"type": "text",
"text": "{\"result\":{\"status\":\"success\",\"id\":\"31502dc5-9a3b-8164-aa9c-001dfb9cb942\"}}"
}
],
"isError": false
}
List Teams
POST /notion/notion-get-teams
Content-Type: application/json
{}
Response:
{
"content": [
{
"type": "text",
"text": "{\"joinedTeams\":[],\"otherTeams\":[],\"hasMore\":false}"
}
],
"isError": false
}
List Users
POST /notion/notion-get-users
Content-Type: application/json
{}
Response:
{
"content": [
{
"type": "text",
"text": "{\"results\":[{\"type\":\"person\",\"id\":\"237d872b-594c-81d6-b88e-000200ac4d04\",\"name\":\"John Doe\",\"email\":\"john@example.com\"},{\"type\":\"bot\",\"id\":\"b638ec59-55e9-4889-8dc1-a523ff2c8677\",\"name\":\"Notion MCP\"}],\"has_more\":false}"
}
],
"isError": false
}
Code Examples
JavaScript
const response = await fetch('https://gateway.maton.ai/notion/notion-search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
},
body: JSON.stringify({
query: 'meeting notes',
query_type: 'internal'
})
});
const data = await response.json();
console.log(data);
Python
import os
import requests
response = requests.post(
'https://gateway.maton.ai/notion/notion-search',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'query': 'meeting notes',
'query_type': 'internal'
}
)
print(response.json())
Property Types Reference
When creating or updating pages in databases:
| Property Type | Format |
|---|---|
| Title | "Title Property": "Page Title" |
| Text | "Text Property": "Some text" |
| Number | "Number Property": 42 |
| Checkbox | "Checkbox Property": "__YES__" or "__NO__" |
| Select | "Select Property": "Option Name" |
| Multi-select | "Multi Property": "Option1, Option2" |
| Date (start) | "date:Date Property:start": "2024-12-25" |
| Date (end) | "date:Date Property:end": "2024-12-31" |
| Date (is datetime) | "date:Date Property:is_datetime": 1 |
| Place (name) | "place:Location:name": "Office HQ" |
| Place (coordinates) | "place:Location:latitude": 37.7749 |
Special naming: Properties named "id" or "url" must be prefixed with userDefined: (e.g., "userDefined:URL").
Error Handling
| Status | Meaning |
|---|---|
| 400 | Missing MCP connection or invalid tool name |
| 401 | Invalid or missing Maton API key |
| 429 | Rate limited (10 req/sec per account) |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
notion. For example:
- Correct:
https://gateway.maton.ai/notion/v1/search - Incorrect:
https://gateway.maton.ai/v1/search
Notes
- All IDs are UUIDs (with or without hyphens)
- MCP tool responses wrap content in
{"content": [{"type": "text", "text": "..."}], "isError": false}format - The
textfield contains JSON-stringified data that should be parsed - Use
notion-fetchto get page/database structure before creating or updating pages - For databases, fetch first to get the data source ID (
collection://...URL)