Notion MCP Server
A production-grade Notion MCP server with full CRUD, search, and schema validation capabilities for AI-assisted productivity workflows.
README
Notion MCP Server
A production-grade Model Context Protocol (MCP) server for Notion, providing comprehensive database operations with schema validation and intelligent property handling.
Features
- 🔧 Full CRUD Operations - Create, read, update pages across Notion databases
- 🔍 Smart Search & Query - Powerful filtering and search capabilities
- 📊 Schema Management - Automatic schema fetching and validation
- 🎨 Content Formatting - Markdown ↔ Notion blocks conversion
- ⚡ Async Architecture - High-performance async operations
- 🛡️ Type Safety - Comprehensive property validation
- 🔄 API 2025-09-03 - Uses latest Notion API with data sources
Architecture
kas-fastmcp/
├── main.py # Entry point
├── config.py # NotionConfig, loads databases.yaml
├── notion_server/
│ ├── server.py # FastMCP("KasNotionMCP") instance
│ ├── deps.py # Shared NotionClient + SchemaManager singletons
│ ├── core/
│ │ ├── client.py # NotionClient (httpx wrapper, 429 retry, connection pool)
│ │ ├── schema.py # SchemaManager (10-min TTL, disk cache)
│ │ └── formatters.py # PropertyFormatter, BlockFormatter (markdown <-> blocks)
│ ├── tools/
│ │ ├── query.py # notion_query, notion_find_page_by_name, notion_search, notion_list_data_sources, notion_discover_databases
│ │ ├── pages.py # notion_get_page, notion_get_data_source, notion_create_item, notion_update_item
│ │ ├── content.py # notion_get_page_content, notion_append_content, notion_replace_content
│ │ └── schema_sync.py # notion_sync_schemas, notion_validate_config
│ └── utils/
│ └── validators.py # PropertyValidator
└── databases.yaml # Database configuration
Design Principles
- Separation of Concerns - Core logic separated from MCP layer
- No Circular Dependencies - Clean, linear dependency flow
- Immutable Configuration - Config is never mutated at runtime
- Schema-First - Validate against actual Notion schemas
- Testable - Each layer can be tested independently
Installation
Prerequisites
- Python 3.10+
- Notion Integration with API access
- Notion databases with integration added
Setup
- Clone the repository
git clone <your-repo-url>
cd kas-fastmcp
- Install dependencies
uv sync
- Configure environment
cp .env.example .env
# Edit .env and add your NOTION_TOKEN
- Configure databases
cp databases.yaml.example databases.yaml
# Edit databases.yaml and add your database configurations
Getting Your Notion Credentials
-
Create Integration
- Go to https://www.notion.so/my-integrations
- Click "New integration"
- Give it a name and select your workspace
- Copy the "Internal Integration Token"
-
Get Data Source IDs
- Open your database in Notion
- Click "..." menu → Settings
- Manage data sources → Copy data source ID
- Add to
databases.yaml
-
Add Integration to Databases
- Open each database in Notion
- Click "..." menu → Connections
- Add your integration
Configuration
databases.yaml
Define all your Notion databases:
zettelkasten:
data_source_id: "your-data-source-id-here"
database_id: "your-database-id-here"
title_property: "title"
description: "Personal knowledge management"
habits:
data_source_id: "your-data-source-id-here"
database_id: "your-database-id-here"
title_property: "title"
description: "Habit tracking"
.env
NOTION_TOKEN=ntn_your_token_here
NOTION_API_VERSION=2025-09-03
Usage
Running the Server
Development:
python main.py
With FastMCP CLI:
fastmcp run main.py
Claude Desktop Integration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"kas-notion": {
"command": "python",
"args": ["/path/to/kas-fastmcp/main.py"]
}
}
}
Available Tools (15 total)
Query Tools (query.py)
notion_query- Query pages from a databasenotion_find_page_by_name- Find page by exact titlenotion_search- Workspace-wide searchnotion_list_data_sources- List data sources for a databasenotion_discover_databases- Discover all accessible databases
Page Tools (pages.py)
notion_get_page- Get page with properties (fully paginated wheninclude_content=True)notion_get_data_source- Get database schemanotion_create_item- Create new pagenotion_update_item- Update page properties
Content Tools (content.py)
notion_get_page_content- Get page content as markdownnotion_append_content- Append blocks to pagenotion_replace_content- Clear all blocks on a page and write new markdown content
Schema Tools (schema_sync.py)
notion_sync_schemas- Sync schemas from Notion and optionally update confignotion_validate_config- Validate current database configuration
Available Resources
notion://databases- Lists all configured databases as JSONnotion://databases/{source_name}/schema- Returns the cached schema for a specific database
Testing
Connection Testing
Quick token verification:
python connection_testing/token_verification.py
Full connection test:
python connection_testing/test_connection.py
This will:
- Validate your configuration
- Test API connectivity
- Check database access
- Fetch schemas
Validation Testing
Test the property validator directly:
python test_validation.py
Development
Adding a New Database
- Add to
databases.yaml:
my_database:
data_source_id: "your-id"
database_id: "your-id"
title_property: "Name"
description: "My new database"
- No code changes needed! The server automatically loads all databases from the YAML file.
Adding a New Tool
- Choose the appropriate module (
query.py,pages.py, orcontent.py) - Use the
@mcp.tooldecorator - Import and use core modules
- Document with clear docstrings
Example:
from notion_server.server import mcp
from notion_server.deps import _client # shared singleton — do not instantiate a new one
@mcp.tool
async def my_new_tool(param: str) -> dict:
"""
Tool description.
Args:
param: Parameter description
Returns:
Result description
"""
result = await _client.get(f"endpoint/{param}")
return result
Project Structure Explained
core/ - Pure business logic, no MCP dependencies
- Testable independently
- Reusable in other projects
- No side effects
tools/ - MCP tool definitions
- Thin wrappers around core modules
- Handle MCP-specific concerns
- Minimal business logic
utils/ - Shared utilities
- Validators
- Helpers
- Common functions
Validation System
How It Works
Properties are validated against Notion's schema before API calls:
# Automatic validation
notion_create_item(
source_name="zettelkasten",
properties={
"title": {"title": [{"text": {"content": "My Note"}, "type": "text"}]},
"nonexistent": {"rich_text": [...]} # ❌ Validation error!
}
)
What Gets Validated
✅ Property exists in schema
✅ Property type matches
✅ Select options are valid
✅ Read-only properties rejected
✅ Required properties present (strict mode)
Direct Validation
Use the validator directly in your code:
from notion_server.core import SchemaManager, NotionClient
from notion_server.utils import PropertyValidator
client = NotionClient()
schema_manager = SchemaManager(client)
schema = await schema_manager.get_schema("zettelkasten")
validator = PropertyValidator(schema)
is_valid, errors = validator.validate_properties(properties)
Troubleshooting
Common Issues
"NOTION_TOKEN not found"
- Create
.envfile from.env.example - Add your token from https://www.notion.so/my-integrations
"Database not found (404)"
- Check database_id in
databases.yaml - Ensure integration is added to the database
- Verify integration has access
"No databases configured"
- Create
databases.yamlfromdatabases.yaml.example - Add at least one database configuration
"Property validation failed"
- Check property names match your schema
- Ensure values are in correct Notion format
- Verify select options are valid
Debug Mode
Enable detailed logging by checking server output:
python main.py 2>&1 | tee server.log
API Reference
NotionClient
client = NotionClient(token=None, api_version=None)
await client.get(endpoint)
await client.post(endpoint, payload)
await client.patch(endpoint, payload)
SchemaManager
schema_manager = SchemaManager(client)
schema = await schema_manager.get_schema(source_name)
data_source_id = await schema_manager.get_data_source_id(source_name)
config = schema_manager.get_source_config(source_name)
PropertyFormatter
formatter = PropertyFormatter()
title = formatter.extract_title(properties)
formatted = formatter.format_for_display(properties)
BlockFormatter
formatter = BlockFormatter()
markdown = formatter.to_markdown(blocks)
blocks = formatter.from_markdown(markdown)
Contributing
Code Style
- Use type hints
- Write docstrings
- Keep functions focused
- Follow separation of concerns
Testing
- Test core modules independently
- Test tools through MCP protocol
- Validate against real Notion databases
License
MIT
Acknowledgments
- Built with FastMCP
- Uses Notion API 2025-09-03
- Developed for AI-powered productivity workflows
See CHANGELOG.md for version history.
Support
For issues, questions, or contributions, please open an issue on GitHub.
Built with ❤️ for AI-powered productivity
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
mcp-server-qdrant
这个仓库展示了如何为向量搜索引擎 Qdrant 创建一个 MCP (Managed Control Plane) 服务器的示例。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器