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.
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
| Herramienta | Descripción |
|---|---|
hn_get_stories | Obtiene 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_thread | Obtiene 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_user | Obtiene un perfil de usuario con karma, información personal y, opcionalmente, una página de envíos resueltos |
hn_search_content | Busca 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) yoffsetpara 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,offsetyhasMore; mientras queden más historias,truncatedse establece ynoticenombra el siguienteoffset, y unnoticeexplica 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
failedIdscon unnoticeque 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
itemIdmásdepth(0–10, predeterminado 3; 0 devuelve el elemento sin comentarios) ymaxComments(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 devuelvenextCursor; pasarlo de vuelta comocursorcon el mismoitemIdcontinú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 unitemIddiferente falla coninvalid_cursor - La raíz lleva sus relaciones y estado:
parentpara un comentario,pollpara una opción de encuesta,partsyoptionsresueltos (texto y votos, en cada profundidad) para una encuesta, y marcadoresdeleted/dead. El encabezadocontent[]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/parentIdpara reconstrucción del árbol, máschildCounty una banderaisOpcuando el autor del comentario coincide con el autor del elemento raíz truncatedytruncationReason(count,size,rate_limitedodepthcuando 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.noticeinforma comentarios eliminados/muertos omitidos durante el recorrido y el siguiente paso: el cursor, o undepthmayor o un id de comentario comoitemId- Los comentarios y opciones de encuesta cuya obtención falló tras reintentos se listan en
failedIds, ynoticelos nombra con el reintento; los comentarios fallidos viajan ennextCursor, 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 demaxComments/depth
hn_get_user herramienta
usernamedistingue mayúsculas y minúsculas y se recorta;includeSubmissions(predeterminado false) resuelve envíos recientes, consubmissionCount(1–50, predeterminado 10) ysubmissionOffsetpaginando 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
parenty un envío de opción de encuesta supoll, por lo que cualquiera enlaza de vuelta a lo que respondió - El enriquecimiento repite
submissionOffsety 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
failedIdscon unnoticeque nombra el reintento; una ventana donde todos los envíos fallaron devuelve el error ascendente clasificado
hn_search_content herramienta
queryde texto libre más filtrostags(story/comment/poll/job/ask_hn/show_hn/front_page),author,storyId,dateRangeyminPoints;sortpor relevancia o fecha;count(1–50, predeterminado 30) ypagepara paginaciónqueryes opcional cuando se establece un filtro: lista Ask HN, Show HN, encuestas o trabajos por sí solos, o pasa elstoryIdde una historia contags: "comment"para buscar dentro de un hilo. Sin consulta y sin filtro falla conmissing_query_or_filter- Los límites
dateRangeaceptan ISO 8601 (YYYY,YYYY-MM,YYYY-MM-DDo una fecha-hora con desplazamiento opcional), leídos como UTC y ambos exclusivos; un rango vacío o desordenado falla coninvalid_date_range minPointsse aplica a historias y encuestas: los comentarios y trabajos no llevan puntos, por lo que combinarlo contags: "comment"o"job"falla conmin_points_unscored_typeview: "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 ahn_get_threadpara leer el cuerpo- Los metadatos de resaltado (
highlights.title,highlights.text,matchedWords) muestran qué términos coincidieron y dónde - El enriquecimiento informa
totalHits,pagey eltotalPagesrealmente alcanzable, no derivado detotalHits, ya que las consultas amplias informan muchos más resultados de los que Algolia servirá. Mientras queden más páginas,truncatedse establece ynoticenombra el siguientepage; una página más allá del final recibe un aviso nombrando la última página válida (opage: 0má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
instructionsa nivel de servidor reenviado a clientes LLM eninitialize: 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_threadtambié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 elRetry-Afterascendente comoretryAfter), y un pardepth/parentIden 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 ennotice, yhn_get_threaddevuelve unnextCursorautocontenido, 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"dehn_search_contentelimina 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
- Bun v1.4.0 o superior (o Node.js >= 24)
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.
| Variable | Descripción | Predeterminado |
|---|---|---|
HN_CONCURRENCY_LIMIT | Máximo de solicitudes HTTP concurrentes para obtención de elementos por lotes (entero, 1–50). | 10 |
MCP_TRANSPORT_TYPE | Transporte: stdio o http. | stdio |
MCP_HTTP_PORT | Puerto del servidor HTTP. | 3010 |
MCP_HTTP_HOST | Host del servidor HTTP. | 127.0.0.1 |
MCP_SESSION_MODE | Manejo 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_LEVEL | Nivel de registro: debug, info, notice, warning, error. | info |
LOGS_DIR | Directorio 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
| Directorio | Propósito |
|---|---|
src/index.ts | Punto 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/catchen la lógica de las herramientas - Usa
ctx.logpara 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.