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)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

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)
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool returns a dictionary with content, metadata, and relationships, which is helpful. However, it doesn't disclose important behavioral traits like whether this is a read-only operation (implied by 'Get' but not explicit), pagination behavior (hinted at by parameters but not explained), rate limits, authentication requirements, or error handling. For a tool with 4 parameters and no annotation coverage, this leaves significant gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded: the first sentence clearly states the purpose, followed by context about pages, return format, and an example. Each sentence adds value without redundancy. However, the structure could be slightly improved by explicitly separating usage notes from the example, but overall it's efficient and well-organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 optional parameters, no annotations, but with an output schema), the description is moderately complete. It covers the purpose, basic context, return format, and provides an example. However, it lacks guidance on tool selection versus siblings, detailed behavioral traits, and parameter usage beyond the schema. The output schema existence reduces the need to explain return values, but other gaps remain, making it adequate but with clear room for improvement.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents all 4 parameters. The description adds minimal value beyond the schema: it mentions 'optional filtering' generally and provides an example using project_id, but doesn't explain parameter interactions, default behaviors, or the meaning of 'page_number' and 'page_size' in context. With high schema coverage, the baseline is 3, and the description doesn't significantly enhance parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get all pages/documents with optional filtering.' It specifies the verb ('Get'), resource ('pages/documents'), and scope ('all'), and provides context about what pages are in this system. However, it doesn't explicitly differentiate from sibling tools like 'get_page' (singular) or 'quick_search', which might offer alternative ways to retrieve page information.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides minimal usage guidance. It mentions 'optional filtering' and gives an example for filtering by project_id, but doesn't explain when to use this tool versus alternatives like 'get_page' (for a single page) or 'quick_search' (for broader searches). There's no explicit guidance on when this tool is preferred or when other tools might be more appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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