Skip to main content
Glama
crazyrabbitLTC

Twitter MCP Server

analyzeSentiment

Analyze emotional tone in tweets matching a search query to gauge public sentiment on topics, with options for sample size and keyword frequency analysis.

Instructions

Perform sentiment analysis on tweets matching a query

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query for sentiment analysis
sampleSizeNoNumber of tweets to analyze (default: 50)
includeKeywordsNoInclude keyword frequency analysis (default: true)

Implementation Reference

  • The core handler function for the analyzeSentiment tool. Searches for tweets using the query, applies keyword-based sentiment scoring with predefined positive/negative emoji-inclusive keywords, computes sentiment distribution, overall sentiment, confidence, keyword analysis, and returns formatted analytics.
    export const handleAnalyzeSentiment: SocialDataHandler<SentimentAnalysisArgs> = async (
        _client: any,
        { query, sampleSize = 50, includeKeywords = true }: SentimentAnalysisArgs
    ) => {
        try {
            const socialClient = getSocialDataClient();
            
            if (!socialClient) {
                return createMissingApiKeyResponse('Sentiment Analysis');
            }
            
            const result = await socialClient.searchTweets({
                query,
                maxResults: sampleSize
            });
    
            const tweets = result.data || [];
            
            if (tweets.length === 0) {
                return createSocialDataResponse(`No tweets found for sentiment analysis: ${query}`);
            }
    
            // Simple sentiment analysis based on keywords
            const positiveKeywords = ['good', 'great', 'awesome', 'love', 'amazing', 'excellent', 'perfect', 'happy', 'wonderful', '❀️', '😊', 'πŸ‘', 'πŸŽ‰'];
            const negativeKeywords = ['bad', 'terrible', 'awful', 'hate', 'horrible', 'worst', 'disgusting', 'angry', 'sad', '😑', '😒', 'πŸ‘Ž', 'πŸ’”'];
            
            let positive = 0, negative = 0, neutral = 0;
            const sentimentBreakdown = tweets.map((tweet: any) => {
                const text = tweet.text?.toLowerCase() || '';
                const positiveCount = positiveKeywords.filter(word => text.includes(word)).length;
                const negativeCount = negativeKeywords.filter(word => text.includes(word)).length;
                
                let sentiment: string;
                if (positiveCount > negativeCount) {
                    sentiment = 'positive';
                    positive++;
                } else if (negativeCount > positiveCount) {
                    sentiment = 'negative';
                    negative++;
                } else {
                    sentiment = 'neutral';
                    neutral++;
                }
                
                return {
                    tweet_id: tweet.id_str,
                    text: tweet.text?.substring(0, 150),
                    sentiment,
                    confidence: Math.max(positiveCount, negativeCount) > 0 ? 'medium' : 'low',
                    engagement: (tweet.favorite_count || 0) + (tweet.retweet_count || 0)
                };
            });
    
            let keywordAnalysis = {};
            if (includeKeywords) {
                const allText = tweets.map((t: any) => t.text).join(' ').toLowerCase();
                const words = allText.match(/\b\w+\b/g) || [];
                const wordFreq = words.reduce((freq: any, word: string) => {
                    if (word.length > 3) {
                        freq[word] = (freq[word] || 0) + 1;
                    }
                    return freq;
                }, {});
                
                keywordAnalysis = {
                    top_keywords: Object.entries(wordFreq)
                        .sort(([,a], [,b]) => (b as number) - (a as number))
                        .slice(0, 10)
                        .map(([word, count]) => ({ word, count })),
                    positive_indicators: positiveKeywords.filter(word => allText.includes(word)),
                    negative_indicators: negativeKeywords.filter(word => allText.includes(word))
                };
            }
    
            const sentimentAnalysis = {
                query,
                sample_size: tweets.length,
                analysis_date: new Date().toISOString(),
                sentiment_distribution: {
                    positive: {
                        count: positive,
                        percentage: Math.round((positive / tweets.length) * 100)
                    },
                    negative: {
                        count: negative,
                        percentage: Math.round((negative / tweets.length) * 100)
                    },
                    neutral: {
                        count: neutral,
                        percentage: Math.round((neutral / tweets.length) * 100)
                    }
                },
                overall_sentiment: positive > negative ? 'Positive' : negative > positive ? 'Negative' : 'Neutral',
                confidence_level: tweets.length > 30 ? 'High' : tweets.length > 10 ? 'Medium' : 'Low',
                keyword_analysis: keywordAnalysis,
                sample_tweets: {
                    most_positive: sentimentBreakdown.filter(t => t.sentiment === 'positive')
                        .sort((a, b) => b.engagement - a.engagement)[0],
                    most_negative: sentimentBreakdown.filter(t => t.sentiment === 'negative')
                        .sort((a, b) => b.engagement - a.engagement)[0]
                }
            };
    
            return createSocialDataResponse(
                formatAnalytics(sentimentAnalysis, `Sentiment Analysis: "${query}"`)
            );
        } catch (error) {
            throw new Error(formatSocialDataError(error as Error, 'sentiment analysis'));
        }
    };
  • Input schema definition for the analyzeSentiment tool, specifying query as required, optional sampleSize (10-200), and includeKeywords boolean.
    analyzeSentiment: {
        description: 'Perform sentiment analysis on tweets matching a query',
        inputSchema: {
            type: 'object',
            properties: {
                query: {
                    type: 'string',
                    description: 'Search query for sentiment analysis'
                },
                sampleSize: {
                    type: 'number',
                    description: 'Number of tweets to analyze (default: 50)',
                    minimum: 10,
                    maximum: 200
                },
                includeKeywords: {
                    type: 'boolean',
                    description: 'Include keyword frequency analysis (default: true)'
                }
            },
            required: ['query']
        }
    },
  • src/index.ts:494-497 (registration)
    Dispatches tool calls to the handleAnalyzeSentiment handler in the main MCP server request handler switch statement.
    case 'analyzeSentiment': {
        const args = request.params.arguments as any;
        response = await handleAnalyzeSentiment(client, args);
        break;
  • TypeScript interface defining the arguments for the sentiment analysis handler, matching the tool schema.
    interface SentimentAnalysisArgs {
        query: string;
        sampleSize?: number;
        includeKeywords?: boolean;
    }
  • src/index.ts:81-82 (registration)
    Imports the handleAnalyzeSentiment function (likely re-exported from handlers/socialdata/index.js) for use in the main server.
    handleAnalyzeSentiment,
    handleTrackVirality

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/crazyrabbitLTC/mcp-twitter-server'

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