Skip to main content
Glama

get_pages

Retrieve documents with rich text content, attachments, and project organization using optional filters for project, creator, or pagination.

Instructions

Get all pages/documents with optional filtering.

Pages in Productive are documents that can contain rich text content, attachments, and are organized within projects.

Returns: Dictionary containing pages with content, metadata, and relationships

Example: get_pages(project_id=1234) # Get all pages for a specific project

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_idNoOptional project ID to filter pages by
creator_idNoOptional creator ID to filter pages by
page_numberNoPage number for pagination
page_sizeNoOptional number of pages per page (max 200)

Implementation Reference

  • Core handler function for the 'get_pages' MCP tool. Constructs API params from inputs, calls client.get_pages(), applies filter_page_list_response for list view sanitization, handles errors with ctx logging.
    async def get_pages(
        ctx: Context,
        project_id: int = None,
        creator_id: int = None,
        page_number: int = None,
        page_size: int = config.items_per_page
    ) -> ToolResult:
        """List pages (docs) with optional filters and pagination.
    
        Developer notes:
        - Supports project_id and creator_id filters.
        - Enforces configurable default page[size] if not provided.
        - Sorts by most recent updates first.
        - Applies utils.filter_response to sanitize (body excluded via type='pages').
        - Uses consistent scalar filters: filter[project_id][eq], filter[creator_id][eq]
        """
        try:
            await ctx.info("Fetching pages")
            params = {}
            if page_number is not None:
                params["page[number]"] = page_number
            params["page[size]"] = page_size
            if project_id is not None:
                params["filter[project_id][eq]"] = project_id
            if creator_id is not None:
                params["filter[creator_id][eq]"] = creator_id
            params["sort"] = "-updated_at"
            result = await client.get_pages(params=params if params else None)
            await ctx.info("Successfully retrieved pages")
            
            # For lists, remove heavy fields like body explicitly
            filtered = filter_page_list_response(result)
            
            return filtered
            
        except ProductiveAPIError as e:
            await _handle_productive_api_error(ctx, e, "pages")
        except Exception as e:
            await ctx.error(f"Unexpected error fetching pages: {str(e)}")
            raise e
  • server.py:478-509 (registration)
    FastMCP tool registration for 'get_pages' including input schema via Annotated[Field] with descriptions and example usage. Delegates implementation to tools.get_pages.
    @mcp.tool
    async def get_pages(
        ctx: Context,
        project_id: Annotated[
            int, Field(description="Optional project ID to filter pages by")
        ] = None,
        creator_id: Annotated[
            int, Field(description="Optional creator ID to filter pages by")
        ] = None,
        page_number: Annotated[int, Field(description="Page number for pagination")] = None,
        page_size: Annotated[
            int, Field(description="Optional number of pages per page (max 200)")
        ] = None,
    ) -> Dict[str, Any]:
        """Get all pages/documents with optional filtering.
    
        Pages in Productive are documents that can contain rich text content,
        attachments, and are organized within projects.
    
        Returns:
            Dictionary containing pages with content, metadata, and relationships
    
        Example:
            get_pages(project_id=1234)  # Get all pages for a specific project
        """
        return await tools.get_pages(
            ctx,
            project_id=project_id,
            creator_id=creator_id,
            page_number=page_number,
            page_size=page_size,
        )
  • ProductiveClient HTTP API wrapper method called by the handler to fetch pages data from /pages endpoint with query params and full error/retry handling.
    async def get_pages(self, params: Optional[dict] = None) -> Dict[str, Any]:
        """Get all pages with optional filtering
        Supports filtering by project_id, creator_id, edited_at, id
        """
        return await self._request("GET", "/pages", params=params)
  • Supporting utility function applied in the handler to sanitize page list responses: removes heavy 'body' content, adds webapp_url, cleans nulls/empties, preserves essential metadata.
    def filter_page_list_response(response: Dict[str, Any]) -> Dict[str, Any]:
        """Filter page list responses to keep metadata and drop heavy body field.
    
        - Removes attributes.body from each page item
        - Keeps other attributes as-is after general cleaning
        - Preserves meta (cleaned) and adds webapp_url per item
        """
        if not isinstance(response, dict):
            return response
    
        filtered: Dict[str, Any] = {}
    
        # Process data array
        if "data" in response and isinstance(response["data"], list):
            filtered_data = []
            for item in response["data"]:
                if isinstance(item, dict) and item.get("type") == "pages":
                    new_item = {"id": item.get("id"), "type": item.get("type")}
                    attrs = item.get("attributes", {})
                    if isinstance(attrs, dict):
                        # Copy attributes without body
                        new_attrs = dict(attrs)
                        new_attrs.pop("body", None)
                        new_item["attributes"] = new_attrs
                    _add_webapp_url(new_item)
                    filtered_data.append(new_item)
                else:
                    filtered_data.append(item)
            filtered["data"] = filtered_data
    
        # Keep meta if present (has useful info like total_count)
        if "meta" in response:
            filtered["meta"] = _clean_meta_object(response["meta"])
    
        return remove_null_and_empty(filtered)

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/druellan/Productive-GET-MCP'

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