Athena MCP
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 ──▶ PostgresWiki.js contiene la verdad. El índice vectorial solo ayuda a encontrar cosas, y se puede eliminar y reconstruir en cualquier momento.
Ponlo en marcha | |
Úsalo | |
Ejecútalo de verdad | |
Referencia |
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 -dLuego:
Abre Wiki.js y completa el asistente de configuración.
En Wiki.js: Administración → API, actívala, crea un token y ponlo en
.envcomoWIKI_API_TOKEN.docker compose up -dde nuevo para que lo recoja.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/mcpDeja 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 |
| Búsqueda por palabras clave y semántica, fusionada. Cada resultado lleva una ruta. |
| Markdown completo de una página |
| Esquema de encabezados, sin el cuerpo |
| Añadir bajo un encabezado, dejando el resto intacto |
| Nueva página Markdown |
| Reemplazar el cuerpo de una página |
| Mover o renombrar |
| Eliminar y quitarla del índice |
| Archivar una conversación bajo |
| Nota rápida en |
| Todo, con rutas y marcas de tiempo |
| 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=30Panel | 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 enableInstala 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/athenaEjecuta 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.js, y el panel de control bajo |
| 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 secretEstablece 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.com4. 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 |
|
| actualización WebSocket, límite de cuerpo de 100M |
|
| |
|
| 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 psCompleta 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: 401Copias 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 | sí |
Imágenes y archivos subidos | sí |
Registro de actividad y vectores de búsqueda | sí |
Contabilidad del índice, registros OAuth | no, se reconstruye o reconecta |
| 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 scheduleConfigú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 dashboardTe 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, mcp, indexer | acceso completo a la base de datos |
| mcp, indexer | la API de Wiki.js |
| mcp | el endpoint MCP |
| panel de control | el inicio de sesión del panel de control |
| panel de control, mcp, indexer | un rol de base de datos de solo SELECT |
Lo que se ejecuta
Servicio | Puerto | Qué es |
| interno | Datos de Wiki.js, registro de actividad y vectores mediante pgvector |
| 3000 | La wiki que lees y editas |
| interno | El modelo de incrustación, en CPU |
| 8080 | A lo que tu IA se conecta |
| 8081 | Mantiene el índice vectorial al día con la wiki |
| 8082 | Métricas |
| 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/amd64y no se ejecutará de forma nativa. ApuntaEMBEDDINGS_PROVIDER=openaia un endpoint compatible con OpenAI como Ollama en su lugar.
Variable | Por defecto | Notas |
|
| Raíz de cada montaje bind |
|
| Se muestra en la página de inicio de sesión y en el panel |
|
|
|
|
| Sellos de procedencia y rutas con fecha |
|
| La base de datos de Wiki.js |
|
| Registro de actividad y vectores, creado automáticamente |
|
| Idioma del contenido |
|
| Se usa para enlaces del panel |
| obligatorio | Origen https desnudo, sin ruta |
|
| Cuánto tiempo se reutilizan las cifras del panel |
|
| Cambiarlo reindexa todo |
|
|
|
|
| Intervalo de reconciliación completo |
|
| Límite máximo de tamaño de fragmento |
| ver | 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=Stricty 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:
Claude se registra y recibe un id de cliente generado. Ningún secreto tuyo está involucrado.
Claude te envía a una página de inicio de sesión en tu propio servidor.
Escribes
MCP_TOKENcomo contraseña. Ese es el paso de aprobación humana.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 mcpSi 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 -dSíntoma | Causa |
Un servicio termina al arrancar mostrando la configuración | Falta una variable obligatoria o sigue siendo |
Claude no puede conectarse, no hay página de inicio de sesión |
|
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 |
|
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 -dEl í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, testPaquete | Qué es |
| Cliente de Wiki.js, fragmentación, fusión de búsqueda, vectores, autenticación, configuración |
| Servidor MCP, servidor de autorización OAuth, las herramientas |
| Bucle de sincronización, embeddings, escrituras de vectores, API de búsqueda interna |
| Interfaz de métricas |
| Contenedor de copia de seguridad y restauración |
| El sitio de una página |
| 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.
This server cannot be installed
Maintenance
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
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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