Capítulo core de la guía Neblla — el contrato base del SDK. Imprescindible: el resto de capítulos asumen lo que hay aquí. # Neblla — core ## Qué es Neblla es un SDK multijugador que se carga con una etiqueta ` ``` Cuando la página carga, `onNeblla` se ejecuta con el SDK ya conectado. El script tag NO necesita `type="module"`. ## Estructura canónica — 6 ficheros Si la app vive alojada en Neblla, el link público apunta a `index.html`. La plantilla canónica son seis archivos; solo `index.html` es obligatorio, el resto existen si la app los necesita. Front-end (corre en el navegador): - **index.html** — markup. Carga `main.css`, `client.js` y el SDK de Neblla. - **main.css** — estilos. - **client.js** — corre en cada dispositivo conectado. Tiene acceso al DOM. El código va dentro de `window.onNeblla = (neblla) => { ... }`. Server-side (lógica aislada, sin DOM, carpeta `server/`): - **server/lobby.js** — usuarios que aún no están en partida. Matchmaking: recoger "estoy listo", agrupar, reservar sala, anunciar emparejamiento. - **server/gathering.js** — sala creada esperando a empezar. Decide *cuándo* arranca la partida (los casos comunes se configuran sin código en `Settings → Rooms`). - **server/room.js** — la sala en marcha. Autoridad del juego: valida acciones, mantiene estado, anuncia cambios. Contrato de la estructura: - `index.html` referencia `main.css` y `client.js` con `` y `
Connecting…
``` ### main.css ```css body { font-family: system-ui, sans-serif; max-width: 640px; margin: 2rem auto; } #players .player { padding: .25rem .5rem; } #players .player.presenter::before { content: "★ "; } #players .player.me { font-weight: bold; } ``` ### client.js ```javascript let isLobby = true; window.onNeblla = (neblla) => { const $ = (id) => document.getElementById(id); $('status').textContent = 'Connected. In: ' + neblla.room.name; $('roomControls').hidden = false; $('loginBtn').onclick = () => neblla.login(); $('logoutBtn').onclick = () => neblla.logout(); $('createRoomBtn').onclick = () => neblla.createRoom(); $('leaveRoomBtn').onclick = () => neblla.leaveRoom(); $('joinRoomBtn').onclick = () => { const code = $('roomCodeInput').value.trim(); if (code) neblla.joinRoom(code); }; neblla.on('joinRoom', (event) => { isLobby = event.isLobby; $('status').textContent = 'In room: ' + event.room.name; $('leaveRoomBtn').hidden = isLobby; renderPlayers(); if (isLobby) { $('game').innerHTML = ''; } else { // render game board } }); neblla.on('messageFromServer', (event) => { if (event.data.type === 'state') { // render(event.data.state); } }); neblla.on('state', (state) => { // render(state); }); neblla.on('memberJoinedRoom', renderPlayers); neblla.on('memberLeftRoom', renderPlayers); neblla.on('publicInfo', renderPlayers); neblla.on('newPresenter', renderPlayers); neblla.on('login', () => { $('loginBtn').hidden = true; $('logoutBtn').hidden = false; }); neblla.on('logout', () => { $('loginBtn').hidden = false; $('logoutBtn').hidden = true; }); function renderPlayers() { const el = $('players'); el.innerHTML = ''; neblla.room.users.forEach(u => { const div = document.createElement('div'); div.className = 'player' + (u.isPresenter ? ' presenter' : '') + (u.isMe ? ' me' : ''); div.textContent = (u.publicInfo.name || u.id.slice(0, 8)) + (u.isMe ? ' (you)' : ''); el.appendChild(div); }); } }; ``` ### room.js ```javascript // Top-level. neblla en scope global. Sin DOM. Sin neblla.me. let gameState = neblla.state || null; neblla.on('messageToServer', (event) => { if (event.data.type === 'action') { gameState = applyAction(gameState, event.user.id, event.data); neblla.setState(gameState); } }); neblla.on('memberJoinedRoom', () => { if (gameState) neblla.setState(gameState); }); ``` ### Adaptaciones - **Host + controllers**: en `client.js` ramificas con `if (neblla.me.isPresenter) renderPresenterView(); else renderControllerView();`. - **Collab realtime**: `room.js` vacío. Todos los peers iguales. `sendMessage` para cambios locales, aplicar entrantes en el handler. - **Solo + save**: `room.js` vacío, sin salas. Solo `neblla.save()` y `neblla.me.data`. ================================================================================ Capítulo MCP de la guía Neblla — el servidor MCP para que una IA cree y gestione apps. El contrato base está en core.txt. # El servidor MCP Endpoint: `https://neblla.com/mcp`. Es la puerta por la que cualquier IA puede crear apps Neblla, subir ficheros, gestionar productos y usuarios, y publicar en la galería en nombre del desarrollador. ## Autenticación OAuth con Bearer token. El recurso protegido anuncia su metadata en `https://neblla.com/.well-known/oauth-protected-resource/mcp`. El access token tiene su propio TTL y revocación. Tres tools funcionan sin autenticación: `neblla_get_llms`, `neblla_get_app_url` y `neblla_validate_requirements`. El resto requiere el token del desarrollador. ## Tools Guía y descubrimiento (sin auth): - `neblla_get_llms` — descarga esta guía por secciones (`section`: core, salas, servidor, identidad, social, pagos, ia, publicar, referencia, mcp, index, full). Llamar ANTES de diseñar o escribir código: como mínimo `core`, y después las secciones de las features que la app use. Si una sección no cabe en una respuesta, comprueba `hasMore` y repite con `offset = nextOffset`. - `neblla_get_app_url` — la URL pública canónica de una app en la galería. Nunca construyas la URL a mano. - `neblla_validate_requirements` — recorre el mapa de decisiones de creación (el mismo del wizard del dashboard). Ver "El flujo de decisiones" más abajo. Apps: - `neblla_create_app` — crea una app. Devuelve el `appId` para las operaciones siguientes. - `neblla_list_apps` — lista las apps del desarrollador autenticado. - `neblla_get_app` — todo sobre UNA app: configuración y estado (features, salas, créditos de hoy, suscripción, galería, comunidad, IA…), todas las URLs en las que responde, dominios propios y si hay relé TURN. - `neblla_update_app` — actualiza configuración, solo los campos a cambiar: name, sandbox, addDomain, removeDomain, ai, inbox (booleano u objeto `{ maxPerUser, maxOutboundPerUser }`), features, community, matchmaking, serverMode (`js` | `wasm` | `infra`, este último solo Pro), rooms (un cambio que pierde datos de salas responde `rooms_data_loss`: repetir con `confirmRoomsChange: true` solo si el dev acepta), p2p, connections, theme, permissionJustifications, guestTtl, pattern. Escribir en una app con borrador lo descarta (`draftDiscarded: true`). - `neblla_set_slug` — comprueba (`checkOnly`), pone o quita (`slug: null`) el nombre legible de la URL pública. - `neblla_copy_app_config` — copia los ajustes de comportamiento de una app tuya a otra (nunca nombre, ficheros, dominios, slug, galería ni facturación). - `neblla_delete_app` — borra la app y todos sus ficheros, permanente. Ficheros: - `neblla_upload_file` — sube o actualiza un fichero de la app. - `neblla_list_files` — lista los ficheros (path, mimeType, size, timestamps). - `neblla_get_file` — contenido de un fichero (los de texto siempre como texto). - `neblla_delete_file` — borra un fichero. - `neblla_move_file` — renombra o mueve un fichero (oldPath → newPath). Versiones seguras: - `neblla_snapshot` — copia local de los ficheros antes de un cambio grande: `create`, `rollback` (restaura, sobrescribe lo actual) o `discard`. - `neblla_draft` — borrador privado de la app (id `_`): `info`, `create`, `apply` (lo promueve sobre la app en vivo) o `discard`. Se edita pasando el id del borrador a las tools de ficheros y de app. - `neblla_archive_app` — archiva una app sin uso; se restaura sola la próxima vez que alguien la abre. Productos: - `neblla_create_product` — crea un producto comprable. - `neblla_update_product` — actualiza un producto (solo campos a cambiar). - `neblla_list_products` — lista los productos de una app. - `neblla_delete_product` — borra un producto, permanente. Galería: - `neblla_publish_app` — publica en la galería pública (metadata en publicar.txt). - `neblla_unpublish_app` — quita la app de la galería; ficheros y URL se conservan. Usuarios finales (backoffice): - `neblla_list_users` — lista los usuarios finales de una app (no la cuenta del dev). - `neblla_search_users_by_email` — busca usuarios por email parcial (mín. 2 caracteres). - `neblla_get_user` — registro completo de un usuario: saved data, public info, compras, hilo de comunicaciones. - `neblla_update_user_data` — reemplaza el objeto `data` de un usuario (el mismo que lee/escribe `neblla.save()`). - `neblla_set_ban_status` — `none` | `banned` | `terminated` (borra sus datos locales y lo desconecta) | `community_muted`. Efecto en vivo. - `neblla_ban_settings` — lee o cambia el mensaje que ven los jugadores baneados. - `neblla_consume_purchase` — consume unidades de un producto comprado por un jugador (atómico; `insufficient_amount` si no llega). Salas y moderadores: - `neblla_list_rooms` — salas persistentes con tipo, dueños, lista blanca, moderadores, datos, mensajes y gente dentro ahora. - `neblla_get_room_history` — historial de chat de una sala (paginado) para moderar. - `neblla_delete_room_message` — borra un mensaje de chat. - `neblla_create_room` / `neblla_update_room` / `neblla_delete_room` — salas predefinidas: crear, cambiar datos o moderadores (en vivo), borrar. LOBBY está protegida. - `neblla_list_moderators` / `neblla_set_moderator` — moderadores de la app (por id de usuario de la app). Comunidad: - `neblla_list_community_entries`, `neblla_get_community_entry`, `neblla_moderate_community_entry`, `neblla_publish_community_entry` — entries publicadas por los usuarios: listar, leer con denuncias, moderar, sembrar como dev. - `neblla_get_community_config` — configuración resuelta y uso del almacenamiento de la comunidad. Reseñas de la galería: - `neblla_list_reviews` — reseñas con media, número y distribución 1–5. - `neblla_reply_review` — publica, cambia o borra (`text: ""`) la respuesta pública del dev; se traduce sola al idioma del autor. Comunicaciones (backoffice): - `neblla_list_communications` — lista comunicaciones de TODOS los usuarios de una app, filtradas y ordenadas; para triar el inbox. - `neblla_send_communication` — manda un mensaje del dev a un usuario; le llega en vivo por el SDK. - `neblla_set_communication_read` — marca leída/no leída (lado dev); el usuario se entera en vivo. - `neblla_set_communication_important` — toggle del flag "important"; metadata solo-dev, no se difunde al usuario. - `neblla_delete_communication` — borra una comunicación del hilo; el usuario recibe `communicationDeleted`. Cuenta: Integraciones: - `neblla_git` — la app sigue a un repo de GitHub (el repo manda: mientras está vinculada, los ficheros no se editan en Neblla — se edita en el repo y se hace push). `repos` (los repos del dev si entró con GitHub), `branches`, `status`, `link` (url, branch, subdir, syncMode; con `auto` y GitHub vinculado, el webhook se registra solo), `sync` (solo baja lo que cambió), `set_mode`, `test_branch` (una copia privada `_test` con la misma configuración que sigue otra rama, nunca en la galería; `null` la quita) y `unlink`. - `neblla_byodb` — base de datos propia del dev: `status`, `validate`, `migrate` (mueve todos los datos de los jugadores), `cancel`. Motores: mongo, turso, postgres, mysql. Las credenciales se guardan cifradas y nunca se devuelven. Cobros: - `neblla_payments_account` — la cuenta de cobros (Stripe Connect) del dev: `status`, `onboarding_link` y `dashboard_link` (enlaces que abre una persona). Los productos siguen ocultos a los jugadores hasta que la cuenta está activa. Cuenta: - `neblla_developer_profile` — perfil del dev (nombre, idioma, emails, si hay clave de Claude guardada); cambia `name` / `lang`. - `neblla_developer_email` — añade un email (queda pendiente hasta que su dueño confirma) o reenvía la confirmación. - `neblla_delete_account` — por seguridad el MCP nunca borra cuentas directamente; devuelve una URL para que el usuario lo haga. Solo en la web, a propósito: contraseña, rotar el token, guardar la clave de Claude, quitar emails o cambiar el principal, suscribirse a Pro y comprar cuota (hasta que se revise la facturación). ## Flujo recomendado de creación 1. `neblla_validate_requirements` — recorrer el mapa de decisiones (abajo) hasta `have_enough_context: true`. 2. `neblla_get_llms` — cargar `core` + las secciones de las features encendidas. 3. `neblla_create_app({ name, decisions })` — crea la app, scaffoldea el patrón con sus piezas tematizadas y escribe la config completa. Devuelve el `appId`. 4. `neblla_upload_file` (y `neblla_get_file` / `neblla_move_file` para editar) — editar los ficheros scaffoldeados siguiendo la estructura canónica de core.txt. Los ficheros están en producción en cuanto se suben. 5. `neblla_get_app_url` — obtener la URL canónica y entregarla al usuario. Publicar en la galería (`neblla_publish_app`) es un paso aparte y opcional. ## El flujo de decisiones `neblla_validate_requirements` recorre el MISMO mapa de decisiones que el wizard del dashboard: las dos superficies son la misma entrevista. El contrato para el modelo que crea la app: 1. Pasa en `decisions` todo lo que la petición del usuario ya responde o implica. 2. La respuesta lista cada decisión pendiente con `how_to_infer` (cómo deducirla tú mismo) y `ask_user` (si merece molestar al humano). **Responde tú mismo todo lo que puedas.** Solo las decisiones devueltas en `ask_user_questions` que de verdad no puedas inferir se preguntan al usuario — todas juntas, en un único mensaje. 3. Repite con el set completo hasta `have_enough_context: true`. Las decisiones con default y `ask_user: never` se autocompletan; las "preguntables" (p. ej. `aiPayer`, que decide quién paga) exigen respuesta explícita — tuya o del usuario — y nunca se rellenan en silencio. 4. Pasa el `decisions` devuelto tal cual a `neblla_create_app`. El campo `plan` de la respuesta te enseña antes de crear qué patrón, piezas y config van a salir. Las decisiones: `kind` (game|app), `players` (single | lobby-only | multi — single = un dispositivo; lobby-only = todos en un único espacio común sin salas; multi = salas separadas), `structure` (turn-based | host-controllers | collab-realtime; solo cuando players=multi), las piezas `login`/`friends`/`purchases`/`ai`/`communications` (booleanos), `connections` (all|p2p|server), `serverMode` (js|wasm), `roomsPolicy` (ephemeral|public|private|mixed), `matchmakingMode` (nocode|custom), `aiPayer` (off|browser|server; solo con ai), `palette` ({primary, secondary, alert} en hex — elígela TÚ según el carácter de la app) y `typography` (modern|editorial|techy|serif|playful — también tuya). El scaffold resultante: el patrón elegido + una pieza de UI con diseño por cada feature encendida (login, salas, jugadores, chat, tienda, caja de IA, amigos, inbox), tematizadas con la paleta y tipografía vía variables CSS en main.css. Los ficheros server/ salen del patrón. Tu trabajo después es EDITAR esos ficheros, no reescribirlos desde cero.