hn-mcp-server
Feed e busca do Hacker News
Documentação
@cyanheads/hn-mcp-server
Navegue pelos feeds, discussões e perfis de usuários do Hacker News com busca em texto completo via MCP. STDIO ou Streamable HTTP.
Servidor Público Hospedado: https://hn.caseyjhand.com/mcp
Visão Geral
Feeds, discussões e perfis da API Firebase do Hacker News e da API de Busca Algolia. Navegue por feeds ranqueados, leia discussões completas em árvore, consulte perfis de usuários e pesquise histórias, comentários, enquetes e empregos por palavra-chave ou por tipo, autor, história pai, data ou pontuação. Funciona como processo stdio, servidor Streamable HTTP local ou o endpoint público hospedado acima.
Ferramentas
| Ferramenta | Descrição |
|---|---|
hn_get_stories | Busca histórias de um feed do HN (top, new, best, ask, show, jobs), com título, URL, pontuação, autor e contagem de comentários |
hn_get_thread | Obtém um item e sua árvore de comentários como discussão encadeada, com controles de profundidade e contagem de comentários e um cursor para paginar discussões longas |
hn_get_user | Busca um perfil de usuário com karma, sobre e, opcionalmente, uma página de envios resolvidos |
hn_search_content | Pesquisa histórias, comentários, enquetes e empregos via Algolia por palavra-chave, apenas por filtros, ou ambos — tipo, autor, história pai, intervalo de datas e pontos mínimos |
Referência de capacidades
hn_get_stories ferramenta
- Seis tipos de feed:
top,new,best,ask,show,jobs;count(1–100, padrão 30) eoffsetpara paginação - Retorna id, tipo, título, url, domínio, pontuação, autor, timestamp, contagem de comentários e texto do corpo — cada campo omitido (não nulo) quando o HN não o fornece
- O enriquecimento reporta
total,offsetehasMore; enquanto houver mais histórias,truncatedé definido enoticenomeia o próximooffset, e umnoticeexplica páginas vazias (feed vazio, deslocamento além do fim, ou todo item na página excluído/sinalizado). A última página não carrega truncamento - Itens cuja busca falhou após tentativas são listados em
failedIdscom umnoticenomeando a tentativa; uma página onde todo item falhou retorna o erro upstream classificado em vez de uma lista vazia
hn_get_thread ferramenta
itemIdmaisdepth(0–10, padrão 3; 0 retorna o item sem comentários) emaxComments(1–200, padrão 50) limitando uma resposta em todos os níveis, junto com um orçamento fixo de texto de 64.000 bytes que corta apenas entre comentários- Travessia em largura ranqueada como o HN — comentários de nível superior mais bem ranqueados são resolvidos primeiro, respostas são preenchidas apenas após o nível acima ser esgotado
- Retomável: uma resposta interrompida por
maxComments, o orçamento de bytes, ou um limite de taxa upstream retornanextCursor; passá-lo de volta comocursorcom o mesmoitemIdcontinua no próximo comentário não visto. O cursor carrega os próprios comentários não visitados, então respostas re-ranqueadas não podem deslocá-lo, e o servidor não mantém estado de sessão. Um cursor passado com umitemIddiferente falha cominvalid_cursor - A raiz carrega seus relacionamentos e estado:
parentpara um comentário,pollpara uma opção de enquete,partseoptionsresolvidos (texto e votos, em cada profundidade) para uma enquete, e marcadoresdeleted/dead. O cabeçalhocontent[]nomeia o tipo — um título,Comment by <author>,Poll option by <author> on poll <id>— e marca uma raiz excluída ou morta - A lista plana de comentários carrega
depth/parentIdpara reconstrução da árvore, além dechildCounte um sinalizadorisOpquando o autor do comentário corresponde ao autor do item raiz truncatedetruncationReason(count,size,rate_limitedoudepthquando respostas estão abaixo do limite de profundidade) são definidos apenas enquanto houver comentários não lidos — nunca em uma página terminal, incluindo uma discussão que carrega por completo exatamente no limite.noticereporta comentários excluídos/mortos omitidos durante a travessia e o próximo passo: o cursor, ou umdepthmaior ou um id de comentário comoitemId- Comentários e opções de enquete cuja busca falhou após tentativas são listados em
failedIds, enoticeos nomeia com a tentativa; comentários com falha viajam emnextCursor, então paginar os tenta novamente em vez de pulá-los. O item raiz ainda é retornado, e uma falha nunca é passada como limitemaxComments/depth
hn_get_user ferramenta
usernamediferencia maiúsculas de minúsculas e é aparado;includeSubmissions(padrão falso) resolve envios recentes, comsubmissionCount(1–50, padrão 10) esubmissionOffsetpaginando um histórico longo- O perfil inclui karma, data de criação e texto sobre (HTML removido); envios filtram itens mortos/excluídos
- Um envio de comentário carrega seu
parente um envio de opção de enquete seupoll, então ambos vinculam de volta ao que responderam - O enriquecimento ecoa
submissionOffsete o deslocamento a enviar em seguida, ou um aviso quando o deslocamento solicitado está além do fim do histórico - Envios cuja busca falhou após tentativas são listados em
failedIdscom umnoticenomeando a tentativa; uma janela onde todo envio falhou retorna o erro upstream classificado
hn_search_content ferramenta
queryde texto livre mais filtrostags(story/comment/poll/job/ask_hn/show_hn/front_page),author,storyId,dateRangeeminPoints;sortpor relevância ou data;count(1–50, padrão 30) epagepara paginaçãoqueryé opcional quando um filtro é definido — liste Ask HN, Show HN, enquetes ou empregos sozinhos, ou passe ostoryIdde uma história comtags: "comment"para pesquisar dentro de uma discussão. Sem consulta e sem filtro falha commissing_query_or_filter- Limites
dateRangeaceitam ISO 8601 (YYYY,YYYY-MM,YYYY-MM-DDou uma data-hora com deslocamento opcional), lidos como UTC e ambos exclusivos; um intervalo vazio ou fora de ordem falha cominvalid_date_range minPointsse aplica a histórias e enquetes — comentários e empregos não carregam pontos, então combiná-lo comtags: "comment"ou"job"falha commin_points_unscored_typeview: "compact"remove os dois campos de texto do corpo (text,highlights.text), que de outra forma repetem um comentário longo duas vezes por resultado — passe um id de resultado parahn_get_threadpara ler o corpo- Metadados de destaque (
highlights.title,highlights.text,matchedWords) mostram quais termos corresponderam e onde - O enriquecimento reporta
totalHits,pagee ototalPagesrealmente alcançável — não derivado detotalHits, já que consultas amplas reportam muito mais resultados do que o Algolia servirá. Enquanto mais páginas permanecerem,truncatedé definido enoticenomeia o próximopage; uma página além do fim recebe um aviso nomeando a última página válida (oupage: 0além do teto de 1.000 resultados do Algolia)
Recursos
Construído sobre @cyanheads/mcp-ts-core: transportes stdio e Streamable HTTP, autenticação plugável (none / jwt / oauth), armazenamento trocável (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), registro estruturado com rastreamento OpenTelemetry opcional.
Específico do HN:
- Duas APIs upstream: a API Firebase do HN para feeds, itens e usuários; a API de Busca HN do Algolia para busca em texto completo
- Busca em lote concorrente com paralelismo configurável para resolução de itens (
HN_CONCURRENCY_LIMIT) - Corpos HTML reduzidos a texto simples — entidades decodificadas uma vez, blocos de código mantidos verbatim, cada link renderizado uma vez como sua URL completa — enquanto títulos, que não são HTML embora alguns carreguem entidades, são decodificados por entidade e nunca removidos por tags
instructionsem nível de servidor encaminhado a clientes LLM eminitialize— tipos de item, reutilização de ID entre ferramentas, nomes de usuário sensíveis a maiúsculas, esparsidade de campos- Nenhuma chave de API necessária — ambas as APIs upstream são públicas
Saída amigável a agentes:
- Falha parcial graciosa — um item cuja busca falha após tentativas é reportado por ID em
failedIds, separado de itens excluídos, mortos ou inexistentes, em vez de encolher silenciosamente o resultado;hn_get_threadtambém conta os comentários excluídos e mortos que descarta, e um item com limite de taxa interrompe o lote em vez de enviar cada item restante por sua própria escada de tentativas - Contratos de saída discriminados — razões de erro tipadas (
item_not_found,upstream_rate_limited,upstream_html, …) com texto de recuperação por razão (um limite de taxa repassa oRetry-Afterdo upstream comoretryAfter), e um pardepth/parentIdem cada comentário para que chamadores reconstruam a árvore sem adivinhar aninhamento - Proveniência de paginação — cada listagem paginada ecoa o deslocamento que usou (
offset,submissionOffset,page) mais o valor exato do próximo deslocamento emnotice, ehn_get_threadretorna umnextCursorautocontido, para que um agente possa retomar sem recomputar estado - Modelagem de resposta — remoção de HTML, normalização de URL e extração de domínio removem ruído de marcação upstream; o
view: "compact"dehn_search_contentremove os dois campos de texto do corpo que de outra forma duplicam o texto completo de um resultado - Texto upstream cercado — em
content[], cada linha de um corpo é citada em bloco e títulos são escapados apenas onde poderiam quebrar o Markdown ao redor, para que um comentário não possa passar por uma linha escrita pelo servidor
Começando
Instância Pública Hospedada
Uma instância pública está disponível em https://hn.caseyjhand.com/mcp — sem necessidade de instalação. Aponte qualquer cliente MCP para ela via Streamable HTTP:
{
"mcpServers": {
"hn-mcp-server": {
"type": "streamable-http",
"url": "https://hn.caseyjhand.com/mcp"
}
}
}
Auto-hospedado / Local
Adicione ao arquivo de configuração do seu cliente MCP:
{
"mcpServers": {
"hn-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/hn-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Ou com npx (sem necessidade de Bun):
{
"mcpServers": {
"hn-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/hn-mcp-server"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Ou com 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"
]
}
}
}
Pré-requisitos
- Bun v1.4.0 ou superior (ou Node.js >= 24)
Instalação
git clone https://github.com/cyanheads/hn-mcp-server.git
cd hn-mcp-server
bun install
Configuração
Toda a configuração é feita via variáveis de ambiente. Nenhuma chave de API necessária — as APIs do HN são públicas.
| Variável | Descrição | Padrão |
|---|---|---|
HN_CONCURRENCY_LIMIT | Máximo de requisições HTTP concorrentes para buscas de itens em lote (inteiro, 1–50). | 10 |
MCP_TRANSPORT_TYPE | Transporte: stdio ou http. | stdio |
MCP_HTTP_PORT | Porta do servidor HTTP. | 3010 |
MCP_HTTP_HOST | Host do servidor HTTP. | 127.0.0.1 |
MCP_SESSION_MODE | Manipulação de sessão HTTP: stateful, stateless ou auto (resolve para stateful). O servidor declara stateless no código-fonte, e a imagem Docker publicada e .env.example também o definem; um valor de ambiente significativo ainda sobrescreve. | stateless |
MCP_LOG_LEVEL | Nível de registro: debug, info, notice, warning, error. | info |
LOGS_DIR | Diretório para arquivos de registro (apenas Node.js). | <project-root>/logs |
Consulte .env.example para a lista completa de substituições opcionais. |
Executando o servidor
Desenvolvimento local
-
Modo de desenvolvimento (recarga automática):
MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts # stdio MCP_TRANSPORT_TYPE=http bun --watch src/index.ts # HTTP -
Compilar e executar:
bun run rebuild bun run start:stdio # or start:http -
Executar verificações e testes:
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
O Dockerfile usa por padrão transporte HTTP, modo de sessão sem estado e registra logs em /var/log/hn-mcp-server. As dependências opcionais do OpenTelemetry são instaladas por padrão — compile com --build-arg OTEL_ENABLED=false para omiti-las.
Estrutura do projeto
| Diretório | Finalidade |
|---|---|
src/index.ts | Ponto de entrada do createApp() — registra ferramentas e inicializa o serviço HN. |
src/config/ | Análise e validação de variáveis de ambiente específicas do servidor com Zod. |
src/mcp-server/tools/definitions/ | Definições de ferramentas (*.tool.ts). |
src/services/hn/ | Cliente da API HN Firebase + Algolia e tipos de domínio. |
tests/ | Testes unitários e de integração espelhando src/. |
Guia de desenvolvimento
Consulte CLAUDE.md para diretrizes de desenvolvimento e regras arquiteturais. A versão resumida:
- Handlers lançam exceções, o framework captura — sem
try/catchna lógica das ferramentas - Use
ctx.logpara registro de logs no escopo da solicitação - Todas as ferramentas são somente leitura — nenhum escopo de autenticação é necessário
- Encapsule chamadas de API externas: valide os dados brutos → normalize para o tipo de domínio → retorne o esquema de saída; nunca invente campos ausentes
Contribuindo
Issues são bem-vindas. Execute as verificações antes de enviar:
bun run devcheck
bun run test
Licença
Apache-2.0 — consulte LICENSE para detalhes.