Skip to main content
Glama
jaipandya

product-hunt-mcp

by jaipandya

get_posts

Retrieve Product Hunt posts with filtering options like featured status, topics, date ranges, and sorting by ranking, votes, or recency.

Instructions

    Retrieve a list of Product Hunt posts with various filtering and sorting options.

    Parameters:
    - featured (bool, optional): Only return featured posts if True.
    - topic (str, optional): Filter by topic slug.
    - order (str, optional): Sorting order. Valid values: RANKING (default), NEWEST, VOTES, FEATURED_AT.
    - count (int, optional): Number of posts to return (default: 10, max: 20).
    - after (str, optional): Pagination cursor for next page.
    - url (str, optional): Filter posts by URL.
    - twitter_url (str, optional): Filter posts by Twitter URL.
    - posted_before (str, optional): ISO datetime to filter posts posted before this date.
    - posted_after (str, optional): ISO datetime to filter posts posted after this date.

    Returns:
    - success (bool)
    - data (dict): If successful, contains:
        - posts (list): List of post objects (id, name, description, etc.)
        - pagination (dict): { end_cursor, has_next_page }
    - error (dict, optional)
    - rate_limits (dict)

    Notes:
    - This is not a keyword search; use filters to narrow results.
    - If no posts match, `posts` will be an empty list.
    - Invalid date formats return a user-friendly error.
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
featuredNo
topicNo
orderNoRANKING
countNo
afterNo
urlNo
twitter_urlNo
posted_beforeNo
posted_afterNo

Implementation Reference

  • The main execution logic for the 'get_posts' tool. Fetches a list of Product Hunt posts using GraphQL query with applied filters, pagination, and validation.
    def get_posts(
        featured: bool = None,
        topic: str = None,
        order: str = "RANKING",
        count: int = 10,
        after: str = None,
        url: str = None,
        twitter_url: str = None,
        posted_before: str = None,
        posted_after: str = None,
    ) -> Dict[str, Any]:
        """
        Retrieve a list of Product Hunt posts with various filtering and sorting options.
    
        Parameters:
        - featured (bool, optional): Only return featured posts if True.
        - topic (str, optional): Filter by topic slug.
        - order (str, optional): Sorting order. Valid values: RANKING (default), NEWEST, VOTES, FEATURED_AT.
        - count (int, optional): Number of posts to return (default: 10, max: 20).
        - after (str, optional): Pagination cursor for next page.
        - url (str, optional): Filter posts by URL.
        - twitter_url (str, optional): Filter posts by Twitter URL.
        - posted_before (str, optional): ISO datetime to filter posts posted before this date.
        - posted_after (str, optional): ISO datetime to filter posts posted after this date.
    
        Returns:
        - success (bool)
        - data (dict): If successful, contains:
            - posts (list): List of post objects (id, name, description, etc.)
            - pagination (dict): { end_cursor, has_next_page }
        - error (dict, optional)
        - rate_limits (dict)
    
        Notes:
        - This is not a keyword search; use filters to narrow results.
        - If no posts match, `posts` will be an empty list.
        - Invalid date formats return a user-friendly error.
        """
        params = {
            k: v
            for k, v in {
                "featured": featured,
                "topic": topic,
                "order": order,
                "count": count,
                "after": after,
                "url": url,
                "twitter_url": twitter_url,
                "posted_before": posted_before,
                "posted_after": posted_after,
            }.items()
            if v is not None
        }
        logger.info("posts.get_posts called", extra=params)
    
        # Apply pagination defaults
        variables = apply_pagination_defaults(count, after)
    
        # Add order parameter
        variables["order"] = order
    
        # Add optional filters
        if featured is not None:
            variables["featured"] = featured
        if topic:
            variables["topic"] = topic
        if url:
            variables["url"] = url
        if twitter_url:
            variables["twitterUrl"] = twitter_url
        if posted_before:
            variables["postedBefore"] = posted_before
        if posted_after:
            variables["postedAfter"] = posted_after
    
        result, rate_limits, error = execute_graphql_query(POSTS_QUERY, variables)
    
        if error:
            # If there's a GraphQL error related to date format, provide a more user-friendly message
            if (
                "code" in error
                and error["code"] == "GRAPHQL_ERROR"
                and any(
                    "postedBefore" in str(e) or "postedAfter" in str(e)
                    for e in error.get("details", [])
                )
            ):
                return format_response(
                    False,
                    error={
                        "code": "INVALID_DATE_FORMAT",
                        "message": "The provided date format is invalid. Please use ISO 8601 format (e.g., 2023-01-01T00:00:00Z)",
                    },
                    rate_limits=rate_limits,
                )
            return format_response(False, error=error, rate_limits=rate_limits)
    
        # Extract posts
        posts_data = result["data"]["posts"]
    
        return format_response(
            True,
            data={
                "posts": posts_data["edges"],
                "pagination": extract_pagination(posts_data["pageInfo"]),
            },
            rate_limits=rate_limits,
        )
  • POSTS_SCHEMA defines the input parameter validation rules used by the get_posts tool, including types, constraints, and valid values.
    POSTS_SCHEMA = {
        "featured": {"type": bool},
        "topic": {"type": str},
        "order": {"type": str, "valid_values": ["RANKING", "NEWEST", "VOTES", "FEATURED_AT"]},
        "count": {"type": int, "min_value": 1, "max_value": 20},
        "after": {"type": str},
        "url": {"type": str},
        "twitter_url": {"type": str},
        "posted_before": {"type": str, "is_iso8601": True},
        "posted_after": {"type": str, "is_iso8601": True},
    }
  • The @mcp.tool() decorator along with other decorators registers the get_posts function as an MCP tool within the register_post_tools function.
    @require_token
    @handle_errors
    @validate_with_schema(POSTS_SCHEMA)
    def get_posts(
  • Invocation of register_post_tools(mcp) in the main CLI entry point, which triggers the registration of post-related tools including get_posts.
    register_post_tools(mcp)
Behavior4/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. It effectively describes several behavioral traits: the tool returns paginated results with 'after' cursor and 'has_next_page', handles empty results gracefully ('posts will be an empty list'), includes rate limit information in returns, and provides user-friendly error handling for invalid date formats. This covers important operational aspects beyond basic functionality.

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 well-structured with clear sections (purpose, parameters, returns, notes) and front-loaded with the core functionality. While comprehensive, some sentences could be more concise (e.g., the parameters section is detailed but necessary given schema coverage). The information density is high with minimal waste.

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

Completeness5/5

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

Given the tool's complexity (9 parameters, no annotations, no output schema), the description provides complete context. It covers purpose, all parameters with semantics, return structure including success/error handling, pagination behavior, edge cases (empty results), and operational notes. This is comprehensive for a list-retrieval tool with filtering capabilities.

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

Parameters5/5

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

With 0% schema description coverage and 9 parameters, the description provides comprehensive parameter documentation that fully compensates for the schema gap. It explains each parameter's purpose, data types, optional status, default values, valid values for 'order', and constraints like 'max: 20' for count. This adds significant meaning beyond what the bare schema provides.

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: 'Retrieve a list of Product Hunt posts with various filtering and sorting options.' This specifies the verb ('retrieve'), resource ('Product Hunt posts'), and scope ('list with filtering/sorting'). However, it doesn't explicitly differentiate from sibling tools like 'get_post_details' or 'search_topics' beyond mentioning 'not a keyword search' in notes.

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

Usage Guidelines3/5

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

The description provides some usage context through the 'Notes' section, stating 'This is not a keyword search; use filters to narrow results.' This implies when to use this tool (for filtered listing) versus alternatives like search tools. However, it doesn't explicitly name sibling alternatives or provide clear when-not-to-use guidance beyond the keyword search note.

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/jaipandya/producthunt-mcp-server'

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