notion-custom-mcp
NotionCustomMCP - Agent Instructions
This document provides comprehensive guidance for AI agents on how to use the NotionCustomMCP (Model Context Protocol) server effectively.
Overview
The mssql-readonly MCP server provides read-only access to:
-
MSSQL Databases:
- StonePro: Business/ERP database with customer, job, and order data
- NotionArchive: Archived Notion data including jobs, pieces, and archives
-
Notion API (read-only operations):
- Query Notion databases, pages, and blocks
- Search across the workspace
- List users and databases
All operations are READ-ONLY. No modifications to data are allowed.
Available Databases
| Database | Description | Key Data |
|---|---|---|
stonepro |
StonePro Business/ERP database | Customers, Jobs, Orders, Inventory, Production data |
notionarchive |
NotionArchive database | Archived Notion data: Jobs, Pieces, Archives, Slabs, Cutouts |
Tool Reference
1. Database Discovery Tools
list_databases
Lists available databases that can be queried.
Parameters: None
Use when: You need to see which databases are available for querying.
Example output:
{
"databases": [
{
"name": "stonepro",
"description": "StonePro Business/ERP database",
"contains": "Customers, Jobs, Orders, Inventory, Production data"
},
{
"name": "notionarchive",
"description": "NotionArchive database",
"contains": "Archived Notion data: Jobs, Pieces, Archives, Slabs, Cutouts"
}
]
}
list_tables
List all tables in the specified database.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
database |
string | ✅ | stonepro or notionarchive |
Use when: You need to explore what tables exist in a database before querying.
Example output:
{
"database": "notionarchive",
"table_count": 15,
"view_count": 3,
"tables": ["dbo.Jobs", "dbo.Pieces", "dbo.Archives"],
"views": ["dbo.vw_ActiveJobs"]
}
describe_table
Get detailed information about a table's structure.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
database |
string | ✅ | stonepro or notionarchive |
table_name |
string | ✅ | Table name (e.g., dbo.Jobs or just Jobs) |
Use when: You need to understand column types, primary keys, and row counts before writing queries.
Example output:
{
"database": "notionarchive",
"table": "dbo.Jobs",
"row_count": 1523,
"column_count": 12,
"columns": [
{"name": "JobId", "type": "int", "nullable": false},
{"name": "JobName", "type": "nvarchar(255)", "nullable": true}
],
"primary_keys": ["JobId"]
}
search_tables
Search for tables matching a name pattern.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
database |
string | ✅ | stonepro or notionarchive |
pattern |
string | ✅ | Search pattern (e.g., Job finds Jobs, JobDetails) |
Use when: You don't know the exact table name but know part of it.
2. Query Execution Tools
execute_query
Execute a read-only SELECT query against the database.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
database |
string | ✅ | - | stonepro or notionarchive |
query |
string | ✅ | - | SQL SELECT query to execute |
max_rows |
integer | ❌ | 100 | Maximum rows to return (max 1000) |
Use when: You need to retrieve specific data from the database.
Security notes:
- ⚠️ Only SELECT queries are allowed
- INSERT, UPDATE, DELETE, DROP, CREATE, ALTER are blocked
- EXEC, stored procedures, and system commands are blocked
- Multiple statements (using
;) are blocked
Example:
SELECT TOP 10 JobId, JobName, Status
FROM dbo.Jobs
WHERE Status = 'Active'
ORDER BY CreatedDate DESC
get_sample_data
Get sample rows from a table.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
database |
string | ✅ | - | stonepro or notionarchive |
table_name |
string | ✅ | - | Table name |
limit |
integer | ❌ | 10 | Number of rows (max 100) |
Use when: You want to quickly see what data looks like in a table without writing a query.
test_connections
Test connections to both databases.
Parameters: None
Use when: You need to verify database connectivity before running queries.
3. Notion API Tools
notion_search
Search across all Notion pages and databases in the workspace.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | ❌ | "" |
Search query (empty returns recent items) |
filter_type |
string | ❌ | - | Filter by page or database |
page_size |
integer | ❌ | 20 | Results to return (max 100) |
Use when: You need to find pages or databases by name or content.
notion_list_databases
List all Notion databases accessible to the integration.
Parameters: None
Use when: You need to see what Notion databases are available for querying.
Example output:
{
"database_count": 5,
"databases": [
{
"id": "abc123...",
"title": "Jobs Database",
"url": "https://notion.so/...",
"property_count": 15,
"property_names": ["Name", "Status", "Customer", "Due Date"]
}
]
}
notion_get_database_schema
Get the schema (properties) of a Notion database.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
database_id |
string | ✅ | The Notion database ID |
Use when: You need to understand what properties a database has before querying it.
Returns: Property names, types, select/multi-select options, relation targets, formula expressions, etc.
notion_query_database
Query a Notion database with optional filtering and sorting.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
database_id |
string | ✅ | - | The Notion database ID |
filter_json |
string | ❌ | - | JSON string of Notion filter object |
sorts_json |
string | ❌ | - | JSON string of Notion sorts array |
page_size |
integer | ❌ | 50 | Results to return (max 100) |
Use when: You need to retrieve filtered data from a Notion database.
Example filter_json:
{"property": "Status", "select": {"equals": "Done"}}
Example sorts_json:
[{"property": "Created", "direction": "descending"}]
notion_get_page
Get a Notion page by ID.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
page_id |
string | ✅ | The Notion page ID |
Use when: You need to retrieve all properties of a specific page.
notion_get_page_content
Get the content blocks of a Notion page.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page_id |
string | ✅ | - | The Notion page ID |
max_blocks |
integer | ❌ | 100 | Maximum blocks to return |
Use when: You need to read the actual content (paragraphs, headings, lists, etc.) of a page.
notion_list_users
List all users in the Notion workspace.
Parameters: None
Use when: You need to see who has access to the workspace or resolve user IDs to names.
Common Workflows
Exploring a Database
-
See available databases:
list_databases -
List tables in a database:
list_tables(database: "notionarchive") -
Find tables by name:
search_tables(database: "notionarchive", pattern: "Job") -
Understand table structure:
describe_table(database: "notionarchive", table_name: "dbo.Jobs") -
Preview data:
get_sample_data(database: "notionarchive", table_name: "dbo.Jobs", limit: 5)
Querying Specific Data
-
Write a SELECT query:
execute_query( database: "notionarchive", query: "SELECT JobId, JobName FROM dbo.Jobs WHERE Status = 'Active'", max_rows: 50 ) -
Join multiple tables:
execute_query( database: "notionarchive", query: "SELECT j.JobName, p.PieceName FROM dbo.Jobs j INNER JOIN dbo.Pieces p ON j.JobId = p.JobId", max_rows: 100 )
Working with Notion Data
-
Find a Notion database:
notion_list_databases -
Check database schema:
notion_get_database_schema(database_id: "abc123...") -
Query with filters:
notion_query_database( database_id: "abc123...", filter_json: '{"property": "Status", "select": {"equals": "Active"}}', sorts_json: '[{"property": "Name", "direction": "ascending"}]' ) -
Get page details:
notion_get_page(page_id: "xyz789...")
Comparing MSSQL and Notion Data
-
Get archived data from MSSQL:
execute_query( database: "notionarchive", query: "SELECT NotionPageId, JobName FROM dbo.Jobs WHERE IsArchived = 1" ) -
Get live data from Notion:
notion_query_database(database_id: "...") -
Compare the results to find discrepancies
Error Handling
Query Validation Errors
If you see "Only SELECT queries are allowed" or similar:
- ✅ Make sure your query starts with
SELECTorWITH - ✅ Remove any semicolons or multiple statements
- ✅ Don't use INSERT, UPDATE, DELETE, EXEC, or stored procedures
Database Connection Errors
If queries fail with connection errors:
-
Test connectivity:
test_connections -
Check which database is failing and report to the user
Notion API Errors
Common Notion API errors:
- "object not found": The page/database ID is invalid or not shared with the integration
- "unauthorized": The integration doesn't have access to that resource
- "rate_limited": Too many requests; wait and retry
Configuration
The MCP server uses environment variables from .env:
# MSSQL - StonePro
STONEPRO_SERVER=DS-DB01\\SQLSERVER202201
STONEPRO_DATABASE=StonePro
STONEPRO_USERNAME=SA
STONEPRO_PASSWORD=your_password
# MSSQL - NotionArchive
NOTIONARCHIVE_SERVER=DS-DB01\\SQLSERVER202201
NOTIONARCHIVE_DATABASE=NotionArchive
NOTIONARCHIVE_USERNAME=SA
NOTIONARCHIVE_PASSWORD=your_password
# Notion API
NOTION_API_KEY=ntn_your_api_key
NOTION_VERIFY_SSL=true
Security Best Practices
-
All queries are validated - The server blocks dangerous SQL patterns before execution
-
Read-only by design - No way to modify data through this MCP server
-
Sanitized identifiers - Table and column names are sanitized to prevent injection
-
Notion is read-only - Only GET operations are available; no page creation/modification
-
Credentials are masked - Passwords are never exposed in error messages or logs
Quick Reference Card
| Task | Tool | Key Parameters |
|---|---|---|
| List databases | list_databases |
- |
| List tables | list_tables |
database |
| Search tables | search_tables |
database, pattern |
| Describe table | describe_table |
database, table_name |
| Run SQL query | execute_query |
database, query, max_rows |
| Get sample data | get_sample_data |
database, table_name, limit |
| Test connections | test_connections |
- |
| Search Notion | notion_search |
query, filter_type |
| List Notion DBs | notion_list_databases |
- |
| DB schema | notion_get_database_schema |
database_id |
| Query Notion DB | notion_query_database |
database_id, filter_json, sorts_json |
| Get page | notion_get_page |
page_id |
| Get page content | notion_get_page_content |
page_id, max_blocks |
| List users | notion_list_users |
- |
VS Code Integration
Add to your .vscode/mcp.json or user mcp.json:
{
"servers": {
"mssql-readonly": {
"type": "stdio",
"command": "uv",
"args": [
"--directory",
"C:\\Users\\<username>\\Custom_MCP\\NotionCustomMCP",
"--native-tls",
"run",
"python",
"-m",
"src.server"
]
}
}
}
Project Structure
NotionCustomMCP/
├── src/
│ ├── __init__.py
│ ├── server.py # Main MCP server with all tools
│ ├── config.py # Configuration management (pydantic-settings)
│ ├── database/
│ │ ├── connection.py # MSSQL connection manager
│ │ └── queries.py # Query validation and execution
│ └── notion/
│ └── client.py # Notion API client wrapper
├── .env # Environment variables (DO NOT COMMIT)
├── .env.example # Template for environment setup
└── pyproject.toml # Project dependencies