Admira.tv · Model Context Protocol

El canal, conectado

Servidor MCP de la capa Emitir: agentes (silicio) y personas (carbono) consultan qué hay en antena en cada pantalla, el proof-of-play de cada circuito y cómo poner una pantalla a emitir. Y desde hoy, federación: admira.tv se coordina con circuitos externos y viceversa. Y el player condicionado: sin presencia válida, playlist; con presencia, contenido según el productor y las reglas. Zapatillas solo detecta tipología.

comprobando servidor…

Cambios del 8 y 9 de octubre de 2026

Registro para el Consejo y los agentes, cada PR con su descripción y su documentación: cambios-2026-10-09.md · Help. admira.tv #54–#67 (playlists, «Qué emite», modelo único E1–E7, verja sin bucle, parrilla en el iPad) y, como referencia, omnipublicity-api #34, pixeria #86–#88, pixer-worker #8, xpaceos #100 y el player nativo de iPad. Documentación, no herramientas MCP nuevas.

EN: 8–9 Oct 2026 changelog for the council and agents, with every PR described and linked.

Programación · modelo único de playlists

Playlists + asignaciones + un resolver por capas (emergencia 500 · pagada 400 · propia 300 · por defecto 200 · relleno 100) en la D1 admira-programacion. Hasta el corte (E13) manda el KV de siempre: la D1 se compara (sombra) y se mantiene al día (espejo). Estado del 9-oct: 34 playlists + 34 asignaciones importadas (versión 68); sombra y espejo encendidos por Carlos, motor apagado (versión 69). Contrato: programacion.md · diseño: playlists-modelo-unico.md · panel: /programacion/sombra/ · Help.

GET    /api/programacion?screen=<id>[&at=<ISO|ms>][&tag=<hashtag>]        resolver de solo lectura (sesión; virtuales públicas)
GET|POST|PUT|PATCH|DELETE /api/programacion/{playlists,asignaciones,circuitos}[/:id]   rev obligatoria · 409 · 428 · 422
GET    /api/programacion/historial/<tipo>/<id> · /api/programacion/auditoria[?accion=espejo_fallido]
POST   /api/programacion/importar[?aplicar=1&pisar=1&archivar=1&cursor=]     simula por defecto
GET|POST /api/programacion/banderas  {sombra?, espejo?, motivo}                 sólo sesión · motor → 409 hasta E13
GET    /api/programacion/sombra[?veredicto=distinta&motivo=&pantalla=]

Escritura con la sesión del portal (permiso digitalsignage-player) o la clave de servicio PROGRAMACION_SERVICE_KEY (X-Programacion-Key), que hoy no está puesta. No hay herramienta MCP de programación: es HTTP con sesión. No encender motor ni forzarlo por SQL.

EN: Unified playlist model. Legacy KV still drives players; shadow comparison and double write are on, the engine stays off until E13.

Qué emite cada pantalla · /api/emision

Réplica pura de rebuild() de canal.html: qué fuente gana ahora (o a otra hora), por qué, qué capas quedan anuladas y las piezas en orden. Pila: Xtore › mural › previo › hashtag › «Por defecto» › sincro › Stock con la parrilla cada 4. observado.vivo = latido en /signage/now hace menos de 5 min. Contrato: emision.md · página: /emision/ · Help.

GET https://admira.tv/api/emision?screen=<pantalla>[&at=<ISO|ms>][&tag=<hashtag>][&resumen=1]
→ {fuente, motivo, etiqueta, capas[{id, estado, anuladaPor}], piezas[{pos, titulo, duracion, procedencia}], avisos, supuestos,
   observado{vivo, standby, ahora, publicada{n, parece, coincide}}}

Sin efectos. Pantallas virtuales sin sesión; el resto con sesión digitalsignage-player o visor. No abras canal.html?screen=<pantalla real> para mirar: se hace pasar por ella.

EN: What a screen plays now and what overrides its default playlist, without side effects.

Player nativo de iPad

App SwiftUI + AVPlayer (repo privado csilvasantin/admira-player-ipad, v0.1 del 9-oct): «Por defecto» + Stock + parrilla cada 4, disco primero, sin red, informa a /signage/now, /control/playlist, /screen/cache y /emit. Verificado en el iPad de Admin como altadis-bcn-001-p1-vertical (vivo: true). Instalación por USB con tools/instalar-ipad.sh <pantalla>; firma gratuita de 7 días (caduca el 16-oct). Fuera de la v1: sincro, mural, hashtag del mando, Xtore y órdenes del mando. ipad-nativo.md · Help.

EN: Native iPad player; free signing expires every 7 days and needs a reinstall.

Probar en el iPad por USB

Para agentes del Mac Mini: ~/Claude/tools/ipad-venv/bin/pymobiledevice3 — capturas (developer dvt screenshot, sin sudo), abrir apps (developer dvt launch), Safari (webinspector opened-tabs|launch|js-shell) y puente CDP (webinspector cdp → http://127.0.0.1:9222/). Requiere Modo de desarrollador e Inspector web + Automatización remota en Safari. Trampas: emparejamiento atascado, «Desarrollador no fiable» y sesiones de automatización sin cookies. ipad-pruebas.md.

EN: How agents screenshot, open apps and drive Safari on the USB iPad.

Páginas con verja · ready() y expired()

Toda página que llame a una API al arrancar espera a AdmiraTvAuth.ready() (antes de la primera llamada y de sus refrescos) y pasa cada 401 por AdmiraTvAuth.expired() (una recarga por minuto como mucho). Arregló el bucle de recarga de /users/ y /remotecontrol/ (#64). acceso-paginas.md · Help.

((window.AdmiraTvAuth && AdmiraTvAuth.ready) ? AdmiraTvAuth.ready() : Promise.resolve()).then(arrancar);
if (r.status === 401 && !AdmiraTvAuth.expired()) avisar('Sesión caducada: vuelve a entrar');

EN: Gated pages wait for ready() and route 401s through expired().

Xtore · audiencia de sesión

Contrato XpaceOS / Pixeria · Esquema JSON · Help. Contadores y enlace entre ventanas implementados; importación de sesiones en Pixeria y nuevas tools remotas pendientes.

IEU · inicio automático de Puerta Cam

Pendiente de implementar en IEU. Guía para su mantenedor: al entrar con la cuenta configurada, seleccionar Store · Santa Rosa 19, abrir Entrada, orientar el visor hacia Puerta Cam y mostrar su vídeo. Incluye puntos de integración, cancelación, permisos y pruebas de aceptación.

Instrucciones de implementación → · Resumen en Help. Es documentación de un cambio solicitado; no añade una herramienta MCP ni activa la cámara automáticamente hoy.

Player · contrato de integración (r60)

Para trabajar con el player sin modificar el nuestro: identidad por URL o broadcast, lectura de estado sin sesión, cola de órdenes con acuse, playlists por pantalla y precedencias. Documento completo: player-contract.md · Help.

POST https://api.admira.store/locations/cmd  {"id":"<pantalla>","screen":"<pantalla>","cmd":"fit-fill"}
GET  https://api.admira.store/locations/cmd/ack?id=<pantalla>&action=<action>   → ack.status executed|failed|ignored
GET  https://api.admira.store/signage/now?screen=<pantalla>      → item, sinc, standby, device.display.fit/rotation
GET  https://api.admira.store/screen/cache?screen=<pantalla>     → ready[], total, downloading[{id,pct}]
GET  https://brain.digitalavatar.ai/control/playlist?screen=<pantalla>-tema   → playlist programada del kiosko

Órdenes: next prev first last · pause play · mute unmute vol-up vol-down · standby resume · rotation-0|90|180|270 · fit-fill|fit-editorial · goto-N · seek-S · content-NUM · content-clear · tag-X · tag- · play<num> · info-show|info-hide · hardreload. Al id del circuito llegan a todas sus pantallas.

Lo que hace solo: descarga única con adopción al arrancar; sincro predescargada; #ID nunca online hasta estar en disco; autoactualización al cambiar de pieza (≥ r60); Rellenar sin recortar; goto-N aguanta en sincro; duración editorial única (imagen/audio/interactivo) igual en antena, proof-of-play y mando. Cadencias del worker: now 5 min / 4, caché 10 / 7; throttled:"budget" = presupuesto KV agotado hasta las 00:00 UTC.

Precedencia: emparejamiento con player virtual > sincro/modo remoto del circuito > local. Android: identidad por am broadcast -n tv.admira.signage/.ConfigReceiver (el -n es obligatorio); el standby se conserva al reinstalar. Jardinets (15-09): iPad de Admin ipad-admin-mupi (vertical, principal) + Fold 8 samsung-galaxy-fold-8-mupi (horizontal) + iPhone iphone17-mupi (vertical), ambos vía SITE_EXTRA, + Tab A11, tema música.

Player Android · instalar, controlar y diagnosticar

Guía completa para agentes: Android por USB, OTA, MCP y Xtore. Incluye comandos, contratos, errores, permisos, pairing y criterios de aceptación. Help paso a paso.

{"name":"player_info","arguments":{"platform":"android","section":"all"}}

Secciones: overview, features, releases, install, config, controls, conditional, diagnostics. Sin platform se conserva macOS. Es documentación: no instala ni controla el teléfono. Las versiones del APK, canal web y configuración se verifican por separado.

Reinstalar conserva standby. App abierta y online no significan contenido reproduciéndose. Comprobar ficha local, estado fresco, avance de vídeo y salida de audio. El perfil Zapatillas detecta categorías, no sexo ni edad; un equipo físico necesita emparejamiento y relé autenticado.

Player virtual · alta en flota

Presencia continua: solo xtore-virtual-zapatillas, confirmada en dos fotogramas fuertes o tres para bicicleta de confianza baja, incluso quieta. Último fotograma y evidencia fuerte menores de 1,5 s; edad desde captura, no final de inferencia. Memoria de asociación hasta 8 s no significa visibilidad. Prioridad bici/moto/coche/persona; presencia/cajas caducan a 1,5 s; persona no tiene cola. Coche/moto/bici conservan solo su contenido 2 s más (3,5 s desde última evidencia fuerte), sin simular cajas ni renovar por observaciones antiguas. Prioridad bici → moto → coche → persona; un vehículo nuevo más prioritario puede ganar. Al caducar pasa a otra categoría vigente o neutral. Pausa/desconexión cancelan de inmediato. El respaldo del hijo revisa su TTL de 6 s cada 2 s. No cambia el bus físico descrito más abajo.

Overlay y conteo: rectángulos sobre ROI con clase/#ID y color estable por trayectoria, no identidad. Texto distribuido sin solapamiento, oculto si no cabe; cajas efímeras, borradas al caducar/pausar/ocultar/desconectar/recalibrar. Estar quieto mantiene música sin sumar pasos. Capturas de 6 s independientes, sin renovación por presencia. Solo categoría al player, sin IDs, geometría ni fotos; patinete manual sin overlay automático ni comando. Misión DCL-ea4f046ffd30919c4c705137: documentación de implementación, no verificación de cámara real ni herramientas nuevas.

H · ocultar personas: botón o tecla H con foco en el analizador, fuera de campos de texto. Transformación local, temporal y reversible del previo ROI; el iPad conserva el vídeo original con cajas según la implementación del 13 de septiembre, conservando calle y rectángulos/etiquetas frescos. Aprende fondo en regiones despejadas durante tres fotogramas estables y dilata las cajas de persona; donde no hay fondo conocido muestra trama y rótulo, no inventa detalle. Memoria volátil, reinicio en pausa/ocultación/reencuadre/desconexión y caducidad de 1,5 s sin frame fresco. El detector cuenta y decide sobre el original; H oculta el original de Capturas, pero recortes/Twins son independientes. No usa IA externa ni garantiza anonimización, identidad o evidencia de seguridad; no es grabación.

H y cola de vehículos: documentación local 0.2.11 / Help .9, misión DCL-b7723cea465ac94cb685d1de; incluidas en v.12.09.2026.r15.15:52; contrato, no estado en vivo.

La ficha se mueve dentro del visor completo del player, no dentro del rectángulo del vídeo: una pieza horizontal no reduce su altura y el giro interno del MUPI no gira la ficha. En Xtore se conserva la proyección exterior del iframe; la ficha no sale de él ni modifica la calibración.

Preset: el analizador guarda automáticamente el último encuadre de cámara, iPad y DS en este navegador/origen (solo coordenadas, dimensiones y fecha). Se recupera tras compartir una fuente con proporción compatible, sin conceder permisos; «Arrancar cámara y análisis» inicia con preset compatible, mientras «Compartir sin analizar» conserva el inicio manual. Admite superficies parciales. El operador revisa giros/zoom y remarca cuando cambie la escena.

Ficha y mini mando: doble toque o Ctrl+I abre «Contenido en antena». X o Escape con foco en la ficha la cierran; la cabecera se arrastra o se mueve con flechas al enfocarla con Tab (Mayús mueve más), con botones de foco visible y toolbar fijo al desplazar el cuerpo. Cierre, arrastre y ecualizador son comunes; Anterior/Siguiente, Sonido/Mute y Repetir pieza quedan restringidos al perfil musical exacto de Xtore, en sus presentaciones previa e integrada. Repetir reinicia audio/vídeo al terminar naturalmente y no se habilita para imágenes, sincro, directo o standby. Navegar, cambiar de pieza/contexto base-condicional, un error o neutral por TTL desarman el loop; repetir no renueva presencia ni capturas. Cerrar la ficha no lo cancela. «1 de X» describe la playlist efectiva (condicional de una pieza: «1 de 1»), no emisión confirmada; AUDIO/LIVE/MUTE ocupa su propia fila bajo el ecualizador. Solo controla la instancia y su lista actual, sin editar Flota ni hardware ni evitar el permiso de sonido. Son funciones UI documentadas en el manifest y el MCP local 0.2.11 / Help 2026-09-12.9, no herramientas MCP nuevas ni estado en vivo.

El virtual de zapatillas en Xtore tiene base musical sin necesitar vídeo en directo de la cámara. Su iframe usa xtoreMusic=1 y muted=0, con permiso de sonido sujeto al navegador. Una playlist Por defecto asociada tiene prioridad y conserva su orden; sin asociación elige los últimos cinco contenidos Pixeria con etiqueta exacta #musica, normalizada en mayúsculas, acentos y prefijo #. Tipos automáticos: audio, music, video y animation, por createdAt descendente; no imágenes, interactivos ni locuciones. Audios y vídeos musicales terminan con ended, no con un temporizador editorial. El autoplay bloqueado espera gesto, sin saltar de pieza ni atribuirlo a un códec. El motor condicional queda neutro hasta activar el analizador y confirmar presencia fresca con regla/creatividad, aunque el sujeto no se mueva. Renovar la misma pieza no la reinicia. Pausa/desconexión inmediatas o ausencia sin otra categoría vigente (persona 1,5 s; vehículos 3,5 s incluyendo cola musical de 2 s, no presencia visual) = vuelta a base desde principio; neutral sin interrupción no reinicia la pieza. La asociación sigue en Parrilla con sesión del portal; no hay una escritura MCP anónima. Contrato y límites.

La asociación se relee cada 30 s. Un fallo inicial comunica playlist-unavailable y reintenta sin asumir ausencia; una playlist asociada no reproducible no se reemplaza. playlist-empty indica esa lista no reproducible o falta de coincidencias musicales sin asociación. media-state.musicSource distingue playlist de pixeria-musica; no acredita reproducción. La selección sin filtro existe solo como helper interno y no cambia otros players.

Player que corre en un navegador, sin hardware dedicado asociado. Ficha persistente, playlist compartida; abrir, cerrar o reiniciar su instancia no crea otro equipo. Help · Flota.

player_virtual_registerscreen (prefijo virtual- o xtore-virtual-), name, circuit, mode? (local por defecto o conditional). Exige clave de flota. Alta idempotente: mismos datos devuelven la ficha; un ID ocupado con datos distintos se rechaza sin sobrescribir. No abre navegador, no marca online, no modifica playlists ni reglas. Devuelve channel_url, playlist_url y control_url.
player_virtual_pairdevice, screen (o null), revision?. Empareja un player real con un player virtual registrado: adopta su playlist, parrilla, modo y reglas, y si el virtual es condicional sigue su bus de audiencia (el analizador de Xtore publica ahí solo la categoría). null desvincula. Idempotente. Clave de flota.
player_virtual_listscreen? o cursor?. Registro público, 100 fichas por página; seguir cursor hasta null. Alias GET /virtual-players. Estar registrado no acredita presencia ni emisión.
{"name":"player_virtual_register","arguments":{"screen":"xtore-virtual-zapatillas","name":"Xtore · zapatillas — Player virtual","circuit":"admiranext","mode":"conditional"}}

Contrato local musical: persona → Top Gun / Take My Breath Away (1786533143983-n2y09e); coche/moto → Regreso al Futuro / The Power of Love (1786532932584-a1412h); bici → Pixeria #998, AZUL Y NEGRO - Me estoy volviendo loco (1789214248874-j6qqjo). Con persona y bici confirmadas se mantienen ambos conteos y gana bici para música. Su vídeo comienza al 50 % de la duración real en cada nueva activación/repetición, sin reseek por renovaciones. Solo este condicional del screen exacto: no cambia la base, otras pantallas ni reglas globales, ni infiere sexo/edad/identidad. Medio ausente o no reproducible: base. Presencia automática: frescura de 1,5 s y renovación cada 1000 ms. Los 6 s son respaldo del hijo desde la última orden y plazo manual desde clic, no desde playing; la carga consume parte del plazo. Botones «Reglas musicales · probar» solo sin intención de análisis ni calibración, sin pasos/capturas/histórico; se neutralizan al solicitar análisis. Son UI, no herramientas MCP. Revisión documental 2026-09-12.8, misión DCL-0c79557c38bde9f05e2ec381: nueva regla de bici incluida en v.12.09.2026.r14.15:30; no es estado en vivo.

El inicio de bici espera duración real para posicionar antes de reproducir. Si no llega en 10 s o falla el seek, se permite iniciar desde cero con aviso en consola, sin prometer el 50 % ni bloquear indefinidamente. El permiso de sonido sigue siendo necesario.

Recuperación del analizador: conserva intención ante ocultación, mute temporal, más de 3 s sin fotogramas o resize proporcional. Cada 500 ms verifica vista visible, misma fuente sin mute, nuevo fotograma, marcas válidas y ninguna inferencia pendiente. Pausa manual, desconexión, recalibración, formato incompatible o error del detector cancelan esa intención. Con gemelo XpaceOS enlazado y latidos frescos puede continuar oculto; sin enlace se suspende. No concede nuevos permisos de captura. Contrato estructurado.

La vista Xtore usa un iframe opaco, sin cámara ni captura ni credenciales. Lee catálogo y su playlist pública fija mediante un puente con lista cerrada; sus cuatro reglas musicales son locales, no las reglas globales. El catálogo es el índice público completo, con límite de 2 MiB/10 s: se filtra antes de elegir cinco y se conserva el último catálogo válido ante fallos. No aceptar URLs o IDs de hardware enviados por el iframe. No se migra ni elimina el histórico de IDs de sesión antiguos.

Endpoint

https://mcp-tv.admira.store/mcp

Herramientas

on_airQué emite una pantalla ahora + audiencia por cámara + si está conectada.
airtime_reportProof-of-play de un circuito: pases, segundos, por tipo y segmento.
channel_urlConstruye la URL del canal para poner una pantalla a emitir (opcional mode local|sync|conditional, audience=remote, muted).
player_xtore_mapSin argumentos. Devuelve pantalla, circuito, superficie metahuman del gemelo, URL del canal (mode=conditional&audience=remote), endpoints del bus (get, post, events, stream), carriles por tipología con su asset resuelto y origen (stock|arg|placeholder|null), con_anuncio/sin_anuncio, stock (escaneados/total/truncado), briefs para el creador, contrato (ttl 2 s · neutro 6 s · fade 350 ms · debug off) y estado vivo (bus + modo + reglas). Lectura abierta.
player_conditional_settarget? (circuito de segmentación, «xtanco») · screen? («xtanco-totem») · auto_assets? (bool, por defecto true: busca en el Stock el ítem cuyo externalRef empiece por admiranext:xtore:<lane>; gana sobre los args) · matrix_asset? (id del Stock para person+m; fallback placeholder Matrix) · topgun_asset? (person+f; fallback placeholder Top Gun) · persona_asset? (person+u) · coche_asset? (car) · moto_asset? (motorcycle) · bici_asset? (bicycle) — estos cuatro sin fallback: sin asset la regla se omite · neutral_assets? (array de ids; [] = carrusel del canal = playlist) · with_age? (bool: añade nino|joven|adulto|senior antes de las reglas por sexo) · mode? (local|sync|conditional, por defecto conditional). Escribe en este orden r-hombre · r-mujer · r-persona · r-coche · r-moto · r-bici · r-neutro (catch-all, kind any, minCount 0, assets []) en omnipublicity /segmentation?target=, cada una con campo kind y tag kind:<kind> (omnipublicity no conserva kind: conserva el tag y el id, y el canal lee rule.kind || tag || id), y fija el modo en /locations/mode (404 tolerado). SUSTITUYE el conjunto de reglas del target. Devuelve lo escrito, lo releído, sin_anuncio y briefs. Clave de flota.
player_consume_labelscreen? · kind? (person|car|motorcycle|bicycle; omitido = person) · sex? (m|f|u; obligatorio si kind = person, para el resto se normaliza a u) · age_band? (child|youth|adult|senior|unknown) · confidence? (0..1, 0.8) · source? (≤80) · ts? (ISO; stale si es anterior al último aceptado o fuera de la ventana de 2 s ±500 ms) · idempotency_key?. Es lo mismo que el POST del bus; devuelve la decisión de carril + poll_url + stream_url. Clave de flota.
player_conditional_statusscreen? · target?. Devuelve bus {fresh, ttl_ms, label, decision, server_ts}, mode, rules (con el kind decodificado de rule.kind || tag), los últimos 10 events, tipologias, lanes con asset resuelto, con_anuncio/sin_anuncio, briefs, contract, get_url y stream_url. Lectura abierta. Alias HTTP: GET /player/<screen>/health (reglas solo como counts/ids/kinds).
player_smokescreen?. Siete pasos midiendo latencia: m → Matrix, f → Top Gun, car → Coche, motorcycle → Moto, bicycle → Bici, person+u → Persona y, tras esperar el TTL (2 s), sin label → creative playlist. Devuelve {ok, male_to_matrix, female_to_topgun, car_to_coche, motorcycle_to_moto, bicycle_to_bici, unknown_to_persona, neutral_playlist_ok, latency_ms:{…, budget:2000}, steps, get_url, stream_url}. No toca reglas ni modo; deja el bus en neutro. Clave de flota.
player_embedCómo embeber el player condicionado en un panel externo: URL, iframe, postMessage, bus y carriles. Lectura abierta.
player_infoDocumentación del player nativo: qué es, kiosko desatendido, versiones, instalación y configuración.
quien_soyCon qué identidad te ve el servidor: flota AdmiraNeXT, circuito externo o anónimo.

Federación con circuitos externos

Protocolo federacion/1, simétrico: las mismas herramientas a los dos lados. Nosotros pedimos antena en sus pantallas; ellos nos la ofrecen en las nuestras y, al aceptar, la pieza entra de verdad en la playlist de cada pantalla del canal. Lecturas abiertas; escrituras con clave de flota; el circuito externo usa su clave cx_….

circuit_infoFicha de admira.tv como circuito: formatos, canales, pantallas vivas, cómo federarse.
circuitsDirectorio: nuestros canales con pantallas vivas + circuitos externos federados.
circuit_registerAlta de un circuito externo; entrega su clave cx_. Clave de flota.
circuit_screensPantallas de un canal nuestro o de un circuito externo (en vivo si expone MCP).
request_airtimeNosotros → ellos: pedir antena en un circuito externo (se entrega por su MCP). Clave de flota.
offer_airtimeEllos → nosotros: el circuito externo nos ofrece una pieza; queda pendiente de decisión.
airtime_decideaccept = emisión real en las pantallas elegidas · stop la retira · reject. Clave de flota.
airtime_statusEstado e historial de una antena federada, refrescado contra el circuito remoto.
airtime_report {id}Proof-of-play de una antena federada: despachos y qué emite ahora cada pantalla.

Para federar tu circuito: expón offer_airtime y airtime_status por MCP, dinos endpoint y clave, y te registramos. Referencia: circuito-espejo · protocolo: federacion.md · directorio: /federacion.

Player condicionado · Xtore

Qué es. Un tótem sin cámara que cambia de anuncio según quién pasa por delante. Sin nadie → emite su playlist normal. Alguien delante → el anuncio de su sexo (hombre → «Matrix», mujer → «Top Gun», persona sin sexo → «Persona») o de su tipología (coche → «Coche», moto → «Moto», bici → «Bici»). En cuanto se va (2 s sin label) vuelve a la playlist. Caso real: tótem xtanco-totem del circuito xtanco (Xtore Valencia), misión FLT-100243 / FLT-100280.

Flujo (de la puerta a la pantalla, < 2 s):

Puerta Cam (IEU, pestaña compartida)
   │  vídeo, nunca sale del navegador
   ▼
Analizador local  admira.tv/videoanalytics/xtore/   (COCO-SSD: person · car · motorcycle · bicycle)
   │  POST /audience/<screen>  {kind, sex, age_band, confidence, ts, source}   (clave de flota)
   ▼
Bus de audiencia  mcp-tv.admira.store  (Durable Object AudienceBus, un estado por pantalla, TTL 2 s)
   │  GET /audience/<screen> cada 500 ms   →  {fresh, label, decision:{lane, creative}}
   ▼
Canal  admira.tv/canal.html?audience=remote   →  carril Matrix | TopGun | Persona | Coche | Moto | Bici | neutral

Carriles (lo decide el bus; el canal casa la regla de la matriz con el mismo nombre):

person + m→ Matrix · regla r-hombre
person + f→ TopGun · regla r-mujer
person + u→ Persona · regla r-persona
car→ Coche · regla r-coche
motorcycle→ Moto · regla r-moto
bicycle→ Bici · regla r-bici
sin label fresco→ neutral · creative playlist · regla catch-all r-neutro (minCount 0, assets []) = playlist normal; hold neutro 6 s, fade 350 ms

Un kind que no es person nunca casa las reglas de sexo (su sex se normaliza a u). age_band omitido = unknown (nunca se rellena adult); solo kind y sex deciden el carril. Servidor: mcp-tv v2.2.0 (retrocompatible con labels v2.1 sin kind).

Contrato del label (v3) — lo que publica el analizador (o cualquier sidecar) en el bus:

{
  "kind":       "person" | "car" | "motorcycle" | "bicycle",     // omitido = person
  "sex":        "m" | "f" | "u",                                   // obligatorio si kind = person; el resto → u
  "age_band":   "child" | "youth" | "adult" | "senior" | "unknown",// opcional; omitido = unknown
  "confidence": 0..1,                                              // opcional (por defecto 0.8)
  "ts":         "2026-09-11T18:40:12.345Z",                        // ISO; opcional = ahora
  "source":     "puerta-cam"                                       // ≤ 80 caracteres
}

Publicar un label (clave de flota en Authorization: Bearer o X-MCP-Key; Idempotency-Key opcional, misma clave → mismo event_id, se recuerdan las últimas 50):

curl -s -X POST https://mcp-tv.admira.store/audience/xtanco-totem \
  -H "Authorization: Bearer $ADMIRA_FLEET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: puerta-cam-$(date +%s%3N)" \
  -d '{"kind":"person","sex":"m","age_band":"adult","confidence":0.91,"ts":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'","source":"puerta-cam"}'

→ 200 {"ok":true,"accepted":true,"screen":"xtanco-totem","event_id":"ev_…","decision":{"lane":"Matrix","creative":"Matrix","kind":"person","sex":"m"}}
   (con {"kind":"car"} → decision {"lane":"Coche","creative":"Coche","kind":"car","sex":"u"})
→ 400 {"ok":false,"accepted":false,"error":"stale"}   # ts anterior al último aceptado o fuera de la ventana de 2 s (±500 ms; futuro > 500 ms también)
→ 401 {"ok":false,"accepted":false,"error":"clave de flota requerida","ayuda":"Authorization: Bearer  · https://www.admiranext.com/mcp/#identidad"}

Rutas HTTP del bus y del player (CORS abierto, Cache-Control: no-store; lecturas sin clave):

GET /audience/<screen>estado actual. {ok, screen, fresh, ttl_ms:2000, label|null, decision:{lane, creative, kind, sex, age_ms, hold_ms, since}, server_ts} (decision.kind/sex = null sin label fresco). Es lo que el canal sondea cada 500 ms.
GET /audience/<screen>/events?limit=20últimos eventos (publish con el label; lane_change con from/to/creative/cause publish|expiry), más recientes primero; retención 60 s / 100.
GET /audience/<screen>/streamSSE (text/event-stream, CORS abierto): event: hello al conectar, event: audience con {screen, fresh, lane, creative, kind, sex, age_ms, hold_ms, label, server_ts} en cada cambio de carril o publish, keepalive cada 15 s y event: bye a los 5 min (reconectar).
POST /audience/<screen>publica un label (contrato de arriba). Clave de flota.
GET /player/<screen>(?circuit= opcional) ficha de embebido = tool player_embed: {screen, circuit, url_player, url_gemelo, iframe_html, aspect, postmessage:{in, out, origin_allowed}, bus:{get, post, events, stream, health}, lanes, sin_anuncio, briefs, contract}.
GET /player/<screen>/healthalias de player_conditional_status con las reglas resumidas: {ok, screen, target, release, bus:{fresh, ttl_ms, label, decision, server_ts}, mode:{id, mode}, rules:{count, ids, kinds, assigned, updatedAt, default}, events[10], contract, get_url, stream_url}.
curl -s https://mcp-tv.admira.store/audience/xtanco-totem        # respuesta real del 11-09-2026
{"ok":true,"screen":"xtanco-totem","fresh":false,"ttl_ms":2000,
 "label":{"kind":"car","sex":"u","age_band":"adult","confidence":0.9,"ts":"2026-09-11T16:51:12.926Z","ts_ms":1789145472926,"source":"smoke-neo"},
 "decision":{"lane":"neutral","creative":"playlist","kind":null,"sex":null,"age_ms":5072,"hold_ms":3152,"since":"2026-09-11T16:51:15.150Z"},
 "server_ts":"2026-09-11T16:51:17.998Z"}

curl -s "https://mcp-tv.admira.store/audience/xtanco-totem/events?limit=2"
{"ok":true,"screen":"xtanco-totem","events":[
 {"id":"ev_…","type":"lane_change","ts":"…","from":"Coche","to":"neutral","creative":"playlist","cause":"expiry"},
 {"id":"ev_…","type":"publish","ts":"…","kind":"car","sex":"u","age_band":"adult","confidence":0.9,"source":"smoke-neo","label_ts":"…"}],"total":100}

# SSE en el navegador (sin clave)
const es = new EventSource('https://mcp-tv.admira.store/audience/xtanco-totem/stream');
es.addEventListener('audience', ev => { const d = JSON.parse(ev.data); pintar(d.lane, d.creative, d.kind, d.sex, d.fresh, d.hold_ms); });
es.addEventListener('bye', () => { es.close(); /* reabrir: cierra a los 5 min */ });

Herramientas MCP del player condicionado (argumentos tal cual el inputSchema; por defecto pantalla xtanco-totem y target xtanco):

player_xtore_mapSin argumentos. Devuelve pantalla, circuito, superficie metahuman del gemelo, URL del canal (mode=conditional&audience=remote), endpoints del bus (get, post, events, stream), carriles por tipología con su asset resuelto y origen (stock|arg|placeholder|null), con_anuncio/sin_anuncio, stock (escaneados/total/truncado), briefs para el creador, contrato (ttl 2 s · neutro 6 s · fade 350 ms · debug off) y estado vivo (bus + modo + reglas). Lectura abierta.
player_conditional_settarget? (circuito de segmentación, «xtanco») · screen? («xtanco-totem») · auto_assets? (bool, por defecto true: busca en el Stock el ítem cuyo externalRef empiece por admiranext:xtore:<lane>; gana sobre los args) · matrix_asset? (id del Stock para person+m; fallback placeholder Matrix) · topgun_asset? (person+f; fallback placeholder Top Gun) · persona_asset? (person+u) · coche_asset? (car) · moto_asset? (motorcycle) · bici_asset? (bicycle) — estos cuatro sin fallback: sin asset la regla se omite · neutral_assets? (array de ids; [] = carrusel del canal = playlist) · with_age? (bool: añade nino|joven|adulto|senior antes de las reglas por sexo) · mode? (local|sync|conditional, por defecto conditional). Escribe en este orden r-hombre · r-mujer · r-persona · r-coche · r-moto · r-bici · r-neutro (catch-all, kind any, minCount 0, assets []) en omnipublicity /segmentation?target=, cada una con campo kind y tag kind:<kind> (omnipublicity no conserva kind: conserva el tag y el id, y el canal lee rule.kind || tag || id), y fija el modo en /locations/mode (404 tolerado). SUSTITUYE el conjunto de reglas del target. Devuelve lo escrito, lo releído, sin_anuncio y briefs. Clave de flota.
player_consume_labelscreen? · kind? (person|car|motorcycle|bicycle; omitido = person) · sex? (m|f|u; obligatorio si kind = person, para el resto se normaliza a u) · age_band? (child|youth|adult|senior|unknown) · confidence? (0..1, 0.8) · source? (≤80) · ts? (ISO; stale si es anterior al último aceptado o fuera de la ventana de 2 s ±500 ms) · idempotency_key?. Es lo mismo que el POST del bus; devuelve la decisión de carril + poll_url + stream_url. Clave de flota.
player_conditional_statusscreen? · target?. Devuelve bus {fresh, ttl_ms, label, decision, server_ts}, mode, rules (con el kind decodificado de rule.kind || tag), los últimos 10 events, tipologias, lanes con asset resuelto, con_anuncio/sin_anuncio, briefs, contract, get_url y stream_url. Lectura abierta. Alias HTTP: GET /player/<screen>/health (reglas solo como counts/ids/kinds).
player_smokescreen?. Siete pasos midiendo latencia: m → Matrix, f → Top Gun, car → Coche, motorcycle → Moto, bicycle → Bici, person+u → Persona y, tras esperar el TTL (2 s), sin label → creative playlist. Devuelve {ok, male_to_matrix, female_to_topgun, car_to_coche, motorcycle_to_moto, bicycle_to_bici, unknown_to_persona, neutral_playlist_ok, latency_ms:{…, budget:2000}, steps, get_url, stream_url}. No toca reglas ni modo; deja el bus en neutro. Clave de flota.
player_embedscreen? («xtanco-totem») · circuit? («xtanco»). Devuelve url_player, url_gemelo (xpaceos.com/admira-xp?totem=condicional), iframe_html 9:16, postmessage {in, out, origin_allowed}, bus {get, post, events, stream, health}, lanes con asset resuelto o null, sin_anuncio, briefs y contract. Lectura abierta. Alias HTTP: GET /player/<screen>[?circuit=].
channel_urlscreen · circuit · format? · cam? · clean? · mode? (local|sync|conditional) · audience? (remote = el canal lee el bus y arranca en condicional blindado) · muted?.

Embeber el tótem en un panel externo (analítica, gemelo, dashboard). Un solo iframe; el canal se gobierna con la URL:

<!-- tal cual lo devuelve GET /player/xtanco-totem (iframe_html) -->
<iframe id="totem"
  src="https://admira.tv/canal.html?clean=1&screen=xtanco-totem&circuit=xtanco&max=24&format=9%3A16&mode=conditional&audience=remote&muted=1"
  title="Admira.tv · xtanco-totem" style="width:100%;aspect-ratio:9/16;border:0;background:#000"
  allow="autoplay; fullscreen" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" loading="eager"></iframe>

postMessage que SALE del canal (en cada cambio de carril, igual que media-state):

const ORIGENES = ['https://admira.tv', 'https://www.admira.tv'];
window.addEventListener('message', e => {
  if (!ORIGENES.includes(e.origin) || !e.data || e.data.source !== 'admira-tv-canal') return;
  if (e.data.event === 'audience')    pintar(e.data.lane, e.data.kind, e.data.sex, e.data.fresh, e.data.ts);
  if (e.data.event === 'media-state') console.log('media', e.data.state, e.data.asset);
  // audience    = {source:'admira-tv-canal', event:'audience', lane:'Matrix|TopGun|Persona|Coche|Moto|Bici|neutral',
  //                sex:'m|f|u', kind:'person|car|motorcycle|bicycle|none', fresh:bool, ts}
  // media-state = {source:'admira-tv-canal', event:'media-state', state:'playing|paused|ended|error', asset}
});

postMessage que ENTRA al canal (simula público sin cámara ni bus). El canal solo lo acepta como objeto {source:'xpaceos-robot-cli', command} y desde los orígenes del puente: https://www.xpaceos.com, https://xpaceos.com o http://localhost:* / 127.0.0.1:* (un panel en otro dominio debe usar el bus: player_consume_label / POST). Responde {source:'admira-tv-canal', requestId, ok, message}.

const totem = document.getElementById('totem').contentWindow;
totem.postMessage({source:'xpaceos-robot-cli', requestId:'x1', command:'admiratv audiencia coche'}, 'https://admira.tv');
//   admiratv audiencia m|f|u|coche|moto|bici|persona    (u/off = vuelve al público real)
//   admiratv sincro|condicional|local                    (modo de playout)
//   playnext · playprevious · mute · unmute

Diagnóstico dentro del canal: window.__adtvAudienceRemote (polls, ok, fails, lane, sex, kind, last) y el CLI local /forcecam m|f|u|coche|moto|bici|persona. Con ?audience_api= y ?seg_api= el canal lee un bus o una matriz de prueba sin tocar producción.

Anuncios por tipología: del creador al carril en 3 pasos

1. Crear las piezas en el creador TikTok de AdmiraNeXT. Cada tool de lectura (player_xtore_map, player_conditional_status, player_embed, GET /player/<screen>) devuelve briefs: un deep-link por carril sin anuncio, https://www.admiranext.com/tiktok/?brief=<encodeURIComponent(JSON)> con {"campana":"Xtore · Puerta Cam","lane":"coche","tipologia":"coche","titulo":"…","mensaje":"…","externalId":"admiranext:xtore:coche","formato":"9:16","duracion":15} (uno por lane persona, coche, moto, bici). La página admiranext.com/tiktok/xtore.html los trae ya cargados (4 briefs). La pieza generada sube al Stock con la identidad externalRef = admiranext:xtore:<lane> (el creador la publica desde brief.externalId); el Stock no expone ese campo: deriva el id fijo del asset como auto- + 20 hex de sha256(externalId) → persona auto-5b9834e34116f1260f2f · coche auto-9a75882d2e3a36bce8e6 · moto auto-50312e3b227b47324a9b · bici auto-f8e5daa9669ca4adff65. Clave estable: dos clics no hacen dos piezas. Los productos del folleto se crean desde /contentcatalogue/ (deep-link ?producto=).

2. Cablear: player_conditional_set {} (con auto_assets por defecto) busca en el Stock (/stock/list?limit=200&type=video|image; no pagina: 200 más recientes por tipo) el ítem cuyo externalRef empiece por admiranext:xtore:<lane>, escribe las 7 reglas en /segmentation?target=xtanco y fija conditional. (v2.2.1, en despliegue: comprueba además la existencia de los ids fijos con GET api.admira.store/stock/asset/<id> y engancha la regla). Los carriles sin anuncio se omiten y vuelven en sin_anuncio + briefs: mientras tanto ese público ve la playlist normal (rótulo CONDICIONAL · playlist normal), nunca negro.

3. Comprobar: player_smoke (7 pasos) o player_consume_label {kind:"car"} → decision.lane = "Coche"; GET /player/xtanco-totem/health enseña rules.kinds (p. ej. r-coche:car) y el último carril. En el tótem el rótulo pasa a CONDICIONAL · Coche en < 2 s; sin label 2 s → playlist.

Placeholders de hoy (Stock, hasta los de Lucas): Matrix 1786643541589-rb1gz3 · Top Gun 1786533143983-n2y09e; el neutro ya no tiene creative fijo (es la playlist). Estado real el 11-09-2026 18:51: reglas vivas r-hombre · r-mujer · r-neutro; Persona/Coche/Moto/Bici sin anuncio aún. Guía para personas: /help/#xtore. Analizador: /videoanalytics/xtore/. Contrato del canal: CANAL.md § «Audiencia remota» y «Tipología» del repo admira-tv.

Preparar demo Xtore / Prepare Xtore demo

ES: Entrada desde Analítica de vídeo; cuadrática compartida cerrada, preparación del detector y comprobación de cámara, encuadre, análisis, reproducción y sonido. Compartir requiere selección explícita en Chrome.

EN: Video Analytics entry; shared closed quadratic shell, detector preparation and camera/framing/analysis/playback/audio checks. Chrome capture selection stays explicit.

Contrato ES/EN · Tutorial ES/EN · Abrir setup / Open setup. Documentación; no nueva herramienta ni comando remoto / Documentation; no new tool or remote command.

Conectar

{ "mcpServers": { "admiratv": { "url": "https://mcp-tv.admira.store/mcp" } } }

Starbucks · Acceso a Matrix / Enter Matrix

ES: En la vista Humano de Starbucks Passeig de Gràcia 103, pulsa el halo difuminado de la fachada para entrar en Matrix en la misma pestaña. No hay rótulo sobre la foto. El halo late suavemente y se intensifica al pasar el ratón o enfocar con Tab; Enter abre el gemelo. Con movimiento reducido permanece estático.

EN: In the Human view of Starbucks Passeig de Gràcia 103, click the blurred entrance halo to open Matrix in the same tab. There is no label over the photograph. The halo gently pulses and brightens on hover or keyboard focus; Tab then Enter opens the twin. Reduced motion keeps the halo static.

Starbucks · vista de calle / street view

macOS 1.7.2 · pantalla física y playlists por orientación sincronizadas / physical display and synchronized orientation playlists