Skip to main content
Glama
simple-search.js13.4 kB
import readlineSync from "readline-sync"; import { McpClient } from "./mcp-client.js"; import fs from "fs"; async function main() { let client = null; try { console.log("Starting MCP client..."); // Create a new client with a different port to avoid conflicts client = new McpClient({ port: 3006, // Use a different port than the default debug: process.env.DEBUG === "true", maxPortAttempts: 5, portIncrement: 1, startServer: true, // Always start a new server }); await client.start(); console.log("MCP client started successfully!"); // Get search parameters console.log("\n=== Twitter Search ===\n"); // Ask for search type console.log("Search options:"); console.log("[1] Search by username (get user's tweets)"); console.log("[2] Search by keyword (search all tweets)"); const searchType = readlineSync.question("\nSelect search type (1 or 2): "); if (searchType === "1") { // Search by username const username = readlineSync.question( "\nEnter Twitter username to search (without @): " ); if (!username.trim()) { console.log("No username provided. Exiting..."); await client.stop(); return; } const count = readlineSync.question( "Number of tweets to retrieve (default: 5): " ); const tweetCount = parseInt(count) || 5; const includeReplies = readlineSync.keyInYNStrict("Include replies?"); const includeRetweets = readlineSync.keyInYNStrict("Include retweets?"); console.log(`\nSearching for ${tweetCount} tweets from @${username}...`); console.log(`Including replies: ${includeReplies ? "Yes" : "No"}`); console.log(`Including retweets: ${includeRetweets ? "Yes" : "No"}`); try { // Create a direct JSON-RPC request const requestId = Math.floor(Math.random() * 10000); const request = { jsonrpc: "2.0", id: requestId.toString(), method: "tools/call", params: { name: "get_user_tweets", arguments: { username: username, count: tweetCount, includeReplies: includeReplies, includeRetweets: includeRetweets, }, }, }; if (process.env.DEBUG === "true") { console.log( "DEBUG: Request payload:", JSON.stringify(request, null, 2) ); } // Register a response handler const searchPromise = new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error("Timeout waiting for response from Twitter")); }, 30000); // 30 second timeout client.responseHandlers.set(requestId.toString(), (response) => { clearTimeout(timeoutId); if (process.env.DEBUG === "true") { console.log( "DEBUG: Raw response:", JSON.stringify(response, null, 2) ); } // Check for error in the response content if ( response.result && response.result.content && response.result.content.length > 0 && response.result.content[0].isError ) { const errorMessage = response.result.content[0].text || "Unknown error in response"; reject(new Error(errorMessage)); return; } // Check for standard error format if (response.error) { reject(new Error(response.error.message || "Unknown error")); return; } // If we get here, the search was successful if ( response.result && response.result.content && response.result.content.length > 0 ) { try { // Try to parse the tweet data from the response const contentText = response.result.content[0].text; if (contentText) { try { const tweetData = JSON.parse(contentText); if ( tweetData && tweetData.tweets && Array.isArray(tweetData.tweets) ) { resolve(tweetData.tweets); return; } } catch (parseError) { console.log( "Warning: Failed to parse tweet data:", parseError.message ); } } } catch (error) { console.log( "Warning: Exception while processing response:", error.message ); } reject(new Error("Failed to extract tweets from response")); } else { reject(new Error("Invalid response from MCP server")); } }); // Send the request client.sendRequest(request); }); const tweets = await searchPromise; if (tweets.length === 0) { console.log("\nNo tweets found for this user."); } else { console.log( `\n✅ Found ${tweets.length} tweets from @${username}:\n` ); tweets.forEach((tweet, index) => { console.log(`[${index + 1}] Tweet ID: ${tweet.id}`); console.log( ` Date: ${new Date(tweet.createdAt).toLocaleString()}` ); console.log( ` Text: ${tweet.text.substring(0, 100)}${ tweet.text.length > 100 ? "..." : "" }` ); console.log( ` Likes: ${tweet.likeCount}, Retweets: ${tweet.retweetCount}, Replies: ${tweet.replyCount}` ); console.log(""); }); // Ask if user wants to save a tweet ID for replying later if ( readlineSync.keyInYNStrict( "\nDo you want to save a tweet ID for replying later?" ) ) { const tweetIndex = parseInt( readlineSync.question("Enter the number of the tweet to save: ") ) - 1; if (tweetIndex >= 0 && tweetIndex < tweets.length) { const tweetId = tweets[tweetIndex].id; fs.writeFileSync("./last-tweet-id.txt", tweetId); console.log(`Tweet ID ${tweetId} saved for future replies.`); } else { console.log("Invalid tweet number. No tweet ID saved."); } } } } catch (error) { console.error(`\n❌ ERROR: Failed to search tweets: ${error.message}`); if (error.details) { console.error( "Error details:", JSON.stringify(error.details, null, 2) ); } } } else if (searchType === "2") { // Search by keyword const query = readlineSync.question("\nEnter search query: "); if (!query.trim()) { console.log("No search query provided. Exiting..."); await client.stop(); return; } const count = readlineSync.question( "Number of tweets to retrieve (default: 10): " ); const tweetCount = parseInt(count) || 10; console.log( `\nSearching for ${tweetCount} tweets matching "${query}"...` ); try { // Create a direct JSON-RPC request const requestId = Math.floor(Math.random() * 10000); const request = { jsonrpc: "2.0", id: requestId.toString(), method: "tools/call", params: { name: "search_tweets", arguments: { query: query, count: tweetCount, }, }, }; if (process.env.DEBUG === "true") { console.log( "DEBUG: Request payload:", JSON.stringify(request, null, 2) ); } // Register a response handler const searchPromise = new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error("Timeout waiting for response from Twitter")); }, 30000); // 30 second timeout client.responseHandlers.set(requestId.toString(), (response) => { clearTimeout(timeoutId); if (process.env.DEBUG === "true") { console.log( "DEBUG: Raw response:", JSON.stringify(response, null, 2) ); } // Check for error in the response content if ( response.result && response.result.content && response.result.content.length > 0 && response.result.content[0].isError ) { const errorMessage = response.result.content[0].text || "Unknown error in response"; reject(new Error(errorMessage)); return; } // Check for standard error format if (response.error) { reject(new Error(response.error.message || "Unknown error")); return; } // If we get here, the search was successful if ( response.result && response.result.content && response.result.content.length > 0 ) { try { // Try to parse the tweet data from the response const contentText = response.result.content[0].text; if (contentText) { try { const tweetData = JSON.parse(contentText); if ( tweetData && tweetData.tweets && Array.isArray(tweetData.tweets) ) { resolve(tweetData.tweets); return; } } catch (parseError) { console.log( "Warning: Failed to parse tweet data:", parseError.message ); } } } catch (error) { console.log( "Warning: Exception while processing response:", error.message ); } reject(new Error("Failed to extract tweets from response")); } else { reject(new Error("Invalid response from MCP server")); } }); // Send the request client.sendRequest(request); }); const tweets = await searchPromise; if (tweets.length === 0) { console.log("\nNo tweets found matching your search query."); } else { console.log( `\n✅ Found ${tweets.length} tweets matching "${query}":\n` ); tweets.forEach((tweet, index) => { console.log(`[${index + 1}] Tweet ID: ${tweet.id}`); console.log( ` Author: ${tweet.author.name} (@${tweet.author.username})` ); console.log( ` Date: ${new Date(tweet.createdAt).toLocaleString()}` ); console.log( ` Text: ${tweet.text.substring(0, 100)}${ tweet.text.length > 100 ? "..." : "" }` ); console.log( ` Likes: ${tweet.likeCount}, Retweets: ${tweet.retweetCount}, Replies: ${tweet.replyCount}` ); console.log(""); }); // Ask if user wants to save a tweet ID for replying later if ( readlineSync.keyInYNStrict( "\nDo you want to save a tweet ID for replying later?" ) ) { const tweetIndex = parseInt( readlineSync.question("Enter the number of the tweet to save: ") ) - 1; if (tweetIndex >= 0 && tweetIndex < tweets.length) { const tweetId = tweets[tweetIndex].id; fs.writeFileSync("./last-tweet-id.txt", tweetId); console.log(`Tweet ID ${tweetId} saved for future replies.`); } else { console.log("Invalid tweet number. No tweet ID saved."); } } } } catch (error) { console.error(`\n❌ ERROR: Failed to search tweets: ${error.message}`); if (error.details) { console.error( "Error details:", JSON.stringify(error.details, null, 2) ); } } } else { console.log("Invalid selection. Exiting..."); } // Clean up console.log("\nCleaning up..."); await client.stop(); console.log("Done!"); } catch (error) { console.error("Error:", error); if (client) { try { await client.stop(); } catch (stopError) { console.error("Error stopping client:", stopError); } } process.exit(1); } } main();

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/ryanmac/agent-twitter-client-mcp'

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