66
77Tools provided
88--------------
9- * ``notion_search`` – search for reminders, notes, and stored information (e.g.,
9+ * ``notion_search`` – search for reminders, notes, and stored information (e.g.,
1010 "names to remember", "project ideas"). Wraps the `/v1/search` endpoint.
1111* ``notion_retrieve_page`` – retrieve detailed content from a reminder or note page,
12- fetching metadata and block content via `/v1/pages/{page_id}` and
12+ fetching metadata and block content via `/v1/pages/{page_id}` and
1313 `/v1/blocks/{page_id}/children`.
1414* ``notion_create_page`` – create new reminder notes or memory storage pages (e.g.,
1515 "Names to Remember", "Books to Read") using `/v1/pages`.
1616* ``notion_append_block_children`` – add new content to existing reminders or notes
17- (e.g., add a new name to your "names to remember" page) via
17+ (e.g., add a new name to your "names to remember" page) via
1818 `/v1/blocks/{block_id}/children`.
1919* ``notion_update_block`` – update or modify content in existing reminder blocks
2020 via `/v1/blocks/{block_id}`.
2121
2222Common use cases
2323----------------
24- * Remember names: Create a "Names to Remember" page, search it when needed, and
24+ * Remember names: Create a "Names to Remember" page, search it when needed, and
2525 add new names as you meet people.
26- * Store information: Create topic-specific notes (e.g., "Project Ideas",
26+ * Store information: Create topic-specific notes (e.g., "Project Ideas",
2727 "Books to Read") that you can search and update later.
28- * Manage reminders: Build lists and notes that help you remember important
28+ * Manage reminders: Build lists and notes that help you remember important
2929 information, tasks, or ideas.
3030
3131Required environment variables
@@ -91,7 +91,7 @@ def run(self) -> None: ...
9191
9292NOTION_BASE_URL = "https://api.notion.com/v1"
9393DEFAULT_NOTION_VERSION = "2022-06-28"
94- DEFAULT_SEARCH_BLOCK_LIMIT = 20
94+ DEFAULT_SEARCH_BLOCK_LIMIT = 100
9595
9696
9797@dataclass (slots = True )
@@ -399,7 +399,7 @@ def _format_result_heading(entry: Dict[str, Any]) -> str:
399399 object_type = entry .get ("object" , "unknown" ).title ()
400400 identifier = entry .get ("id" , "(unknown id)" )
401401 title = _extract_title (entry ) or "(untitled)"
402- return f"{ object_type } • { title } • ID: { identifier } "
402+ return f"{ object_type } • { title } • PAGE ID: { identifier } "
403403
404404
405405def _format_search_results (
@@ -426,12 +426,13 @@ def _format_search_results(
426426
427427def _format_page_summary (page : Dict [str , Any ]) -> str :
428428 title = _extract_title (page ) or "(untitled)"
429+ page_id = page .get ("id" , "(unknown id)" )
429430 url = page .get ("url" )
430431 last_edited = page .get ("last_edited_time" )
431432 created_time = page .get ("created_time" )
432433 properties = page .get ("properties" , {})
433434
434- lines = [f"Title: { title } " ]
435+ lines = [f"Title: { title } " , f"PAGE ID: { page_id } " ]
435436 if url :
436437 lines .append (f"URL: { url } " )
437438 if created_time :
@@ -531,38 +532,38 @@ async def _collect_search_details(
531532
532533def _generate_search_variations (query : Optional [str ]) -> List [str ]:
533534 """Generate search query variations to improve match likelihood.
534-
535+
535536 This function creates multiple search strategies:
536537 1. The original query as-is
537538 2. Individual significant words (4+ characters) from the query
538539 3. Common phrase patterns
539-
540+
540541 This helps overcome Notion API's exact-match limitations.
541542 """
542543 if not query :
543544 return []
544-
545+
545546 variations = [query .strip ()]
546-
547+
547548 # Split query into words and add significant terms individually
548549 words = query .strip ().lower ().split ()
549-
550+
550551 # Filter for words that are 4+ characters (skip "to", "at", "the", "and", etc.)
551552 significant_words = [w for w in words if len (w ) >= 4 ]
552-
553+
553554 # Add individual significant words as search variations
554555 for word in significant_words :
555556 if word not in variations :
556557 variations .append (word )
557-
558+
558559 # Add common multi-word combinations if query has multiple words
559560 if len (significant_words ) >= 2 :
560561 # Try pairs of adjacent words
561562 for i in range (len (significant_words ) - 1 ):
562563 pair = f"{ significant_words [i ]} { significant_words [i + 1 ]} "
563564 if pair not in variations :
564565 variations .append (pair )
565-
566+
566567 return variations
567568
568569
@@ -576,10 +577,10 @@ async def _perform_enhanced_search(
576577 max_variations : int = 3 ,
577578) -> tuple [List [Dict [str , Any ]], Optional [str ]]:
578579 """Perform enhanced search with multiple query variations.
579-
580+
580581 Tries the original query first, and if results are insufficient,
581582 attempts searches with query variations to find more relevant matches.
582-
583+
583584 Returns deduplicated results and the next cursor from the best search.
584585 """
585586 # Try the original query first
@@ -593,14 +594,14 @@ async def _perform_enhanced_search(
593594 response = await _request ("POST" , "/search" , json = payload )
594595 results = response .get ("results" ) or []
595596 next_cursor = response .get ("next_cursor" )
596-
597+
597598 # If we got good results or there's no query, return immediately
598599 if len (results ) >= 3 or not query or start_cursor :
599600 return results , next_cursor
600-
601+
601602 # Try search variations to find more matches
602603 variations = _generate_search_variations (query )
603-
604+
604605 # Skip the first variation (original query) since we already tried it
605606 # Limit the number of additional API calls
606607 for variation in variations [1 :max_variations ]:
@@ -613,26 +614,45 @@ async def _perform_enhanced_search(
613614 try :
614615 variation_response = await _request ("POST" , "/search" , json = variation_payload )
615616 variation_results = variation_response .get ("results" ) or []
616-
617+
617618 # Deduplicate results by ID
618619 existing_ids = {r .get ("id" ) for r in results }
619620 for result in variation_results :
620621 result_id = result .get ("id" )
621622 if result_id and result_id not in existing_ids :
622623 results .append (result )
623624 existing_ids .add (result_id )
624-
625+
625626 # If we now have enough results, stop searching
626627 if len (results ) >= 5 :
627628 break
628629 except NotionAPIError :
629630 # If a variation search fails, continue with others
630631 continue
631-
632+
632633 return results , next_cursor
633634
634635
635- @mcp .tool ("notion_search" )
636+ @mcp .tool (
637+ "notion_search" ,
638+ description = (
639+ "Search Notion for reminders, notes, and information you want to remember. "
640+ "Returns matching pages with their PAGE IDs (look for 'ID: ...' in results). "
641+ "Use this to find pages like 'Names to Remember', 'Project Ideas', or any stored information. "
642+ "When searching for specific details (like someone's name), this will return relevant pages WITH their content. "
643+ "If you see 'Additional blocks available' in the results, the content was truncated - "
644+ "immediately call notion_retrieve_page using the PAGE ID (not block IDs) with include_children=true to get ALL content. "
645+ "\n \n "
646+ "CRITICAL SEARCH STRATEGY for finding specific information: "
647+ "When user asks about a specific person/thing (e.g., 'who is the old lady at the park'), "
648+ "DO NOT search for that exact phrase. Instead: "
649+ "1. Search for the relevant page by title (e.g., 'Names' or 'Names to Remember'). "
650+ "2. Read through ALL the returned content to find matching entries. "
651+ "3. If no results or truncated, use notion_retrieve_page to get COMPLETE content. "
652+ "Notion search is literal - 'old lady at the park' won't match 'old lady park' in the content. "
653+ "Always retrieve the full page and search through it yourself for specific details."
654+ )
655+ )
636656async def notion_search (
637657 query : Optional [str ] = None ,
638658 * ,
@@ -650,21 +670,17 @@ async def notion_search(
650670 - Finding notes about specific topics or subjects
651671 - Retrieving stored reminders and memory aids
652672 - Looking up information you've saved for later recall
653-
654- This search now uses an enhanced multi-strategy approach that tries query variations
655- to overcome Notion API's exact-match limitations, making it better at finding
656- relevant pages even with partial or fuzzy queries.
657-
673+
658674 Examples:
659- - Search "names to remember" to find a note containing names
675+ - Search "names to remember" or "Names" to find a note containing names
660676 - Search "project ideas" to retrieve saved project notes
661677 - Search "books to read" to find your reading list
662-
678+
663679 Authentication requires ``NOTION_TOKEN`` (preferred) or ``NOTION_API_KEY`` to
664680 be present in the environment. Optional ``NOTION_VERSION`` mirrors the
665681 upstream configuration and defaults to ``2022-06-28``. Set
666682 ``include_content=False`` to return metadata only. ``content_block_limit``
667- controls how many child blocks are retrieved per page (defaults to 20 ).
683+ controls how many child blocks are retrieved per page (defaults to 100 ).
668684 """
669685
670686 # Use enhanced search that tries multiple query variations
@@ -675,7 +691,7 @@ async def notion_search(
675691 start_cursor = start_cursor ,
676692 page_size = page_size ,
677693 )
678-
694+
679695 block_limit = max (1 , content_block_limit or DEFAULT_SEARCH_BLOCK_LIMIT )
680696 details = (
681697 await _collect_search_details (results , block_limit = block_limit )
@@ -685,25 +701,37 @@ async def notion_search(
685701 return _format_search_results (results , details , next_cursor )
686702
687703
688- @mcp .tool ("notion_retrieve_page" )
704+ @mcp .tool (
705+ "notion_retrieve_page" ,
706+ description = (
707+ "Retrieve the COMPLETE content of a specific Notion page/note by its PAGE ID. "
708+ "IMPORTANT: Use the PAGE ID from search results (e.g., 'ID: 29896b0b-3790-8118-...'), NOT block IDs. "
709+ "Use this when notion_search returns truncated content and you need ALL blocks from the page. "
710+ "Perfect for reading entire 'Names to Remember' lists or any page where you need to search through ALL entries. "
711+ "Always use include_children=true when you need to find specific information within a page."
712+ )
713+ )
689714async def notion_retrieve_page (
690715 page_id : str ,
691716 * ,
692717 filter_properties : Optional [List [str ]] = None ,
693- include_children : bool = False ,
718+ include_children : bool = True ,
694719 start_cursor : Optional [str ] = None ,
695720 page_size : Optional [int ] = None ,
696721) -> str :
697722 """Retrieve detailed content from a reminder or note page in Notion.
698723
724+ IMPORTANT: page_id must be the PAGE ID from search results (e.g., '29896b0b-3790-8118-b115-e843978e56ba'),
725+ NOT a block ID (which appears in parentheses after block types like 'Paragraph (block-id)').
726+
699727 Use this tool to read the full content of a specific reminder, note, or stored information.
700728 Perfect for accessing complete details after finding a page via search.
701-
729+
702730 Common use cases:
703731 - Read all names from a "names to remember" note
704732 - Review detailed information from a reminder page
705733 - Check the full content of a note you've found
706-
734+
707735 Set ``include_children=True`` to fetch the complete page content with all blocks.
708736 Use ``start_cursor`` and ``page_size`` to paginate through long documents.
709737
@@ -735,7 +763,14 @@ async def notion_retrieve_page(
735763 return f"{ summary } \n \n { blocks_output } "
736764
737765
738- @mcp .tool ("notion_create_page" )
766+ @mcp .tool (
767+ "notion_create_page" ,
768+ description = (
769+ "Create a new reminder note or memory storage page in Notion. "
770+ "Use this to create pages like 'Names to Remember', 'Books to Read', 'Project Ideas', etc. "
771+ "You can set a title and optionally add initial content blocks."
772+ )
773+ )
739774async def notion_create_page (
740775 data : NotionCreatePageInput ,
741776) -> str :
@@ -747,14 +782,14 @@ async def notion_create_page(
747782 - Saving reminders about tasks or things to do
748783 - Creating notes about topics you want to remember
749784 - Storing information for future reference
750-
785+
751786 Examples:
752787 - Create a page titled "Names to Remember" with initial names
753788 - Create a "Project Ideas" page to store your ideas
754789 - Create reminder notes with titles like "Things to Buy" or "Books to Read"
755-
756- If ``parent_id`` is omitted the server will fall back to ``NOTION_DATABASE_ID``
757- or ``NOTION_PAGE_ID``. Provide ``title`` for simple notes or supply ``properties``
790+
791+ If ``parent_id`` is omitted the server will fall back to ``NOTION_DATABASE_ID``
792+ or ``NOTION_PAGE_ID``. Provide ``title`` for simple notes or supply ``properties``
758793 that match your database schema when creating structured entries.
759794
760795 Authentication requires ``NOTION_TOKEN`` (preferred) or ``NOTION_API_KEY``.
@@ -785,15 +820,22 @@ def _build_children_payload(data: NotionAppendChildrenInput) -> Dict[str, Any]:
785820 return {"children" : children }
786821
787822
788- @mcp .tool ("notion_append_block_children" )
823+ @mcp .tool (
824+ "notion_append_block_children" ,
825+ description = (
826+ "Add new content to an existing Notion page/note. "
827+ "Use this to append new entries to lists like adding a new name to 'Names to Remember', "
828+ "a new book to 'Books to Read', or any new reminder to an existing page."
829+ )
830+ )
789831async def notion_append_block_children (data : NotionAppendChildrenInput ) -> str :
790832 """Add new content to an existing reminder or note page in Notion.
791833
792834 Use this tool to add more information to existing notes or reminders. Perfect for:
793835 - Adding a new name to your "names to remember" note
794836 - Appending new items to an existing reminder list
795837 - Adding additional information to a note you've already created
796-
838+
797839 Examples:
798840 - Add "John Smith - met at conference" to your names note
799841 - Append new book titles to your reading list
@@ -835,7 +877,13 @@ def _build_block_update_payload(data: NotionUpdateBlockInput) -> Dict[str, Any]:
835877 return payload
836878
837879
838- @mcp .tool ("notion_update_block" )
880+ @mcp .tool (
881+ "notion_update_block" ,
882+ description = (
883+ "Update or modify existing content in a Notion reminder or note. "
884+ "Use this to correct information, update details, or archive old reminders."
885+ )
886+ )
839887async def notion_update_block (data : NotionUpdateBlockInput ) -> str :
840888 """Update or modify content in an existing reminder or note block.
841889
@@ -844,7 +892,7 @@ async def notion_update_block(data: NotionUpdateBlockInput) -> str:
844892 - Updating a name with additional context or corrections
845893 - Modifying reminder text to reflect changes
846894 - Correcting or enhancing stored information
847-
895+
848896 Examples:
849897 - Update "John" to "John Smith - CEO at Tech Corp"
850898 - Change a reminder note with updated details
0 commit comments