Skip to main content
Glama

MCP Hacker News

by paabloLC
tools.ts14.9 kB
import { fetchFromAPI, fetchMultipleItems } from "./fetch-actions.js"; import { formatTime } from "./helpers.js"; import { HackerNewsItem, HackerNewsUser, HackerNewsUpdates, } from "./types-hn.js"; // ***************************************************** // *********** Hacker News MCP Tools *********** // ***************************************************** export const tools = [ { name: "getTopStories", description: "Get top stories from Hacker News (up to 500 available)", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of stories to return (default: 10, max: 50)", default: 10, }, }, }, execute: async (args: any) => { const limit = Math.min(args.limit || 10, 50); const topIds = await fetchFromAPI<number[]>("/topstories"); if (!topIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch top stories" }), }, ], }; } const stories = await fetchMultipleItems(topIds, 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: `Top ${limit} stories from Hacker News`, stories: formattedStories, }, null, 2 ), }, ], }; }, }, { name: "getBestStories", description: "Get best stories from Hacker News (algorithmically ranked)", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of stories to return (default: 10, max: 50)", default: 10, }, }, }, execute: async (args: any) => { const limit = Math.min(args.limit || 10, 50); const bestIds = await fetchFromAPI<number[]>("/beststories"); if (!bestIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch best stories" }), }, ], }; } const stories = await fetchMultipleItems(bestIds, 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: `Best ${limit} stories from Hacker News`, stories: formattedStories, }, null, 2 ), }, ], }; }, }, { name: "getNewStories", description: "Get newest stories from Hacker News", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of stories to return (default: 10, max: 50)", default: 10, }, }, }, execute: async (args: any) => { const limit = Math.min(args.limit || 10, 50); const newIds = await fetchFromAPI<number[]>("/newstories"); if (!newIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch new stories" }), }, ], }; } const stories = await fetchMultipleItems(newIds, 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} stories from Hacker News`, stories: formattedStories, }, null, 2 ), }, ], }; }, }, { name: "getAskHNStories", description: "Get Ask 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 askIds = await fetchFromAPI<number[]>("/askstories"); if (!askIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch Ask HN stories" }), }, ], }; } const stories = await fetchMultipleItems(askIds, limit); const formattedStories = stories.map((story) => ({ id: story.id, title: story.title, 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}`, text: story.text, })); return { content: [ { type: "text", text: JSON.stringify( { message: `Latest ${limit} Ask HN stories`, stories: formattedStories, }, null, 2 ), }, ], }; }, }, { 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 ), }, ], }; }, }, { name: "getJobStories", description: "Get job postings from Hacker News", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of jobs to return (default: 10, max: 30)", default: 10, }, }, }, execute: async (args: any) => { const limit = Math.min(args.limit || 10, 30); const jobIds = await fetchFromAPI<number[]>("/jobstories"); if (!jobIds) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch job stories" }), }, ], }; } const jobs = await fetchMultipleItems(jobIds, limit); const formattedJobs = jobs.map((job) => ({ id: job.id, title: job.title, url: job.url, score: job.score, author: job.by, time: job.time ? formatTime(job.time) : "unknown", hnUrl: `https://news.ycombinator.com/item?id=${job.id}`, text: job.text, })); return { content: [ { type: "text", text: JSON.stringify( { message: `Latest ${limit} job postings`, jobs: formattedJobs, }, null, 2 ), }, ], }; }, }, { name: "getItem", description: "Get a specific item (story, comment, job, etc.) by ID", inputSchema: { type: "object", properties: { id: { type: "number", description: "The item ID to fetch", }, }, required: ["id"], }, execute: async (args: any) => { const item = await fetchFromAPI<HackerNewsItem>(`/item/${args.id}`); if (!item) { return { content: [ { type: "text", text: JSON.stringify({ error: "Item not found" }) }, ], }; } const formattedItem = { id: item.id, type: item.type, title: item.title, url: item.url, score: item.score, author: item.by, time: item.time ? formatTime(item.time) : "unknown", comments: item.descendants || 0, text: item.text, parent: item.parent, kids: item.kids, hnUrl: `https://news.ycombinator.com/item?id=${item.id}`, }; return { content: [ { type: "text", text: JSON.stringify( { message: `Item ${args.id}`, item: formattedItem, }, null, 2 ), }, ], }; }, }, { name: "getUser", description: "Get user profile information", inputSchema: { type: "object", properties: { username: { type: "string", description: "The username to look up", }, }, required: ["username"], }, execute: async (args: any) => { const user = await fetchFromAPI<HackerNewsUser>(`/user/${args.username}`); if (!user) { return { content: [ { type: "text", text: JSON.stringify({ error: "User not found" }) }, ], }; } const formattedUser = { id: user.id, karma: user.karma, created: user.created ? formatTime(user.created) : "unknown", about: user.about, submittedCount: user.submitted?.length || 0, recentSubmissions: user.submitted?.slice(0, 10) || [], }; return { content: [ { type: "text", text: JSON.stringify( { message: `User profile for ${args.username}`, user: formattedUser, }, null, 2 ), }, ], }; }, }, { name: "getComments", description: "Get comments for a specific item", inputSchema: { type: "object", properties: { id: { type: "number", description: "The item ID to fetch comments for", }, depth: { type: "number", description: "Maximum depth of comments to fetch (default: 2)", default: 2, }, }, required: ["id"], }, execute: async (args: any) => { const item = await fetchFromAPI<HackerNewsItem>(`/item/${args.id}`); if (!item || !item.kids) { return { content: [ { type: "text", text: JSON.stringify({ error: "No comments found" }), }, ], }; } const depth = Math.min(args.depth || 2, 3); const comments = await fetchMultipleItems(item.kids, 20); const formattedComments = comments.map((comment) => ({ id: comment.id, author: comment.by, time: comment.time ? formatTime(comment.time) : "unknown", text: comment.text, parent: comment.parent, kids: comment.kids?.length || 0, hnUrl: `https://news.ycombinator.com/item?id=${comment.id}`, })); return { content: [ { type: "text", text: JSON.stringify( { message: `Comments for item ${args.id}`, totalComments: item.descendants || 0, topLevelComments: formattedComments.length, comments: formattedComments, }, null, 2 ), }, ], }; }, }, { name: "getMaxItemId", description: "Get the current maximum item ID", inputSchema: { type: "object", properties: {} }, execute: async (args: any) => { const maxId = await fetchFromAPI<number>("/maxitem"); return { content: [ { type: "text", text: JSON.stringify( { message: "Current maximum item ID", maxItemId: maxId, }, null, 2 ), }, ], }; }, }, { name: "getUpdates", description: "Get recently updated items and profiles", inputSchema: { type: "object", properties: {} }, execute: async (args: any) => { const updates = await fetchFromAPI<HackerNewsUpdates>("/updates"); if (!updates) { return { content: [ { type: "text", text: JSON.stringify({ error: "Failed to fetch updates" }), }, ], }; } return { content: [ { type: "text", text: JSON.stringify( { message: "Recent updates", recentlyUpdatedItems: updates.items.slice(0, 10), recentlyUpdatedProfiles: updates.profiles.slice(0, 10), }, null, 2 ), }, ], }; }, }, ];

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/paabloLC/mcp-hacker-news'

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