Skip to main content
Glama

API-get-one-pager

Retrieve complete Notion pages with all blocks, databases, and related content for comprehensive content extraction and analysis.

Instructions

Recursively retrieve a full Notion page with all its blocks, databases, and related content

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
page_idYesIdentifier for a Notion page
maxDepthNoMaximum recursion depth (default: 5)
includeDatabasesNoWhether to include linked databases (default: true)
includeCommentsNoWhether to include comments (default: true)
includePropertiesNoWhether to include detailed page properties (default: true)
maxParallelRequestsNoMaximum number of parallel requests (default: 15)
batchSizeNoBatch size for parallel processing (default: 10)
timeoutMsNoTimeout in milliseconds (default: 300000)
runInBackgroundNoProcess request in background without timeout (default: true)

Implementation Reference

  • Registration of the 'API-get-one-pager' tool in the ListToolsRequestSchema handler, defining its name, description, and input schema.
    const onePagerTool = {
      name: 'API-get-one-pager',
      description: 'Recursively retrieve a full Notion page with all its blocks, databases, and related content',
      inputSchema: {
        type: 'object',
        properties: {
          page_id: {
            type: 'string',
            description: 'Identifier for a Notion page',
          },
          maxDepth: {
            type: 'integer',
            description: 'Maximum recursion depth (default: 5)',
          },
          includeDatabases: {
            type: 'boolean',
            description: 'Whether to include linked databases (default: true)',
          },
          includeComments: {
            type: 'boolean',
            description: 'Whether to include comments (default: true)',
          },
          includeProperties: {
            type: 'boolean',
            description: 'Whether to include detailed page properties (default: true)',
          },
          maxParallelRequests: {
            type: 'integer',
            description: 'Maximum number of parallel requests (default: 15)',
          },
          batchSize: {
            type: 'integer',
            description: 'Batch size for parallel processing (default: 10)',
          },
          timeoutMs: {
            type: 'integer',
            description: 'Timeout in milliseconds (default: 300000)',
          },
          runInBackground: {
            type: 'boolean',
            description: 'Process request in background without timeout (default: true)',
          }
        },
        required: ['page_id'],
      } as Tool['inputSchema'],
    };
    
    tools.push(onePagerTool);
    console.log(`- ${onePagerTool.name}: ${onePagerTool.description}`);
  • Dispatch point in CallToolRequestSchema handler that routes calls to 'API-get-one-pager' to the handleOnePagerRequest method.
    if (name === 'API-get-one-pager') {
      return await this.handleOnePagerRequest(params);
    }
  • Primary handler function for the 'API-get-one-pager' tool. Parses parameters, handles background/foreground modes, and orchestrates recursive page retrieval or returns processing status.
    private async handleOnePagerRequest(params: any) {
      if (params.runInBackground !== false) {
        console.log('Starting One Pager request processing:', params.page_id);
      }
      
      const options: RecursiveExplorationOptions = {
        maxDepth: params.maxDepth || 5,
        includeDatabases: params.includeDatabases !== false,
        includeComments: params.includeComments !== false,
        includeProperties: params.includeProperties !== false,
        maxParallelRequests: params.maxParallelRequests || 15,
        skipCache: params.skipCache || false,
        batchSize: params.batchSize || 10,
        timeoutMs: params.timeoutMs || 300000, // Increased timeout to 5 minutes (300000ms)
        runInBackground: params.runInBackground !== false,
      };
      
      if (options.runInBackground) {
        console.log('Exploration options:', JSON.stringify(options, null, 2));
      }
      
      try {
        const startTime = Date.now();
        
        // Check if we should run in background mode
        if (options.runInBackground) {
          // Return immediately with a background processing message
          // The actual processing will continue in the background
          this.runBackgroundProcessing(params.page_id, options);
          
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  status: 'processing',
                  message: `Request processing for page ${params.page_id} started in background`,
                  page_id: params.page_id,
                  request_time: new Date().toISOString(),
                  options: {
                    maxDepth: options.maxDepth,
                    includeDatabases: options.includeDatabases,
                    includeComments: options.includeComments,
                    includeProperties: options.includeProperties,
                    timeoutMs: options.timeoutMs
                  }
                }),
              },
            ],
          };
        }
        
        // Foreground processing (standard behavior)
        const pageData = await this.retrievePageRecursively(params.page_id, options);
        
        const duration = Date.now() - startTime;
        if (options.runInBackground) {
          console.log(`One Pager completed in ${duration}ms for page ${params.page_id}`);
        }
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                ...pageData,
                _meta: {
                  processingTimeMs: duration,
                  retrievedAt: new Date().toISOString(),
                  options: {
                    maxDepth: options.maxDepth,
                    includeDatabases: options.includeDatabases,
                    includeComments: options.includeComments,
                    includeProperties: options.includeProperties
                  }
                }
              }),
            },
          ],
        };
      } catch (error) {
        if (options.runInBackground) {
          console.error('Error in One Pager request:', error);
        }
        const errorResponse = {
          status: 'error',
          message: error instanceof Error ? error.message : String(error),
          code: error instanceof HttpClientError ? error.status : 500,
          details: error instanceof HttpClientError ? error.data : undefined,
          timestamp: new Date().toISOString()
        };
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(errorResponse),
            },
          ],
        };
      }
    }
  • Core helper function that performs recursive retrieval of a Notion page, including blocks, databases, properties, and comments with caching, timeouts, and parallel processing.
    private async retrievePageRecursively(pageId: string, options: RecursiveExplorationOptions, currentDepth: number = 0): Promise<any> {
      if (options.runInBackground) {
        console.log(`Recursive page exploration: ${pageId}, depth: ${currentDepth}/${options.maxDepth || 5}`);
      }
      
      const timeoutPromise = new Promise<never>((_, reject) => {
        if (options.timeoutMs && options.timeoutMs > 0) {
          setTimeout(() => reject(new Error(`Operation timed out after ${options.timeoutMs}ms`)), options.timeoutMs);
        }
      });
      
      try {
        // Check maximum depth
        if (currentDepth >= (options.maxDepth || 5)) {
          if (options.runInBackground) {
            console.log(`Maximum depth reached: ${currentDepth}/${options.maxDepth || 5}`);
          }
          return { id: pageId, note: "Maximum recursion depth reached" };
        }
        
        // 1. Get basic page info (check cache)
        let pageData: any;
        if (!options.skipCache && this.pageCache.has(pageId)) {
          pageData = this.pageCache.get(pageId);
          if (options.runInBackground) {
            console.log(`Page cache hit: ${pageId}`);
          }
        } else {
          // Retrieve page info via API call
          const operation = this.findOperation('API-retrieve-a-page');
          if (!operation) {
            throw new Error('API-retrieve-a-page method not found.');
          }
          
          if (options.runInBackground) {
            console.log(`Notion API call: ${operation.method.toUpperCase()} ${operation.path} (pageId: ${pageId})`);
          }
          
          // Only race with timeout if timeoutMs is set
          let response;
          if (options.timeoutMs && options.timeoutMs > 0) {
            response = await Promise.race([
              this.httpClient.executeOperation(operation, { page_id: pageId }),
              timeoutPromise
            ]) as any;
          } else {
            response = await this.httpClient.executeOperation(operation, { page_id: pageId });
          }
          
          if (response.status !== 200) {
            if (options.runInBackground) {
              console.error('Error retrieving page information:', response.data);
            }
            return { 
              id: pageId,
              error: "Failed to retrieve page", 
              status: response.status,
              details: response.data 
            };
          }
          
          pageData = response.data;
          // Only cache successful responses
          this.pageCache.set(pageId, pageData);
        }
        
        // Collection of tasks to be executed in parallel for improved efficiency
        const parallelTasks: Promise<any>[] = [];
        
        // 2. Fetch block content (register async task)
        const blocksPromise = this.retrieveBlocksRecursively(pageId, options, currentDepth + 1);
        parallelTasks.push(blocksPromise);
        
        // 3. Fetch property details (if option enabled)
        let propertiesPromise: Promise<any> = Promise.resolve(null);
        if (options.includeProperties && pageData.properties) {
          propertiesPromise = this.enrichPageProperties(pageId, pageData.properties, options);
          parallelTasks.push(propertiesPromise);
        }
        
        // 4. Fetch comments (if option enabled)
        let commentsPromise: Promise<any> = Promise.resolve(null);
        if (options.includeComments) {
          commentsPromise = this.retrieveComments(pageId, options);
          parallelTasks.push(commentsPromise);
        }
        
        // Execute all tasks in parallel
        if (options.timeoutMs && options.timeoutMs > 0) {
          await Promise.race([Promise.all(parallelTasks), timeoutPromise]);
        } else {
          await Promise.all(parallelTasks);
        }
        
        // Integrate results into the main page data
        const enrichedPageData = { ...pageData };
        
        // Add block content
        const blocksData = await blocksPromise;
        enrichedPageData.content = blocksData;
        
        // Add property details (if option enabled)
        if (options.includeProperties && pageData.properties) {
          const enrichedProperties = await propertiesPromise;
          if (enrichedProperties) {
            enrichedPageData.detailed_properties = enrichedProperties;
          }
        }
        
        // Add comments (if option enabled)
        if (options.includeComments) {
          const comments = await commentsPromise;
          if (comments && comments.results && comments.results.length > 0) {
            enrichedPageData.comments = comments;
          }
        }
        
        return enrichedPageData;
      } catch (error) {
        if (error instanceof Error && error.message.includes('timed out')) {
          if (options.runInBackground) {
            console.error(`Timeout occurred while processing page ${pageId} at depth ${currentDepth}`);
          }
          return { 
            id: pageId, 
            error: "Operation timed out", 
            partial_results: true,
            note: `Processing exceeded timeout limit (${options.timeoutMs}ms)`
          };
        }
        
        if (options.runInBackground) {
          console.error(`Error in retrievePageRecursively for page ${pageId}:`, error);
        }
        return { 
          id: pageId, 
          error: error instanceof Error ? error.message : String(error),
          retrievalFailed: true
        };
      }
    }
  • Helper function for recursively retrieving and enriching block children, handling databases, child pages, with batched parallel processing.
    private async retrieveBlocksRecursively(blockId: string, options: RecursiveExplorationOptions, currentDepth: number): Promise<any[]> {
      if (options.runInBackground) {
        console.log(`Recursive block exploration: ${blockId}, depth: ${currentDepth}/${options.maxDepth || 5}`);
      }
      
      if (currentDepth >= (options.maxDepth || 5)) {
        if (options.runInBackground) {
          console.log(`Maximum depth reached: ${currentDepth}/${options.maxDepth || 5}`);
        }
        return [{ note: "Maximum recursion depth reached" }];
      }
      
      try {
        const operation = this.findOperation('API-get-block-children');
        if (!operation) {
          throw new Error('API-get-block-children method not found.');
        }
        
        const blocksResponse = await this.handleBlockChildrenParallel(operation, { 
          block_id: blockId,
          page_size: 100
        }, options);
        
        const blocksData = JSON.parse(blocksResponse.content[0].text);
        const blocks = blocksData.results || [];
        
        if (blocks.length === 0) {
          return [];
        }
        
        const batchSize = options.batchSize || 10;
        const enrichedBlocks: any[] = [];
        
        // Process blocks in batches for memory optimization and improved parallel execution
        for (let i = 0; i < blocks.length; i += batchSize) {
          const batch = blocks.slice(i, i + batchSize);
          
          // Process each batch in parallel
          const batchResults = await Promise.all(
            batch.map(async (block: any) => {
              this.blockCache.set(block.id, block);
              
              const enrichedBlock = { ...block };
              
              // Collection of async tasks for this block
              const blockTasks: Promise<any>[] = [];
              
              // Process child blocks recursively
              if (block.has_children) {
                blockTasks.push(
                  this.retrieveBlocksRecursively(block.id, options, currentDepth + 1)
                    .then(childBlocks => { enrichedBlock.children = childBlocks; })
                    .catch(error => {
                      console.error(`Error retrieving child blocks for ${block.id}:`, error);
                      enrichedBlock.children_error = { message: String(error) };
                      return [];
                    })
                );
              }
              
              // Process database blocks (if option enabled)
              if (options.includeDatabases && 
                  (block.type === 'child_database' || block.type === 'linked_database')) {
                const databaseId = block[block.type]?.database_id;
                if (databaseId) {
                  blockTasks.push(
                    this.retrieveDatabase(databaseId, options)
                      .then(database => { enrichedBlock.database = database; })
                      .catch(error => {
                        console.error(`Error retrieving database ${databaseId}:`, error);
                        enrichedBlock.database_error = { message: String(error) };
                      })
                  );
                }
              }
              
              // Process page blocks or linked pages - optimization
              if (block.type === 'child_page' && currentDepth < (options.maxDepth || 5) - 1) {
                const pageId = block.id;
                blockTasks.push(
                  this.retrievePageBasicInfo(pageId, options)
                    .then(pageInfo => { enrichedBlock.page_info = pageInfo; })
                    .catch(error => {
                      console.error(`Error retrieving page info for ${pageId}:`, error);
                      enrichedBlock.page_info_error = { message: String(error) };
                    })
                );
              }
              
              // Wait for all async tasks to complete
              if (blockTasks.length > 0) {
                await Promise.all(blockTasks);
              }
              
              return enrichedBlock;
            })
          );
          
          enrichedBlocks.push(...batchResults);
        }
        
        return enrichedBlocks;
      } catch (error) {
        console.error(`Error in retrieveBlocksRecursively for block ${blockId}:`, error);
        return [{ 
          id: blockId, 
          error: error instanceof Error ? error.message : String(error),
          retrievalFailed: true
        }];
      }
    }

Latest Blog Posts

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/Taewoong1378/notion-readonly-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server