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
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.