| name | easy-notion-mcp-integration |
| description | Connect AI agents to Notion using markdown instead of raw JSON through MCP server with 42 tools for reading, writing, and managing pages and databases |
| triggers | ["connect to Notion through MCP","read and write Notion pages as markdown","set up Notion MCP server","add database entries to Notion","convert Notion blocks to markdown","configure Notion integration with OAuth","upload files to Notion pages","query Notion databases with filters"] |
Easy Notion MCP Integration
Skill by ara.so — MCP Skills collection.
Overview
easy-notion-mcp is a production-grade Model Context Protocol (MCP) server that bridges AI agents to Notion using GitHub-flavored markdown instead of raw Notion API JSON. It provides 42 MCP tools for reading and writing Notion content with round-trip fidelity across 24 block types including toggles, columns, callouts, tables, and file uploads.
Key Features
- Markdown-first I/O: Agents work with markdown; server handles all Notion block JSON conversion
- Database ergonomics: Write simple property objects like
{ "Status": "Done" } with automatic schema mapping
- Dual transport: Stdio (API token) or HTTP (OAuth/bearer token)
- Optional Redis: Shared schema cache and OAuth persistence for multi-instance deployments
- Security defaults: Content sanitization, URL validation, workspace-root file containment
Installation
Quick Start with npx (Recommended)
Configure in your MCP client (Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"notion": {
"command": "npx",
"args": ["-y", "easy-notion-mcp"],
"env": {
"NOTION_TOKEN": "YOUR_NOTION_INTEGRATION_TOKEN"
}
}
}
}
From Source
git clone https://github.com/Grey-Iris/easy-notion-mcp.git
cd easy-notion-mcp
npm install
npm run build
MCP client configuration:
{
"mcpServers": {
"notion": {
"command": "node",
"args": ["/absolute/path/to/easy-notion-mcp/dist/index.js"],
"env": {
"NOTION_TOKEN": "YOUR_NOTION_INTEGRATION_TOKEN",
"NOTION_ROOT_PAGE_ID": "optional_default_parent_page_id"
}
}
}
}
Getting a Notion Integration Token
- Visit https://www.notion.so/my-integrations
- Click + New integration
- Name your integration and select capabilities
- Copy the Internal Integration Token (starts with
ntn_)
- Share target pages/databases with your integration
Configuration
Environment Variables
Core Settings:
NOTION_TOKEN=ntn_your_integration_token_here
NOTION_ROOT_PAGE_ID=abc123def456
NOTION_TRUST_CONTENT=false
NOTION_MCP_WORKSPACE_ROOT=/path/to/workspace
LOG_LEVEL=info
HTTP Transport (OAuth or Static Bearer):
NOTION_MCP_BEARER=your_random_secret_token
NOTION_OAUTH_CLIENT_ID=your_oauth_client_id
NOTION_OAUTH_CLIENT_SECRET=your_oauth_client_secret
PORT=3333
NOTION_MCP_BIND_HOST=127.0.0.1
Redis (Optional Multi-Instance Persistence):
REDIS_ENABLED=true
REDIS_URL=redis://127.0.0.1:6379/0
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
REDIS_KEY_PREFIX=easy-notion:
REDIS_CONNECT_TIMEOUT_MS=10000
REDIS_MAX_RETRIES=3
.env File Setup
cp .env.example .env
MCP Tools Reference
Page Operations
read_page
Read a Notion page as markdown.
{
"name": "read_page",
"arguments": {
"page_id": "abc123def456"
}
}
{
"markdown": "# Page Title\n\nContent here...",
"warnings": []
}
create_page
Create a new page with markdown content.
{
"name": "create_page",
"arguments": {
"title": "My New Page",
"markdown": "# Heading\n\nSome **bold** text.",
"parent_page_id": "parent123"
}
}
{
"id": "new_page_id",
"url": "https://notion.so/...",
"title": "My New Page"
}
update_page
Update page content with markdown.
{
"name": "update_page",
"arguments": {
"page_id": "abc123",
"markdown": "# Updated Content\n\nNew paragraph.",
"mode": "replace"
}
}
append_to_page
Append markdown to existing page.
{
"name": "append_to_page",
"arguments": {
"page_id": "abc123",
"markdown": "\n## New Section\n\nAppended content."
}
}
delete_page
Move page to trash.
{
"name": "delete_page",
"arguments": {
"page_id": "abc123"
}
}
Database Operations
query_database
Query database with filters and sorts.
{
"name": "query_database",
"arguments": {
"database_id": "db123",
"filter": {
"property": "Status",
"select": { "equals": "In Progress" }
},
"sorts": [
{ "property": "Created", "direction": "descending" }
],
"page_size": 50
}
}
{
"results": [
{
"id": "entry123",
"properties": { "Status": "In Progress", "Name": "Task 1" },
"markdown": "Task content..."
}
],
"has_more": false
}
get_database
Get database schema and properties.
{
"name": "get_database",
"arguments": {
"database_id": "db123"
}
}
{
"id": "db123",
"title": "Tasks",
"properties": {
"Name": { "type": "title" },
"Status": {
"type": "select",
"options": ["To Do", "In Progress", "Done"]
},
"Due Date": { "type": "date" }
}
}
add_database_entry
Create new database entry with automatic schema conversion.
{
"name": "add_database_entry",
"arguments": {
"database_id": "db123",
"properties": {
"Name": "New Task",
"Status": "To Do",
"Due Date": "2026-12-31",
"Tags": ["urgent", "bug"]
},
"markdown": "## Task Details\n\nDetailed description here."
}
}
{
"id": "new_entry_id",
"url": "https://notion.so/...",
"properties": { }
}
update_database_entry
Update database entry properties and/or content.
{
"name": "update_database_entry",
"arguments": {
"page_id": "entry123",
"properties": {
"Status": "Done",
"Completed": true
},
"markdown": "Updated task description."
}
}
Search Operations
search_notion
Full-text search across workspace.
{
"name": "search_notion",
"arguments": {
"query": "project planning",
"filter": { "property": "object", "value": "page" },
"sort": { "direction": "descending", "timestamp": "last_edited_time" },
"page_size": 20
}
}
{
"results": [
{
"id": "page123",
"title": "Q4 Project Planning",
"url": "https://notion.so/...",
"last_edited_time": "2026-07-03T10:30:00Z"
}
],
"has_more": false
}
File Operations
upload_file
Upload local file to a page (stdio transport only).
{
"name": "upload_file",
"arguments": {
"page_id": "abc123",
"file_path": "/workspace/documents/report.pdf",
"caption": "Q3 Financial Report"
}
}
add_image
Add image from URL to page.
{
"name": "add_image",
"arguments": {
"page_id": "abc123",
"url": "https://example.com/image.png",
"caption": "Architecture Diagram"
}
}
Block Operations
create_block
Create specific block types.
{
"name": "create_block",
"arguments": {
"parent_id": "page123",
"type": "callout",
"content": {
"icon": "💡",
"text": "Important note here",
"color": "blue_background"
}
}
}
{
"name": "create_block",
"arguments": {
"parent_id": "page123",
"type": "toggle",
"content": {
"text": "Click to expand",
"children": [
{ "type": "paragraph", "text": "Hidden content" }
]
}
}
}
Common Patterns
Reading and Updating a Page
const readResponse = await callTool("read_page", {
page_id: "abc123def456"
});
const existingMarkdown = readResponse.markdown;
const updatedMarkdown = existingMarkdown + "\n## New Section\n\nAdded content.";
await callTool("update_page", {
page_id: "abc123def456",
markdown: updatedMarkdown,
mode: "replace"
});
Creating a Task in Database
const schema = await callTool("get_database", {
database_id: "tasks_db_id"
});
const newTask = await callTool("add_database_entry", {
database_id: "tasks_db_id",
properties: {
"Task Name": "Implement feature X",
"Status": "In Progress",
"Priority": "High",
"Assignee": "john@example.com",
"Due Date": "2026-08-15",
"Tags": ["backend", "api"]
},
markdown: `## Description
Implement the new feature X as specified in RFC-123.
### Acceptance Criteria
- [ ] API endpoints created
- [ ] Tests written
- [ ] Documentation updated`
});
console.log(`Created task: ${newTask.url}`);
Querying and Processing Database Entries
const results = await callTool("query_database", {
database_id: "tasks_db_id",
filter: {
"and": [
{
"property": "Status",
"select": { "does_not_equal": "Done" }
},
{
"property": "Priority",
"select": { "equals": "High" }
}
]
},
sorts: [
{ "property": "Due Date", "direction": "ascending" }
]
});
for (const entry of results.results) {
console.log(`Task: ${entry.properties["Task Name"]}`);
console.log(`Due: ${entry.properties["Due Date"]}`);
console.log(`Content: ${entry.markdown}`);
if (shouldUpdate(entry)) {
await callTool("update_database_entry", {
page_id: entry.id,
properties: {
"Status": "In Review"
}
});
}
}
Creating a Page with Rich Content
const richContent = `# Project Documentation
## Overview
This project implements **critical features** for Q4 delivery.
## Architecture

## Tasks
- [x] Initial setup
- [ ] Core implementation
- [ ] Testing
## Notes
> 💡 Remember to update the API docs after deployment.
\`\`\`typescript
// Example usage
const client = new NotionClient({ token: process.env.NOTION_TOKEN });
const page = await client.getPage("page_id");
\`\`\`
## Next Steps
1. Review code
2. Deploy to staging
3. Monitor metrics
`;
const page = await callTool("create_page", {
title: "Q4 Project Documentation",
markdown: richContent,
parent_page_id: process.env.NOTION_ROOT_PAGE_ID
});
Searching and Organizing Content
const searchResults = await callTool("search_notion", {
query: "planning",
filter: { "property": "object", "value": "page" },
sort: {
"direction": "descending",
"timestamp": "last_edited_time"
}
});
const indexMarkdown = searchResults.results
.map(r => `- [${r.title}](${r.url})`)
.join("\n");
await callTool("create_page", {
title: "Planning Documents Index",
markdown: `# Planning Documents\n\n${indexMarkdown}`,
parent_page_id: process.env.NOTION_ROOT_PAGE_ID
});
CLI Usage
The project includes a CLI for low-context Notion access:
npx easy-notion-mcp --profile readonly
npx easy-notion-mcp --profile readwrite
NOTION_TOKEN=ntn_xxx npx easy-notion-mcp --profile readwrite
Development Workflow
Local Development
npm install
npm run dev
NOTION_TOKEN=ntn_xxx npm start
NOTION_TOKEN=ntn_xxx NOTION_MCP_BEARER=$(openssl rand -hex 32) npm run start:http
Testing
npm test
npm run test:watch
npm run typecheck
npm run lint
npm run build
Integration Tests
cp .env.example .env
npm run test:e2e
HTTP Transport Setup
Static Bearer Token
export NOTION_MCP_BEARER=$(openssl rand -hex 32)
NOTION_TOKEN=ntn_xxx NOTION_MCP_BEARER=$NOTION_MCP_BEARER npm run start:http
Configure HTTP client:
{
"mcpServers": {
"notion-http": {
"url": "http://127.0.0.1:3333/mcp",
"transport": "http",
"headers": {
"Authorization": "Bearer YOUR_BEARER_TOKEN"
}
}
}
}
OAuth Flow
NOTION_OAUTH_CLIENT_ID=your_client_id \
NOTION_OAUTH_CLIENT_SECRET=your_client_secret \
npm run start:http
Troubleshooting
Common Issues
NOTION_TOKEN is required error:
{
"env": {
"NOTION_TOKEN": "ntn_your_token"
}
}
401 invalid_token (HTTP mode):
outside the allowed workspace root error:
NOTION_MCP_WORKSPACE_ROOT=/path/to/workspace
Redis connection warnings:
redis-server
REDIS_ENABLED=false npm start
Unknown property name errors:
Stale schema cache:
redis-cli KEYS "easy-notion:schema:*" | xargs redis-cli DEL
Enable Debug Logging
LOG_LEVEL=debug npm start
Debug logs include:
- Full Notion API request/response bodies
- Schema cache hits/misses
- Block conversion warnings
- Redis operation details
Testing Connectivity
const result = await callTool("read_page", {
page_id: "known_shared_page_id"
});
console.log(result.markdown);
const search = await callTool("search_notion", {
query: "test",
page_size: 1
});
console.log(search.results);
Security Considerations
Content Sanitization
By default, all read operations include a content-notice prefix warning agents about potentially untrusted content:
NOTION_TRUST_CONTENT=true
File Upload Restrictions
- File uploads via
file:// URLs are stdio transport only
- All paths must be within
NOTION_MCP_WORKSPACE_ROOT
- Symlinks are validated to prevent escape
- Use HTTPS URLs for HTTP transport
Workspace Isolation
NOTION_MCP_WORKSPACE_ROOT=/workspace/notion-files
Bearer Token Security
openssl rand -hex 32
Advanced Configuration
Redis for Multi-Instance Deployments
REDIS_ENABLED=true
REDIS_URL=redis://redis.internal:6379/0
REDIS_KEY_PREFIX=prod-notion:
for i in {1..3}; do
PORT=$((3333 + i)) npm run start:http &
done
Custom Cache TTL
Schema cache TTL is not directly configurable but can be adjusted in code:
const DEFAULT_TTL = 3600;
Property Type Mapping
The server automatically converts between markdown-friendly values and Notion property types:
- Title/Rich Text: Plain string → Notion rich text
- Number: Numeric string → Number
- Date: ISO string → Date object
- Select/Multi-select: String/Array → Option references
- People: Email string/Array → Person references
- Checkbox: Boolean → Boolean
- URL: String → URL (validated)
- Email: String → Email (validated)
- Phone: String → Phone number
- Files: URL string/Array → External file references
Resources
License
MIT License - See project LICENSE file for details.