Skip to main content
Glama

X (Twitter) MCP Server

by Dishant27
twitter-api.ts10.6 kB
import Twitter from 'twitter-api-v2'; import { Config, TwitterError, TwitterUser, Tweet, TwitterList } from './types'; export class TwitterClient { private client: Twitter; constructor(config: Config) { this.client = new Twitter({ appKey: config.apiKey, appSecret: config.apiSecretKey, accessToken: config.accessToken, accessSecret: config.accessTokenSecret, }); } /** * Post a new tweet */ async postTweet(text: string): Promise<{ id: string }> { try { const { data } = await this.client.v2.tweet(text); return { id: data.id }; } catch (error) { this.handleTwitterError(error); throw error; // TypeScript needs this } } /** * Search for tweets */ async searchTweets(query: string, count: number): Promise<{ tweets: Tweet[]; users: Record<string, TwitterUser> }> { try { const result = await this.client.v2.search(query, { max_results: count, 'tweet.fields': 'created_at,public_metrics,author_id', 'user.fields': 'profile_image_url,description,created_at,verified,public_metrics', expansions: 'author_id', }); const tweets: Tweet[] = result.data.data.map(tweet => ({ id: tweet.id, text: tweet.text, authorId: tweet.author_id, createdAt: tweet.created_at, publicMetrics: { retweetCount: tweet.public_metrics?.retweet_count || 0, replyCount: tweet.public_metrics?.reply_count || 0, likeCount: tweet.public_metrics?.like_count || 0, quoteCount: tweet.public_metrics?.quote_count || 0, }, })); const users: Record<string, TwitterUser> = {}; result.includes.users?.forEach(user => { users[user.id] = { id: user.id, name: user.name, username: user.username, description: user.description, profileImageUrl: user.profile_image_url, verified: user.verified || false, followersCount: user.public_metrics?.followers_count || 0, followingCount: user.public_metrics?.following_count || 0, createdAt: user.created_at || '', }; }); return { tweets, users }; } catch (error) { this.handleTwitterError(error); throw error; // TypeScript needs this } } /** * Get user profile information */ async getUserProfile(username?: string): Promise<TwitterUser> { try { const fields = 'profile_image_url,description,created_at,verified,public_metrics'; // Get current user profile if no username is provided const user = username ? await this.client.v2.userByUsername(username, { 'user.fields': fields }) : await this.client.v2.me({ 'user.fields': fields }); const userData = user.data; return { id: userData.id, name: userData.name, username: userData.username, description: userData.description, profileImageUrl: userData.profile_image_url, verified: userData.verified || false, followersCount: userData.public_metrics?.followers_count || 0, followingCount: userData.public_metrics?.following_count || 0, createdAt: userData.created_at || '', }; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Update user profile */ async updateProfile(profileData: { name?: string; description?: string; location?: string; url?: string; }): Promise<TwitterUser> { try { const result = await this.client.v1.updateAccountProfile(profileData); return { id: result.id_str, name: result.name, username: result.screen_name, description: result.description, profileImageUrl: result.profile_image_url_https, verified: result.verified, followersCount: result.followers_count, followingCount: result.friends_count, createdAt: result.created_at, }; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Follow a user */ async followUser(username: string): Promise<TwitterUser> { try { const result = await this.client.v2.follow( await this.getUserIdByUsername(username) ); if (!result.data.following) { throw new TwitterError('Failed to follow user'); } return this.getUserProfile(username); } catch (error) { this.handleTwitterError(error); throw error; } } /** * Unfollow a user */ async unfollowUser(username: string): Promise<TwitterUser> { try { const result = await this.client.v2.unfollow( await this.getUserIdByUsername(username) ); if (!result.data.following) { return this.getUserProfile(username); } else { throw new TwitterError('Failed to unfollow user'); } } catch (error) { this.handleTwitterError(error); throw error; } } /** * Get followers of a user */ async getFollowers(username?: string, count: number = 20): Promise<TwitterUser[]> { try { const userId = username ? await this.getUserIdByUsername(username) : (await this.client.v2.me()).data.id; const result = await this.client.v2.followers(userId, { max_results: count, 'user.fields': 'profile_image_url,description,created_at,verified,public_metrics', }); return result.data.map(user => ({ id: user.id, name: user.name, username: user.username, description: user.description, profileImageUrl: user.profile_image_url, verified: user.verified || false, followersCount: user.public_metrics?.followers_count || 0, followingCount: user.public_metrics?.following_count || 0, createdAt: user.created_at || '', })); } catch (error) { this.handleTwitterError(error); throw error; } } /** * Get users that a user is following */ async getFollowing(username?: string, count: number = 20): Promise<TwitterUser[]> { try { const userId = username ? await this.getUserIdByUsername(username) : (await this.client.v2.me()).data.id; const result = await this.client.v2.following(userId, { max_results: count, 'user.fields': 'profile_image_url,description,created_at,verified,public_metrics', }); return result.data.map(user => ({ id: user.id, name: user.name, username: user.username, description: user.description, profileImageUrl: user.profile_image_url, verified: user.verified || false, followersCount: user.public_metrics?.followers_count || 0, followingCount: user.public_metrics?.following_count || 0, createdAt: user.created_at || '', })); } catch (error) { this.handleTwitterError(error); throw error; } } /** * Create a Twitter list */ async createList(name: string, description?: string, isPrivate: boolean = false): Promise<TwitterList> { try { const result = await this.client.v2.createList({ name, description, private: isPrivate, }); return { id: result.data.id, name: result.data.name, description: result.data.description || '', memberCount: 0, followerCount: 0, private: result.data.private || false, ownerId: await this.getCurrentUserId(), }; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Get list information */ async getListInfo(listId: string): Promise<TwitterList> { try { const result = await this.client.v2.list(listId, { 'list.fields': 'follower_count,member_count,owner_id,private', }); return { id: result.data.id, name: result.data.name, description: result.data.description || '', memberCount: result.data.member_count || 0, followerCount: result.data.follower_count || 0, private: result.data.private || false, ownerId: result.data.owner_id || '', }; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Get user lists */ async getUserLists(): Promise<TwitterList[]> { try { const userId = await this.getCurrentUserId(); const result = await this.client.v2.listsOwned(userId, { 'list.fields': 'follower_count,member_count,owner_id,private', }); return result.data.map(list => ({ id: list.id, name: list.name, description: list.description || '', memberCount: list.member_count || 0, followerCount: list.follower_count || 0, private: list.private || false, ownerId: list.owner_id || userId, })); } catch (error) { this.handleTwitterError(error); throw error; } } /** * Helper: Get user ID by username */ private async getUserIdByUsername(username: string): Promise<string> { try { const result = await this.client.v2.userByUsername(username); return result.data.id; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Helper: Get current user ID */ private async getCurrentUserId(): Promise<string> { try { const result = await this.client.v2.me(); return result.data.id; } catch (error) { this.handleTwitterError(error); throw error; } } /** * Error handler */ private handleTwitterError(error: any): never { console.error('Twitter API error:', error); // Handle rate limiting if (error.code === 88 || (error.errors && error.errors[0]?.code === 88)) { throw new TwitterError('Twitter rate limit exceeded', 88, error); } // Handle auth errors if ([32, 89, 135, 215, 226].includes(error.code) || (error.errors && [32, 89, 135, 215, 226].includes(error.errors[0]?.code))) { throw new TwitterError('Twitter authentication error', error.code || 0, error); } // For all other errors const message = error.message || (error.errors && error.errors[0]?.message) || 'Unknown Twitter API error'; const code = error.code || (error.errors && error.errors[0]?.code) || 0; throw new TwitterError(message, code, error); } }

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

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