idiradocs-mcp
# idiradocs-mcp
Servidor [MCP](https://modelcontextprotocol.io/) que le da a Claude acceso a la documentación oficial de **CyberArk / Idira** ([docs.cyberark.com](https://docs.cyberark.com/)), para que pueda responder preguntas citando contenido real de la doc en lugar de inventar o alucinar información.
Sin bases de datos vectoriales, sin pipelines de embeddings y sin ninguna API de pago: la búsqueda corre sobre un índice local generado por un crawler propio, y la lectura de páginas se hace en vivo contra el sitio real.
## Índice
- [Por qué existe esto](#por-qué-existe-esto)
- [Cómo funciona](#cómo-funciona)
- [Instalación](#instalación)
- [Generar el índice de búsqueda](#generar-el-índice-de-búsqueda)
- [Conectarlo a Claude](#conectarlo-a-claude)
- [Herramientas expuestas](#herramientas-expuestas)
- [Configuración](#configuración)
- [Detalles de implementación](#detalles-de-implementación)
- [Limitaciones conocidas](#limitaciones-conocidas)
- [Scripts disponibles](#scripts-disponibles)
## Por qué existe esto
Preguntarle a un LLM sobre productos específicos de CyberArk (PAM, CPM, PSM, etc.) sin darle acceso a la documentación real tiene un riesgo alto de respuestas plausibles pero incorrectas. Este servidor MCP resuelve eso dándole a Claude dos herramientas concretas: **buscar** en la doc y **leer** una página puntual, siempre citando la fuente.
## Cómo funciona
```
┌──────────────────────┐
│ npm run crawl │ (una vez, o para refrescar el índice cuando sea necesario)
└──────────┬───────────┘
│ recorre docs.cyberark.com
▼
┌──────────────────────┐
│ data/docs-index.json │ índice local (una entrada por sección de cada página)
└──────────┬───────────┘
│ MiniSearch
▼
Claude ──▶ search_cyberark_docs ──▶ lista de secciones candidatas (título, URL#anchor, snippet)
│
▼
Claude ──▶ get_cyberark_doc_page ──▶ fetch en vivo + Markdown limpio de esa URL
```
- **`search_cyberark_docs`**: busca por palabras clave en el índice local generado por el crawler. Rápido, sin llamadas externas.
- **`get_cyberark_doc_page`**: trae la página real desde `docs.cyberark.com` en el momento de la consulta, limpia el HTML de navegación y la devuelve en Markdown con los links resueltos a URLs absolutas. Siempre refleja el contenido actual del sitio, aunque el índice esté desactualizado.
El índice no guarda la página completa como un solo bloque: la divide por encabezado (cada `<h1>`-`<h6>` con `id`, que en el sitio coincide con el anchor de navegación) y guarda cada sección por separado. Esto hace que un resultado de búsqueda apunte directo a `.../pagina.htm#SeccionEspecifica` en lugar de a la página entera, y que el snippet devuelto sea del fragmento relevante y no de un promedio de toda la página.
El flujo esperado es que el modelo primero busque, elija la página más relevante, y después la lea completa antes de responder.
## Instalación
Requiere Node.js 18 o superior.
```bash
git clone https://github.com/AlexPerez7/idiradocs-mcp.git
cd idiradocs-mcp
npm install
npm run build
```
## Generar el índice de búsqueda
`search_cyberark_docs` necesita un índice previo — sin este paso no tiene nada para buscar:
```bash
npm run crawl
```
Por defecto arranca desde la página de inicio de la doc y se detiene a las **150 páginas**, para que la primera corrida sea rápida. Para un índice más completo, se puede ajustar el alcance con variables de entorno:
```bash
# Ejemplo: indexar 2000 páginas de PAM - Self-Hosted en particular
CRAWL_SEEDS=https://docs.cyberark.com/pam-self-hosted/latest/en/content/resources/_topnav/cc_home.htm CRAWL_MAX_PAGES=2000 npm run crawl
```
Es necesario volver a ejecutar `npm run crawl` para refrescar el índice — la documentación cambia con el tiempo y el índice no se actualiza solo.
## Conectarlo a Claude
### Claude Desktop
Agregar el servidor en `claude_desktop_config.json`:
> En instalaciones desde Microsoft Store, este archivo suele estar en
> `%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\claude_desktop_config.json`
> en vez del `%APPDATA%\Claude\` habitual.
```json
{
"mcpServers": {
"idiradocs": {
"command": "node",
"args": ["/ruta/absoluta/a/idiradocs-mcp/dist/index.js"]
}
}
}
```
Es necesario reiniciar Claude Desktop por completo (no alcanza con minimizar) para que tome el nuevo servidor.
### Claude Code
Se puede registrar como servidor MCP de usuario o de proyecto:
```bash
claude mcp add idiradocs -- node /ruta/absoluta/a/idiradocs-mcp/dist/index.js
```
## Herramientas expuestas
### `search_cyberark_docs`
Busca en el índice local.
| Parámetro | Tipo | Descripción |
|---|---|---|
| `query` | `string` | Términos de búsqueda, en español o inglés |
| `count` | `number` (opcional, default `5`) | Cantidad máxima de resultados (1-10) |
Devuelve título (página — sección), URL (con anchor a la sección específica cuando existe) y un fragmento de texto por cada resultado.
### `get_cyberark_doc_page`
Trae y limpia el contenido de una página puntual.
| Parámetro | Tipo | Descripción |
|---|---|---|
| `url` | `string` | URL completa, debe pertenecer a `https://docs.cyberark.com/` |
Devuelve el contenido en Markdown, con título y URL fuente al inicio. Rechaza cualquier URL fuera de ese dominio.
## Configuración
No hay ninguna variable obligatoria. Estas solo afectan a `npm run crawl`:
| Variable | Default | Descripción |
|---|---|---|
| `CRAWL_SEEDS` | página de inicio de la doc | URLs separadas por coma desde donde arrancar el crawl |
| `CRAWL_MAX_PAGES` | `150` | Tope de páginas a visitar |
| `CRAWL_CONCURRENCY` | `3` | Requests en paralelo |
| `CRAWL_DELAY_MS` | `150` | Pausa entre lotes de requests, para no sobrecargar el sitio |
Estas variables pueden definirse en un archivo `.env` (ver `.env.example`) o pasarse inline al comando.
## Detalles de implementación
- Las páginas de `docs.cyberark.com` son HTML estático generado por **MadCap Flare**. El contenido real de cada página vive en `<div id="mc-main-content">`, y los elementos de navegación/chrome llevan `class="nocontent"` — esa es la convención que se usa tanto para indexar como para leer una página.
- El sitio devuelve una página 404 personalizada a requests cuyo `User-Agent` no parece un navegador (mitigación anti-bot); por eso tanto el crawler como el fetch de páginas envían un `User-Agent` de Chrome.
- La búsqueda usa [MiniSearch](https://github.com/lucaong/minisearch) con boost en el título de página y de sección, y coincidencia difusa (fuzzy) y por prefijo — no es una búsqueda semántica, es coincidencia de palabras con tolerancia a errores de tipeo. Se evaluó agregar una capa de embeddings locales, pero se descartó: la librería estándar para eso en Node (transformers.js) trae dependencias transitivas (`sharp`/libvips, `adm-zip`) con vulnerabilidades altas sin parche disponible.
- `get_cyberark_doc_page` valida que la URL sea `https://docs.cyberark.com/*` antes de hacer el fetch, para que el servidor no pueda usarse como proxy hacia otros dominios.
## Limitaciones conocidas
- El índice de búsqueda es una foto del momento del crawl — no se actualiza solo. Si la doc cambia, hay que volver a correr `npm run crawl`.
- La relevancia de `search_cyberark_docs` es por palabras clave, no semántica: sinónimos o paráfrasis pueden no encontrar la página correcta aunque exista.
- El crawler solo sigue links dentro de `docs.cyberark.com` y con extensión `.htm`/`.html`; no cubre PDFs, videos embebidos ni contenido cargado dinámicamente con JavaScript.
## Scripts disponibles
| Comando | Qué hace |
|---|---|
| `npm run build` | Compila TypeScript a `dist/` |
| `npm start` | Corre el servidor MCP (`dist/index.js`) sobre stdio |
| `npm run dev` | Compila en watch mode |
| `npm run crawl` | Compila y corre el crawler, genera/actualiza `data/docs-index.json` |
TDQS
Scored across 2 tools
The two tools are clearly distinct: one searches an index, the other retrieves a specific page's content. There is no functional overlap or ambiguity between them.
Both tools follow the same verb_noun pattern with the 'cyberark_docs' domain prefix: 'search_cyberark_docs' and 'get_cyberark_doc_page'. Consistency is perfect.
With only 2 tools, the server is minimal and might feel thin for broader documentation workflows, but it covers the core search-and-retrieve pattern adequately. It is slightly under the typical well-scoped range.
The domain is documentation lookup, and search + fetch provides a complete read-only workflow. Missing features like browsing sections or listing all pages are minor gaps that agents can work around.