关于 hn-mcp-server
用于浏览 Hacker News 信息流、讨论、用户资料和可搜索帖子的 MCP 服务,适合读者与研究人员。它提供分层评论、用户查询及由 Algolia 支持的内容搜索。
主要功能
- 01浏览六类 Hacker News 信息流
- 02分页读取评论串
- 03获取用户资料和投稿
- 04搜索故事、评论、投票和职位
- 05按作者、日期或分数筛选
适用场景
- 01跟踪最新或热门 Hacker News 帖子
- 02分段阅读较长讨论并继续翻页
- 03按关键词、作者或日期查找帖子
MCP 相关
提供四个工具,用于访问 Hacker News 信息流、讨论串、用户资料和内容搜索。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 | 获取用户资料,包括 karma、about,以及可选的一页已解析投稿 |
hn_search_content | 通过 Algolia 搜索故事、评论、投票和招聘信息;可按关键词搜索、仅使用筛选条件搜索,或同时使用两者——筛选条件包括类型、作者、父故事、日期范围和最低分数 |
功能参考
hn_get_stories 工具
- 六种动态类型:
top、new、best、ask、show、jobs;count(默认值为 30)和offset用于分页。单次调用最多返回 100 条动态;count最大可设为 1,000,但实际每次最多返回 100 条,并通过notice指明下一个offset - 返回 id、类型、标题、url、域名、分数、作者、时间戳、评论数和正文文本;HN 未提供的字段会省略(而非设为 null)
- 丰富结果会报告
total、offset和hasMore;还有动态时会设置truncated,并通过notice指明下一个offset;若页面为空(动态流为空、offset超出范围,或页面中的所有条目都已删除或标记),则会通过notice说明。最后一页不会标记为截断 - 多次重试后仍获取失败的条目会列在
failedIds中,notice会指出重试情况;若页面中的所有条目都获取失败,则会返回分类后的上游错误,而非空列表
hn_get_thread 工具
itemId加上depth(默认值为 3;设为 0 时只返回条目,不含评论)和maxComments(默认值为 50),用于限制单次响应中所有层级的评论总数;此外还有固定的 64,000 字节文本预算,截断时只会在评论之间进行。单次调用最多解析 10 层和 200 条评论;depth最大可设为 100,但实际最多解析 10 层;maxComments最大可设为 1,000,但实际最多解析 200 条,并通过notice指明应作为itemId传入的评论,或可用于获取剩余内容的nextCursor- 按 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,或将评论 id 作为itemId;对于因深度限制而被截断回复的已省略评论,会在notice中指出其 id - 多次重试后仍获取失败的评论和投票选项会列在
failedIds中,notice会指出这些条目及重试情况;获取失败的评论会保留在nextCursor中,因此翻页重试时会重新获取,而不会跳过。仍会返回根条目,且绝不会将获取失败伪装成maxComments/depth限制
hn_get_user 工具
username区分大小写,且会去除首尾空格;includeSubmissions(默认值为 false)会解析近期提交内容,并通过submissionCount(默认值为 10)和submissionOffset分页浏览较长的历史记录。每次调用最多解析 50 条提交内容;如果submissionCount不超过 1,000,则会以每批 50 条的方式提供,并通过notice指明下一个submissionOffset- 个人资料包含 karma、创建日期和关于文本(会去除 HTML);提交内容会过滤掉已删除或已标记为无效的条目
- 评论提交内容包含其
parent,投票选项提交内容包含其poll,因此两者都能链接回所回复的内容 - 丰富结果会回显
submissionOffset和下一个要发送的偏移量;如果请求的偏移量超出历史记录范围,则会返回一条提示 - 重试后仍获取失败的提交内容会列在
failedIds中,并通过notice指明重试情况;如果某个批次中的所有提交内容都获取失败,则返回已分类的上游错误
hn_search_content 工具
- 支持全文
query,以及tags(story/comment/poll/job/ask_hn/show_hn/front_page)、author(区分大小写的精确用户名)、storyId、dateRange和minPoints筛选条件;可按相关性或日期排序;count(默认值为 30)和page用于分页。每次调用最多返回 50 条结果;count不超过 1,000 时,将从请求页面的首条结果开始,以 50 条为一页返回,并通过notice指明接下来应传入哪个count: 50页面;如果非零page的首条结果不在任何一页 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",以搜索单个讨论串。没有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_rangeminPoints适用于故事和投票——评论和招聘信息没有积分,因此与tags: "comment"或"job"同时使用时会失败并返回min_points_unscored_typeview: "compact"会省略两个正文文本字段(text、highlights.text);否则每条结果中的长评论会重复出现两次——传入结果 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会去除首尾空格,dateRange为实际应用的 UTC 开区间边界,精确到秒),以及填入默认值后的sort、count和view——因此,保存的结果能说明其回答的内容 - 丰富结果会报告
totalHits、page和实际可访问的totalPages——这些页数并非根据totalHits推算,因为宽泛查询的命中数远超 Algolia 可提供的范围。totalHitsExact: false表示totalHits是估算值;reachableHits: 1000表示匹配结果超过 Algolia 的 1,000 条上限;这类搜索的最后一页会通过notice指出该上限,并建议使用更窄的dateRange时间范围来获取其余结果。仍有更多页面时,会设置truncated并通过notice指出下一页的page;请求超出末尾的页面时,会通过notice指出最后一个有效页面(超出上限时为page: 0)
功能
基于 @cyanheads/mcp-ts-core 构建:支持 stdio 和 Streamable HTTP 传输、可插拔身份验证(none / jwt / oauth)、可切换存储(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1),并提供结构化日志记录和可选的 OpenTelemetry 跟踪。
HN 专属功能:
- 支持两个上游 API:用于获取信息流、条目和用户的 HN Firebase API;用于全文搜索的 Algolia HN Search API
- 并发批量获取,在解析条目时可配置并行度(
HN_CONCURRENCY_LIMIT) - 将 HTML 正文剥离为纯文本——实体只解码一次、代码块保持原样、每个链接只以其完整 URL 呈现一次;标题虽然不是 HTML,但有些包含实体,因此会进行实体解码,且绝不剥离标签
- 在
initialize时,将服务级别的instructions转发给大模型客户端——包括条目类型、跨工具复用 ID、区分大小写的用户名以及字段稀疏性 - 无需 API 密钥——两个上游 API 均为公开 API
适合 Agent 使用的输出:
- 优雅处理部分失败——条目重试后仍获取失败时,会按 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,让 Agent 无需重新计算状态即可继续处理 - 响应整形——剥离 HTML、规范化 URL 和提取域名,以去除上游标记噪声;
hn_search_content的view: "compact"会省略两个正文文本字段,否则它们会与命中项的完整文本重复 - 对上游文本进行围栏处理——在
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 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。