Hacker News MCP Server for Claude — Top Stories, Comments & Search Without an API Key
We pulled the Hacker News logic out of our internal content agent and shipped it as a standalone MCP server. Now any Claude session can read HN — no API key, no signup, install in 30 seconds.
necl-hn-mcpis a free open-source MCP server giving Claude access to Hacker News across 5 tools:hn_top_stories,hn_category,hn_get_story,hn_get_comments,hn_search.- No API key required — HN's Firebase API and Algolia search are public and unlimited (we tested 200+ requests/minute without throttling).
- Install via
uvxin 30 seconds — no Python venv setup, no PyPI dependency, no config file edits beyondmcp.json. - Under 200 lines of Python total — auditable in 5 minutes, MIT-licensed.
- Returns clean structured dicts that LLMs chain in 2-3 tool calls (
search → top story → comments → summary). - Production-tested: powers our internal HN→content pipeline that's been running 3× daily for 6 months. We extracted the MCP from it in about 2 hours.
This is our MCP-101 entry point, so we wrote it for two readers: the developer who just wants to give Claude access to Hacker News, and the developer who keeps hearing "MCP" and wants a plain explanation with a working example. You can read it top to bottom or jump to the part you need.
What is MCP, explained from scratch
Model Context Protocol (MCP) is a standard, released by Anthropic in late 2024, for connecting AI agents to external data and tools. To understand why it matters, it helps to see the problem it replaced.
The problem before MCP
Say you want Claude to read your GitHub issues, GPT-4 to query your Postgres, and a third model to post to Slack. Before MCP, each of those connections was custom plumbing. You wrote a GitHub adapter for one model's tool-calling format, a separate Postgres adapter for another, and a Slack adapter for a third. Change the model and you rewrote the adapters. Add a tool and you wrote it three times, once per model.
This is the classic M×N integration problem: M models times N tools equals a lot of glue code that nobody wants to maintain. Every team building AI features ended up re-implementing the same Slack connector, the same database reader, the same file-system bridge.
What the protocol standardizes
MCP collapses M×N into M+N. A tool is built once as an MCP server. Any model with an MCP client can use it. The server author never thinks about which model will call them; the model never thinks about how the tool is implemented.
What the protocol actually pins down:
- Tool discovery. A client asks a server "what can you do?" and gets back a typed list: tool names, descriptions, and input schemas. The model reads these descriptions to decide what to call.
- Tool invocation. The client sends a structured call (tool name plus arguments), the server runs it and returns a structured result. Under the hood this is JSON-RPC 2.0.
- Other primitives. Beyond tools, MCP also defines resources (readable data the model can pull in, like files) and prompts (reusable prompt templates a server can offer). Most servers, including ours, start with tools because that is the most common need.
Think of it like USB for AI: one cable, any device. You do not buy a different cable for every peripheral, and you do not write a different connector for every model.
stdio vs HTTP/SSE servers
MCP servers run in one of two transport modes, and the difference is mostly about where the server lives.
- stdio (standard input/output). The server runs as a local subprocess on your machine. The client launches it, then talks to it over stdin/stdout. This is the simplest mode, needs no network, and is ideal for tools that touch your local environment or hit public APIs.
necl-hn-mcpuses stdio. - HTTP / SSE (Server-Sent Events). The server runs remotely and the client connects over the network. This suits multi-user hosted services, tools behind a company firewall, or anything you do not want every user to install locally.
Both modes deliver data into the model the same way. The transport is an implementation detail; the tool list and the call/response cycle look identical to the model.
How a client discovers and calls a tool
A client is the app the model runs inside — Claude Desktop, Claude Code, Cursor, and others all ship MCP clients. The lifecycle is short:
- Launch. The client reads its config (for example
mcp.json), and for each stdio server it spawns the subprocess. - Handshake + discovery. The client and server exchange capabilities, then the client asks for the tool list. Now the model knows
hn_searchexists, what it does, and what arguments it takes. - Reasoning. You ask a question. The model decides whether a tool helps, and if so, which one and with what arguments.
- Call + response. The client sends the call to the server, the server returns a result, the model reads it and keeps reasoning — often calling another tool with the output of the first.
The point is that you never wrote integration code. You added a few lines to a config file, and the model figured out the rest from the tool descriptions.
Why MCP matters for Claude specifically
With MCP servers installed, Claude Desktop and Claude Code stop being a chat box and start being an agent wired into your real tools. Claude can:
- Read your filesystem and codebase
- Query your Postgres
- Post to Slack
- Search Hacker News (with our server)
- Anything else someone has built a server for
There are 20,000+ MCP servers in public catalogs as of mid-2026. Most are stdio (run locally), some are HTTP/SSE (run remotely). The ecosystem grew fast precisely because building a server is small work — which we will show you later in this article.
Why we built necl-hn-mcp
We have been running an internal content agent for 6 months — a Telegram bot that aggregates Hacker News three times a day and drafts posts for the team. It is the engine behind our NeCL Telegram channel. Most of what we write riffs off something we noticed on HN that day.
The HN-fetching logic in that agent was clean enough that pulling it out as a standalone MCP took us exactly 2 hours. We tested the full tool chain in Claude Desktop the same evening. Now any AI agent — not just our internal bot — can read HN through the same plumbing. The server is live on mcp.so and Glama.
What the 5 tools do
Each tool below returns a plain Python dict that serializes to clean JSON. We show a realistic prompt and a trimmed version of what comes back so you can see what the model actually reads.
hn_top_stories(limit, hours)
Top N stories from the last N hours, ranked by score. Default: top 10 from the last 24h.
"Show me the top 5 HN stories from the last 6 hours"
{
"stories": [
{
"id": 38420000,
"title": "Show HN: I built a local-first MCP server",
"url": "https://example.com/mcp",
"score": 412,
"by": "patio11",
"descendants": 188,
"time": "2026-06-10T08:12:00Z"
}
],
"count": 5,
"window_hours": 6
}
hn_category(category, limit)
Pull stories from one specific category: top, new, best, ask, show, job.
"What's in Ask HN right now?" "Show me the top 3 Show HN posts about AI agents"
{
"category": "show",
"stories": [
{
"id": 38421111,
"title": "Show HN: Open-source voice dictation for macOS",
"url": "https://github.com/...",
"score": 96,
"by": "necl",
"descendants": 41
}
],
"count": 3
}
hn_get_story(story_id)
Full metadata for a single story — title, URL, score, author, comment count, posted time.
"Get details for HN story 38420000"
{
"id": 38420000,
"title": "Show HN: I built a local-first MCP server",
"url": "https://example.com/mcp",
"score": 412,
"by": "patio11",
"descendants": 188,
"time": "2026-06-10T08:12:00Z",
"type": "story"
}
hn_get_comments(story_id, limit)
Top-level comment thread for a story, in HN ranking order. Each comment carries replies_count so the model knows whether a deeper discussion exists worth fetching.
"What are people saying about HN story 38420000?"
{
"story_id": 38420000,
"comments": [
{
"id": 38420042,
"by": "tptacek",
"text": "stdio transport is the right default for local tools because...",
"replies_count": 7
}
],
"count": 1
}
hn_search(query, sort, limit)
Full-text search across HN posts and comments via the Algolia HN API. Sort by relevance or date.
"Search HN for posts about RAG performance, sorted by date" "Find recent HN discussions on Claude vs GPT-4"
{
"query": "RAG performance",
"sort": "date",
"hits": [
{
"id": 38419000,
"title": "Ask HN: How do you benchmark RAG performance?",
"points": 73,
"num_comments": 54,
"created_at": "2026-06-09T19:40:00Z"
}
],
"total_hits": 312
}
The search tool returns a clean shape — {query, sort, hits, total_hits} — instead of the raw, deeply nested Algolia payload. That keeps the model's context small and its tool-chaining cheap.
Install in 30 seconds
The recommended path uses uvx (a modern Python install runner — no venv, no pip install into a global environment).
Add this to your mcp.json (Claude Desktop, Claude Code, or any MCP client):
{
"mcpServers": {
"necl-hn": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/adjacentai/necl-hn-mcp.git",
"necl-hn-mcp"
]
}
}
}
Restart Claude. Type a HN question. It just works.
If you do not have uvx yet: pip install uv or brew install uv.
MCP vs function calling vs plugins
These three terms get mixed up constantly. They overlap, but they sit at different layers.
| Function calling | Plugins (legacy) | MCP | |
|---|---|---|---|
| What it is | A model's raw ability to emit a structured tool call | Vendor-specific app integrations (e.g. early ChatGPT plugins) | An open protocol for exposing tools, resources, and prompts |
| Scope | One model's API | One vendor's product | Cross-model, cross-client |
| Who defines the tool | You, inline in each API request | The plugin author, per platform spec | The server author, once, for everyone |
| Reuse across apps | None — rewrite per app | Limited to that platform | High — any MCP client can use the server |
| Transport | In the API call | HTTP via vendor manifest | stdio or HTTP/SSE |
| Best for | A single app you control end to end | (Largely superseded) | Tools you want many agents and clients to reuse |
The short version: function calling is the mechanism a model uses to request a tool. MCP is the standard for packaging and discovering those tools so you write them once. necl-hn-mcp is an MCP server; when Claude uses it, function calling is what happens under the hood.
How to build your own MCP server
The fastest path in Python is FastMCP, the high-level layer in the official MCP SDK. You write plain async functions, decorate them, and the SDK handles the protocol, schema generation, and stdio loop. Here is the same three-file shape we used for necl-hn-mcp.
1. The data client (hn.py)
Keep your API logic separate from the MCP layer. This makes the tools testable and the server file thin.
# hn.py — async client for the public HN APIs
import httpx
FIREBASE = "https://hacker-news.firebaseio.com/v0"
ALGOLIA = "https://hn.algolia.com/api/v1"
async def get_item(item_id: int) -> dict:
async with httpx.AsyncClient() as c:
r = await c.get(f"{FIREBASE}/item/{item_id}.json")
r.raise_for_status()
return r.json()
async def top_story_ids(limit: int) -> list[int]:
async with httpx.AsyncClient() as c:
r = await c.get(f"{FIREBASE}/topstories.json")
r.raise_for_status()
return r.json()[:limit]
async def search(query: str, sort: str, limit: int) -> dict:
path = "search_by_date" if sort == "date" else "search"
async with httpx.AsyncClient() as c:
r = await c.get(f"{ALGOLIA}/{path}",
params={"query": query, "hitsPerPage": limit})
r.raise_for_status()
return r.json()
2. The tools (server.py)
The decorator is the whole trick. @mcp.tool() reads your function's name, docstring, and type hints, and turns them into the schema the model discovers. The docstring is not decoration — it is what the model reads to decide when to call the tool, so write it like a prompt.
# server.py — expose HN functions as MCP tools
from mcp.server.fastmcp import FastMCP
import hn
mcp = FastMCP("necl-hn")
@mcp.tool()
async def hn_top_stories(limit: int = 10, hours: int = 24) -> dict:
"""Get the top Hacker News stories from the last N hours, ranked by score."""
ids = await hn.top_story_ids(limit)
stories = [await hn.get_item(i) for i in ids]
return {"stories": stories, "count": len(stories), "window_hours": hours}
@mcp.tool()
async def hn_get_comments(story_id: int, limit: int = 10) -> dict:
"""Get the top-level comments for a Hacker News story, in ranking order."""
story = await hn.get_item(story_id)
kid_ids = (story.get("kids") or [])[:limit]
comments = []
for cid in kid_ids:
c = await hn.get_item(cid)
comments.append({
"id": c["id"],
"by": c.get("by"),
"text": c.get("text", ""),
"replies_count": len(c.get("kids") or []),
})
return {"story_id": story_id, "comments": comments, "count": len(comments)}
@mcp.tool()
async def hn_search(query: str, sort: str = "relevance", limit: int = 10) -> dict:
"""Full-text search across HN posts and comments. sort = 'relevance' or 'date'."""
raw = await hn.search(query, sort, limit)
hits = [{
"id": h.get("objectID"),
"title": h.get("title"),
"points": h.get("points"),
"num_comments": h.get("num_comments"),
"created_at": h.get("created_at"),
} for h in raw.get("hits", [])]
return {"query": query, "sort": sort, "hits": hits,
"total_hits": raw.get("nbHits", 0)}
3. The entrypoint (__main__.py)
# __main__.py — run the server over stdio
from server import mcp
def main() -> None:
mcp.run() # defaults to stdio transport
if __name__ == "__main__":
main()
That is the entire shape. Add the remaining tools the same way, point your mcp.json at the package, and restart your client. Two design notes from building ours:
- Return small, clean dicts. The model pays for every token of tool output. Strip the API's nested junk down to the fields a reader actually needs, the way
hn_searchabove flattens the Algolia hit. - Treat docstrings and type hints as the interface. They become the tool description and input schema. Vague docstrings lead to the model calling the wrong tool.
What you can build with this
Six concrete patterns we have seen people apply HN data to.
- Daily content brief. Pull top HN stories, summarize the meta-trends, draft cross-platform posts. This is exactly what our internal content agent does every morning before we sit down to write.
- Competitive research bot. Search HN for mentions of competitors, surface critical comments, and track how sentiment moves over weeks.
- Trend detector. Monitor
newandbeston a cron. Alert when a topic spikes — a new framework hitting the front page, say — before it shows up in newsletters. - Newsletter pipeline. Keyword search over a time window, cluster the results, and have the model draft an editorial digest with links.
- Customer-discovery agent. Search Ask HN for the exact problem your product solves, then draft cold-outreach that references the real thread.
- Hiring and ecosystem radar. Watch the
jobcategory and "Who is hiring" threads for roles, stacks, and companies in your niche, and summarize what is in demand this month.
Each of these used to require glue code to hit the HN API directly. Now it is one Claude conversation with the right tools attached.
Why no API key
HN's Firebase API and Algolia HN search are both fully public. No registration, no key, no rate limits worth worrying about — we ran 200+ requests/minute during testing without a single throttle. We deliberately picked HN as the pilot MCP for exactly this reason.
This is a real lesson for anyone shipping MCPs: start with public-data tools first. Most authors complicate distribution on day one with OAuth flows and key setup, which kills install rate. Save the OAuth dance for v2, after people already have your server running.
How an MCP server actually works (under the hood)
Our server is the ~150 lines you saw above, split across three files: the HN client, the FastMCP tool wrapper, and the stdio entrypoint. When Claude uses it, this is the cycle:
- Claude Desktop spawns
uvx --from git+... necl-hn-mcpas a subprocess at startup. - The subprocess speaks JSON-RPC over stdin/stdout — it reads requests on stdin and writes replies on stdout.
- On a tool call, our function fetches HN data, trims it to a clean dict, and returns it.
- Claude reads the response and keeps reasoning — frequently chaining
hn_search → hn_get_story → hn_get_commentsto go from a vague query to a summarized discussion in two or three calls.
That is the whole protocol. No magic, no hosted service, nothing leaves your machine except the public HN requests.
Pair this with our Skill
If you are using necl-hn-mcp for content research, pair it with necl-content-poster — our Claude Skill that turns one draft into three platform-tuned posts (Telegram RU, LinkedIn EN, Threads EN).
End-to-end pipeline: the HN MCP finds the story, the content-poster Skill writes the three posts, you publish. The MCP supplies the data; the Skill supplies the writing process. They are deliberately split so you can use either on its own.
See also
- Claude Skills + Cross-Platform Content Generator — pair this MCP with our content Skill
- Marketplace Publishing Playbook for MCPs and Skills — how we shipped this MCP to 13 catalogs
- necl-hn-mcp on GitHub — source code, install, examples
- necl-content-poster Skill — Skill that turns MCP-found stories into posts
- Stop Using GPT-4 for Everything — related: AI cost optimization
- Cream Mic — AI desktop assistant by NeCL — another open-source NeCL project
FAQ
What is MCP and how is it different from a regular API?
MCP (Model Context Protocol) is a standardized way for AI agents to discover and call tools. Unlike a regular API, an MCP server declares its tools — names, descriptions, and input schemas — so a model can use them without any per-app integration code.
Do I need to know Python to use this?
No. You install via `uvx`, which handles Python for you. Add the JSON snippet to `mcp.json`, restart your client, and you are done.
Will Claude rate-limit me on HN?
No. HN's Firebase and Algolia APIs are public with no enforced rate limits. Even hundreds of queries per minute are fine.
Does this work in Claude Code (the CLI), not just Claude Desktop?
Yes. Any MCP client supports it — Claude Code, Cursor, Continue, Cody, and custom clients all use the same `mcp.json` shape.
What is the difference between a stdio and an HTTP MCP server?
A stdio server runs as a local subprocess and talks over standard input/output, which is what `necl-hn-mcp` does. An HTTP/SSE server runs remotely over the network. The model sees the tools the same way either way.
Can I use this commercially?
Yes, it is MIT licensed. You can build paid products on top of it. We appreciate attribution but do not require it.
Is the source code small enough to audit?
Yes — under 200 lines total across two files: [hn.py](https://github.com/adjacentai/necl-hn-mcp/blob/main/src/necl_hn_mcp/hn.py) and [server.py](https://github.com/adjacentai/necl-hn-mcp/blob/main/src/necl_hn_mcp/server.py).
How is this different from just hitting the HN API directly?
Hitting the API directly means writing and maintaining glue code in every app. An MCP server makes the same tools discoverable by any AI agent — your prompt is the only "integration" you write.
Need a custom MCP server wired to your internal APIs, databases, or SaaS tools? We build them. .
Tell us about your project