hn_story
Retrieve details for a Hacker News story using its story ID.
Instructions
Get details for a specific story by ID
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| story_id | Yes | The ID of the story to fetch |
Implementation Reference
- index.ts:194-207 (registration)Registration of the hn_story tool in the ListToolsRequestSchema handler. Defines the tool name, description, and input schema requiring a story_id number.
{ name: "hn_story", description: "Get details for a specific story by ID", inputSchema: { type: "object", properties: { story_id: { type: "number", description: "The ID of the story to fetch" } }, required: ["story_id"] } }, - index.ts:301-328 (handler)Handler for the hn_story tool inside the CallToolRequestSchema. It validates the story_id argument, fetches the story via api.getItemDetails(), formats it using formatStoryAsText(), and returns the result.
if (name === "hn_story") { const storyId = typeof args?.story_id === 'number' ? args.story_id : NaN; if (isNaN(storyId)) { throw new Error('Story ID must be a number'); } const story = await api.getItemDetails(storyId) as Story | null; if (!story) { throw new Error(`Story with ID ${storyId} not found`); } const formattedStory = { id: story.id, title: story.title, by: story.by, time: api.formatTime(story.time), url: story.url, text: story.text ? api.cleanText(story.text) : '', score: story.score, commentsCount: story.kids?.length || 0 }; return { content: [ { type: "text", text: formatStoryAsText(formattedStory) } ] }; } - index.ts:87-124 (helper)Helper methods used by the hn_story handler: getItemDetails() fetches a story by ID from the Firebase API, formatTime() converts timestamps, and cleanText() sanitizes HTML entities.
async getItemDetails(itemId: number): Promise<Story | Comment | null> { try { const response = await axios.get(`${baseUrl}/item/${itemId}.json`); return response.data; } catch (error) { console.error(`Error fetching item ${itemId}:`, error); return null; } } async getComments(commentIds: number[] = []): Promise<Comment[]> { if (!commentIds || commentIds.length === 0) { return []; } try { const commentPromises = commentIds.map(id => this.getItemDetails(id)); const comments = await Promise.all(commentPromises); return comments.filter((comment): comment is Comment => comment !== null); } catch (error) { console.error('Failed to load comments:', error); return []; } } formatTime(timestamp: number): string { const date = new Date(timestamp * 1000); return date.toLocaleString(); } cleanText(text: string | undefined): string { if (!text) return ''; return text .replace(/>/g, '>') .replace(/</g, '<') .replace(/&/g, '&') .replace(/"/g, '"') .replace(/<[^>]*>?/gm, ''); } - index.ts:408-426 (helper)The formatStoryAsText() helper function that formats a FormattedStory object into a human-readable text block for the tool response.
function formatStoryAsText(story: FormattedStory): string { if (!story) { return "Story not found."; } let result = `Title: ${story.title} ID: ${story.id} By: ${story.by} Published: ${story.time} Score: ${story.score} Comments: ${story.commentsCount} URL: ${story.url || 'N/A'}`; if (story.text) { result += `\n\nContent:\n${story.text}`; } return result; } - index.ts:8-17 (schema)The Story interface and FormattedStory interface that define the shape of data used by the hn_story handler.
interface Story { id: number; title: string; by: string; time: number; url?: string; score: number; kids?: number[]; text?: string; type: string;