hn-mcp-server

Feeds y búsqueda de Hacker News

Documentación

@cyanheads/hn-mcp-server

Explora los feeds, hilos y perfiles de usuarios de Hacker News con búsqueda de texto completo vía MCP. STDIO o Streamable HTTP.

4 Herramientas

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Servidor público alojado: https://hn.caseyjhand.com/mcp


Resumen

Feeds, hilos y perfiles de la API Firebase de Hacker News y la API de búsqueda de Algolia. Explora feeds clasificados, lee hilos de comentarios completos, consulta perfiles de usuarios y busca historias, comentarios, encuestas y trabajos por palabra clave o por tipo, autor, historia principal, fecha o puntuación. Funciona como proceso stdio, como servidor HTTP Streamable local o mediante el endpoint público alojado anterior.

Herramientas

HerramientaDescripción
hn_get_storiesObtiene historias de un feed de HN (top, new, best, ask, show, jobs), con título, URL, puntuación, autor y número de comentarios
hn_get_threadObtiene un elemento y su árbol de comentarios como discusión en hilo, con controles de profundidad y límite de comentarios, y un cursor para paginar hilos largos
hn_get_userObtiene un perfil de usuario con karma, información personal y, opcionalmente, una página de envíos resueltos
hn_search_contentBusca historias, comentarios, encuestas y trabajos vía Algolia por palabra clave, solo por filtros, o ambos: tipo, autor, historia principal, rango de fechas y puntos mínimos

Referencia de capacidades

hn_get_stories herramienta

  • Seis tipos de feed: top, new, best, ask, show, jobs; count (1–100, predeterminado 30) y offset para paginación
  • Devuelve id, tipo, título, url, dominio, puntuación, autor, marca de tiempo, número de comentarios y texto del cuerpo; cada campo se omite (no nulo) cuando HN no lo proporciona
  • El enriquecimiento informa total, offset y hasMore; mientras queden más historias, truncated se establece y notice nombra el siguiente offset, y un notice explica páginas vacías (feed vacío, desplazamiento más allá del final, o cada elemento de la página eliminado/marcado). La última página no lleva truncamiento
  • Los elementos cuya obtención falló tras reintentos se listan en failedIds con un notice que nombra el reintento; una página donde todos los elementos fallaron devuelve el error ascendente clasificado en lugar de una lista vacía

hn_get_thread herramienta

  • itemId más depth (0–10, predeterminado 3; 0 devuelve el elemento sin comentarios) y maxComments (1–200, predeterminado 50) limitando una respuesta en todos los niveles, junto con un presupuesto fijo de texto de 64,000 bytes que corta solo entre comentarios
  • Recorrido en anchura clasificado como HN: los comentarios de nivel superior mejor clasificados se resuelven primero, las respuestas se completan solo después de agotar el nivel superior
  • Reanudable: una respuesta detenida por maxComments, el presupuesto de bytes o un límite de velocidad ascendente devuelve nextCursor; pasarlo de vuelta como cursor con el mismo itemId continúa en el siguiente comentario no visto. El cursor lleva los comentarios no visitados en sí, por lo que las respuestas re-clasificadas no pueden desplazarlo, y el servidor no mantiene estado de sesión. Un cursor pasado con un itemId diferente falla con invalid_cursor
  • La raíz lleva sus relaciones y estado: parent para un comentario, poll para una opción de encuesta, parts y options resueltos (texto y votos, en cada profundidad) para una encuesta, y marcadores deleted/dead. El encabezado content[] nombra el tipo: un título, Comment by <author>, Poll option by <author> on poll <id> — y marca una raíz eliminada o muerta
  • La lista plana de comentarios lleva depth/parentId para reconstrucción del árbol, más childCount y una bandera isOp cuando el autor del comentario coincide con el autor del elemento raíz
  • truncated y truncationReason (count, size, rate_limited o depth cuando las respuestas están por debajo del límite de profundidad) se establecen solo mientras queden comentarios sin leer, nunca en una página terminal, incluido un hilo que se carga completo exactamente en el límite. notice informa comentarios eliminados/muertos omitidos durante el recorrido y el siguiente paso: el cursor, o un depth mayor o un id de comentario como itemId
  • Los comentarios y opciones de encuesta cuya obtención falló tras reintentos se listan en failedIds, y notice los nombra con el reintento; los comentarios fallidos viajan en nextCursor, por lo que paginar los reintenta en lugar de omitirlos. El elemento raíz aún se devuelve, y un fallo nunca se presenta como un límite de maxComments/depth

hn_get_user herramienta

  • username distingue mayúsculas y minúsculas y se recorta; includeSubmissions (predeterminado false) resuelve envíos recientes, con submissionCount (1–50, predeterminado 10) y submissionOffset paginando a través de un historial largo
  • El perfil incluye karma, fecha de creación y texto de información personal (HTML eliminado); los envíos filtran elementos muertos/eliminados
  • Un envío de comentario lleva su parent y un envío de opción de encuesta su poll, por lo que cualquiera enlaza de vuelta a lo que respondió
  • El enriquecimiento repite submissionOffset y el desplazamiento a enviar a continuación, o un aviso cuando el desplazamiento solicitado está más allá del final del historial
  • Los envíos cuya obtención falló tras reintentos se listan en failedIds con un notice que nombra el reintento; una ventana donde todos los envíos fallaron devuelve el error ascendente clasificado

hn_search_content herramienta

  • query de texto libre más filtros tags (story/comment/poll/job/ask_hn/show_hn/front_page), author, storyId, dateRange y minPoints; sort por relevancia o fecha; count (1–50, predeterminado 30) y page para paginación
  • query es opcional cuando se establece un filtro: lista Ask HN, Show HN, encuestas o trabajos por sí solos, o pasa el storyId de una historia con tags: "comment" para buscar dentro de un hilo. Sin consulta y sin filtro falla con missing_query_or_filter
  • Los límites dateRange aceptan ISO 8601 (YYYY, YYYY-MM, YYYY-MM-DD o una fecha-hora con desplazamiento opcional), leídos como UTC y ambos exclusivos; un rango vacío o desordenado falla con invalid_date_range
  • minPoints se aplica a historias y encuestas: los comentarios y trabajos no llevan puntos, por lo que combinarlo con tags: "comment" o "job" falla con min_points_unscored_type
  • view: "compact" elimina los dos campos de texto del cuerpo (text, highlights.text), que de otro modo repiten un comentario largo dos veces por resultado: pasa un id de resultado a hn_get_thread para leer el cuerpo
  • Los metadatos de resaltado (highlights.title, highlights.text, matchedWords) muestran qué términos coincidieron y dónde
  • El enriquecimiento informa totalHits, page y el totalPages realmente alcanzable, no derivado de totalHits, ya que las consultas amplias informan muchos más resultados de los que Algolia servirá. Mientras queden más páginas, truncated se establece y notice nombra el siguiente page; una página más allá del final recibe un aviso nombrando la última página válida (o page: 0 más allá del techo de 1,000 resultados de Algolia)

Características

Construido sobre @cyanheads/mcp-ts-core: transportes stdio y Streamable HTTP, autenticación conectable (none / jwt / oauth), almacenamiento intercambiable (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), registro estructurado con rastreo OpenTelemetry opcional.

Específico de HN:

  • Dos APIs ascendentes: la API Firebase de HN para feeds, elementos y usuarios; la API de búsqueda HN de Algolia para búsqueda de texto completo
  • Obtención por lotes concurrente con paralelismo configurable para resolución de elementos (HN_CONCURRENCY_LIMIT)
  • Cuerpos HTML reducidos a texto plano: entidades decodificadas una vez, bloques de código mantenidos verbatim, cada enlace renderizado una vez como su URL completa; mientras que los títulos, que no son HTML aunque algunos llevan entidades, se decodifican por entidad y nunca se eliminan etiquetas
  • instructions a nivel de servidor reenviado a clientes LLM en initialize: tipos de elementos, reutilización de ID entre herramientas, nombres de usuario sensibles a mayúsculas, escasez de campos
  • No se requieren claves API: ambas APIs ascendentes son públicas

Salida amigable para agentes:

  • Fallo parcial elegante: un elemento cuya obtención falla tras reintentos se informa por ID en failedIds, aparte de elementos eliminados, muertos o inexistentes, en lugar de reducir silenciosamente el resultado; hn_get_thread también cuenta los comentarios eliminados y muertos que descarta, y un elemento con límite de velocidad detiene el lote en lugar de enviar cada elemento restante por su propia escalera de reintentos
  • Contratos de salida discriminados: razones de error tipadas (item_not_found, upstream_rate_limited, upstream_html, …) con texto de recuperación por razón (un límite de velocidad pasa el Retry-After ascendente como retryAfter), y un par depth/parentId en cada comentario para que los llamadores reconstruyan el árbol sin adivinar el anidamiento
  • Procedencia de paginación: cada listado paginado repite el desplazamiento que usó (offset, submissionOffset, page) más el valor exacto del siguiente desplazamiento en notice, y hn_get_thread devuelve un nextCursor autocontenido, para que un agente pueda reanudar sin recomputar estado
  • Modelado de respuesta: eliminación de HTML, normalización de URL y extracción de dominio eliminan el ruido de marcado ascendente; el view: "compact" de hn_search_content elimina los dos campos de texto del cuerpo que de otro modo duplican el texto completo de un resultado
  • Texto ascendente delimitado: en content[], cada línea de un cuerpo se cita en bloque y los títulos se escapan solo donde podrían romper el Markdown circundante, para que un comentario no pueda pasar por una línea escrita por el servidor

Primeros pasos

Instancia pública alojada

Una instancia pública está disponible en https://hn.caseyjhand.com/mcp: no se requiere instalación. Apunta cualquier cliente MCP a ella vía Streamable HTTP:

{
  "mcpServers": {
    "hn-mcp-server": {
      "type": "streamable-http",
      "url": "https://hn.caseyjhand.com/mcp"
    }
  }
}

Autoalojado / Local

Añade a tu archivo de configuración del 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"
      }
    }
  }
}

O con npx (sin necesidad 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"
      }
    }
  }
}

O con 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"
      ]
    }
  }
}

Requisitos previos

Instalación

git clone https://github.com/cyanheads/hn-mcp-server.git
cd hn-mcp-server
bun install

Configuración

Toda la configuración es mediante variables de entorno. No se requieren claves API: las APIs de HN son públicas.

VariableDescripciónPredeterminado
HN_CONCURRENCY_LIMITMáximo de solicitudes HTTP concurrentes para obtención de elementos por lotes (entero, 1–50).10
MCP_TRANSPORT_TYPETransporte: stdio o http.stdio
MCP_HTTP_PORTPuerto del servidor HTTP.3010
MCP_HTTP_HOSTHost del servidor HTTP.127.0.0.1
MCP_SESSION_MODEManejo de sesiones HTTP: stateful, stateless o auto (resuelve a stateful). El servidor declara stateless en el código fuente, y la imagen Docker publicada y .env.example también lo establecen; un valor de entorno significativo aún lo anula.stateless
MCP_LOG_LEVELNivel de registro: debug, info, notice, warning, error.info
LOGS_DIRDirectorio para archivos de registro (solo Node.js).<project-root>/logs
Consulta .env.example para ver la lista completa de anulaciones opcionales.

Ejecutar el servidor

Desarrollo local

  • Modo de desarrollo (recarga automática):

    MCP_TRANSPORT_TYPE=stdio bun --watch src/index.ts   # stdio
    MCP_TRANSPORT_TYPE=http bun --watch src/index.ts    # HTTP
    
  • Compilar y ejecutar:

    bun run rebuild
    bun run start:stdio   # or start:http
    
  • Ejecutar comprobaciones y pruebas:

    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

El Dockerfile usa por defecto transporte HTTP, modo de sesión sin estado y registra en /var/log/hn-mcp-server. Las dependencias opcionales de OpenTelemetry se instalan por defecto; compila con --build-arg OTEL_ENABLED=false para omitirlas.

Estructura del proyecto

DirectorioPropósito
src/index.tsPunto de entrada de createApp() — registra herramientas e inicializa el servicio de HN.
src/config/Análisis y validación de variables de entorno específicas del servidor con Zod.
src/mcp-server/tools/definitions/Definiciones de herramientas (*.tool.ts).
src/services/hn/Cliente de API de HN Firebase + Algolia y tipos de dominio.
tests/Pruebas unitarias y de integración que reflejan src/.

Guía de desarrollo

Consulta CLAUDE.md para las pautas de desarrollo y reglas arquitectónicas. La versión breve:

  • Los manejadores lanzan excepciones, el framework las captura — sin try/catch en la lógica de las herramientas
  • Usa ctx.log para el registro con ámbito de solicitud
  • Todas las herramientas son de solo lectura — no se requieren ámbitos de autenticación
  • Envuelve las llamadas a API externas: valida los datos brutos → normaliza al tipo de dominio → devuelve el esquema de salida; nunca inventes campos faltantes

Contribuciones

Las incidencias son bienvenidas. Ejecuta las comprobaciones antes de enviar:

bun run devcheck
bun run test

Licencia

Apache-2.0 — consulta LICENSE para más detalles.