Skip to main content
Glama

Atenea

Una wiki personal a la que tu IA escribe, y que puedes navegar tú mismo.

Atenea coloca un servidor MCP delante de Wiki.js. Tu asistente busca en la wiki, lee páginas y archiva otras nuevas: notas, documentación, conversaciones completas. Todo lo que escribe es una página Markdown ordinaria que puedes abrir, editar y conservar mucho después de que un modelo concreto haya desaparecido.

Claude / ChatGPT / Cursor
        │  MCP over HTTPS
        ▼
   athena-mcp ──── search ──▶ Wiki.js (keyword) + Postgres (meaning)
        │          read ────▶ Wiki.js
        └────────  write ───▶ Wiki.js ──▶ athena-indexer ──▶ Postgres

Wiki.js contiene la verdad. El índice vectorial solo ayuda a encontrar cosas, y se puede eliminar y reconstruir en cualquier momento.


Inicio rápido

Local, en unos cinco minutos. Para cualquier cosa en internet, lee primero Desplegar en un servidor.

git clone https://github.com/jannismilz/athena.git
cd athena
cp .env.example .env
$EDITOR .env          # fill in every CHANGE_ME, one per secret:
                      #   openssl rand -hex 32
docker compose up -d

Luego:

  1. Abre Wiki.js y completa el asistente de configuración.

  2. En Wiki.js: Administración → API, actívala, crea un token y ponlo en .env como WIKI_API_TOKEN.

  3. docker compose up -d de nuevo para que lo recoja.

  4. Abre el panel de control e inicia sesión con DASHBOARD_TOKEN.

Nada publica un puerto, así que accede a los servicios a través de tu proxy inverso, o añade un mapeo temporal de ports: mientras lo pruebas.

El primer arranque descarga un modelo de incrustación de unos pocos cientos de MB. El indexador vuelve a intentarlo hasta que esté listo, así que embeddings con aspecto de no saludable durante uno o dos minutos en el primer arranque es normal.


Conecta tu IA

Todo se sirve desde MCP_PUBLIC_URL, que debe ser un origen https:// desnudo con ninguna ruta. No /mcp.

Claude.ai → Configuración → Conectores → Añadir conector personalizado

  • URL: https://athena-mcp.example.com/mcp

  • Deja vacío el ID de cliente y el secreto. Atenea registra el cliente por sí mismo.

  • Una página del navegador pide una contraseña. Es tu MCP_TOKEN.

Cursor, Claude Desktop y otros clientes de cabecera

{
  "mcpServers": {
    "athena": {
      "url": "https://athena-mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_MCP_TOKEN" }
    }
  }
}

Herramientas

Herramienta

Lo que hace

search_knowledge

Búsqueda por palabras clave y semántica, fusionada. Cada resultado lleva una ruta.

get_page

Markdown completo de una página

get_page_structure

Esquema de encabezados, sin el cuerpo

append_to_page

Añadir bajo un encabezado, dejando el resto intacto

create_page

Nueva página Markdown

update_page

Reemplazar el cuerpo de una página

move_page

Mover o renombrar

delete_page

Eliminar y quitarla del índice

save_conversation

Archivar una conversación bajo conversations/YYYY/MM/

capture_note

Nota rápida en inbox/ para archivarla después

list_pages

Todo, con rutas y marcas de tiempo

get_wiki_stats

Tamaño, forma y desactualización, para que la IA pueda responder qué falta

append_to_page es la que vale la pena conocer: añadir un hecho cuesta un párrafo, no una reescritura de toda la página.

Por qué recupera bien. Los términos exactos alcanzan el índice de texto completo de Wiki.js, las preguntas vagas alcanzan el índice vectorial, y los resultados se fusionan con fusión de rango recíproco para que ninguna fuente pueda ocultar a la otra. Los fragmentos registran los encabezados que están sobre ellos, por lo que lo que se devuelve conserva su contexto. Cada página que un asistente toca lleva la marca de cuál fue y cuándo, tomada del cliente autenticado en lugar de lo que el modelo afirma sobre sí mismo.


Panel de control

Su propio servicio, en el puerto 8082. Inicia sesión con DASHBOARD_TOKEN; no hay token en ninguna URL. Para scripts, usa un encabezado bearer:

curl -H "Authorization: Bearer $DASHBOARD_TOKEN" \
  https://wiki.example.com/dashboard/api/metrics?days=30

Panel

Responde a

Contenido

páginas, palabras, por área, más grandes, quedándose obsoletos

Actividad de la IA

llamadas por día, qué herramientas, qué asistente, lectura vs escritura

Búsquedas que no encontraron nada

lo que tu wiki no pudo responder

Salud del índice

fragmentos almacenados, páginas indexadas, cuánto retraso

Copia de seguridad

cuándo terminó la última ejecución, qué tamaño, adónde fue

La tercera fila es la que se gana su lugar. Cada entrada es una página que vale la pena escribir.

Es de solo lectura por partida doble: nunca escribe, y se conecta a Postgres como athena_readonly, un rol que solo tiene SELECT y nada más. Las cifras se agregan en Postgres y se almacenan en caché, por lo que una actualización cuesta casi nada.


Desplegar en un servidor

Un VPS de 4 GB ejecuta todo, incluyendo el modelo de incrustación en CPU.

1. Host y cortafuegos

sudo ufw default deny incoming && sudo ufw default allow outgoing
sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw enable

Instala Docker, luego crea un usuario que posea el despliegue:

sudo useradd --create-home --shell /bin/bash athena
sudo usermod -aG docker athena
sudo mkdir -p /srv/athena && sudo chown athena:athena /srv/athena

Ejecuta compose como ese usuario, nunca con sudo, o los montajes bind acaban siendo propiedad de root. La pertenencia al grupo docker equivale a root en el host, así que mantenlo pequeño.

2. DNS

Dos registros A que apunten al host:

Nombre

Sirve

wiki.example.com

Wiki.js, y el panel de control bajo /dashboard/

athena-mcp.example.com

el endpoint MCP

3. Configura

cd /srv/athena
git clone https://github.com/jannismilz/athena.git .
cp .env.example .env
chmod 600 .env        # it holds every secret

Establece como mínimo:

ATHENA_DATA_DIR=/srv/athena/data
POSTGRES_PASSWORD=...
MCP_TOKEN=...
DASHBOARD_TOKEN=...
DASHBOARD_DB_PASSWORD=...
MCP_PUBLIC_URL=https://athena-mcp.example.com
WIKI_PUBLIC_URL=https://wiki.example.com

4. Proxy inverso

Ningún contenedor publica un puerto. Todo vive en la red Docker athena, a la que se une tu proxy. Enruta estos:

Host

Hacia

Notas

wiki.example.com

wikijs:3000

actualización WebSocket, límite de cuerpo de 100M

wiki.example.com/dashboard/

dashboard:8082

athena-mcp.example.com

mcp:8080

no debe almacenar en búfer, flujos MCP

Reenvía X-Forwarded-For: los inicios de sesión limitan por dirección, y sin él cada intento parecería venir del proxy.

Ejecuta nginx como un contenedor unido a la red athena, como se muestra a continuación, o en el host con un mapeo de ports: enlazado a 127.0.0.1.

server {
    listen 80;
    server_name wiki.example.com;

    location / {
        proxy_pass http://wikijs:3000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        client_max_body_size 100M;
        proxy_read_timeout 120s;
    }

    location /dashboard/ {
        proxy_pass http://dashboard:8082/;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name athena-mcp.example.com;

    location / {
        proxy_pass http://mcp:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # MCP streams responses. Without these, long tool calls appear to hang.
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 300s;
    }
}

Luego emite certificados con certbot, o termina TLS donde ya lo hagas.

5. Inicia, luego bloquea la wiki

docker compose up -d && docker compose ps

Completa el asistente de Wiki.js inmediatamente. Hasta que lo hagas, cualquiera que encuentre el host puede reclamar la cuenta de administrador. Luego, en Wiki.js:

  • Grupos → Invitados: elimina el acceso de lectura, a menos que quieras la wiki pública.

  • Autenticación: desactiva el auto-registro.

  • API: actívala y crea el token para WIKI_API_TOKEN.

6. Verifica

curl -s https://athena-mcp.example.com/health

# Must reject unauthenticated calls:
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://athena-mcp.example.com/mcp
# expected: 401

Copias de seguridad

Un solo pg_dump es una copia de seguridad completa. Wiki.js guarda páginas, historial, usuarios, permisos, configuraciones y los bytes de cada archivo subido en Postgres. Las subidas viven en la tabla assetData; los archivos bajo data/wikijs/uploads son solo una caché. El registro de actividad de Atenea y los vectores de búsqueda están en una segunda base de datos en el mismo servidor.

Datos

En la copia de seguridad

Páginas, historial, usuarios, configuraciones

Imágenes y archivos subidos

Registro de actividad y vectores de búsqueda

Contabilidad del índice, registros OAuth

no, se reconstruye o reconecta

.env

no, guarda una copia en un gestor de contraseñas

El contenedor backup se ejecuta cada hora. Cada ejecución vuelca ambas bases de datos, comprueba que cada volcado es legible, conserva una copia local, lo envía a tu destino de rclone, verifica que la subida coincida, y solo entonces poda. Una ejecución fallida nunca puede eliminar tu última copia de seguridad buena.

docker compose run --rm backup now            # take one now
docker compose run --rm backup restore list   # see what exists
docker compose logs -f backup                 # watch the schedule

Configúralo completamente en .env. Cualquier destino de rclone funciona: S3, Backblaze, Wasabi, MinIO, Hetzner. Deja BACKUP_REMOTE vacío para mantener las copias solo en el host.

Añade un remote de cifrado y apunta BACKUP_REMOTE a él. El destino solo recibe entonces cifrado, incluyendo los nombres de archivo.

BACKUP_REMOTE=crypt:
RCLONE_CONFIG_CRYPT_TYPE=crypt
RCLONE_CONFIG_CRYPT_REMOTE=s3:my-bucket/athena
RCLONE_CONFIG_CRYPT_PASSWORD=<rclone obscure ...>
RCLONE_CONFIG_CRYPT_PASSWORD2=<rclone obscure ...>

Guarda ambas contraseñas en tu gestor de contraseñas. Sin ellas las copias son ilegibles, incluso por ti.

Restaurar

Practica esto antes de que lo necesites. Una restauración que nadie ha ejecutado es una suposición.

docker compose run --rm backup restore list
docker compose stop wikijs mcp indexer dashboard
docker compose run --rm backup restore run 2026-08-18T115529Z
docker compose start wikijs mcp indexer dashboard

Te pide que escribas el nombre de la base de datos para confirmar. restore fetch <stamp> descarga una copia sin restaurarla, e informa si cada volcado es legible.

El índice de búsqueda se repara después: el indexador vuelve a leer cada página y re-incrusta cualquier cosa cuyo contenido haya cambiado.


Configuración

Todo viene del entorno. Cada servicio valida su propia configuración al arrancar y sale con una lista de lo que está mal, por lo que un error tipográfico falla inmediatamente en lugar de a las tres de la mañana.

Los cinco secretos, todos generados por ti. Ninguna credencial de Claude, OpenAI ni de nadie más se almacena nunca en .env.

Secreto

En posesión de

Protege

POSTGRES_PASSWORD

postgres, mcp, indexer

acceso completo a la base de datos

WIKI_API_TOKEN

mcp, indexer

la API de Wiki.js

MCP_TOKEN

mcp

el endpoint MCP

DASHBOARD_TOKEN

panel de control

el inicio de sesión del panel de control

DASHBOARD_DB_PASSWORD

panel de control, mcp, indexer

un rol de base de datos de solo SELECT

Lo que se ejecuta

Servicio

Puerto

Qué es

postgres

interno

Datos de Wiki.js, registro de actividad y vectores mediante pgvector

wikijs

3000

La wiki que lees y editas

embeddings

interno

El modelo de incrustación, en CPU

mcp

8080

A lo que tu IA se conecta

indexer

8081

Mantiene el índice vectorial al día con la wiki

panel de control

8082

Métricas

backup

ninguno

Volcado, verificación y envío cada hora

No hay una base de datos vectorial separada. Los vectores viven en Postgres, por lo que una copia de seguridad lo cubre todo.

En hosts ARM la imagen de embeddings se publica solo para linux/amd64 y no se ejecutará de forma nativa. Apunta EMBEDDINGS_PROVIDER=openai a un endpoint compatible con OpenAI como Ollama en su lugar.

Variable

Por defecto

Notas

ATHENA_DATA_DIR

./data

Raíz de cada montaje bind

ATHENA_INSTANCE_NAME

Athena

Se muestra en la página de inicio de sesión y en el panel

ATHENA_LOG_LEVEL

info

debug, info, warn, error

TZ

UTC

Sellos de procedencia y rutas con fecha

POSTGRES_DB

wiki

La base de datos de Wiki.js

ATHENA_DB

athena

Registro de actividad y vectores, creado automáticamente

WIKI_LOCALE

en

Idioma del contenido

WIKI_PUBLIC_URL

http://localhost:3000

Se usa para enlaces del panel

MCP_PUBLIC_URL

obligatorio

Origen https desnudo, sin ruta

METRICS_CACHE_SECONDS

60

Cuánto tiempo se reutilizan las cifras del panel

EMBEDDINGS_MODEL

intfloat/multilingual-e5-small

Cambiarlo reindexa todo

EMBEDDINGS_PROVIDER

tei

tei, o openai para un endpoint compatible

INDEX_INTERVAL_SECONDS

300

Intervalo de reconciliación completo

CHUNK_MAX_CHARS

1200

Límite máximo de tamaño de fragmento

BACKUP_*

ver .env.example

Programación, retención, destino de rclone

Changing EMBEDDINGS_MODEL changes the vector width, and vectors from two models cannot be compared, so the indexer rebuilds the table and re-embeds every page. Wiki.js content is untouched.


Seguridad

Each container receives only the credentials it uses. The dashboard gets neither POSTGRES_PASSWORD nor WIKI_API_TOKEN, so compromising it yields read access and nothing more. Check at any time:

docker inspect athena-dashboard -f '{{range .Config.Env}}{{println .}}{{end}}' | grep -iE 'PASSWORD|TOKEN'
  • Las solicitudes MCP no autenticadas reciben 401 y ninguna explicación.

  • Ambas rutas de inicio de sesión limitan la velocidad tras 5 fallos por dirección; un enlace de inicio de sesión se agota después de 3 intentos.

  • Las sesiones del panel son cookies firmadas que llevan una caducidad y un nonce, nunca el token. Se rechazan HttpOnly, SameSite=Strict y las peticiones POST entre sitios.

  • Las comparaciones de secretos son de tiempo constante.

  • Las cabeceras de proxy se confían solo desde loopback, por lo que un cliente remoto no puede falsificar su dirección para evitar una limitación.

  • Los contenedores se ejecutan como un usuario no root.

Deliberadamente ausente: permisos por herramienta. Cualquier cliente autenticado puede llamar a todas las herramientas, incluida delete_page. Wiki.js conserva el historial de la página, por lo que un borrado es recuperable, pero trata MCP_TOKEN como acceso de escritura total a tu wiki. Athena también asume un único propietario; Wiki.js tiene sus propios usuarios para leer la wiki.

MCP_TOKEN funciona de dos maneras, porque los clientes de IA se autentican de dos maneras.

Clientes por cabecera, como Cursor y Claude Desktop, envían Authorization: Bearer <MCP_TOKEN>. Ese es todo el mecanismo.

Claude.ai en el navegador no puede hacer eso. Sus conectores personalizados solo admiten OAuth, y la especificación MCP requiere registro dinámico de clientes, por lo que un servidor que acepte Claude en el navegador tiene que ser un servidor de autorización. Athena implementa uno:

  1. Claude se registra y recibe un id de cliente generado. Ningún secreto tuyo está involucrado.

  2. Claude te envía a una página de inicio de sesión en tu propio servidor.

  3. Escribes MCP_TOKEN como contraseña. Ese es el paso de aprobación humana.

  4. Athena emite tokens de Claude que Athena misma acuñó.

Esos tokens se escriben en data/mcp/oauth-state.json, nunca en .env. Revócalos con:

rm data/mcp/oauth-state.json && docker compose restart mcp

Si nunca usas Claude en el navegador, ignora todo esto. La ruta de bearer no lo toca.


Operaciones

docker compose logs -f mcp
curl -s localhost:8081/stats | python3 -m json.tool

# Force a full reconciliation
docker compose exec -T indexer bun -e 'await fetch("http://127.0.0.1:8081/sync",{method:"POST"})'

Actualización. Haz siempre una copia de seguridad primero: Wiki.js ejecuta sus propias migraciones al arrancar, y esas no son reversibles deteniendo el contenedor.

docker compose run --rm backup now
git pull && docker compose build && docker compose up -d

Síntoma

Causa

Un servicio termina al arrancar mostrando la configuración

Falta una variable obligatoria o sigue siendo CHANGE_ME

Claude no puede conectarse, no hay página de inicio de sesión

MCP_PUBLIC_URL tiene una ruta, o no es https

El inicio de sesión rechaza la contraseña correcta

Limitado tras 5 fallos, espera un minuto

No hay resultados de búsqueda semántica

embeddings sigue descargando, revisa sus registros

El panel muestra páginas desactualizadas

El indexador se está poniendo al día, revisa sus registros

Las llamadas a herramientas fallan con 401

El archivo de estado se ha borrado o el token ha cambiado, reconecta el cliente

Postgres termina, "database files are incompatible"

La versión mayor de la imagen cambió sobre datos existentes

Postgres no leerá un directorio de datos escrito por una versión mayor diferente. Vuelca, borra, restaura:

docker compose run --rm backup now             # on the OLD version
docker compose down
mv data/postgres data/postgres.old             # keep until you are happy
# edit the image tag in docker-compose.yml and the FROM line in
# docker/backup/Dockerfile to the same new major version
docker compose build backup
docker compose up -d postgres
docker compose run --rm backup restore run <stamp>   # once per database
docker compose up -d

El índice de vectores se restaura con todo lo demás, por lo que no se vuelve a incrustar nada.


Desarrollo

bun install
bun test          # 145 tests
bun run check     # typecheck, lint, test

Paquete

Qué es

packages/core

Cliente de Wiki.js, fragmentación, fusión de búsqueda, vectores, autenticación, configuración

packages/mcp

Servidor MCP, servidor de autorización OAuth, las herramientas

packages/indexer

Bucle de sincronización, embeddings, escrituras de vectores, API de búsqueda interna

packages/dashboard

Interfaz de métricas

docker/backup

Contenedor de copia de seguridad y restauración

website/

El sitio de una página

themes/wikijs/

CSS y JS opcionales para Wiki.js

Bun ejecuta TypeScript directamente, por lo que no hay paso de compilación y los contenedores ejecutan el código fuente. bun run --cwd packages/dashboard preview escribe un preview.html con datos de ejemplo.

Cómo encaja todo:

  • El indexador es incremental. Genera una huella de cada página y se salta cualquier cosa sin cambios, por lo que una pasada sobre una wiki intacta no cuesta nada.

  • Cada servicio con credenciales de administrador prepara la base de datos al arrancar, bajo un bloqueo asesor, por lo que el orden de inicio no importa.

  • El panel es HTML renderizado en el servidor con gráficos SVG en línea. Sin JavaScript de cliente, sin librería de gráficos, sin paso de compilación.

Publicación del sitio web. website/index.html se despliega en GitHub Pages en cada push que lo toque. Habilita Pages una vez manualmente primero: Settings → Pages → Build and deployment → Source: GitHub Actions. Esto no se puede automatizar, porque crear un sitio Pages necesita un token con derechos de administración y GITHUB_TOKEN no los tiene.


Licencia

Apache-2.0. Consulta LICENSE.

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Driflyte MCP server which lets AI assistants query topic-specific knowledge from web and GitHub.

  • Person-owned, portable AI memory as a remote MCP server, readable and writable by any MCP client.

  • An MCP server that gives your AI access to the source code and docs of all public github repos

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jannismilz/athena'

If you have feedback or need assistance with the MCP directory API, please join our Discord server