Skip to main content
Glama
Arindam200

Reddit MCP Server

create_post

Create and publish new posts to Reddit subreddits with custom titles, content, flairs, and post types.

Instructions

Create a new post in a subreddit.

Args:
    subreddit: Name of the subreddit to post in (with or without 'r/' prefix)
    title: Title of the post (max 300 characters)
    content: Content of the post (text for self posts, URL for link posts)
    flair: Flair to add to the post. Must be an available flair in the subreddit
    is_self: Whether this is a self (text) post (True) or link post (False)

Returns:
    Dictionary containing information about the created post

Raises:
    ValueError: If input validation fails or flair is invalid
    RuntimeError: For other errors during post creation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
subredditYes
titleYes
contentYes
flairNo
is_selfNo

Implementation Reference

  • The core handler function for the 'create_post' MCP tool. It validates inputs, checks subreddit and flair availability, creates self or link posts using PRAW's subreddit.submit(), formats the result using _format_post, and handles various errors like rate limits and subreddit access issues.
    @mcp.tool()
    @require_write_access
    def create_post(
        subreddit: str,
        title: str,
        content: str,
        flair: Optional[str] = None,
        is_self: bool = True,
    ) -> Dict[str, Any]:
        """Create a new post in a subreddit.
    
        Args:
            subreddit: Name of the subreddit to post in (with or without 'r/' prefix)
            title: Title of the post (max 300 characters)
            content: Content of the post (text for self posts, URL for link posts)
            flair: Flair to add to the post. Must be an available flair in the subreddit
            is_self: Whether this is a self (text) post (True) or link post (False)
    
        Returns:
            Dictionary containing information about the created post
    
        Raises:
            ValueError: If input validation fails or flair is invalid
            RuntimeError: For other errors during post creation
        """
        manager = RedditClientManager()
        if not manager.client:
            raise RuntimeError("Reddit client not initialized")
    
        # Input validation
        if not subreddit or not isinstance(subreddit, str):
            raise ValueError("Subreddit name is required")
        if not title or not isinstance(title, str):
            raise ValueError("Post title is required")
        if len(title) > 300:
            raise ValueError("Title must be 300 characters or less")
        if not content or not isinstance(content, str):
            raise ValueError("Post content/URL is required")
    
        # Clean up subreddit name (remove r/ prefix if present)
        clean_subreddit = subreddit[2:] if subreddit.startswith("r/") else subreddit
    
        try:
            logger.info(f"Creating post in r/{clean_subreddit}")
            subreddit_obj = manager.client.subreddit(clean_subreddit)
    
            # Verify subreddit exists and is postable
            _ = subreddit_obj.display_name
    
            # Check if flair is valid if provided
            if flair:
                try:
                    available_flairs = [
                        f["text"] for f in subreddit_obj.flair.link_templates
                    ]
                    if flair not in available_flairs:
                        raise ValueError(
                            f"Invalid flair. Available flairs: {', '.join(available_flairs)}"
                        )
                except Exception as flair_error:
                    logger.warning(f"Error checking flairs: {flair_error}")
                    raise ValueError(
                        "Failed to verify flair. The subreddit may not allow link flairs."
                    ) from flair_error
    
            # Create the post
            try:
                if is_self:
                    submission = subreddit_obj.submit(
                        title=title[:300],  # Ensure title is within limit
                        selftext=content,
                        flair_id=flair,
                        send_replies=True,
                    )
                else:
                    # Validate URL for link posts
                    if not content.startswith(("http://", "https://")):
                        content = f"https://{content}"
                    submission = subreddit_obj.submit(
                        title=title[:300],  # Ensure title is within limit
                        url=content,
                        flair_id=flair,
                        send_replies=True,
                    )
    
                logger.info(f"Post created successfully: {submission.permalink}")
    
                return {
                    "post": _format_post(submission),
                    "metadata": {
                        "created_at": _format_timestamp(time.time()),
                        "subreddit": clean_subreddit,
                        "is_self_post": is_self,
                        "permalink": f"https://reddit.com{submission.permalink}",
                        "id": submission.id,
                    },
                }
    
            except Exception as post_error:
                logger.error(f"Failed to create post in r/{clean_subreddit}: {post_error}")
                if "RATELIMIT" in str(post_error).upper():
                    raise RuntimeError(
                        "You're doing that too much. Please wait before posting again."
                    ) from post_error
                if "TOO_OLD" in str(post_error):
                    raise RuntimeError(
                        "This subreddit only allows posts from accounts with a minimum age or karma."
                    ) from post_error
                if "SUBREDDIT_NOEXIST" in str(post_error):
                    raise ValueError(f"r/{clean_subreddit} does not exist") from post_error
                raise RuntimeError(f"Failed to create post: {post_error}") from post_error
    
        except Exception as e:
            logger.error(f"Error in create_post for r/{clean_subreddit}: {e}")
            if "private" in str(e).lower():
                raise ValueError(
                    f"r/{clean_subreddit} is private or cannot be accessed"
                ) from e
            if "banned" in str(e).lower():
                raise ValueError(
                    f"r/{clean_subreddit} has been banned or doesn't exist"
                ) from e
            if "not found" in str(e).lower():
                raise ValueError(f"r/{clean_subreddit} not found") from e
            if isinstance(e, (ValueError, RuntimeError)):
                raise
            raise RuntimeError(f"Failed to create post: {e}") from e
  • server.py:1150-1150 (registration)
    The @mcp.tool() decorator registers the create_post function as an MCP tool.
    @mcp.tool()
  • Decorator applied to create_post that enforces write permissions by checking RedditClientManager for non-read-only mode and user authentication.
    def require_write_access(func: F) -> F:
        """Decorator to ensure write access is available."""
    
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            reddit_manager = RedditClientManager()
            if reddit_manager.is_read_only:
                raise ValueError(
                    "Write operation not allowed in read-only mode. Please provide valid credentials."
                )
            if not reddit_manager.check_user_auth():
                raise Exception(
                    "Authentication required for write operations. "
                    "Please provide valid REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
                )
            return func(*args, **kwargs)
    
        return cast(F, wrapper)
  • Helper function used by create_post to format the response post information with stats, metadata, links, and engagement analysis.
    def _format_post(post: praw.models.Submission) -> str:
        """Format post information with AI-driven insights."""
        content_type = "Text Post" if post.is_self else "Link Post"
        content = post.selftext if post.is_self else post.url
    
        flags = []
        if post.over_18:
            flags.append("NSFW")
        if hasattr(post, "spoiler") and post.spoiler:
            flags.append("Spoiler")
        if post.edited:
            flags.append("Edited")
    
        # Add image URL section for non-self posts
        image_url_section = (
            f"""
            • Image URL: {post.url}"""
            if not post.is_self
            else ""
        )
    
        return f"""
            • Title: {post.title}
            • Type: {content_type}
            • Content: {content}
            • Author: u/{str(post.author)}
            • Subreddit: r/{str(post.subreddit)}{image_url_section}
            • Stats:
            - Score: {post.score:,}
            - Upvote Ratio: {post.upvote_ratio * 100:.1f}%
            - Comments: {post.num_comments:,}
            • Metadata:
            - Posted: {_format_timestamp(post.created_utc)}
            - Flags: {", ".join(flags) if flags else "None"}
            - Flair: {post.link_flair_text or "None"}
            • Links:
            - Full Post: https://reddit.com{post.permalink}
            - Short Link: https://redd.it/{post.id}
    
            📈 Engagement Analysis:
            - {_analyze_post_engagement(post.score, post.upvote_ratio, post.num_comments)}
    
            🎯 Best Time to Engage:
            - {_get_best_engagement_time(post.created_utc, post.score)}
            """
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It does well by mentioning input validation failures and runtime errors, and clarifies the content parameter's dual purpose (text for self posts, URL for link posts). However, it doesn't address important behavioral aspects like authentication requirements, rate limits, whether the operation is idempotent, or what specific information the return dictionary contains.

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 (Args, Returns, Raises) and front-loads the core purpose. Every sentence adds value, though the formatting with section headers could be slightly more concise. The information density is high with minimal wasted words.

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

Completeness4/5

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

For a 5-parameter mutation tool with no annotations and no output schema, the description does an excellent job covering parameter semantics and basic error conditions. It falls short of a perfect score because it doesn't fully describe the return value (only states it's a 'dictionary containing information about the created post' without specifying what information) or address authentication/rate limiting concerns that are important for API tools.

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?

Given 0% schema description coverage, the description fully compensates by providing detailed semantic information for all 5 parameters. It explains what each parameter represents, includes constraints (max 300 characters for title), clarifies format options (with/without 'r/' prefix for subreddit), explains the dual nature of the content parameter, and specifies requirements (flair must be available in the subreddit).

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

Purpose5/5

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

The description clearly states the specific action ('Create a new post') and target resource ('in a subreddit'), distinguishing it from sibling tools that are primarily read operations (like get_submission_by_id, get_top_posts) or other actions (like reply_to_post, join_subreddit). The verb+resource combination is precise and unambiguous.

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

Usage Guidelines4/5

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

The description provides clear context about when to use this tool (to create posts in subreddits) and implicitly distinguishes it from sibling tools that retrieve or interact with existing content. However, it doesn't explicitly state when NOT to use it or name specific alternatives for similar operations, keeping it from a perfect score.

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/Arindam200/reddit-mcp'

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