analyzeSentiment
Evaluate sentiment in tweets by analyzing text matching a search query; includes keyword frequency and customizable sample size for insights into public opinion.
Instructions
Perform sentiment analysis on tweets matching a query
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| includeKeywords | No | Include keyword frequency analysis (default: true) | |
| query | Yes | Search query for sentiment analysis | |
| sampleSize | No | Number of tweets to analyze (default: 50) |
Implementation Reference
- Main handler function implementing the analyzeSentiment tool. Performs keyword-based sentiment analysis on tweets retrieved via search query.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')); } };
- TypeScript interface defining the input arguments for the sentiment analysis handler.interface SentimentAnalysisArgs { query: string; sampleSize?: number; includeKeywords?: boolean; }
- src/socialdata-tools.ts:343-365 (schema)MCP tool schema definition including description and input validation schema for analyzeSentiment.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)Dispatch/registration of the analyzeSentiment tool in the main MCP server request handler switch statement.case 'analyzeSentiment': { const args = request.params.arguments as any; response = await handleAnalyzeSentiment(client, args); break;
- src/index.ts:81-83 (registration)Import of the handleAnalyzeSentiment handler function into the main index.ts for tool dispatching.handleAnalyzeSentiment, handleTrackVirality } from './handlers/socialdata/index.js';