Browse Hacker News feeds, threads, and user profiles with full-text search.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Fetch stories from an HN feed (top, new, best, ask, show, jobs), with title, URL, score, author, and comment count for each story.
Which HN feed to fetch. "top" includes jobs. "ask" and "show" are Ask HN / Show HN posts.
Number of stories to return, at most 100 per call. A larger value is served at 100, and the notice names the offset for the next page. Larger counts take longer.
Number of stories to skip from the start of the feed. Use with count for pagination.
Fetch stories from an HN feed (top, new, best, ask, show, jobs), with title, URL, score, author, and comment count for each story.
| Argument | Type | Description |
|---|---|---|
| feed* | string | Which HN feed to fetch. "top" includes jobs. "ask" and "show" are Ask HN / Show HN posts. |
| count | integer | Number of stories to return, at most 100 per call. A larger value is served at 100, and the notice names the offset for the next page. Larger counts take longer. |
| offset | integer | Number of stories to skip from the start of the feed. Use with count for pagination. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"feed"
],
"properties": {
"feed": {
"enum": [
"top",
"new",
"best",
"ask",
"show",
"jobs"
],
"type": "string",
"description": "Which HN feed to fetch. \"top\" includes jobs. \"ask\" and \"show\" are Ask HN / Show HN posts."
},
"count": {
"type": "integer",
"default": 30,
"maximum": 1000,
"minimum": 1,
"description": "Number of stories to return, at most 100 per call. A larger value is served at 100, and the notice names the offset for the next page. Larger counts take longer."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Number of stories to skip from the start of the feed. Use with count for pagination."
}
},
"additionalProperties": false
}Get an item and its comment tree as a threaded discussion, with child comments resolved recursively. Use depth 0 for an item-only lookup. A long thread comes back in pages: pass the returned nextCursor as cursor to continue.
| Argument | Type | Description |
|---|---|---|
| depth | integer | How many levels of replies to resolve, at most 10 per call. 0 = just the item, no comments. 1 = direct replies only. A larger value is served at 10. Replies below the depth served are never fetched — to read them, raise depth up to 10, or call again with a comment from the deepest level returned, or one notice names, as itemId to drill into its subtree. Ignored when cursor is set: the depth the cursor was issued with applies. |
| cursor | string | nextCursor from a previous call, passed with the same itemId, to continue the traversal at the next unseen comment without repeating or skipping any. Omit to start from the top. |
| itemId* | integer | ID of the story, comment, job, poll, or poll option to fetch the thread for. |
| maxComments | integer | Maximum comments in one response, across all depth levels, at most 200 per call. A larger value is served at 200, and nextCursor continues from there. Highest-ranked top-level comments resolve first; replies fill in only after the level above is exhausted. A response also stops at a fixed 64,000-byte text budget. When either limit stops the traversal, nextCursor continues it. |
Get an HN user profile with karma, about, and optionally their most recent submissions resolved into full items.
| Argument | Type | Description |
|---|---|---|
| username* | string | HN username. Case-sensitive. Trimmed; blank or whitespace-only input is rejected. |
| submissionCount | integer | Page size — how many submissions to resolve per call, at most 50 per call. A larger value is served at 50, and the notice names the submissionOffset for the next page. Only used when includeSubmissions is true. |
| submissionOffset | integer | How many submissions to skip before resolving, counting back from the most recent. Use with submissionCount to page through a long history: request offset 0, then advance by the page size served, and so on. The enrichment block echoes submissionOffset and, when more remain, the offset to send next. Only used when includeSubmissions is true. |
| includeSubmissions | boolean | Resolve the user's most recent submissions into full items. Without this, only the submission count is available. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"username"
],
"properties": {
"username": {
"type": "string",
"minLength": 1,
"description": "HN username. Case-sensitive. Trimmed; blank or whitespace-only input is rejected."
},
"submissionCount": {
"type": "integer",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Page size — how many submissions to resolve per call, at most 50 per call. A larger value is served at 50, and the notice names the submissionOffset for the next page. Only used when includeSubmissions is true."
},
"submissionOffset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "How many submissions to skip before resolving, counting back from the most recent. Use with submissionCount to page through a long history: request offset 0, then advance by the page size served, and so on. The enrichment block echoes submissionOffset and, when more remain, the offset to send next. Only used when includeSubmissions is true."
},
"includeSubmissions": {
"type": "boolean",
"default": false,
"description": "Resolve the user's most recent submissions into full items. Without this, only the submission count is available."
}
},
"additionalProperties": false
}Search Hacker News stories, comments, polls, and jobs via Algolia — by keyword, by filters alone, or both. Filterable by content type, author, parent story, date range, and minimum points.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number for pagination (0-indexed), counted in pages of count results. |
| sort | string | Sort order. "relevance" for best match, "date" for most recent first. |
| tags | string | Filter results by content type: "story", "comment", "poll", or "job", or the story subsets "ask_hn", "show_hn", and "front_page". "front_page" is the HN front page right now (about 30 stories); no filter selects stories from past front pages. The nearest proxy is "story" with dateRange and minPoints — without a query, the default relevance sort ranks by points. Omit to search all types, which also returns poll options (type "pollopt"): records with only an id, author, vote count, and date — hn_get_thread reads an option's text and poll. |
| view | string | How much of each hit to return. "full" includes every field. "compact" omits the two body-text fields — `text` and `highlights.text` — which together can repeat a long comment twice per hit; everything else (id, title, url, domain, author, points, comment count, timestamp, parent story, title highlight, matchedWords) is unchanged. Use "compact" to scan many results, then pass a hit id to hn_get_thread to read the body you skipped. |
| count | integer | Number of results to return, at most 50 per call. A larger value is served at 50: the search runs at count 50 from the hit where the requested page starts, and the notice names the count and page to pass next. With page above 0, that hit (page × count) must begin a page of 50 hits. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"itemId"
],
"properties": {
"depth": {
"type": "integer",
"default": 3,
"maximum": 100,
"minimum": 0,
"description": "How many levels of replies to resolve, at most 10 per call. 0 = just the item, no comments. 1 = direct replies only. A larger value is served at 10. Replies below the depth served are never fetched — to read them, raise depth up to 10, or call again with a comment from the deepest level returned, or one notice names, as itemId to drill into its subtree. Ignored when cursor is set: the depth the cursor was issued with applies."
},
"cursor": {
"type": "string",
"description": "nextCursor from a previous call, passed with the same itemId, to continue the traversal at the next unseen comment without repeating or skipping any. Omit to start from the top."
},
"itemId": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "ID of the story, comment, job, poll, or poll option to fetch the thread for."
},
"maxComments": {
"type": "integer",
"default": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum comments in one response, across all depth levels, at most 200 per call. A larger value is served at 200, and nextCursor continues from there. Highest-ranked top-level comments resolve first; replies fill in only after the level above is exhausted. A response also stops at a fixed 64,000-byte text budget. When either limit stops the traversal, nextCursor continues it."
}
},
"additionalProperties": false
}| query | string | Search terms. Supports simple keywords — Algolia handles stemming and relevance, and a hit must contain every term. Trimmed before searching; blank or whitespace-only input is rejected. To find discussions of a page, pass its URL alone: a query with no spaces that starts with http://, https://, or www. is searched without that prefix, so submissions under either scheme, with or without www., all match. Omit for a filter-only search, which needs at least one of tags, author, storyId, minPoints, or a dateRange bound. |
| author | string | Filter results to a specific author — useful for finding a user's posts on a topic. Matches the HN username exactly and is case-sensitive, so a re-cased name finds nothing. Trimmed before filtering; omit the field to search all authors rather than passing a blank string. |
| storyId | integer | Restrict results to one discussion: the id of a story or poll root, combined with the other filters. Pair with tags "comment" to search within a thread. Take it from hits[].storyId, from a poll hit's own id (poll hits carry no storyId), or from hn_get_thread — the root item's id, or its item.storyId when the root is a comment. A comment id matches nothing. |
| dateRange | object | Filter to a creation-time window with a start, an end, or both. An empty object is rejected — omit dateRange instead. |
| minPoints | integer | Minimum score. Applies to stories and polls, including the ask_hn, show_hn, and front_page subsets. Comments and jobs carry no points in the search index, so any minPoints excludes them — combining it with tags "comment" or "job" is rejected. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"page": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Page number for pagination (0-indexed), counted in pages of count results."
},
"sort": {
"enum": [
"relevance",
"date"
],
"type": "string",
"default": "relevance",
"description": "Sort order. \"relevance\" for best match, \"date\" for most recent first."
},
"tags": {
"enum": [
"story",
"comment",
"poll",
"job",
"ask_hn",
"show_hn",
"front_page"
],
"type": "string",
"description": "Filter results by content type: \"story\", \"comment\", \"poll\", or \"job\", or the story subsets \"ask_hn\", \"show_hn\", and \"front_page\". \"front_page\" is the HN front page right now (about 30 stories); no filter selects stories from past front pages. The nearest proxy is \"story\" with dateRange and minPoints — without a query, the default relevance sort ranks by points. Omit to search all types, which also returns poll options (type \"pollopt\"): records with only an id, author, vote count, and date — hn_get_thread reads an option's text and poll."
},
"view": {
"enum": [
"full",
"compact"
],
"type": "string",
"default": "full",
"description": "How much of each hit to return. \"full\" includes every field. \"compact\" omits the two body-text fields — `text` and `highlights.text` — which together can repeat a long comment twice per hit; everything else (id, title, url, domain, author, points, comment count, timestamp, parent story, title highlight, matchedWords) is unchanged. Use \"compact\" to scan many results, then pass a hit id to hn_get_thread to read the body you skipped."
},
"count": {
"type": "integer",
"default": 30,
"maximum": 1000,
"minimum": 1,
"description": "Number of results to return, at most 50 per call. A larger value is served at 50: the search runs at count 50 from the hit where the requested page starts, and the notice names the count and page to pass next. With page above 0, that hit (page × count) must begin a page of 50 hits."
},
"query": {
"type": "string",
"minLength": 1,
"description": "Search terms. Supports simple keywords — Algolia handles stemming and relevance, and a hit must contain every term. Trimmed before searching; blank or whitespace-only input is rejected. To find discussions of a page, pass its URL alone: a query with no spaces that starts with http://, https://, or www. is searched without that prefix, so submissions under either scheme, with or without www., all match. Omit for a filter-only search, which needs at least one of tags, author, storyId, minPoints, or a dateRange bound."
},
"author": {
"type": "string",
"minLength": 1,
"description": "Filter results to a specific author — useful for finding a user's posts on a topic. Matches the HN username exactly and is case-sensitive, so a re-cased name finds nothing. Trimmed before filtering; omit the field to search all authors rather than passing a blank string."
},
"storyId": {
"type": "integer",
"maximum": 9007199254740991,
"description": "Restrict results to one discussion: the id of a story or poll root, combined with the other filters. Pair with tags \"comment\" to search within a thread. Take it from hits[].storyId, from a poll hit's own id (poll hits carry no storyId), or from hn_get_thread — the root item's id, or its item.storyId when the root is a comment. A comment id matches nothing.",
"exclusiveMinimum": 0
},
"dateRange": {
"type": "object",
"properties": {
"end": {
"type": "string",
"pattern": "^(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?(?:Z|[+-](\\d{2}):(\\d{2}))?)?)?)?$",
"description": "Exclusive upper bound — only items created strictly before this instant match. Same formats and UTC reading as start, and must be later than start. A date-only end excludes that whole UTC day: to include it, pass the next day or a full timestamp."
},
"start": {
"type": "string",
"pattern": "^(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?(?:Z|[+-](\\d{2}):(\\d{2}))?)?)?)?$",
"description": "Exclusive lower bound — only items created strictly after this instant match. ISO 8601: YYYY, YYYY-MM, YYYY-MM-DD, or YYYY-MM-DDThh:mm[:ss[.sss]] with an optional Z or ±hh:mm offset. Reduced and date-only forms mean UTC midnight at the start of that period; a date-time without an offset is read as UTC."
}
},
"description": "Filter to a creation-time window with a start, an end, or both. An empty object is rejected — omit dateRange instead."
},
"minPoints": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Minimum score. Applies to stories and polls, including the ask_hn, show_hn, and front_page subsets. Comments and jobs carry no points in the search index, so any minPoints excludes them — combining it with tags \"comment\" or \"job\" is rejected."
}
},
"additionalProperties": false
}