search_trending
Identify trending topics and keywords from top HackerNews posts by analyzing post trends and filtering by word length or post count.
Instructions
Find current trending topics and keywords from top HackerNews posts
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| minWordLength | No | ||
| postCount | No |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"minWordLength": {
"default": 4,
"maximum": 10,
"minimum": 3,
"type": "number"
},
"postCount": {
"default": 50,
"maximum": 100,
"minimum": 10,
"type": "number"
}
},
"type": "object"
}
Implementation Reference
- src/tools/index.ts:203-262 (handler)Implements the core logic for the 'search_trending' MCP tool: fetches the top HackerNews stories using hnClient.getTopStories(), retrieves details for the top N posts, filters valid story posts, extracts words from titles (lowercased, non-punctuation, length >= minWordLength, excluding common stopwords and numbers), counts word frequencies, and returns the top 20 trending words with counts and percentage of posts containing them, along with analysis summary.async ({ postCount, minWordLength }) => { try { const topStoryIds = await hnClient.getTopStories(); const postsToAnalyze = topStoryIds.slice(0, postCount || 50); const posts = await hnClient.getMultipleItems(postsToAnalyze); const validPosts = posts.filter(post => post && post.title && post.type === "story"); // Extract and count words from titles const wordCounts = new Map<string, number>(); const commonWords = new Set(['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'man', 'end', 'why', 'let', 'put', 'say', 'she', 'too', 'use']); validPosts.forEach(post => { if (post?.title) { const words = post.title.toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter((word: string) => word.length >= (minWordLength || 4) && !commonWords.has(word) && !/^\d+$/.test(word) ); words.forEach((word: string) => { wordCounts.set(word, (wordCounts.get(word) || 0) + 1); }); } }); // Get top trending words const trendingTopics = Array.from(wordCounts.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 20) .map(([word, count]) => ({ word, count, percentage: ((count / validPosts.length) * 100).toFixed(1) })); return { content: [{ type: "text", text: JSON.stringify({ analysis_summary: { posts_analyzed: validPosts.length, total_unique_words: wordCounts.size, min_word_length: minWordLength }, trending_topics: trendingTopics, timestamp: new Date().toISOString() }, null, 2) }] }; } catch (error) { logger.error("Failed to get trending topics:", error); return { content: [{ type: "text", text: `Error analyzing trending topics: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }
- src/tools/index.ts:192-263 (registration)Registers the 'search_trending' tool via server.registerTool() within the setupTools function, including title 'Search Trending Topics', description, and Zod inputSchema with optional postCount (10-100, default 50) and minWordLength (3-10, default 4) parameters, linking to the inline handler.// Search trending topics server.registerTool( "search_trending", { title: "Search Trending Topics", description: "Find current trending topics and keywords from top HackerNews posts", inputSchema: { postCount: z.number().min(10).max(100).default(50).optional(), minWordLength: z.number().min(3).max(10).default(4).optional() } }, async ({ postCount, minWordLength }) => { try { const topStoryIds = await hnClient.getTopStories(); const postsToAnalyze = topStoryIds.slice(0, postCount || 50); const posts = await hnClient.getMultipleItems(postsToAnalyze); const validPosts = posts.filter(post => post && post.title && post.type === "story"); // Extract and count words from titles const wordCounts = new Map<string, number>(); const commonWords = new Set(['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'man', 'end', 'why', 'let', 'put', 'say', 'she', 'too', 'use']); validPosts.forEach(post => { if (post?.title) { const words = post.title.toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter((word: string) => word.length >= (minWordLength || 4) && !commonWords.has(word) && !/^\d+$/.test(word) ); words.forEach((word: string) => { wordCounts.set(word, (wordCounts.get(word) || 0) + 1); }); } }); // Get top trending words const trendingTopics = Array.from(wordCounts.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 20) .map(([word, count]) => ({ word, count, percentage: ((count / validPosts.length) * 100).toFixed(1) })); return { content: [{ type: "text", text: JSON.stringify({ analysis_summary: { posts_analyzed: validPosts.length, total_unique_words: wordCounts.size, min_word_length: minWordLength }, trending_topics: trendingTopics, timestamp: new Date().toISOString() }, null, 2) }] }; } catch (error) { logger.error("Failed to get trending topics:", error); return { content: [{ type: "text", text: `Error analyzing trending topics: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } );
- src/tools/index.ts:195-201 (schema)Input schema definition using Zod for the search_trending tool, specifying postCount: z.number().min(10).max(100).default(50).optional() and minWordLength: z.number().min(3).max(10).default(4).optional().{ title: "Search Trending Topics", description: "Find current trending topics and keywords from top HackerNews posts", inputSchema: { postCount: z.number().min(10).max(100).default(50).optional(), minWordLength: z.number().min(3).max(10).default(4).optional() }