Skip to main content
Glama

Twitter MCP Server

by aali-1
twitter.ts3.45 kB
import { TwitterApi } from 'twitter-api-v2'; import { twitterCredentials } from './config.js'; import logger from './logger.js'; import { Tweet, PostTweetParams, SearchParams, TimelineParams, RateLimitInfo } from './types.js'; export class TwitterService { private client: TwitterApi; private rateLimitInfo: RateLimitInfo = { remaining: 300, reset: Date.now() + 900000, limit: 300, }; constructor() { // Create client with OAuth 1.0a for posting tweets this.client = new TwitterApi({ appKey: twitterCredentials.apiKey, appSecret: twitterCredentials.apiSecret, accessToken: twitterCredentials.accessToken, accessSecret: twitterCredentials.accessTokenSecret, }); } private async handleApiCall<T>(apiCall: () => Promise<T>): Promise<T> { try { const result = await apiCall(); logger.info('Twitter API call successful'); return result; } catch (error: any) { logger.error('Twitter API error', { error: error.message, code: error.code }); if (error.code === 429) { throw new Error('Rate limit exceeded. Please try again later.'); } if (error.code === 401) { throw new Error('Authentication failed. Please check your Twitter credentials.'); } if (error.code === 403) { throw new Error('Access forbidden. You may not have permission to access this resource.'); } throw new Error(`Twitter API error: ${error.message}`); } } async postTweet(params: PostTweetParams): Promise<Tweet> { logger.info('Posting tweet', { text: params.text.substring(0, 50) + '...' }); const tweetParams: any = { text: params.text, }; if (params.reply_to_tweet_id) { tweetParams.reply = { in_reply_to_tweet_id: params.reply_to_tweet_id }; } const result = await this.handleApiCall(() => this.client.v2.tweet(tweetParams) ); return { id: result.data.id, text: result.data.text, created_at: (result.data as any).created_at, }; } async searchTweets(params: SearchParams): Promise<Tweet[]> { logger.info('Searching tweets', { query: params.query }); const searchParams: any = { query: params.query, max_results: params.max_results || 10, }; const result = await this.handleApiCall(() => this.client.v2.search(params.query, { ...searchParams, 'tweet.fields': ['created_at', 'public_metrics', 'author_id'], }) ); return (result.data as unknown as Tweet[]) || []; } async getTimeline(params: TimelineParams): Promise<Tweet[]> { logger.info('Getting user timeline', { username: params.username }); // First get user by username const user = await this.handleApiCall(() => this.client.v2.userByUsername(params.username) ); if (!user.data) { throw new Error(`User ${params.username} not found`); } const timelineParams: any = { max_results: params.max_results || 10, }; const result = await this.handleApiCall(() => this.client.v2.userTimeline(user.data.id, { ...timelineParams, 'tweet.fields': ['created_at', 'public_metrics'], }) ); return (result.data as unknown as Tweet[]) || []; } async getRateLimitInfo(): Promise<RateLimitInfo> { return this.rateLimitInfo; } } export const twitterService = new TwitterService();

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

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