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.

4 Ferramentas

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

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

FerramentaDescrição
hn_get_storiesBusca 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_threadObté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_userBusca um perfil de usuário com karma, sobre e, opcionalmente, uma página de envios resolvidos
hn_search_contentPesquisa 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) e offset para 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, offset e hasMore; enquanto houver mais histórias, truncated é definido e notice nomeia o próximo offset, e um notice explica 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 failedIds com um notice nomeando a tentativa; uma página onde todo item falhou retorna o erro upstream classificado em vez de uma lista vazia

hn_get_thread ferramenta

  • itemId mais depth (0–10, padrão 3; 0 retorna o item sem comentários) e maxComments (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 retorna nextCursor; passá-lo de volta como cursor com o mesmo itemId continua 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 um itemId diferente falha com invalid_cursor
  • A raiz carrega seus relacionamentos e estado: parent para um comentário, poll para uma opção de enquete, parts e options resolvidos (texto e votos, em cada profundidade) para uma enquete, e marcadores deleted/dead. O cabeçalho content[] 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/parentId para reconstrução da árvore, além de childCount e um sinalizador isOp quando o autor do comentário corresponde ao autor do item raiz
  • truncated e truncationReason (count, size, rate_limited ou depth quando 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. notice reporta comentários excluídos/mortos omitidos durante a travessia e o próximo passo: o cursor, ou um depth maior ou um id de comentário como itemId
  • Comentários e opções de enquete cuja busca falhou após tentativas são listados em failedIds, e notice os nomeia com a tentativa; comentários com falha viajam em nextCursor, então paginar os tenta novamente em vez de pulá-los. O item raiz ainda é retornado, e uma falha nunca é passada como limite maxComments/depth

hn_get_user ferramenta

  • username diferencia maiúsculas de minúsculas e é aparado; includeSubmissions (padrão falso) resolve envios recentes, com submissionCount (1–50, padrão 10) e submissionOffset paginando 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 parent e um envio de opção de enquete seu poll, então ambos vinculam de volta ao que responderam
  • O enriquecimento ecoa submissionOffset e 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 failedIds com um notice nomeando a tentativa; uma janela onde todo envio falhou retorna o erro upstream classificado

hn_search_content ferramenta

  • query de texto livre mais filtros tags (story/comment/poll/job/ask_hn/show_hn/front_page), author, storyId, dateRange e minPoints; sort por relevância ou data; count (1–50, padrão 30) e page para paginação
  • query é opcional quando um filtro é definido — liste Ask HN, Show HN, enquetes ou empregos sozinhos, ou passe o storyId de uma história com tags: "comment" para pesquisar dentro de uma discussão. Sem consulta e sem filtro falha com missing_query_or_filter
  • Limites dateRange aceitam ISO 8601 (YYYY, YYYY-MM, YYYY-MM-DD ou uma data-hora com deslocamento opcional), lidos como UTC e ambos exclusivos; um intervalo vazio ou fora de ordem falha com invalid_date_range
  • minPoints se aplica a histórias e enquetes — comentários e empregos não carregam pontos, então combiná-lo com tags: "comment" ou "job" falha com min_points_unscored_type
  • view: "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 para hn_get_thread para ler o corpo
  • Metadados de destaque (highlights.title, highlights.text, matchedWords) mostram quais termos corresponderam e onde
  • O enriquecimento reporta totalHits, page e o totalPages realmente alcançável — não derivado de totalHits, já que consultas amplas reportam muito mais resultados do que o Algolia servirá. Enquanto mais páginas permanecerem, truncated é definido e notice nomeia o próximo page; uma página além do fim recebe um aviso nomeando a última página válida (ou page: 0 alé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
  • instructions em nível de servidor encaminhado a clientes LLM em initialize — 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_thread també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 o Retry-After do upstream como retryAfter), e um par depth/parentId em 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 em notice, e hn_get_thread retorna um nextCursor autocontido, 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" de hn_search_content remove 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

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ávelDescriçãoPadrão
HN_CONCURRENCY_LIMITMáximo de requisições HTTP concorrentes para buscas de itens em lote (inteiro, 1–50).10
MCP_TRANSPORT_TYPETransporte: stdio ou http.stdio
MCP_HTTP_PORTPorta do servidor HTTP.3010
MCP_HTTP_HOSTHost do servidor HTTP.127.0.0.1
MCP_SESSION_MODEManipulaçã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_LEVELNível de registro: debug, info, notice, warning, error.info
LOGS_DIRDiretó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órioFinalidade
src/index.tsPonto 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/catch na lógica das ferramentas
  • Use ctx.log para 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.