Skip to main content
Glama
baryhuang

MCP Server - Twitter NoAuth

twitter_get_user_replies

Retrieve recent replies by a specific Twitter user using their ID or username. Access Twitter data without local credential setup through the MCP server, enabling structured API responses. Specify maximum results for tailored output.

Instructions

Get recent replies by a specific user

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
max_resultsNoMaximum number of tweets to return (default: 10)
twitter_access_tokenYesTwitter OAuth2 access token
user_idNoTwitter user ID (optional if username is provided)
usernameNoTwitter username/handle (optional if user_id is provided)

Implementation Reference

  • Core handler function in TwitterClient that implements the tool logic: fetches user's recent replies using Twitter API v2 search/recent endpoint with query 'from:{user_id} is:reply'. Supports username resolution to user_id and handles errors.
    def get_user_replies(self, user_id: str = None, username: str = None, max_results: int = 10) -> str:
        """Get recent replies by a specific user
        
        Args:
            user_id: Twitter user ID
            username: Twitter username/handle (without @ symbol)
            max_results: Maximum number of tweets to return (default: 10)
            
        Returns:
            JSON string with user replies
        """
        try:
            if not self.access_token:
                return json.dumps({
                    "error": "No valid access token provided. Please refresh your token first.",
                    "status": "error"
                })
            
            # If username is provided but not user_id, look up the user_id
            if not user_id and username:
                # Remove @ symbol if it's included
                if username.startswith('@'):
                    username = username[1:]
                    
                logger.debug(f"Looking up user ID for username: {username}")
                user_lookup_result = self.get_user_id_by_username(username)
                user_lookup_data = json.loads(user_lookup_result)
                
                if user_lookup_data.get("status") == "success":
                    user_id = user_lookup_data.get("user_id")
                    logger.debug(f"Found user ID: {user_id}")
                else:
                    return json.dumps({
                        "error": f"Could not find user ID for username: {username}",
                        "details": user_lookup_data.get("error", "No details available"),
                        "status": "error"
                    })
            
            if not user_id:
                return json.dumps({
                    "error": "Either user_id or username is required",
                    "status": "error"
                })
            
            logger.debug(f"Getting replies for user ID: {user_id}, max_results: {max_results}")
            
            # We'll use the search endpoint with a specific query to find replies
            url = f"{self.api_base_url}/tweets/search/recent"
            
            headers = {
                "Authorization": f"Bearer {self.access_token}"
            }
            
            # Query for tweets that are replies from the specified user
            query = f"from:{user_id} is:reply"
            
            params = {
                "query": query,
                "max_results": max_results,
                "tweet.fields": "id,text,created_at,in_reply_to_user_id,conversation_id",
                "expansions": "author_id,in_reply_to_user_id,referenced_tweets.id",
                "user.fields": "id,name,username"
            }
            
            response = requests.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            # Return the raw JSON response
            return json.dumps(response.json())
            
        except requests.exceptions.RequestException as e:
            logger.error(f"API request error: {str(e)}")
            return json.dumps({"error": str(e), "status": "error"})
        except Exception as e:
            logger.error(f"Exception in get_user_replies: {str(e)}")
            return json.dumps({"error": str(e), "status": "error"})
  • Tool registration in the list_tools handler, including name, description, and input schema definition.
    types.Tool(
        name="twitter_get_user_replies",
        description="Get recent replies by a specific user",
        inputSchema={
            "type": "object",
            "properties": {
                "twitter_access_token": {"type": "string", "description": "Twitter OAuth2 access token"},
                "user_id": {"type": "string", "description": "Twitter user ID (optional if username is provided)"},
                "username": {"type": "string", "description": "Twitter username/handle (optional if user_id is provided)"},
                "max_results": {"type": "integer", "description": "Maximum number of tweets to return (default: 10)"}
            },
            "required": ["twitter_access_token"]
        },
    ),
  • Dispatch handler in the MCP server's call_tool function that extracts arguments, validates inputs, instantiates TwitterClient, calls the get_user_replies method, and returns the result.
    elif name == "twitter_get_user_replies":
        user_id = arguments.get("user_id")
        username = arguments.get("username")
        max_results = int(arguments.get("max_results", 10))
        
        if not user_id and not username:
            raise ValueError("Either user_id or username is required for twitter_get_user_replies")
        
        results = twitter.get_user_replies(user_id=user_id, username=username, max_results=max_results)
        return [types.TextContent(type="text", text=results)]
  • Helper method used by get_user_replies to resolve username to user_id via Twitter API users/by/username endpoint.
    def get_user_id_by_username(self, username: str) -> str:
        """Lookup a user ID by username
        
        Args:
            username: Twitter username/handle (without the @ symbol)
            
        Returns:
            JSON string with user data including the user ID
        """
        try:
            if not self.access_token:
                return json.dumps({
                    "error": "No valid access token provided. Please refresh your token first.",
                    "status": "error"
                })
            
            # Remove @ symbol if it's included
            if username.startswith('@'):
                username = username[1:]
                
            logger.debug(f"Looking up user ID for username: {username}")
            
            # Twitter API v2 user lookup by username endpoint
            url = f"{self.api_base_url}/users/by/username/{username}"
            
            headers = {
                "Authorization": f"Bearer {self.access_token}"
            }
            
            params = {
                "user.fields": "id,name,username"
            }
            
            response = requests.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            
            # Extract the user ID from the response
            if "data" in result and "id" in result["data"]:
                user_id = result["data"]["id"]
                logger.debug(f"Found user ID: {user_id} for username: {username}")
                return json.dumps({
                    "user_id": user_id,
                    "data": result["data"],
                    "status": "success"
                })
            else:
                return json.dumps({
                    "error": "User not found or ID not available",
                    "status": "error"
                })
            
        except requests.exceptions.RequestException as e:
            logger.error(f"API request error: {str(e)}")
            return json.dumps({"error": str(e), "status": "error"})
        except Exception as e:
            logger.error(f"Exception in get_user_id_by_username: {str(e)}")
            return json.dumps({"error": str(e), "status": "error"})
Behavior2/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 states the action ('Get recent replies') but lacks details on rate limits, authentication requirements (beyond the required parameter), pagination, error handling, or what 'recent' means (e.g., time frame). This leaves significant gaps for an AI agent to understand how to use it effectively.

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 a single, clear sentence with no wasted words. It's front-loaded with the core action and resource, making it highly efficient and easy to parse. Every word earns its place by conveying essential information without redundancy.

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

Completeness2/5

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

Given the tool's complexity (fetching user replies from an API), lack of annotations, and no output schema, the description is incomplete. It doesn't cover behavioral aspects like rate limits or authentication needs, nor does it hint at the return format (e.g., tweet objects, error responses). This leaves the AI agent with insufficient context for reliable use.

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?

The input schema has 100% description coverage, so the schema already documents all four parameters thoroughly. The description doesn't add any meaning beyond what the schema provides (e.g., it doesn't clarify the relationship between 'user_id' and 'username' or explain 'recent' in context of 'max_results'). Baseline 3 is appropriate when the schema does the heavy lifting.

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 'Get recent replies by a specific user' clearly states the verb ('Get') and resource ('replies by a specific user'), making the purpose immediately understandable. However, it doesn't explicitly differentiate from sibling tools like 'twitter_get_user_tweets' or 'twitter_reply_to_tweet' beyond the 'replies' keyword, which is why it doesn't reach a perfect score.

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 no guidance on when to use this tool versus alternatives. It doesn't mention when to choose this over 'twitter_get_user_tweets' (which might fetch all tweets including replies) or 'twitter_search_tweets' (which could search for replies), nor does it specify any prerequisites or exclusions beyond what's implied by the parameters.

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

Related 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/baryhuang/mcp-twitter-noauth'

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