MathTrail

Para público técnico

Cómo funciona el servicio

MathTrail se sitúa entre el chat que una familia ya usa y su Google Drive. El modelo del chat escribe cada problema, y el servicio decide si llega hasta el niño o la niña. Es un solo programa: no llama a ningún modelo propio y no guarda nada entre peticiones.

  • Un binario de Go en Cloud Run
  • Sin estado
  • Sin base de datos
  • Sin llamadas propias a LLM
  • MCP 2026-07-28 · MCP Apps

Cliente de chat1 · cliente

  • Claude
  • ChatGPT
  • otros hosts de MCP Apps

El modelo del chat escribe el problema, y la tarjeta se lo muestra al niño o la niña.

MCP · llamadas a herramientas por HTTPS

MathTrail en Google Cloud2 · ejecución

Cloud Run · un binario de Go · sin estado

El núcleo: cálculo puro

  • La regla
  • Puntuaciones
  • Comprobaciones
  • Solucionador
  • Contenido

A su alrededor: OAuth, el servidor MCP, la tarjeta, el sellado, los límites.

Drive API v3 · con el propio token de la familia

Google Drive3 · almacenamiento

MathTrail/mathtrail-profile.json

Un archivo por cada niño o niña, en el propio Drive de la familia: lo único que se guarda a largo plazo.

01

El mapa

Quién habla con quién. Ni el niño o la niña ni la persona adulta llegan directamente a MathTrail: hablan con el host del chat. Todo lo que hay en el bloque central es parte de un mismo proceso, no un servicio aparte. Elija cualquier parte para ver qué hace y con qué se conecta.

Partes del servicio

El chat de la familia

Claude en el navegador, en el ordenador y en el teléfono · ChatGPT

Personas

Host del chat

MathTrail

Un binario de Go · Cloud Run · un dominio propio

entre las partes, simples llamadas a funciones

La entrada

Herramientas: leer el perfil, calcular, volver a escribirlo

Cálculo puro, sin red

Alrededor del núcleo

Google y GitHub

Los únicos servicios fuera del binario

La cuenta de Google de la familia

Google Cloud

GitHub

Elija una parte del mapa: todo lo que se conecta con ella sigue resaltado, y lo demás se atenúa.

El chat de la familia

Persona adulta

Madre, padre o docente. Es titular tanto de la cuenta del chat como de la cuenta de Google: conecta MathTrail, inicia sesión una vez, crea el perfil, lleva la clase en el chat y sigue el progreso. Es la única persona que inicia sesión y la única que escribe.

El chat de la familia

Niño o niña

De 1.º a 6.º de primaria. Resuelve los problemas en la tarjeta, dentro del chat de la persona adulta. No tiene inicio de sesión propio.

Host del chat

El modelo del host

Claude o ChatGPT. Escribe el problema, un solucionador en Starlark, una explicación para cada opción incorrecta y una autocomprobación; lleva la clase con palabras allí donde no se dibujan tarjetas. MathTrail en sí no llama a ningún modelo.

Host del chat

Entorno de ejecución de MCP Apps

Dibuja la tarjeta en un marco aislado, le pasa el idioma, la plataforma y el tamaño, y reenvía las llamadas propias de la tarjeta con el token del host; la propia tarjeta nunca ve el token.

Host del chat

Documento de metadatos del cliente

El host se identifica con una dirección HTTPS. MathTrail obtiene el documento de esa dirección, con protección contra SSRF, en lugar de mantener un registro de clientes.

MathTrail · la entrada

Enrutador HTTP

La única entrada: /health, /mcp, los endpoints de OAuth y .well-known. Tiempos de espera, un límite al tamaño del cuerpo, una comprobación de Origin; el emisor es su propio dominio, y cualquier otro Host recibe un 404.

MathTrail · la entrada

Servidor de autorización

MathTrail es su propio servidor OAuth 2.1 con PKCE: metadatos, una pantalla de consentimiento propia, inicio de sesión con Google, tokens, renovación y revocación. Todo lo que guardaría un servidor con estado va sellado en tokens y cookies.

MathTrail · la entrada

Servidor MCP

El protocolo: Streamable HTTP 2026-07-28, sin estado, sobre el SDK oficial de Go. Enumera y llama a las herramientas, sirve la tarjeta como recurso ui:// y entrega al modelo sus instrucciones con su versión.

MathTrail · la entrada

Límites

Un ritmo por dirección antes de iniciar sesión y por cuenta después, y un tope general, todo en memoria; luego, los topes diarios, que cuenta el perfil. Pasado un límite, una frase sencilla que el modelo transmite.

MathTrail · herramientas

Herramientas

Doce herramientas con la misma forma: leer el perfil, calcular, volver a escribirlo. El modelo llama a la mayoría; la tarjeta llama a las suyas: para el problema, la respuesta, el progreso y el formulario del perfil. Nada secreto entra nunca en un resultado.

MathTrail · cálculo puro

La regla

Elige el encargo de forma determinista: tras un error, el mismo tema, para afianzarlo; si no, el tema que lleva más tiempo sin aparecer; la dificultad, del corredor donde la probabilidad de acertar es del 70–85 %. Un tema en el que el niño o la niña va por detrás llega el doble de a menudo. Los temas demasiado difíciles por ahora quedan en pausa, y cuando el nivel general alcanza uno de ellos, cada sexto problema puede traerlo de vuelta como comprobación del tema.

MathTrail · cálculo puro

Puntuaciones

P = 0,2 + 0,8·σ(θ + δ − β). Elo solo se mueve según si una respuesta es correcta, con un paso que se va reduciendo; una escala de ajedrez y once etapas; un tema cuenta como dominado según una estimación prudente.

MathTrail · cálculo puro

Comprobaciones

Lo que debe superar un problema enviado: su estructura y cinco opciones diferentes, las letras del idioma de la clase, explicaciones distintas, el solucionador, la autocomprobación, la legibilidad, los casi duplicados y el dibujo: su formato, sus etiquetas cotejadas con el enunciado y que no lleve escrita la respuesta. Todos los fallos se comunican de una sola vez.

MathTrail · cálculo puro

Solucionador

Ejecuta el programa en Starlark del modelo, que prueba las opciones; exactamente una debe resultar correcta. Límites de pasos y de tiempo, sin red y sin archivos.

MathTrail · cálculo puro

Contenido

Todo lo que va incorporado en el binario y no es código: los catálogos de temas, trampas y habilidades, los problemas de referencia, los esquemas, las plantillas de solucionadores, un ejemplo de cada tipo de dibujo y las instrucciones del modelo con su versión.

MathTrail · alrededor del núcleo

Widget

La tarjeta: Preact compilado en un solo archivo HTML que no carga nada de fuera, con los diccionarios de los idiomas dentro. El diseño funciona desde 320 px, y los botones se pulsan bien con el dedo.

MathTrail · alrededor del núcleo

Almacén

Encuentra el perfil por una marca oculta, compara antes de escribir, concilia dos pestañas, restaura el archivo desde las revisiones fijadas de Drive cuando la persona adulta lo pide, y no se sale de un pequeño presupuesto de llamadas a Drive.

MathTrail · alrededor del núcleo

Sellado

Un mecanismo, dos funciones: sella el inicio de sesión y los tokens de Google dentro de los tokens que guarda el host, y la respuesta de un problema —la del problema actual y la del escrito por adelantado— dentro del perfil. Las claves vienen de Secret Manager y rotan según el id de la clave.

MathTrail · alrededor del núcleo

Registro

Un resumen estructurado por llamada: la herramienta, el resultado, la duración, la versión de las instrucciones; los intentos y los motivos de un rechazo, como eventos del problema. Sin datos personales, sin texto de problemas, sin respuestas.

MathTrail · alrededor del núcleo

Contenedor y configuración

Conecta las partes entre sí y las cierra en orden inverso, trabaja con tiempos de espera y un apagado ordenado, lee toda su configuración del entorno y se niega a arrancar en Cloud Run con opciones de desarrollo activadas.

La cuenta de Google de la familia

Google OAuth

El inicio de sesión de la persona adulta, solo con el permiso drive.file, la renovación y la revocación. Revocar cualquier token pone fin a todo el acceso.

La cuenta de Google de la familia

Drive API v3

El único almacenamiento a largo plazo: un perfil JSON en una carpeta visible llamada MathTrail, en el Drive de la familia, que se lee y se escribe con el propio token de la familia.

Google Cloud

Secret Manager

Las claves de sellado y el secreto de cliente de Google: versiones fijadas, que se leen una vez al arrancar una instancia.

Google Cloud

Artifact Registry

La imagen de cada revisión, por su resumen; las imágenes antiguas se eliminan.

Google Cloud

Cloud Logging

El destino del stdout del servicio. Las líneas con las que se cuenta a los niños y las niñas se guardan 62 días en un bucket propio.

Google Cloud

BigQuery → Data Studio

Los recuentos de uso se guardan durante años y se vuelven a calcular cada noche; ninguna tabla contiene el nombre con el que se cuenta a un niño o una niña. Un informe privado y otro público, que no muestra ningún grupo de menos de diez niños y niñas.

GitHub

Actions

La compilación, las comprobaciones, la publicación de la imagen y el despliegue mediante la Federación de identidades para cargas de trabajo, sin claves de cuentas de servicio.

GitHub

Pages

Este sitio: las páginas de los temas, y la política de privacidad y las condiciones de uso a las que enlaza la pantalla de consentimiento.

02

Una clase, llamada a llamada

El modelo y la tarjeta llaman a las mismas herramientas y se apoyan en una única fuente de verdad: el archivo del perfil en el Drive de la familia. Para que la clase siga, no necesitan decirse nada.

Escenario
  • una llamada
  • su respuesta
  • dentro del host

Problema

  • Persona adulta
  • Tarjeta
  • Modelo del host
  • MathTrail
  • Drive
  1. 1Persona adulta → Modelo del host: «un problema nuevo, por favor»

  2. 2Modelo del host → MathTrail: next_task

  3. El host dibuja la tarjeta en cuanto empieza la llamada: «Preparando el siguiente problema…»

  4. límites · la regla elige el tema, el nivel y la dificultad

  5. 3MathTrail → Drive: escribir la petición abierta

  6. 4MathTrail → Modelo del host: la petición está abierta: obtén el paquete

  7. 5Modelo del host → MathTrail: get_package

  8. 6MathTrail → Modelo del host: encargo · 3 problemas de referencia · trampas · plantillas · ejemplos de dibujos

  9. El modelo escribe el problema, un solucionador en Starlark y una autocomprobación

  10. 7Modelo del host → MathTrail: submit_task: hasta 3 intentos

  11. comprobaciones · el solucionador debe encontrar exactamente una opción correcta

  12. 8MathTrail → Drive: el problema pasa a ser el actual, con su respuesta sellada

  13. 9Tarjeta → MathTrail: read_task: la tarjeta pregunta cada 4 s

  14. 10MathTrail → Tarjeta: texto · dibujo · cinco opciones · pista, sin la respuesta

Cada llamada lee además el perfil desde Drive; esas lecturas se omiten. La tarjeta que espera nunca ve el paquete: sus problemas de referencia llevan sus respuestas. Mientras el niño o la niña resuelve, el modelo escribe por adelantado el siguiente problema con prepare_task, y cuando se pide otro, next_task lo pone enseguida en una tarjeta nueva, sin esta espera.

Respuesta

  • Persona adulta
  • Niño o niña
  • Tarjeta
  • Modelo del host
  • MathTrail
  • Drive
  1. 1Niño o niña → Tarjeta: pulsa C

  2. 2Tarjeta → MathTrail: submit_answer: el chat no gasta ningún turno

  3. solo la primera respuesta · ahora se abre el sello · θ y δ solo se mueven según si es correcta

  4. 3MathTrail → Drive: escribir las puntuaciones y el historial

  5. 4MathTrail → Tarjeta: correcta o no · la trampa detrás de C · el dibujo de la solución · los pasos · la puntuación antes → después

  6. La tarjeta se convierte en el repaso de la respuesta, en el lugar del problema

  7. 5Tarjeta → Modelo del host: ui/update-model-context: una línea, que se lee con el siguiente mensaje de la persona adulta

  8. 6Persona adulta → Modelo del host: «¿por qué hay un poste más?»

  9. 7Modelo del host → Persona adulta: responde sobre este mismo problema, completando la tarjeta

La pulsación registra la respuesta antes de que se explique nada, y la tarjeta se convierte enseguida en el repaso de la respuesta. El chat no gasta ningún turno y el modelo no dibuja nada. El modelo lee la línea de la tarjeta con el siguiente mensaje de la persona adulta y responde a una pregunta sobre el problema sin volver a contar lo que muestra la tarjeta. Si la línea que el host envía al modelo se pierde, el modelo se pone al día en su siguiente llamada.

Acceso

  • Navegador de la familia
  • Host
  • MathTrail
  • Google
  • Drive
  1. 1Host → MathTrail: POST /mcp, sin token

  2. 2MathTrail → Host: 401 · dónde están los metadatos

  3. 3Host → Navegador de la familia: abre /oauth/authorize · PKCE S256

  4. 4Navegador de la familia → MathTrail: authorize

  5. Una pantalla de consentimiento propia: qué cliente, y adónde devolverá a la persona adulta

  6. 5Navegador de la familia → Google: inicio de sesión con Google · solo drive.file

  7. 6Google → Navegador de la familia: de vuelta, con el código de Google

  8. 7Navegador de la familia → MathTrail: /oauth/callback

  9. 8MathTrail → Google: intercambiar el código

  10. 9MathTrail → Drive: /about: ¿de quién es este Drive?

  11. id de usuario = HMAC de la cuenta de Drive · los tokens de Google, sellados en nuestro código

  12. 10MathTrail → Navegador de la familia: 302 · code = mt1.c.…

  13. 11Navegador de la familia → Host: devuelve el código

  14. 12Host → MathTrail: /oauth/token · código + verificador

  15. 13MathTrail → Host: acceso 15 min · actualización 30 días

La persona adulta inicia sesión una sola vez. El servicio no guarda nada de este intercambio: la petición viaja sellada en el parámetro state, y los tokens de Google, en un código sellado.

El problema actual como máquina de estados

Cada regla de «solo una vez» es un campo del perfil, que se comprueba dentro de la misma lectura, cálculo y escritura. Ninguna otra cosa recuerda nada.

nonenext_taskencargo listorequestedsubmit_task ✓sellado, entregadoissuedsubmit_answerregistrada una vezanswered

  • requested ↺rechazado, quedan intentos: no más de tres por petición
  • requested → nonetres rechazos, o 15 minutos desde que se abrió la petición; no se gastan problemas del día, y tres rechazos cuentan como una petición fallida del día
  • issued → requested«Otro problema» antes de una respuesta: se registra como en blanco y la puntuación no cambia
  • answered ↺cualquier respuesta posterior —desde una segunda pestaña o desde el modelo— recibe el primer resultado y no escribe nada
  • answered → requestedel siguiente problema, en una tarjeta nueva; el ya respondido deja de ser el actual
  • answered → issuedel siguiente problema ya estaba escrito: next_task lo pone enseguida en una tarjeta nueva, saltándose requested
  • issued → noneel sello ya no se puede abrir porque la clave ha rotado dos veces; no se registra nada

03

Dónde vive el estado

Todo lo que un servidor con estado guardaría en una base de datos lo guarda otro: en un token sellado que conserva el chat, en una cookie del navegador de la familia o en el propio Drive de la familia. Reinicie el servicio o arranque una segunda copia: no se pierde nada.

El propio MathTrail

Nada

Ni cuentas, ni sesiones, ni registros de clientes, ni tokens de Google. Cada petición lleva todo lo que necesita.

El almacén de tokens del chat

Tokens sellados, opacos para el host

Token de acceso
15 min
Token de actualización
30 días, ≤ 90

Dentro van sellados los tokens de Google, un id de usuario opaco para los límites y el país desde el que la persona adulta inició sesión.

El navegador de la familia

Dos cookies, sin sesión

Clientes aprobados
180 días
Protección del inicio de sesión (CSRF)
10 min

Perderlas cuesta una pantalla de consentimiento más, o volver a iniciar sesión.

El Drive de la familia

Lo único que se guarda a largo plazo

mathtrail-profile.json
hasta que se elimine

El perfil, las puntuaciones y el problema actual, con su respuesta sellada. La persona adulta puede abrir el archivo, y Drive guarda su historial.

Secret Manager

Se leen una vez, al arrancar una instancia

Claves de selladotras una rotación, la clave anterior se sigue aceptando
90 días
Secreto de cliente de Google
fijado

La memoria de la instancia

Solo cachés: se pierden al reiniciar, y nada se rompe

Contadores de ritmo de peticiones
minutos
Dónde está el archivo del perfil
10 min
Documentos de los clientes
de 5 min a 24 h

Lo que la v1 deja fuera a propósito

  • Una base de datos
  • Colas
  • Un almacén de sesiones
  • Una caché compartida
  • Un banco de problemas ya escritos
  • Un cliente de API de LLM
  • REST y OpenAPI

04

El archivo del perfil

Un niño o niña, un archivo JSON: no una caché de alguna verdad guardada en un servidor, sino la verdad misma. Cada bloque tiene un tope que se respeta en cada escritura, así que el archivo nunca crece sin límite: uno normal no pasa de la meta, y uno cerca de sus topes la supera. Los problemas pasados se guardan como huellas, no como texto.

Tamaño del archivo

≈ 61 KB / ≈ 79 KB

Qué tamaño mostrar
BloqueQué contieneQuién lo escribeTamaño
HuellasHasta 200 esbozos de los problemas entregados, para comprobar los casi duplicados; sin texto, sin tema, sin fechanext_tasksubmit_task27 KB / 29 KB
Historial de puntuacionesDónde estaban θ y el δ del tema antes de cada respuesta reciente, y θ y cada tema al comienzo de cada uno de los últimos 7 días con respuestassubmit_answer14 KB / 15 KB
Puntuaciones y temasθ y el punto de partida del niño o la niña; para cada tema: δ, las respuestas, las trampas en las que cayó, el dominio y su nivelnext_tasksubmit_tasksubmit_answer8 KB / 14 KB
Respuestas recientesLas últimas 20 respuestas y problemas en blanco, y nunca menos de las 5 respuestas que lee la reglanext_tasksubmit_answer4 KB / 5 KB
Problema actual, selladoLa respuesta, la trampa detrás de cada opción incorrecta, la solución y el solucionador, en una sola cadena opacanext_tasksubmit_tasksubmit_answer4 KB / 8 KB
Problema actual, abiertoLo que muestra la tarjeta: el texto, el dibujo, cinco opciones, la pista y, después de responder, la propia respuestanext_tasksubmit_tasksubmit_answer2 KB / 4 KB
Petición abiertaMientras se escribe el problema: su id, el encargo, los intentos usados y quién eligió el temanext_taskprepare_tasksubmit_task1,5 KB / 2 KB
Servicio y niño o niñaLa versión del esquema, un UUID aleatorio, la revisión; el seudónimo, el grado, los intereses, las anotaciones; los contadores del díasave_profileedit_profilecada escritura0,6 KB / 1,5 KB

Un problema escrito por adelantado está junto al actual y pesa más o menos lo mismo. El archivo no contiene los textos de los problemas pasados, ni la respuesta en claro hasta que el niño o la niña haya respondido, ni nada que nombre a la persona adulta, ni una etapa: las etapas se calculan cuando se muestran. Drive no tiene escritura condicional, así que el archivo se vuelve a leer antes de guardar y, si ha cambiado, la escritura se rechaza: dos pestañas nunca se sobrescriben entre sí en silencio.

05

Qué se sella y por cuánto tiempo

Un mismo mecanismo cumple dos funciones: sella el inicio de sesión dentro de los tokens que guarda el chat, y la respuesta de un problema —la del actual y la del escrito por adelantado— dentro del perfil. Cada sello lleva su propósito, así que un token de acceso no puede hacerse pasar por un token de actualización, ni una respuesta sellada por ninguna otra cosa.

Un token, desglosado

mt1.a.kQ7fWx.5rJ0Yb2mQ8vK1nT4pXc9…

mt1
versión del formato
propósito
c código · a acceso · r actualización · d cliente · s state · k consentimiento · t respuesta de un problema
id de la clave
se toma de la propia clave, así que una rotación pasa inadvertida
texto cifrado
XChaCha20-Poly1305, un nonce aleatorio de 192 bits

No es un JWT: nadie más que el servicio lo lee, y de una cadena opaca no puede filtrarse ningún claim a un registro.

Vigencias, en escala logarítmica

  • Código de autorización
    60 s
  • Inicio de sesión en curso
    10 min
  • Token de acceso
    15 min
  • Token de actualización
    30 días deslizantes, 90 como máximo
  • Clave de sellado
    se rota cada 90 días
  • Clientes aprobados
    180 días

Un servidor sin estado no puede marcar un código como usado, así que la ventana del código se mantiene pequeña. Tras una rotación, la clave anterior se sigue aceptando, de modo que ni un token ni una respuesta sellada se pierden cuando cambia la clave.