hn-mcp-server について
Hacker Newsのフィード、議論、ユーザープロフィール、投稿を検索するMCPサーバーです。読者や調査担当者向けに、コメントツリー、プロフィール参照、Algoliaによる検索を提供します。
主な機能
- 016種類のHacker Newsフィード表示
- 02コメントスレッドのページ取得
- 03ユーザープロフィールと投稿の取得
- 04ストーリー、コメント、投票、求人の検索
- 05作者、日付、スコアによる絞り込み
活用シーン
- 01新着または人気の投稿を追跡
- 02長い議論を再開可能なページで読む
- 03キーワード、作者、日付で投稿を検索
MCP 対応
Hacker Newsのフィード、スレッド、プロフィール、コンテンツ検索用の4つのツールを提供します。MCPクライアントはSTDIOまたはStreamable HTTPで接続でき、ホスト型エンドポイントも利用できます。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://hn.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-hn https://hn.caseyjhand.com/mcp{"mcpServers":{"cyanheads-hn":{"type":"http","url":"https://hn.caseyjhand.com/mcp"}}}Cursor で開く{"servers":{"cyanheads-hn":{"type":"http","url":"https://hn.caseyjhand.com/mcp"}}}VS Code で開く{"mcpServers":{"cyanheads-hn":{"type":"http","url":"https://hn.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-hn":{"serverUrl":"https://hn.caseyjhand.com/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
4| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| feed | enum | はい | 取得する HN フィード。「top」には jobs が含まれます。「ask」と「show」は Ask HN / Show HN の投稿です。top | new | best | ask | show | jobs |
| count | integer | いいえ | 返す投稿数。数が多いほど時間がかかります。 |
| offset | integer | いいえ | フィードの先頭からスキップする投稿数。ページネーションには count と併用します。 |
詳細
@cyanheads/hn-mcp-server
MCP 経由で Hacker News のフィード、スレッド、ユーザープロフィールを全文検索とともに閲覧できます。STDIO または Streamable HTTP に対応しています。
公開ホストサーバー: https://hn.caseyjhand.com/mcp
概要
Hacker News のフィード、スレッド、プロフィールを利用できます。ランキング付きフィードの閲覧、コメントスレッド全体の読み込み、ユーザーの検索に加え、キーワード、投稿者、日付、スコアでストーリー、コメント、投票、求人を検索できます。
ツール
| ツール | 説明 |
|---|---|
hn_get_stories | HN フィード(top、new、best、ask、show、jobs)から、タイトル、URL、スコア、投稿者、コメント数とともにストーリーを取得します |
hn_get_thread | アイテムとそのコメントツリーをスレッド形式のディスカッションとして取得します。深さとコメント数を指定でき、長いスレッドをページごとに取得するカーソルにも対応しています |
hn_get_user | カルマ、自己紹介、必要に応じて解決済み投稿のページを含むユーザープロフィールを取得します |
hn_search_content | Algolia を利用して、キーワード、フィルターのみ、またはその両方でストーリー、コメント、投票、求人を検索します。種類、投稿者、親ストーリー、日付範囲、最低ポイントで絞り込めます |
機能リファレンス
hn_get_stories ツール
- 6 種類のフィード:
top、new、best、ask、show、jobs。ページネーションにはcount(デフォルトは 30)とoffsetを指定します。1 回の呼び出しで取得できるストーリーは最大 100 件です。countが 1,000 までの場合、100 件ずつ取得し、次のoffsetを示すnoticeが返されます - id、type、title、url、domain、score、author、timestamp、コメント数、本文テキストを返します。HN から提供されないフィールドは、null ではなく省略されます
- 拡張情報では
total、offset、hasMoreが報告されます。ストーリーが残っている場合はtruncatedが設定され、次のoffsetを示すnoticeが返されます。また、空のページ(空のフィード、末尾を超えた offset、またはページ内のすべての項目が削除済み/フラグ付き)についてはnoticeで説明されます。最終ページには切り捨て情報は付きません - 再試行後も取得に失敗した項目は
failedIdsに列挙され、再試行を示すnoticeが付けられます。ページ内のすべての項目の取得に失敗した場合は、空のリストではなく、分類された上流エラーが返されます
hn_get_thread ツール
itemIdに加え、depth(デフォルトは 3。0 を指定するとコメントなしで項目を返します)とmaxComments(デフォルトは 50。全階層を通じた 1 回のレスポンスの上限)を指定できます。コメントの間でのみ切り分ける固定 64,000 バイトのテキスト上限も適用されます。1 回の呼び出しで解決できるのは最大 10 階層、200 コメントです。depthが 100 までの場合は 10 階層まで、maxCommentsが 1,000 までの場合は 200 件まで取得し、次にitemIdとして渡すコメント、または残りのコメントに到達するnextCursorを示すnoticeが返されます- HN の順位に沿って幅優先で探索します。上位にランクされたトップレベルコメントを先に解決し、その階層をすべて処理した後に返信を取得します
- 中断した箇所から再開できます。
maxComments、バイト上限、または上流のレート制限によってレスポンスが中断された場合はnextCursorが返されます。同じitemIdとともにcursorとして渡すと、次に未取得のコメントから続行します。カーソルには未訪問のコメントそのものが含まれるため、返信の順位が変わっても位置がずれることはなく、サーバーはセッション状態を保持しません。このツールが発行していないカーソル、改変されたカーソル、または異なるitemIdとともに渡されたカーソルは、invalid_cursorエラーになります - ルートには関連情報と状態が含まれます。コメントの場合は
parentとstoryId(そのコメントが属するストーリーまたは投票)、投票の選択肢の場合はpoll、投票の場合はpartsと解決済みのoptions(すべての階層におけるテキストと票数)、さらにdeleted/deadマーカーが含まれます。content[]の見出しには種類が記されます。タイトル、Comment by <author>、Poll option by <author> on poll <id>のいずれかで、ルートが削除済みまたは無効であることも示されます - フラットなコメントリストには、ツリーを再構成するための
depth/parentIdに加え、childCountと、コメントの投稿者がスレッドのストーリーを投稿した本人である場合のisOpフラグが含まれます。コメントがルートの場合は、そのコメント自身の投稿者ではなく、storyIdのストーリーの投稿者が対象です。無効または削除済みのコメントは省略されますが、その返信が有効であれば、正しいdepthで返され、省略されたコメントを示すparentIdが付けられます truncatedとtruncationReason(count、size、rate_limited、または返信が深度制限より下にある場合のdepth)は、未読のコメントが残っている場合にのみ設定されます。上限ちょうどでスレッド全体を取得できた場合を含め、最終ページには設定されません。noticeでは、探索中に省略された削除済み/無効のコメントと次の手順を報告します。次の手順はカーソル、より大きなdepth、またはitemIdとして渡すコメント ID です。深度制限によって返信が切り捨てられた省略コメントは、ID で示されます- 再試行後も取得に失敗したコメントと投票の選択肢は
failedIdsに列挙され、再試行を示すnoticeに ID が記載されます。取得に失敗したコメントはnextCursorに含まれるため、ページを再取得するときに再試行され、省略されることはありません。ルートの項目は引き続き返され、取得失敗がmaxComments/depthの制限として扱われることはありません
hn_get_user tool
usernameは大文字と小文字を区別し、前後の空白は削除されます。includeSubmissions(デフォルトは false)を指定すると、最近の投稿を取得します。長い投稿履歴をページ分割するには、submissionCount(デフォルトは 10)とsubmissionOffsetを使用します。1 回の呼び出しで取得できる投稿は最大 50 件です。submissionCountに 50 を超える値(最大 1,000)を指定した場合は 50 件を取得し、次のsubmissionOffsetを示すnoticeが返されます- プロフィールには、カルマ、作成日、自己紹介文(HTML を除去済み)が含まれます。投稿には、削除済みまたは非表示のアイテムは含まれません
- コメントの投稿には
parentが、投票の選択肢の投稿にはpollが含まれるため、どちらも回答対象へのリンクをたどれます - 取得結果には
submissionOffsetと次に送信するオフセットが含まれます。または、指定したオフセットが履歴の末尾を過ぎている場合はnoticeが返されます - 再試行後も取得に失敗した投稿は
failedIdsに一覧表示され、再試行について示すnoticeが返されます。対象範囲内のすべての投稿の取得に失敗した場合は、分類された上流エラーが返されます
hn_search_content ツール
- フリーテキストの
queryに加え、tags(story/comment/poll/job/ask_hn/show_hn/front_page)、author(大文字と小文字を区別する完全一致のユーザー名)、storyId、dateRange、minPointsで絞り込めます。sortでは関連度順または日付順に並べ替え、count(デフォルトは30)とpageでページネーションを指定します。1回の呼び出しで取得できるのは最大50件です。countが1,000以下の場合、指定したページの先頭ヒットから始まる50件のページが返され、次に渡すcount: 50のページを示すnoticeが含まれます。pageが0以外で、その先頭ヒットが50件単位のページの先頭にならない場合は、count_page_unalignedエラーとなり、その位置から始まるcountとpageが示されます。また、そのcountが25未満の場合は、そのヒットを含む50件のページも示されます。 tags: "front_page"は現在のフロントページ(約30件のストーリー)を示し、過去のフロントページは対象外です。過去の期間を調べるには、tags: "story"とdateRange、minPointsを使用してください。- フィルターを設定すれば
queryは省略できます。Ask HN、Show HN、投票、求人を単独で一覧表示するか、ストーリーのstoryIdとtags: "comment"を渡して、1つのスレッド内を検索できます。queryもフィルターも指定しない場合は、missing_query_or_filterエラーとなります。 - ページについて議論されているかを確認するには、そのURLだけを
queryとして渡してください。検索前に先頭のhttp://、https://、またはwww.は削除されるため、どちらのスキームでも、またwww.の有無にかかわらず一致します。出力のqueryとnoticeには、検索に使用した内容が示されます。 dateRangeの範囲には、ISO 8601形式(YYYY、YYYY-MM、YYYY-MM-DD、またはオフセットを省略可能な日時)を指定します。UTCとして解釈され、両端は範囲に含まれません。空の範囲または順序が逆の範囲を指定すると、invalid_date_rangeエラーとなります。minPointsはストーリーと投票に適用されます。コメントと求人にはポイントがないため、tags: "comment"または"job"と併用するとmin_points_unscored_typeエラーとなります。view: "compact"を指定すると、本文テキストのフィールド2つ(text、highlights.text)が省略されます。これらは指定しない場合、長いコメントをヒットごとに2回繰り返します。本文を読むには、ヒットIDをhn_get_threadに渡してください。- 各ヒットには、Algoliaに記録されている場合、その
type(story、comment、poll、pollopt、job)が含まれます。タグを指定しない検索では投票の選択肢も返されます。選択肢にはID、作成者、投票数、日付のみが含まれます。選択肢のテキストと投票内容を読むには、hn_get_threadを使用してください。投票のヒットにはstoryIdがなく、そのid自体が議論のルートです。 - 求人の説明は
textとして返されますが、キーワード検索では検索対象になりません。 - ハイライトのメタデータ(
highlights.title、highlights.text、matchedWords)には、一致した語句とその位置が示されます。 - エンリッチメントでは、適用された
criteriaが返されます。各フィルター設定(前後の空白を除去したauthor、適用された排他的なUTCの範囲を秒単位で示すdateRange)に加え、デフォルト値を補完したsort、count、viewが含まれるため、保存した結果から何に答えるものかが分かります。 - エンリッチメントでは、
totalHits、page、実際に到達可能なtotalPagesが報告されます。広範なクエリではAlgoliaが提供できる件数を大幅に上回るヒットが報告されるため、totalPagesはtotalHitsから算出されません。totalHitsExact: falseはtotalHitsが推定値であることを示し、reachableHits: 1000は検索結果がAlgoliaの1,000件の上限を超えていることを示します。このような検索の最終ページには、上限と、残りを取得するために範囲を狭めたdateRangeが記載されたnoticeが含まれます。残りのページがある場合はtruncatedが設定され、次のpageがnoticeに示されます。最終ページを超えるページを指定すると、最後に有効なページ(上限を超えた場合はpage: 0)を示すnoticeが返されます。
特徴
@cyanheads/mcp-ts-core を基盤としており、stdio および Streamable HTTP トランスポート、差し替え可能な認証(none/jwt/oauth)、切り替え可能なストレージ(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1)、オプションの OpenTelemetry トレーシングに対応した構造化ログを備えています。
HN 固有の機能:
- 2 つの上流 API に対応:フィード、アイテム、ユーザーには HN の Firebase API、全文検索には Algolia の HN Search API を使用します
- アイテム解決のための並行バッチ取得に対応し、並列度を設定できます(
HN_CONCURRENCY_LIMIT) - HTML 本文をプレーンテキストに変換します。エンティティは一度だけデコードし、コードブロックはそのまま保持し、各リンクは完全な URL として一度だけ表示します。HTML ではないタイトルは、一部にエンティティを含む場合があるためデコードしますが、タグの除去は行いません
- サーバーレベルの
instructionsをinitialize時に LLM クライアントへ転送します。アイテムの種類、ツール間での ID の再利用、大文字と小文字を区別するユーザー名、フィールドの欠落について説明します - API キーは不要です。どちらの上流 API も公開されています
エージェントに適した出力:
- 部分的な失敗を適切に処理します。再試行後に取得に失敗したアイテムは、削除済み、非表示、または存在しないアイテムとは区別して、結果を黙って縮小するのではなく ID を
failedIdsで報告します。hn_get_threadは除外した削除済みおよび非表示のコメント数も集計します。また、アイテムがレート制限に達した場合は、残りの各アイテムに個別の再試行を繰り返すのではなく、バッチを停止します - 判別可能な出力コントラクトを提供します。型付きエラー理由(
item_not_found、upstream_rate_limited、upstream_html、…)には理由ごとの復旧用テキストが付属します(レート制限時は上流のRetry-AfterをretryAfterとして渡します)。さらに、すべてのコメントにdepth/parentIdのペアを含めるため、呼び出し側は入れ子の深さを推測せずにツリーを再構築できます - ページネーションの出所情報を提供します。ページ分割されたリストは使用したオフセット(
offset、submissionOffset、page)と、次のオフセットの正確な値をnoticeで返します。hn_search_contentは検索に使用したcriteriaも返し、hn_get_threadは自己完結型のnextCursorを返すため、エージェントは状態を再計算せずに処理を再開できます - 応答を整形します。HTML の除去、URL の正規化、ドメインの抽出によって上流のマークアップ由来のノイズを取り除きます。
hn_search_contentのview: "compact"は、通常なら検索結果の全文と重複する本文フィールド 2 つを省きます - 上流テキストをフェンス化します。
content[]では本文の各行をブロック引用にし、タイトルは周囲の Markdown を崩す可能性がある箇所だけをエスケープするため、コメントがサーバー作成の行として扱われることはありません
はじめに
公開ホストインスタンス
公開インスタンスは https://hn.caseyjhand.com/mcp で利用できます。インストールは不要です。Streamable HTTP を介して、任意の MCP クライアントから接続してください:
{
"mcpServers": {
"hn-mcp-server": {
"type": "streamable-http",
"url": "https://hn.caseyjhand.com/mcp"
}
}
}
セルフホスト/ローカル環境
MCP クライアントの設定ファイルに追加してください:
{
"mcpServers": {
"hn-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/hn-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
または npx を使用する場合(Bun は不要です):
{
"mcpServers": {
"hn-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/hn-mcp-server"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
または Docker を使用する場合:
{
"mcpServers": {
"hn-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/hn-mcp-server:latest"
]
}
}
}
前提条件
- Bun v1.4.0 以降(または Node.js >= 24)
インストール
git clone https://github.com/cyanheads/hn-mcp-server.git
cd hn-mcp-server
bun install
設定
設定はすべて環境変数で行います。API キーは不要です。HN API は公開されています。
| 変数 | 説明 | デフォルト |
|---|---|---|
HN_CONCURRENCY_LIMIT | アイテムのバッチ取得における最大同時 HTTP リクエスト数(整数、1~50)。 | 10 |
MCP_TRANSPORT_TYPE | トランスポート:stdio または http。 | stdio |
MCP_HTTP_PORT | HTTP サーバーのポート。 | 3010 |
MCP_HTTP_HOST | HTTP サーバーのホスト。 | 127.0.0.1 |
MCP_SESSION_MODE | HTTP セッションの処理方法:stateful、stateless、または auto(stateful に解決されます)。ソースではサーバーに stateless が指定されており、公開されている Docker イメージと .env.example でも同様に設定されています。有効な値を環境変数で指定した場合は、引き続きそちらが優先されます。 | stateless |
MCP_LOG_LEVEL | ログレベル:debug、info、notice、warning、error。 | info |
LOGS_DIR | ログファイルのディレクトリ(Node.js のみ)。 | <project-root>/logs |
オプションの上書き設定の全リストは .env.example を参照してください。
サーバーの実行
ローカル開発
-
開発モード(自動リロード):
MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts # stdio MCP_TRANSPORT_TYPE=http bun --watch src/index.ts # HTTP -
ビルドして実行:
bun run rebuild bun run start:stdio # or start:http -
チェックとテストを実行:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite
Docker
docker build -t hn-mcp-server .
docker run --rm -p 3010:3010 hn-mcp-server
Dockerfile は、HTTP トランスポート、ステートレスセッションモード、/var/log/hn-mcp-server へのログ出力をデフォルトで設定します。OpenTelemetry のピア依存関係はデフォルトでインストールされます。省略するには、--build-arg OTEL_ENABLED=false を指定してビルドしてください。
プロジェクト構成
| ディレクトリ | 用途 |
|---|---|
src/index.ts | エントリーポイントの createApp()。ツールを登録し、HN サービスを初期化します。 |
src/config/ | サーバー固有の環境変数を Zod で解析・検証します。 |
src/mcp-server/tools/definitions/ | ツールの定義(*.tool.ts)。 |
src/services/hn/ | HN Firebase と Algolia API のクライアント、およびドメイン型。 |
tests/ | src/ に対応するユニットテストと統合テスト。 |
開発ガイド
開発ガイドラインとアーキテクチャ上のルールについては、CLAUDE.md を参照してください。概要は次のとおりです。
- ハンドラーは例外をスローし、フレームワークが捕捉します。ツールのロジックでは
try/catchを使用しません。 - リクエスト単位のログには
ctx.logを使用します。 - すべてのツールは読み取り専用のため、認証スコープは不要です。
- 外部 API の呼び出しはラップします。生データを検証し、ドメイン型に正規化してから、出力スキーマに沿って返します。不足しているフィールドを捏造してはいけません。
コントリビューション
問題の報告を歓迎します。提出前にチェックを実行してください。
bun run devcheck
bun run test
ライセンス
Apache-2.0 — 詳細は LICENSE を参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。