notion-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| PORT | No | Server listen port. | 8322 |
| FILES_DIR | No | Local file store path. | /data/files |
| SHARED_DIR | No | Path to the cross-MCP shared mount. | /shared |
| NOTION_TOKEN | No | Notion internal integration token (starts with ntn_). Required. | |
| DRIVE_ACCOUNT | No | Google account email for Drive access (optional). | |
| XBERG_BASE_URL | No | Base URL for OCR delegation to an xberg server. | http://xberg:8000 |
| GOOGLE_CLIENT_ID | No | OAuth client ID for Google Drive integration (optional). | |
| MCP_BEARER_TOKEN | No | Bearer token for authenticating MCP clients. Required for HTTP mode, not for stdio mode. | |
| DRIVE_FOLDER_NAME | No | Shared Drive folder name. | mcp-google-accounts |
| XBERG_VLM_API_KEY | No | API key for the xberg VLM backend (optional). | |
| NOTION_API_VERSION | No | Notion API version header. | 2026-03-11 |
| GMAIL_TOKEN_DB_PATH | No | Legacy alternative to GOOGLE_TOKEN_DB_PATH. | |
| GOOGLE_CLIENT_SECRET | No | OAuth client secret for Google Drive integration (optional). | |
| GOOGLE_TOKEN_DB_PATH | No | Path to google-accounts-mcp's SQLite token DB (read-only). Legacy GMAIL_TOKEN_DB_PATH is also honored. | /google-data/tokens.db |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| notion_search_pagesA | Search Notion for pages the integration has access to. Returns titles and page IDs — use these IDs with notion_upload_file / list_files_on_page. The integration only sees pages explicitly shared with it in Notion. in_trash=True searches trashed pages instead of active ones (restore matches with notion_restore_page). |
| notion_search_databasesA | Search Notion for databases the integration has access to. Returns titles, IDs, and property schemas. Use the database ID with notion_query_database, notion_describe_database, or notion_create_page (with database_id). The integration only sees databases explicitly shared with it in Notion. |
| notion_list_files_on_pageA | List all file/image/pdf/video/audio blocks on a Notion page. Returns block IDs and filenames — use block IDs with notion_download_file. |
| notion_purge_shared_filesA | Delete files from the cross-MCP shared mount (SHARED_DIR, /shared inside the container) whose modification time is older than max_age_hours. Use this to clean up attachments handed over from the companion google-accounts-mcp server once they've been uploaded to Notion. A host-level TTL sweep (if you run one) makes this automatic; the tool is mainly for agents that want to clean up proactively at the end of a workflow without waiting for it. Set dry_run=True to list victims without deleting. Only regular files are touched — subdirectories are left alone. |
| notion_purge_fileA | Delete a single file from the cross-MCP shared mount by bare filename. Use this after a successful upload to free the shared slot instead of waiting for the daily TTL sweep (or calling notion_purge_shared_files with max_age_hours=0, which would wipe any unrelated in-flight handoffs). Rejects any filename containing path separators. |
| notion_list_local_filesA | List files available to notion-mcp by scheme, so the agent can verify a handoff before attaching it to Notion. location: • 'files' (default) — the notion-mcp private store under FILES_DIR. These are addressable as source='local:'. • 'shared' — the cross-MCP shared mount under SHARED_DIR. Files placed here by google-accounts-mcp (download_attachment with destination='shared') are addressable as source='shared:'. Optional substring filter on the filename. |
| notion_upload_fileA | Upload a file to Notion and attach it as a new block. source accepts one of:
• parent_page_id is a Notion page ID (find via notion_search_pages) OR any block ID that supports children (toggle, column, callout, ...) to attach the file inside that block. position controls where the new block lands among the parent's children: 'end' (default), 'start', or a block ID to insert directly AFTER that block (get block IDs from notion_read_page(include_block_ids=True) or notion_list_files_on_page). filename overrides the source filename when provided. caption is shown below the file block in Notion. Files larger than 20 MB are uploaded via Notion's multi-part protocol automatically. To swap the file in an existing block without moving it, use notion_replace_file instead. Cross-MCP file transferTo upload a Gmail attachment to a Notion page:
|
| notion_replace_fileA | Replace the file inside an EXISTING file/image/pdf/video/audio block, in place — the block keeps its position on the page. Use this to update a document without re-arranging anything; use notion_upload_file(position=...) to add a new block instead. block_id: the file block to update (from notion_list_files_on_page or
notion_read_page(include_block_ids=True)).
source: same schemes as notion_upload_file ( The new file must map to the same block type as the existing block (e.g. you cannot replace an image block's content with a PDF — Notion blocks are typed). In that case delete the block and upload anew. |
| notion_import_file_from_urlA | Import a file into Notion directly from a public HTTPS URL — the bytes go Notion-side, never through this server or MCP parameters. url must be publicly accessible over HTTPS. filename must carry an extension Notion accepts (it determines the block type). The import is asynchronous: this tool polls until the upload reaches a terminal status or wait_seconds elapses. parent_page_id and position behave as in notion_upload_file. Size limits are plan-dependent (5 MiB free / 5 GiB paid). No sha256 is reported — the bytes never pass through this server; verify via notion_download_file if integrity matters. |
| notion_set_page_visualA | Set or remove a Notion page's icon or cover image. target: 'icon' or 'cover'.
Provide exactly one of:
• emoji — a single emoji character (icon only), e.g. '📊'.
• source — an image via the usual schemes ( |
| notion_describe_databaseA | Retrieve a Notion database's schema. Returns each property name and type — use this to find the exact files & media column name to pass to notion_upload_file_to_database / notion_add_file_to_row. database_id can be found by calling notion_list_files_on_page on the page that holds the inline database (child_database blocks will show), or pasted from the database URL. |
| notion_upload_file_to_databaseA | Upload a file and create a NEW row in a Notion database with the file attached to the given files & media column. source: |
| notion_add_file_to_rowA | Upload a file and attach it to an EXISTING database row's files & media column. page_id: the row (page) ID — paste from the row's "Copy link"
or obtain via notion_search_pages.
source: same schemes as notion_upload_file: Cross-MCP file transferTo attach a Gmail attachment to an existing Notion row:
Files never traverse MCP parameters — no size limits. Alternative via Drive: download_attachment → drive_upload(local_filename=...) → notion_add_file_to_row(source='drive:', ...). |
| notion_batch_add_file_to_rowA | Attach files to many existing Notion rows in one call. items_json is a JSON array of objects. Each item takes the same shape as the single-shot tool's parameters:: default_files_property and default_mode fill in any item that omits those fields — pass them once at the batch level instead of repeating per item. Items are uploaded with bounded concurrency (NOTION_BATCH_CONCURRENCY, default 3) so a batch of N finishes in roughly ceil(N/concurrency) waves rather than serially — a batch of ~8 used to exceed the client's 4-minute timeout when serial. Concurrent items targeting the SAME row are still serialised (the 'append' path is a read-modify-write). The 429/5xx backoff in the client absorbs any rate-limit bursts. Per-item failures are reported in the output (in input order) but do NOT abort the batch — use the per-item status to retry individual items. Files are staged in the shared mount; the typical pattern is to stage N files via google-accounts-mcp download_attachment, call this tool once, then call notion_purge_file to remove each staged file. |
| notion_download_fileA | Download a file from a Notion file/image/pdf/video/audio block. destination is one of: filename overrides the inferred name. When destination=local, filename
may include subdirectories (e.g. Cross-MCP file transfer (Notion → Gmail)To email a file stored in Notion:
|
| notion_extract_file_textA | Extract text from a PDF anywhere notion-mcp can reach — a file stored in Notion, the shared cross-MCP mount, the private store, or the Drive folder — without moving the bytes over MCP. source schemes: 'notion:' (file/pdf block on a page — get block IDs from notion_list_files_on_page), 'shared:', 'local:', 'drive:'. Returns JSON: {"text", "page_count", "pages_returned", "mode", "truncated", "filename"} (+ "ocr_used"/"ocr_pages"/"ocr_engine" when OCR ran). Response ceiling 200 KB — truncation happens on a page boundary with a trailing marker naming the pages to fetch next. pages: pdftotext-style spec — None/"" (all), "3", "1-5", "1,3,5", "1-3,7". Out-of-range pages are silently dropped. mode: 'text' (flowing), 'layout' (preserve columns), 'tables' (extract_tables → markdown). Applies to native extraction only. OCR (scanned / image-only PDFs) — delegated to the fleet xberg service (XBERG_BASE_URL env; pages rasterised locally, sent as PNGs): ocr='auto' (default) — pages whose native text is < 20 chars are OCR'd (tesseract backend, seconds/page). "ocr_error" in the response means OCR was needed but xberg was unreachable/failed. ocr='off' — native extraction only. ocr='force' — OCR every requested page (garbled font encodings). ocr='llm' — transcribe pages with a vision model (xberg vlm backend → LiteLLM; env: XBERG_VLM_API_KEY, optional XBERG_VLM_MODEL / XBERG_VLM_BASE_URL). Best for handwriting and messy tables; slower and metered. |
| notion_render_file_pageA | Render one page of a PDF (from Notion, shared, local, or Drive) as a viewable image. The default return is a real MCP image content block, so you SEE the page directly — for a scanned document this is often all you need. Use when notion_extract_file_text isn't enough: charts, stamps, layouts, visual validation. source schemes as in notion_extract_file_text ('notion:', 'shared:', 'local:', 'drive:'). Set return_base64=True for a JSON envelope {"image_base64", "mime_type", "width", "height", "page", "page_count", "filename"} instead (for relaying bytes programmatically). Errors return a JSON string with an "error" key in both modes. page: 1-indexed. max_width: 200..4000 px (default 1200). format: 'jpeg' (default) or 'png'. quality: JPEG 1..100 (default 75). |
| notion_read_pageA | Read a Notion page's properties and content as markdown. Returns the page title, properties, and full block content rendered as readable markdown. Nested blocks (toggles, list children, columns) are fetched recursively up to max_depth levels. page_id: Notion page ID (from notion_search_pages or a URL). max_depth: how many levels of nested blocks to fetch (1-5, default 3). include_block_ids: if True, each block line is suffixed with an HTML comment containing the block UUID, e.g. . Use these IDs with notion_update_block / notion_delete_block. |
| notion_create_pageA | Create a new Notion page with optional markdown content. For a standalone page under another page, provide parent_page_id. For a database row, provide database_id and properties_json. title: the page title. content: markdown text that becomes the page body. Supports headings, lists, quotes, code blocks, inline formatting. parent_page_id: ID of the parent page. database_id: ID of a database to add the row to. properties_json: JSON object of extra Notion properties (for database rows). Keys are property names, values are Notion API property value objects. icon: emoji character for the page icon (e.g. "🚀"). |
| notion_update_pageA | Update a Notion page's title, icon, or other properties. page_id: the page to update. title: new title (leave empty to keep current). properties_json: JSON object of properties to update. Keys are property names, values are Notion API property value objects. Omitted properties are left unchanged. icon: emoji for page icon, or "remove" to clear it. |
| notion_append_contentA | Append markdown content to an existing Notion page. Parses the markdown into blocks and appends them to the end of the page.
Supports: headings (# ## ###), bullet/numbered/to-do lists, quotes,
fenced code blocks, dividers (---), paragraphs with inline formatting
(bold, italic, page_id: the page to append to. content: markdown text to append. |
| notion_archive_pageA | Archive (soft-delete) a Notion page. The page can be restored from the Notion trash within 30 days using notion_restore_page. page_id: the page to archive. |
| notion_restore_pageA | Restore an archived page from the Notion trash. page_id: the archived page to restore. |
| notion_update_blockA | Update an existing block's text content. block_id: ID of the block to update (from notion_read_page output). content: new text for the block (supports inline markdown). block_type: the block type (paragraph, heading_1, heading_2, heading_3, bulleted_list_item, numbered_list_item, to_do, quote, callout, toggle, code). If omitted, the block is fetched to detect it. |
| notion_delete_blockA | Delete a block from a Notion page. This removes the block and all its children. The deletion is permanent (blocks cannot be restored). block_id: ID of the block to delete. |
| notion_query_databaseA | Query a Notion database with optional filter, sort, and pagination. Returns matching rows with their properties. Use notion_describe_database first to see the schema and property names. database_id: the database to query.
filter_json: Notion API filter object as JSON. Example:
{"property": "Status", "select": {"equals": "Done"}}
Compound example:
{"and": [
{"property": "Status", "select": {"equals": "Active"}},
{"property": "Priority", "number": {"greater_than": 3}}
]}
sorts_json: JSON array of sort objects. Example:
[{"property": "Due", "direction": "ascending"}]
Use "ascending" or "descending".
limit: page size (default 50, max 100 — Notion's hard cap).
start_cursor: opaque cursor returned by a previous call. Pass it
to fetch the next page. When the output shows
|
| notion_create_databaseA | Create a new Notion database under a page. parent_page_id: page to create the database under (it appears as an inline database on that page). title: database title. schema_json: JSON object mapping property names to their type config in Notion API format. A title property is required. Example: { "Name": {"title": {}}, "Status": {"select": {"options": [ {"name": "To Do", "color": "red"}, {"name": "Done", "color": "green"} ]}}, "Due Date": {"date": {}}, "Priority": {"number": {"format": "number"}}, "Tags": {"multi_select": {"options": [ {"name": "bug"}, {"name": "feature"} ]}}, "Assignee": {"people": {}}, "Done": {"checkbox": {}}, "Notes": {"rich_text": {}}, "Link": {"url": {}} } |
| notion_update_databaseA | Update a Notion database's title, description, or property schema. database_id: the database to update. title: new database title (leave empty to keep current). description: new description (leave empty to keep current). properties_json: JSON object of property changes. Notion API format:
Multiple changes can be combined in one call. Use notion_describe_database first to see current schema. |
| notion_get_propertyA | Retrieve a single property from a Notion page with full pagination. Useful for large properties (relations, rollups, long rich_text) that get truncated in the normal page response. Provide either property_name or property_id. page_id: the page to read from. property_name: human-readable property name (looked up from page metadata). property_id: Notion property ID (from notion_describe_database schema). Takes precedence over property_name if both given. |
| notion_get_commentsA | Get all comments on a Notion page. Returns comment text, author, and timestamps. Use this to read discussion threads on a page. page_id: the page to get comments from. |
| notion_add_commentA | Add a comment to a Notion page or reply to a discussion thread. page_id: the page to comment on. text: comment text. Sent as native API markdown — inline formatting, inline equations, and @mentions all work. discussion_id: if provided, the comment is a reply to this existing discussion thread. Get discussion IDs from notion_get_comments. |
| notion_update_commentA | Edit a comment previously created by this integration (Notion returns 404 for comments created by anyone else). comment_id: from notion_get_comments or notion_add_comment. text: replacement text (native API markdown — inline formatting, equations, @mentions). |
| notion_delete_commentA | Delete a comment previously created by this integration (Notion returns 404 for comments created by anyone else). comment_id: from notion_get_comments or notion_add_comment. |
| notion_list_viewsA | List the views defined on a Notion database — name, type, id, and whether each carries a saved filter/sorts. Use the view id with notion_query_view to run a view's saved filter, or with notion_update_view / notion_delete_view to manage it. |
| notion_query_viewA | Run a database view's saved filter and sorts, returning the matching rows (title + id + url). Use this instead of notion_query_database when the view already encodes the filter you want — no filter JSON needed. view_id: from notion_list_views. limit: max rows to return (max 100). |
| notion_create_viewA | Create a view on a Notion database. database_id: the database to add the view to. name: display name for the view. view_type: table | board | list | calendar | timeline | gallery | chart (default table). filter_json: optional Notion filter object as JSON (same shape as notion_query_database's filter_json). sorts_json: optional JSON array of sort objects. |
| notion_update_viewA | Update a view's name, saved filter, and/or sorts. Only provided fields change (fields left empty are preserved). |
| notion_delete_viewA | Delete a view from its database. The database and its rows are untouched — only the saved view configuration is removed. |
| notion_list_usersA | List all users in the Notion workspace. Returns user names, IDs, emails, and types (person or bot). User IDs are needed for people properties and @mentions in comments. |
| notion_get_userA | Get details for a specific Notion user by ID. Returns the user's name, email, avatar URL, and type. Use notion_list_users to find user IDs first. user_id: Notion user UUID. |
| notion_read_page_markdownA | Read a Notion page's content as native Notion-flavored markdown. Uses the direct markdown API endpoint — faster than block-by-block fetching and returns Notion's own markdown representation including special tags for databases, files, and embeds. For block-level editing (update/delete individual blocks), use notion_read_page with include_block_ids=True instead. page_id: Notion page ID. |
| notion_update_page_contentA | Edit page content using search-and-replace on the page's markdown. Finds old_text in the page content and replaces it with new_text. Use notion_read_page_markdown first to see the current content and copy the exact text to replace. page_id: the page to edit. old_text: exact text to find in the page content. new_text: replacement text (use empty string to delete). |
| notion_replace_page_contentA | Replace a page's entire content with new markdown. Overwrites all existing content. Use notion_read_page_markdown first to see what's there. For targeted edits, use notion_update_page_content instead. page_id: the page to replace content on. markdown: new page content in markdown format. |
| notion_move_pageA | Move a Notion page to a new parent page or database. page_id: the page to move. new_parent_page_id: move under this page. new_parent_database_id: move into this database (as a new row). Only one of the two parent args should be set. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/snickery/notion-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server