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…

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 + mMatrix · regla r-hombre
person + fTopGun · regla r-mujer
person + uPersona · regla r-persona
carCoche · regla r-coche
motorcycleMoto · regla r-moto
bicycleBici · regla r-bici
sin label fresconeutral · 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.

Conectar

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