hn-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@hn-mcpshow me the top 10 stories on Hacker News right now"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
hn-mcp
An MCP server for Hacker News. Built against the 2026-07-28 protocol revision and MCP Python SDK v2.
Five read-only tools:
Tool | Does |
| Browse a feed (top / new / best / ask / show / job) |
| One story, job or poll by id |
| A story plus its comment thread, flattened and capped |
| Full-text and date-filtered search over all HN history |
| A user profile |
Run it
uv sync
uv run mcp dev src/hn_mcp/server.py # Inspector, stdio
uv run mcp run src/hn_mcp/server.py --transport streamable-http # HTTP
uv run pytest # 45 tests, no networkRelated MCP server: HN-MCP
Architecture
server.py MCP layer tool schemas, annotations, response models
| no I/O, no parsing
v
hn_client.py service layer Firebase + Algolia, fan-out, BFS, caching
|
v
models.py data layer Story / Comment / User, normalization
cache.py TTL cacheDependencies point one way only. models.py does not know MCP exists, which
is why it can be tested without starting a server.
Design notes
Two upstreams, one vocabulary. Feeds, items and users come from HN's
Firebase API; search comes from HN's Algolia index, because Firebase has no
search endpoint at all. They name the same concepts differently (by vs
author, score vs points, descendants vs num_comments), so both
parse into the same models. The model never learns there were two APIs.
Server-side fan-out. Firebase returns id lists, so a 30-story feed is 31 requests. Doing that in the server costs one tool call; making the model walk the ids costs 31 round trips with raw JSON in context each time.
Comment trees are breadth-first. HN ranks kids by its own ordering, so
breadth-first spends the comment budget on the highest-ranked comments across
the whole thread instead of exhausting it inside one argument. Nesting is
carried by an integer depth rather than nested objects, so the list stays
valid wherever it is cut.
Truncation is reported, never silent. When caps bite, truncated is true
and truncation_hint names the argument to raise. Each comment carries
num_replies, so the model can tell "no replies" from "replies you were not
shown" — a distinction it cannot otherwise make.
Errors are prompts. A failure says what went wrong, which tool to use
instead, and hands back the id needed to recover. Asking get_story for a
comment id returns the parent story's id rather than a validation error.
Constraints live in types. Literal feeds become schema enums;
Field(ge=, le=) becomes minimum/maximum. The docstring explains what
options mean; the type system enforces which are legal.
Stateless. The 2026-07-28 revision removed sessions, so the same code serves stdio and Streamable HTTP with no session plumbing. Any request can land on any instance.
Verified against live HN
search_stories and get_discussion have been run against the real API.
Doing so found three bugs the fixtures could not:
Breadth-first traversal with only a total cap spent the entire comment budget on top-level comments and returned one reply out of forty. Real threads are far wider than they are deep. Fixed with
max_top_level.The truncation hint asserted a rule of thumb instead of measuring, and was wrong about which comments had hidden replies. It now counts them.
Per-comment truncation was computed and discarded, leaving a trailing ellipsis indistinguishable from the author's own. Now
text_truncated.
tests/fake_hn.py carries a wide thread modelled on the real one so the
first of those cannot regress.
Not done yet
list_stories,get_storyandget_userhave not been exercised against live HN, only against fixtures.Never loaded into a host, so tool selection is untested: whether a model picks
search_storiesoverlist_storiesunprompted is the real measure of the docstrings, and it has not been measured.No rate limiting. Fine for stdio, needed before a public HTTP deployment.
search_storiesonly searches stories, not comments (tagsis fixed).
Available Tools
5 toolsget_discussionARead-only
Read a Hacker News comment thread in one call.
Returns the story plus a flat list of comments ordered breadth-first:
all top-level replies, then all of their replies, and so on. HN ranks
kids by its own ordering, so breadth-first spends the comment budget
on the highest-ranked comments across the whole thread rather than
exhausting it inside one argument.
Nesting is carried by each comment's depth rather than by nested
objects, so the list can be cut at any point and stay valid.
Popular threads are wide - often over a hundred top-level comments - so max_top_level bounds how many of them are opened. That is what leaves budget for replies: without it the comment cap is spent entirely on opening statements and no reply is ever reached. Lower it to read an argument in depth, raise it to survey opinions broadly.
Big threads will not fit. When anything is omitted, truncated is true
and truncation_hint names which argument to raise and why.
comments_with_hidden_replies counts how many returned comments have
replies that were left out, and top_level_shown against
top_level_total shows how much of the first level you are seeing.
Deleted and dead comments are skipped entirely.
| Name | Required | Description | Default |
|---|---|---|---|
| story_id | Yes | ||
| max_depth | No | How deep to descend. 0 is top-level replies only, 2 is a good default for getting the sense of a thread. | |
| max_comments | No | Hard cap on total comments returned. | |
| comment_chars | No | Per-comment character cap. | |
| max_top_level | No | How many top-level comments to open. Low values leave budget for replies and give conversation; high values give a survey of opinions with few replies. |
Output Schema
| Name | Required | Description |
|---|---|---|
| story | Yes | |
| comments | Yes | Flat list ordered breadth-first. Use each comment's `depth` to reconstruct nesting. |
| returned | Yes | Comments in this response. |
| truncated | Yes | True when comments were omitted by a depth or count cap. |
| top_level_shown | Yes | Top-level comments included, bounded by max_top_level. |
| top_level_total | Yes | Top-level comments the story actually has. |
| total_in_thread | No | Comments in the whole thread, including omitted. |
| truncation_hint | No | When truncated, names the argument to raise to see more. |
| comments_with_hidden_replies | Yes | Returned comments that have replies not included here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond readOnlyHint/openWorldHint: breadth-first ordering, depth carried as a field rather than nesting, deleted/dead comments skipped entirely, and exactly how truncation manifests. The truncation contract (truncated flag, truncation_hint, comments_with_hidden_replies, top_level_shown vs top_level_total) is precisely the behavioral context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and ordering, then parameters, then truncation, then exclusions — a sensible arc with no filler sentences. It runs longer than most definitions need and the truncation paragraph restates fields the output schema likely already names.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter read tool with annotations and an output schema, everything an agent needs is present: what comes back, in what order, how to tune the budget, and how to detect partial results. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 80%, so the baseline would be 3, but the description adds genuine meaning to max_top_level by explaining the budget trade-off against max_comments ("without it the comment cap is spent entirely on opening statements"). story_id and comment_chars get no added treatment.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ("Read a Hacker News comment thread in one call") and clarifies the return shape ("the story plus a flat list of comments"), which implicitly separates it from get_story. It never names a sibling explicitly, so the boundary with get_story is inferred rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete operational guidance: lower max_top_level to read an argument in depth, raise it to survey opinions broadly, and notes big threads will not fit. It stops short of saying when to reach for get_discussion instead of get_story or search_stories.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_storyARead-only
Fetch one Hacker News story, job or poll by id.
Use this when an id is already known - from list_stories, from search_stories, or from a news.ycombinator.com/item?id=N URL. Returns the story only; call get_discussion to read its comments.
| Name | Required | Description | Default |
|---|---|---|---|
| story_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | HN item id. Pass to get_discussion for comments. |
| url | No | Outbound link. Null for Ask HN and text-only posts. |
| text | No | Body text for Ask HN / Show HN posts, HTML stripped. |
| type | No | story, job or poll. |
| score | No | Upvote count. |
| title | No | Story headline. |
| author | No | Submitter's username. |
| hn_url | Yes | Permalink to the HN discussion page. |
| created_at | No | Submission time, ISO 8601 UTC. |
| num_comments | No | Total comments in the thread, including nested. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint and openWorldHint, and the description usefully adds that the payload is the story only and that comments require a separate call. It stops short of covering failure behavior for deleted or invalid ids, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences with the core action first, then the when-to-use routing, then the scope boundary. No filler and no repetition of the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape needn't be explained, and the description correctly covers the single-item scope and the comments boundary. Only the error/invalid-id path is unaddressed, a minor omission for a one-parameter read.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single story_id parameter has 0% schema description coverage, so the description must compensate; it does so partly by framing the id as obtainable from list/search results or an item?id=N URL. It never states the id is an integer or that it must be a valid HN item id, so the semantics remain slightly loose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb and resource: 'Fetch one Hacker News story, job or poll by id', which also enumerates the item types covered. It cleanly distinguishes itself from get_discussion, which handles the comment side of the same item.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit trigger condition: use when an id is already known, with the three concrete sources (list_stories, search_stories, or a news.ycombinator.com/item?id=N URL). It also names the alternative to reach for comments (get_discussion), so nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_userARead-only
Look up a Hacker News user profile.
Returns karma, account age, bio and submission count. Usernames are case-sensitive, and only accounts with public activity are exposed by HN's API.
This returns the profile, not the user's posts. To find what someone submitted, use search_stories.
| Name | Required | Description | Default |
|---|---|---|---|
| username | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Username. Case-sensitive. |
| about | No | Profile bio, HTML stripped. |
| karma | No | Accumulated karma. |
| hn_url | Yes | Permalink to the user's HN profile. |
| created_at | No | Account creation time, ISO 8601 UTC. |
| submission_count | No | Items in the user's public history. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint; the description adds genuinely new behavioral context the agent needs: case-sensitive usernames and the API's restriction to accounts with public activity. It does not cover failure modes for unknown or private accounts, which leaves a small gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then behavior caveats, then the routing note — good ordering, no filler. The "Returns karma, account age, bio and submission count" sentence partly duplicates the output schema, so it is not maximally lean.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-only lookup with an output schema, everything an agent needs is present: what it fetches, how usernames behave, what HN will not expose, and where to go for submissions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% (no parameter descriptions), so the description must carry the burden — and it does, flagging that usernames are case-sensitive, a real invocation constraint. It stops short of giving format examples or length/shape guidance for the single required parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Look up a Hacker News user profile") and immediately distinguishes itself from the profile-vs-content sibling boundary. An agent can tell this apart from search_stories without inspecting either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the exclusion ("returns the profile, not the user's posts") and routes to the alternative ("use search_stories"). That is the when-to-use/when-not pattern done properly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_storiesARead-only
Browse Hacker News story feeds.
Returns each story's title, score, author, comment count and links - enough to answer "what's on HN right now" in a single call.
Feeds: top front page, ranked by HN's algorithm. Also contains job posts new most recently submitted, unranked best highest-scoring stories of the last few days ask Ask HN text and discussion posts show Show HN project launches job YC portfolio job postings
top and new hold up to 500 ids; ask, show and job hold up to 200, so a large limit on those feeds returns fewer stories than requested.
Every story carries an id. Pass it to get_discussion to read comments -
this tool returns no comment text.
| Name | Required | Description | Default |
|---|---|---|---|
| feed | No | Which HN feed to read. top is the front page, new is newest-first, best is multi-day high scorers, and ask/show/job are the Ask HN, Show HN and jobs sections. See the tool description for when to choose each. | top |
| limit | No | How many stories to return, 1-100. | |
| offset | No | Stories to skip, for paging through a feed. | |
| min_score | No | Drop stories scoring below this. Omit for no filter. |
Output Schema
| Name | Required | Description |
|---|---|---|
| stories | Yes | |
| returned | Yes | Stories in this response. |
| feed_total | Yes | Ids available in the feed before filtering. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description adds real behavioral context beyond that: id-pool caps (500 for top/new, 200 for ask/show/job) mean a large limit silently returns fewer stories, and top includes job posts. Rate limiting and failure behavior are not addressed, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and return payload, then a scannable feed list and a closing handoff to get_discussion. The feed list partially restates the enum, but the ranking notes and id-cap caveat earn their space. Minor redundancy, no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation is not required, yet the description still summarizes the shape briefly. With all four params documented in-schema and the id-cap pitfall disclosed, an agent has everything needed to call this correctly and know when to pivot to get_discussion.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description still adds meaning the schema lacks — the ranking logic behind each feed and the fact that 'top' carries job posts — which helps an agent reason about feed choice. It says nothing extra about limit, offset, or min_score, but the schema documents those fully.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('Browse Hacker News story feeds') and immediately specifies the payload returned: title, score, author, comment count, links. The per-feed enumeration makes it unmistakable versus siblings like get_story or search_stories, which operate on a single item or a query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives selection criteria for each of the six feeds (ranked front page, newest-first, multi-day high scorers, section feeds) and explicitly rules out comment reading: 'Pass it to get_discussion... this tool returns no comment text.' It does not mention search_stories as the alternative for keyword lookup, so the alternatives coverage is partial rather than exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_storiesARead-only
Full-text search across all of Hacker News history.
Use this for anything the feeds cannot answer: a topic, a company, a phrase, or anything older than the current front page. The feed tools only see what is on HN right now; this sees everything ever posted.
sort="relevance" is right for topic questions. sort="date" is right for "what is the latest on X", since the best match may be years old.
days_back filters by age, so days_back=30 with sort="date" answers "what has HN said about X recently".
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Zero-based page of results. | |
| sort | No | relevance ranks by match quality; date is newest first. | relevance |
| limit | No | Hits to return. | |
| query | Yes | ||
| days_back | No | Only items from the last N days. Omit to search all history. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| returned | Yes | Hits in this response. |
| total_hits | Yes | Total matches, which may exceed `returned`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnlyHint) and reach (openWorldHint). The description adds genuinely useful behavioral context on top: results span all history, ranked results may be years stale, and days_back constrains age rather than relevance. It stops short of pagination or result-shape behavior, but the output schema covers returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core capability, then guidance, then parameter advice in short lines. There is mild redundancy between 'across all of Hacker News history' and 'this sees everything ever posted', but every block still carries actionable content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations and an output schema present, the description's remaining obligation is selection guidance and mode choice, both of which it satisfies. Only pagination behavior is unaddressed, a minor gap for a 5-parameter read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, so the baseline is 3, and the description exceeds it by explaining when each sort enum value is the right choice rather than just what it does. It also explains the days_back semantics in combination with sort="date", which the schema does not. Page and limit are left to the schema, which is acceptable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (full-text search over stories) plus explicit scope: all of HN history, versus only the current front page. It draws the boundary against the feed siblings (list_stories) without needing the schema opened.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both directions: use this when the feeds cannot answer, and the feed tools only see current HN. It further routes between the tool's own modes, saying relevance suits topic questions and date suits 'latest on X', including why (best match may be years old). No inference left to the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
get_discussion - First observed
get_story - First observed
get_user - First observed
list_stories - First observed
search_stories
TDQS
Scored across 5 tools
Each tool targets a distinct resource+action: search_stories is full-text history search, list_stories is live feeds, get_story is a single item by id, get_discussion is a comment thread, and get_user is a profile. The descriptions actively contrast the easily-confused pairs (search vs. feeds, story vs. discussion), so an agent can pick correctly without guessing.
All five tools follow the same verb_noun snake_case pattern (search_stories, list_stories, get_story, get_discussion, get_user). The verbs also map cleanly onto intent: list for collections, search for queries, get for single lookups.
Five tools is a tight, well-scoped surface for the Hacker News domain — search, browse, item, thread, user — with no filler or redundant variants. Each tool covers a workflow the others cannot answer.
Core lifecycle is covered: discovery via feeds/search, item retrieval, comment reading, and user lookup. The main gap is author-scoped browsing — get_user explicitly says to use search_stories for a user's submissions, but search_stories is documented only as text search, so per-user submission listing and single-comment/subthread fetch are workarounds rather than first-class operations.
Maintenance
Related MCP Connectors
Browse Hacker News feeds, threads, and user profiles with full-text search.
HN front-page, Algolia full-text search, and Show HN launch tracker.
Hacker News MCP — search and retrieve stories from Hacker News
Hacker News front page and search: stories, points, comments, and which stories link to your domain.
1
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to search, retrieve, and interact with HackerNews content including stories, comments, polls, and user information. Provides comprehensive access to all HackerNews API endpoints with 15 specialized tools for content discovery and analysis.1536 npm5MIT
- AlicenseAqualityDmaintenanceEnables browsing Hacker News, searching discussions, analyzing users, and tracking tech trends with zero setup required—no API keys or authentication needed.516 npm6MIT
- AlicenseAqualityDmaintenanceProvides programmatic access to Hacker News content via the HN Algolia API. It enables AI assistants to search stories, retrieve comments, access user profiles, and explore the front page in real-time.936 npmMIT
- FlicenseAqualityDmaintenanceEnables AI assistants to read and search Hacker News for top stories, comments, user profiles, and job listings using the Firebase and Algolia APIs. It facilitates natural language research into community discussions and technological trends across the HN platform.8-