getShowHNStories
Fetch Show HN submissions from Hacker News to discover new projects and tools shared by developers.
Instructions
Get Show HN stories
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of stories to return (default: 10, max: 30) |
Implementation Reference
- src/tools.ts:242-300 (handler)The complete tool object for getShowHNStories, including the execute handler function that implements the core logic: fetching Show HN story IDs from /showstories, retrieving items, formatting, and returning structured JSON response.{ name: "getShowHNStories", description: "Get Show HN stories", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of stories to return (default: 10, max: 30)", default: 10, }, }, }, execute: async (args: any) => { const limit = Math.min(args.limit || 10, 30); const showIds = await fetchFromAPI<number[]>("/showstories"); if (!showIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch Show HN stories", }), }, ], }; } const stories = await fetchMultipleItems(showIds, limit); const formattedStories = stories.map((story) => ({ id: story.id, title: story.title, url: story.url, score: story.score, author: story.by, comments: story.descendants || 0, time: story.time ? formatTime(story.time) : "unknown", hnUrl: `https://news.ycombinator.com/item?id=${story.id}`, })); return { content: [ { type: "text", text: JSON.stringify( { message: `Latest ${limit} Show HN stories`, stories: formattedStories, }, null, 2 ), }, ], }; }, },
- src/index.ts:44-65 (registration)MCP server handles tools/list to register available tools (including getShowHNStories) and tools/call to execute the selected tool by name.sendResponse(json.id, { tools: tools.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema, })), }); } if (json.method === "tools/call") { const tool = tools.find((tool) => tool.name === json.params.name); if (tool) { const toolResponse = await tool.execute(json.params.arguments); sendResponse(json.id, toolResponse); } else { sendResponse(json.id, { error: { code: -32602, message: `MCP error -32602: Tool ${json.params.name} not found`, }, }); } }
- src/fetch-actions.ts:9-27 (helper)fetchFromAPI helper function used by the handler to fetch '/showstories' IDs and individual story items from the Hacker News API, with caching support.export async function fetchFromAPI<T>(endpoint: string): Promise<T | null> { const cacheKey = endpoint; const cached = getCached<T>(cacheKey); if (cached) return cached; try { const response = await fetch( `https://hacker-news.firebaseio.com/v0${endpoint}.json` ); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); setCache(cacheKey, data); return data; } catch (error) { console.error(`Error fetching ${endpoint}:`, error); return null; } }
- src/helpers.ts:23-35 (helper)formatTime utility function used to format story timestamps into human-readable relative time (e.g., '2h ago').export function formatTime(timestamp: number): string { const date = new Date(timestamp * 1000); const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; return `${days}d ago`; }
- src/fetch-actions.ts:30-44 (helper)fetchMultipleItems helper used to parallel-fetch and filter story items by IDs, excluding deleted/dead items.export async function fetchMultipleItems( ids: number[], maxItems = 30 ): Promise<HackerNewsItem[]> { const limitedIds = ids.slice(0, maxItems); const promises = limitedIds.map((id) => fetchFromAPI<HackerNewsItem>(`/item/${id}`) ); const results = await Promise.all(promises); return results.filter( (item): item is HackerNewsItem => item !== null && !item.deleted && !item.dead ); }