Skip to main content
Glama

Twitter MCP Server

thread.handlers.ts8.41 kB
/** * SocialData.tools thread and conversation analysis handlers * Advanced thread reconstruction and conversation mapping */ import { getSocialDataClient } from '../../socialDataClient.js'; import { SocialDataHandler, FullThreadArgs, ConversationTreeArgs, ThreadMetricsArgs } from '../../types/socialdata.js'; import { createSocialDataResponse, formatSocialDataError, formatTweetList, formatAnalytics, createMissingApiKeyResponse } from '../../utils/socialdata-response.js'; export const handleGetFullThread: SocialDataHandler<FullThreadArgs> = async ( _client: any, { tweetId, includeMetrics = true }: FullThreadArgs ) => { try { const socialClient = getSocialDataClient(); if (!socialClient) { return createMissingApiKeyResponse('Full Thread Analysis'); } // Search for the original tweet and related conversation const mainTweetQuery = `conversation_id:${tweetId}`; const threadResult = await socialClient.searchTweets({ query: mainTweetQuery, maxResults: 100 }); if (!threadResult.data || threadResult.data.length === 0) { // Fallback: search for replies to the tweet const repliesQuery = `to:* in_reply_to_status_id:${tweetId}`; const repliesResult = await socialClient.searchTweets({ query: repliesQuery, maxResults: 50 }); return createSocialDataResponse( formatTweetList(repliesResult.data || [], `Thread replies for tweet ${tweetId}`) ); } // Sort by creation time to reconstruct thread order const sortedThread = threadResult.data.sort((a: any, b: any) => new Date(a.tweet_created_at).getTime() - new Date(b.tweet_created_at).getTime() ); const threadAnalysis = { thread_id: tweetId, total_tweets: sortedThread.length, thread_duration: sortedThread.length > 1 ? { start: sortedThread[0]?.tweet_created_at, end: sortedThread[sortedThread.length - 1]?.tweet_created_at } : null, tweets: sortedThread.map((tweet: any) => ({ id: tweet.id_str, text: tweet.text, author: tweet.user?.screen_name, created_at: tweet.tweet_created_at, metrics: includeMetrics ? { likes: tweet.favorite_count || 0, retweets: tweet.retweet_count || 0, replies: tweet.reply_count || 0 } : undefined })) }; return createSocialDataResponse( formatAnalytics(threadAnalysis, `Full Thread Analysis for ${tweetId}`) ); } catch (error) { throw new Error(formatSocialDataError(error as Error, 'full thread analysis')); } }; export const handleGetConversationTree: SocialDataHandler<ConversationTreeArgs> = async ( _client: any, { tweetId, maxDepth = 3, includeQuotes = true }: ConversationTreeArgs ) => { try { const socialClient = getSocialDataClient(); if (!socialClient) { return createMissingApiKeyResponse('Conversation Tree Analysis'); } // Get direct replies const repliesQuery = `to:* ${tweetId}`; const repliesResult = await socialClient.searchTweets({ query: repliesQuery, maxResults: 50 }); // Get quote tweets if requested let quoteTweets: any[] = []; if (includeQuotes) { const quotesQuery = `url:${tweetId} OR ${tweetId}`; const quotesResult = await socialClient.searchTweets({ query: quotesQuery, maxResults: 25 }); quoteTweets = quotesResult.data || []; } const conversationTree = { root_tweet_id: tweetId, max_depth_analyzed: maxDepth, direct_replies: { count: repliesResult.data?.length || 0, tweets: repliesResult.data?.map((tweet: any) => ({ id: tweet.id_str, text: tweet.text?.substring(0, 280), author: tweet.user?.screen_name, created_at: tweet.tweet_created_at, metrics: { likes: tweet.favorite_count || 0, retweets: tweet.retweet_count || 0 } })) || [] }, quote_tweets: { count: quoteTweets.length, tweets: quoteTweets.map((tweet: any) => ({ id: tweet.id_str, text: tweet.text?.substring(0, 280), author: tweet.user?.screen_name, created_at: tweet.tweet_created_at })) }, engagement_summary: { total_interactions: (repliesResult.data?.length || 0) + quoteTweets.length, reply_rate: repliesResult.data?.length || 0, quote_rate: quoteTweets.length } }; return createSocialDataResponse( formatAnalytics(conversationTree, `Conversation Tree for ${tweetId}`) ); } catch (error) { throw new Error(formatSocialDataError(error as Error, 'conversation tree analysis')); } }; export const handleGetThreadMetrics: SocialDataHandler<ThreadMetricsArgs> = async ( _client: any, { tweetId, analyzeEngagement = true, timeframe = '24h' }: ThreadMetricsArgs ) => { try { const socialClient = getSocialDataClient(); if (!socialClient) { return createMissingApiKeyResponse('Thread Metrics Analysis'); } // Get thread data const threadQuery = `conversation_id:${tweetId}`; const threadResult = await socialClient.searchTweets({ query: threadQuery, maxResults: 100 }); const tweets = threadResult.data || []; if (tweets.length === 0) { return createSocialDataResponse(`No thread data found for tweet ${tweetId}`); } let metrics: any = { thread_id: tweetId, thread_length: tweets.length, timeframe_analyzed: timeframe }; if (analyzeEngagement) { const totalLikes = tweets.reduce((sum: number, tweet: any) => sum + (tweet.favorite_count || 0), 0); const totalRetweets = tweets.reduce((sum: number, tweet: any) => sum + (tweet.retweet_count || 0), 0); const totalReplies = tweets.reduce((sum: number, tweet: any) => sum + (tweet.reply_count || 0), 0); metrics.engagement_metrics = { total_likes: totalLikes, total_retweets: totalRetweets, total_replies: totalReplies, avg_likes_per_tweet: Math.round(totalLikes / tweets.length), avg_retweets_per_tweet: Math.round(totalRetweets / tweets.length), engagement_distribution: tweets.map((tweet: any, index: number) => ({ position: index + 1, likes: tweet.favorite_count || 0, retweets: tweet.retweet_count || 0, engagement_score: (tweet.favorite_count || 0) + (tweet.retweet_count || 0) * 2 })).sort((a: any, b: any) => b.engagement_score - a.engagement_score) }; // Find the most engaging tweet in thread const topTweet = metrics.engagement_metrics.engagement_distribution[0]; metrics.top_performing_tweet = { position: topTweet.position, engagement_score: topTweet.engagement_score, performance_boost: tweets.length > 1 ? Math.round((topTweet.engagement_score / (totalLikes + totalRetweets * 2)) * 100) : 100 }; } return createSocialDataResponse( formatAnalytics(metrics, `Thread Performance Metrics for ${tweetId}`) ); } catch (error) { throw new Error(formatSocialDataError(error as Error, 'thread metrics analysis')); } };

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