Skip to main content
Glama
Arindam200

Reddit MCP Server

reply_to_post

Post a reply to an existing Reddit post by providing the post ID and content, enabling interaction with Reddit discussions.

Instructions

Post a reply to an existing Reddit post.

Args:
    post_id: The ID of the post to reply to (can be full URL, permalink, or just ID)
    content: The content of the reply (1-10000 characters)
    subreddit: The subreddit name if known (for validation, with or without 'r/' prefix)

Returns:
    Dictionary containing information about the created reply and parent post

Raises:
    ValueError: If input validation fails or post is not found
    RuntimeError: For other errors during reply creation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
post_idYes
contentYes
subredditNo

Implementation Reference

  • The core handler function for the 'reply_to_post' MCP tool. It handles posting replies to Reddit posts, including validation, fetching the target submission, checking if the post allows replies, creating the reply via PRAW, and formatting the response with parent post details.
    @require_write_access
    def reply_to_post(
        post_id: str, content: str, subreddit: Optional[str] = None
    ) -> Dict[str, Any]:
        """Post a reply to an existing Reddit post.
    
        Args:
            post_id: The ID of the post to reply to (can be full URL, permalink, or just ID)
            content: The content of the reply (1-10000 characters)
            subreddit: The subreddit name if known (for validation, with or without 'r/' prefix)
    
        Returns:
            Dictionary containing information about the created reply and parent post
    
        Raises:
            ValueError: If input validation fails or post is not found
            RuntimeError: For other errors during reply creation
        """
        manager = RedditClientManager()
        if not manager.client:
            raise RuntimeError("Reddit client not initialized")
    
        # Input validation
        if not post_id or not isinstance(post_id, str):
            raise ValueError("Post ID is required")
        if not content or not isinstance(content, str):
            raise ValueError("Reply content is required")
        if len(content) < 1 or len(content) > 10000:
            raise ValueError("Reply must be between 1 and 10000 characters")
    
        # Clean up subreddit name if provided
        clean_subreddit = None
        if subreddit:
            if not isinstance(subreddit, str):
                raise ValueError("Subreddit name must be a string")
            clean_subreddit = subreddit[2:] if subreddit.startswith("r/") else subreddit
    
        try:
            # Clean up the post_id if it's a full URL or permalink
            clean_post_id = _extract_reddit_id(post_id)
            logger.info(f"Creating reply to post ID: {clean_post_id}")
    
            # Get the submission object
            submission = manager.client.submission(id=clean_post_id)
    
            # Verify the post exists by accessing its attributes
            try:
                post_title = submission.title
                post_author = getattr(submission, "author", None)
                post_subreddit = submission.subreddit
    
                logger.info(
                    f"Replying to post: "
                    f"Title: {post_title}, "
                    f"Author: {post_author}, "
                    f"Subreddit: r/{post_subreddit.display_name}"
                )
    
            except Exception as e:
                logger.exception(f"Failed to access post {clean_post_id}: {e}")
                raise ValueError(f"Post {clean_post_id} not found or inaccessible") from e
    
            # If subreddit was provided, verify we're in the right place
            if (
                clean_subreddit
                and post_subreddit.display_name.lower() != clean_subreddit.lower()
            ):
                raise ValueError(
                    f"Post ID {clean_post_id} belongs to r/{post_subreddit.display_name}, "
                    f"not r/{clean_subreddit}"
                )
    
            # Check if the post is archived or locked
            if getattr(submission, "archived", False):
                raise ValueError("Cannot reply to an archived post")
            if getattr(submission, "locked", False):
                raise ValueError("Cannot reply to a locked post")
    
            # Create the reply
            logger.info(f"Posting reply with content length: {len(content)} characters")
            try:
                reply = submission.reply(body=content)
                logger.info(f"Reply created successfully: {reply.id}")
    
                return {
                    "reply": _format_comment(reply),
                    "parent_post": _format_post(submission),
                    "metadata": {
                        "created_at": _format_timestamp(time.time()),
                        "reply_id": reply.id,
                        "parent_id": clean_post_id,
                        "subreddit": post_subreddit.display_name,
                    },
                }
    
            except Exception as reply_error:
                logger.exception(f"Failed to create reply: {reply_error}")
                if "RATELIMIT" in str(reply_error).upper():
                    raise RuntimeError(
                        "You're doing that too much. Please wait before replying again."
                    ) from reply_error
                if "TOO_OLD" in str(reply_error):
                    raise RuntimeError(
                        "This thread is archived and cannot be replied to"
                    ) from reply_error
                raise RuntimeError(f"Failed to post reply: {reply_error}") from reply_error
    
        except Exception as e:
            logger.exception(f"Error in reply_to_post for ID {post_id}: {e}")
            if isinstance(e, (ValueError, RuntimeError)):
                raise
            raise RuntimeError(f"Failed to create comment reply: {e}") from e
  • server.py:1280-1280 (registration)
    The @mcp.tool() decorator registers the reply_to_post function as an MCP tool in the FastMCP server.
    @require_write_access
  • Decorator applied to reply_to_post that enforces write access requirements by checking Reddit client authentication and read-only status.
    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 reply_to_post to extract the base Reddit post ID from a URL, permalink, or raw ID.
    def _extract_reddit_id(reddit_id: str) -> str:
        """Extract the base ID from a Reddit URL or ID.
    
        Args:
            reddit_id: Either a Reddit ID or a URL containing the ID
    
        Returns:
            The extracted Reddit ID
        """
        if not reddit_id:
            raise ValueError("Empty ID provided")
    
        # If it's a URL, extract the ID part
        if "/" in reddit_id:
            # Handle both standard URLs and permalinks
            parts = [p for p in reddit_id.split("/") if p]
            # The ID is typically the last non-empty part
            reddit_id = parts[-1]
            logger.debug(f"Extracted ID {reddit_id} from URL")
    
        return reddit_id
  • Helper function used by reply_to_post to format the created reply comment for the response.
    def _format_comment(comment: praw.models.Comment) -> str:
        """Format comment information with AI-driven insights."""
        flags = []
        if comment.edited:
            flags.append("Edited")
        if hasattr(comment, "is_submitter") and comment.is_submitter:
            flags.append("OP")
    
        return f"""
            • Author: u/{str(comment.author)}
            • Content: {comment.body}
            • Stats:
            - Score: {comment.score:,}
            - Controversiality: {comment.controversiality if hasattr(comment, "controversiality") else "Unknown"}
            • Context:
            - Subreddit: r/{str(comment.subreddit)}
            - Thread: {comment.submission.title}
            • Metadata:
            - Posted: {_format_timestamp(comment.created_utc)}
            - Flags: {", ".join(flags) if flags else "None"}
            • Link: https://reddit.com{comment.permalink}
    
            💬 Comment Analysis:
            - {_analyze_comment_impact(comment.score, bool(comment.edited), hasattr(comment, "is_submitter"))}
            """
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 adds useful context beyond basic functionality: it specifies return format ('Dictionary containing information about the created reply and parent post'), error conditions ('Raises: ValueError, RuntimeError'), and content length constraints ('1-10000 characters'). However, it lacks details on permissions, rate limits, or side effects like post visibility changes.

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

Conciseness5/5

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

The description is well-structured and front-loaded with the core purpose, followed by organized sections (Args, Returns, Raises). Every sentence adds value: the opening defines the tool, and subsequent sections provide essential operational details without redundancy. It's appropriately sized for a 3-parameter tool with no annotations.

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?

Given no annotations and no output schema, the description does a strong job covering basics: purpose, parameters, returns, and errors. However, for a mutation tool (posting a reply), it lacks details on authentication needs, rate limits, or confirmation of success/failure states beyond error mentions. It's mostly complete but has minor gaps in operational context.

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?

Schema description coverage is 0%, so the description must compensate fully. It adds significant meaning beyond the schema: it explains that 'post_id' accepts multiple formats ('full URL, permalink, or just ID'), specifies character limits for 'content' ('1-10000 characters'), and clarifies that 'subreddit' is optional and used 'for validation, with or without 'r/' prefix'. This provides crucial usage context not in the schema.

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 ('Post a reply') and target resource ('to an existing Reddit post'), distinguishing it from sibling tools like 'reply_to_comment' (for comments) and 'create_post' (for new posts). 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 implies usage context by specifying it's for replying to 'an existing Reddit post', which naturally excludes creating new posts or replying to comments. However, it doesn't explicitly state when to use this tool versus alternatives like 'reply_to_comment' or provide any exclusions or prerequisites beyond the implied scope.

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