Skip to main content
Glama
ccozad

hackernews-mcp

by ccozad
README.md
# Hacker News MCP

<p align="center">
  <img src="images/hackernews_mcp.png" alt="Architecture: you prompt Claude Desktop (the MCP client), which talks to the hackernews-mcp server over stdio; the server queries the HN Algolia Search API over HTTPS" width="640">
</p>

An [MCP](https://modelcontextprotocol.io) server that lets Claude (or any MCP
client) search and read [Hacker News](https://news.ycombinator.com), backed by
HN's free [Algolia](https://hn.algolia.com/api) search API. Ask in plain
language; Claude calls the tools.

![Claude answering “search HN for Rust async” with a ranked list of Hacker News results](images/hackernews-mcp-response.png)

```text
You:    What's the discussion on Rust async runtimes been like this past month?
Claude: → search_hackernews(query="rust async runtime", time_range="past_month")
        Here are the threads HN has been talking about… [summary of real stories]

You:    Dive into the comments on the top one.
Claude: → get_hackernews_thread(item_id="…", max_comments=30)
        The top commenters are split on… [summary of the thread]
```

The two tools compose — a follow-up like *“pull comments on the first item”*
feeds the story id straight from the search into `get_hackernews_thread`:

![Claude summarizing the comment thread for the top story](images/hackernews-mcp-thread.png)

> See [`examples/`](examples/) for full transcripts, and
> [`docs/claude-desktop.md`](docs/claude-desktop.md) to wire it into Claude
> Desktop in about five minutes.

## What's in this repo

Two MCP tools:

- **`search_hackernews`** — search stories and comments by query, with filters
  for tag (`story` / `comment` / `ask_hn` / `show_hn` / `all`), time range, sort
  (relevance or date), and result limit.
- **`get_hackernews_thread`** — fetch a story's comment tree by id, flattened
  depth-first and bounded by `max_comments` / `max_depth` to keep the response
  within an honest token budget (with a `truncated` flag when it was trimmed).

**Tech stack:** Python 3.11+, the official [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk),
[`httpx`](https://www.python-httpx.org/), and — for development —
[`pytest`](https://docs.pytest.org/), [`ruff`](https://docs.astral.sh/ruff/),
and [`pyright`](https://microsoft.github.io/pyright/).

## Install

Uses [`uv`](https://docs.astral.sh/uv/):

```bash
git clone https://github.com/ccozad/hackernews-mcp.git
cd hackernews-mcp
uv sync
```

Run the stdio server directly with `uv run hackernews-mcp` (it speaks the MCP
protocol on stdout, so you normally let a client launch it rather than running it
by hand).

## Use it with Claude Desktop

Add this to your `claude_desktop_config.json` (full guide, config-file locations,
and troubleshooting in [`docs/claude-desktop.md`](docs/claude-desktop.md)):

```json
{
  "mcpServers": {
    "hackernews": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/hackernews-mcp", "run", "hackernews-mcp"]
    }
  }
}
```

Restart Claude Desktop, then ask it to *"search HN for Rust async"* and confirm a
tool call happens. The first time Claude uses a tool, Claude Desktop asks you to
approve it:

![Claude Desktop prompting to allow the “Search hackernews” tool](images/claude-mcp-permission.png)

## Architecture

As shown in the diagram at the top, Claude Desktop is the MCP **client**: on
startup it spawns this server as a subprocess and talks to it over stdio. The
server exposes two tools and forwards their work to HN's Algolia API over HTTPS.

### Exchange sequence

A typical two-tool session — search surfaces a story, then a follow-up dives into
its comments:

```mermaid
sequenceDiagram
    actor User
    participant Desktop as Claude Desktop
    participant Server as hackernews-mcp
    participant Algolia as HN Algolia API

    Note over Desktop,Server: On launch, Desktop spawns the server<br/>and negotiates initialize + tools/list over stdio

    User->>Desktop: "search HN for Rust async"
    Desktop->>Server: tools/call search_hackernews(query="rust async")
    Server->>Algolia: GET /search?query=rust+async&tags=story
    Algolia-->>Server: matching hits (JSON)
    Server-->>Desktop: hits array
    Desktop-->>User: ranked list of stories

    User->>Desktop: "pull comments on the first item"
    Desktop->>Server: tools/call get_hackernews_thread(item_id="…")
    Server->>Algolia: GET /items/{item_id}
    Algolia-->>Server: full nested thread (JSON)
    Note over Server: flatten depth-first, then bound<br/>by max_comments / max_depth
    Server-->>Desktop: root, comments, truncated
    Desktop-->>User: thread summary
```

## How it works

Both tools are thin wrappers over HN's Algolia API. `search_hackernews` maps its
arguments to Algolia's `/search` (or `/search_by_date`) endpoint — tag filters,
a `numericFilters` time window, and `hitsPerPage`. `get_hackernews_thread` pulls
the full nested thread from `/items/{id}` and trims it client-side. Input is
validated before any network call; upstream errors, timeouts, and empty results
all have defined behavior. See the tool docstrings in
[`src/hackernews_mcp/`](src/hackernews_mcp/) for the full contract.

## Development

```bash
uv sync --extra dev      # install dev tools
uv run pytest            # run the test suite (network-mocked)
uv run ruff check .      # lint
uv run ruff format --check .
uv run pyright           # type-check
```

All four checks run in CI on every pull request across Python 3.11 and 3.12. The
suite mocks Algolia and never hits the network; a gated live smoke test runs only
when `HACKERNEWS_MCP_LIVE_TEST=1` is set.

## License

[MIT](LICENSE)

TDQS

A4.3/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no possibility of confusion between tools. The search_hackernews tool has a clear, distinct purpose.

Naming Consistency5/5

With a single tool, naming consistency is not an issue. The name 'search_hackernews' follows a clear verb_noun pattern.

Tool Count2/5

For a server named hackernews-mcp, a single search tool is too few. Users would expect additional tools for fetching stories, comments, user info, and posting, making the scope feel incomplete.

Completeness2/5

The server lacks basic operations for Hacker News interaction, such as getting a story by ID, fetching top stories, or user information. Only search is supported, leaving significant gaps.

Maintenance

ActivityInactive
ResponsivenessResponsive