Für Fachleute
So funktioniert der Dienst
MathTrail steht zwischen dem Chat, den eine Familie bereits nutzt, und dem Google Drive der Familie. Das Modell des Chats schreibt jede Aufgabe, und der Dienst entscheidet, ob sie das Kind erreicht. Der Dienst ist ein einziges Programm: Er ruft kein eigenes Modell auf und behält zwischen den Anfragen nichts.
- Ein Go-Binary auf Cloud Run
- Zustandslos
- Keine Datenbank
- Keine eigenen LLM-Aufrufe
- MCP 2026-07-28 · MCP Apps
Chat-Client1 · Client
- Claude
- ChatGPT
- andere MCP-Apps-Hosts
Das Modell des Chats schreibt die Aufgabe, und die Karte zeigt sie dem Kind.
MCP · Tool-Aufrufe über HTTPS
MathTrail auf Google Cloud2 · Laufzeitumgebung
Cloud Run · ein Go-Binary · zustandslos
Der Kern: reine Berechnung
- Die Regel
- Wertungen
- Prüfungen
- Löser
- Inhalte
Um den Kern herum: OAuth, der MCP-Server, die Karte, die Versiegelung, die Limits.
Drive API v3 · mit dem eigenen Token der Familie
Google Drive3 · Speicher
MathTrail/mathtrail-profile.json
Eine Datei pro Kind, im eigenen Google Drive der Familie: das Einzige, was lange aufbewahrt wird.
01
Die Übersicht
Wer mit wem spricht. Weder das Kind noch die erwachsene Person erreicht MathTrail direkt: Beide sprechen mit dem Chat-Host. Alles im mittleren Block ist Teil eines einzigen Prozesses, kein eigener Dienst. Wählen Sie einen beliebigen Teil, um zu sehen, was er tut und womit er verbunden ist.
02
Eine Lektion, Aufruf für Aufruf
Das Modell und die Karte rufen dieselben Tools auf und stützen sich auf eine einzige Quelle der Wahrheit: die Profildatei im Google Drive der Familie. Damit eine Lektion weitergeht, müssen sie einander nichts mitteilen.
Aufgabe
- Erwachsene Person
- Karte
- Modell des Hosts
- MathTrail
- Drive
1Erwachsene Person → Modell des Hosts: „Eine neue Aufgabe, bitte“
2Modell des Hosts → MathTrail: next_task
Der Host zeigt die Karte an, sobald der Aufruf beginnt: „Die nächste Aufgabe wird vorbereitet…“
Limits · die Regel wählt das Thema, das Niveau und die Schwierigkeit
3MathTrail → Drive: die offene Anfrage schreiben
4MathTrail → Modell des Hosts: die Anfrage ist offen – Paket abholen
5Modell des Hosts → MathTrail: get_package
6MathTrail → Modell des Hosts: Auftrag · 3 Referenzaufgaben · Fallen · Vorlagen · Beispielbilder
Das Modell schreibt die Aufgabe, ein Lösungsprogramm in Starlark und eine Selbstprüfung
7Modell des Hosts → MathTrail: submit_task – bis zu 3 Versuche
Prüfungen · das Lösungsprogramm muss genau eine richtige Antwortmöglichkeit finden
8MathTrail → Drive: die Aufgabe wird zur aktuellen, ihre Antwort versiegelt
9Karte → MathTrail: read_task – die Karte fragt alle 4 s nach
10MathTrail → Karte: Text · Bild · fünf Antwortmöglichkeiten · Tipp – ohne die Antwort
Jeder Aufruf liest außerdem das Profil aus Google Drive – diese Lesezugriffe sind nicht eingezeichnet. Die wartende Karte sieht das Paket nie: Seine Referenzaufgaben enthalten ihre Antworten. Während das Kind löst, schreibt das Modell mit prepare_task schon die nächste Aufgabe, und wenn eine weitere gewünscht wird, legt next_task sie sofort auf eine neue Karte – ohne diese Wartezeit.
Antwort
- Erwachsene Person
- Kind
- Karte
- Modell des Hosts
- MathTrail
- Drive
1Kind → Karte: klickt auf C
2Karte → MathTrail: submit_answer – kostet den Chat keine Runde
nur die erste Antwort · erst jetzt wird das Siegel geöffnet · θ und δ ändern sich nur danach, ob sie richtig ist
3MathTrail → Drive: Wertungen und Verlauf schreiben
4MathTrail → Karte: richtig oder nicht · die Falle hinter C · das Bild der Lösung · die Schritte · die Wertung vorher → nachher
Die Karte wird an der Stelle der Aufgabe zur Besprechung der Antwort
5Karte → Modell des Hosts: ui/update-model-context – eine Zeile, gelesen mit der nächsten Nachricht der erwachsenen Person
6Erwachsene Person → Modell des Hosts: „Warum ein Pfosten mehr?“
7Modell des Hosts → Erwachsene Person: antwortet zu genau dieser Aufgabe und ergänzt die Karte
Der Klick speichert die Antwort, bevor irgendetwas erklärt wird, und die Karte wird sofort zur Besprechung der Antwort. Den Chat kostet das keine Runde, und das Modell zeichnet nichts. Es liest die Zeile der Karte mit der nächsten Nachricht der erwachsenen Person und beantwortet eine Frage zur Aufgabe, ohne die Karte nachzuerzählen. Geht die Zeile des Hosts an das Modell verloren, holt das Modell das beim nächsten Aufruf nach.
Anmeldung
- Browser der Familie
- Host
- MathTrail
- Drive
1Host → MathTrail: POST /mcp, ohne Token
2MathTrail → Host: 401 · wo die Metadaten liegen
3Host → Browser der Familie: öffnet /oauth/authorize · PKCE S256
4Browser der Familie → MathTrail: authorize
Ein eigener Zustimmungsbildschirm: welcher Client und wohin er die erwachsene Person zurückschickt
5Browser der Familie → Google: Anmeldung mit Google · nur drive.file
6Google → Browser der Familie: zurück, mit dem Code von Google
7Browser der Familie → MathTrail: /oauth/callback
8MathTrail → Google: Code eintauschen
9MathTrail → Drive: /about – wessen Drive ist das?
Nutzer-ID = HMAC des Drive-Kontos · Tokens von Google in unseren Code versiegelt
10MathTrail → Browser der Familie: 302 · code = mt1.c.…
11Browser der Familie → Host: gibt den Code zurück
12Host → MathTrail: /oauth/token · Code + Verifier
13MathTrail → Host: Zugriff 15 min · Aktualisierung 30 Tage
Die erwachsene Person meldet sich einmal an. Der Dienst behält nichts von diesem Austausch: Die Anfrage reist versiegelt im Parameter state, die Tokens von Google in einem versiegelten Code.
Die aktuelle Aufgabe als Zustandsautomat
Jede Regel, die „nur einmal“ verlangt, ist ein Feld des Profils und wird im selben Durchgang aus Lesen, Rechnen und Schreiben geprüft. Nichts anderes merkt sich irgendetwas.
nonenext_taskAuftrag bereitrequestedsubmit_task ✓versiegelt, gestelltissuedsubmit_answereinmal gespeichertanswered
requested ↺abgelehnt, Versuche übrig – höchstens drei pro Anfragerequested → nonedrei Ablehnungen oder 15 Minuten seit dem Öffnen der Anfrage; die Aufgaben des Tages werden nicht verbraucht, und drei Ablehnungen zählen als gescheiterte Anfrage des Tagesissued → requested„Neue Aufgabe“ vor einer Antwort – als übersprungen gespeichert, die Wertung bleibt unberührtanswered ↺jede spätere Antwort – aus einem zweiten Tab oder vom Modell – bekommt das erste Ergebnis zurück und schreibt nichtsanswered → requesteddie nächste Aufgabe, auf einer neuen Karte; die beantwortete ist nicht mehr aktuellanswered → issueddie nächste Aufgabe wurde vorab geschrieben – next_task legt sie sofort auf eine neue Karte und überspringt requestedissued → nonedas Siegel lässt sich nicht mehr öffnen – der Schlüssel wurde zweimal rotiert; nichts wird gespeichert
03
Wo der Zustand liegt
Alles, was ein Server mit Zustand in einer Datenbank hielte, bewahrt jemand anderes auf: in einem versiegelten Token, das der Chat hält, in einem Cookie im Browser der Familie oder im eigenen Google Drive der Familie. Starten Sie den Dienst neu oder starten Sie eine zweite Kopie: Nichts geht verloren.
MathTrail selbst
Nichts
Keine Konten, keine Sitzungen, keine Registrierungen, keine Tokens von Google. Jede Anfrage bringt alles mit, was sie braucht.
Der Token-Speicher des Chats
Versiegelte Tokens, für den Host undurchsichtig
- Zugriffstoken
- 15 min
- Token zum Aktualisieren
- 30 Tage, ≤ 90
Darin versiegelt sind die Tokens von Google, eine undurchsichtige Nutzer-ID für die Limits und das Land, aus dem sich die erwachsene Person angemeldet hat.
Der Browser der Familie
Zwei Cookies, keine Sitzung
- Genehmigte Clients
- 180 Tage
- Schutz der Anmeldung (CSRF)
- 10 min
Gehen sie verloren, kostet das einen Zustimmungsbildschirm mehr oder eine neue Anmeldung.
Das Google Drive der Familie
Das Einzige, was lange aufbewahrt wird
mathtrail-profile.json- bis zur Löschung
Das Profil, die Wertungen und die aktuelle Aufgabe mit versiegelter Antwort. Die erwachsene Person kann die Datei öffnen, und Google Drive bewahrt ihren Versionsverlauf auf.
Secret Manager
Einmal gelesen, beim Start einer Instanz
- Siegelschlüsselnach einer Rotation gilt der vorige Schlüssel weiter
- 90 Tage
- Client-Secret von Google
- festgelegt
Der Arbeitsspeicher der Instanz
Nur Caches: bei einem Neustart verloren, und nichts geht kaputt
- Zähler der Anfragerate
- Minuten
- Wo die Profildatei liegt
- 10 min
- Dokumente der Clients
- 5 min bis 24 h
In v1 bewusst weggelassen
- Eine Datenbank
- Warteschlangen
- Ein Sitzungsspeicher
- Ein gemeinsamer Cache
- Ein Vorrat fertiger Aufgaben
- Ein Client für eine LLM-API
- REST und OpenAPI
04
Die Profildatei
Ein Kind, eine JSON-Datei: kein Cache irgendeiner Wahrheit auf einem Server, sondern die Wahrheit selbst. Jeder Block hat eine Obergrenze, die bei jedem Schreiben eingehalten wird, sodass die Datei nie unbegrenzt wächst: Eine übliche hält das Ziel ein, und eine nahe an ihren Obergrenzen überschreitet es. Vergangene Aufgaben werden als Fingerabdrücke aufbewahrt, nicht als Text.
Größe der Datei
≈ 61 KB / ≈ 79 KB
| Block | Was er enthält | Wer ihn schreibt | Größe | |
|---|---|---|---|---|
| Fingerabdrücke | Bis zu 200 Skizzen der gestellten Aufgaben für die Prüfung auf Beinahe-Duplikate – ohne Text, ohne Thema, ohne Datum | next_tasksubmit_task | 27 KB / 29 KB | |
| Verlauf der Wertung | Wo θ und das δ des Themas vor jeder der letzten Antworten standen, und θ und jedes Thema zu Beginn jedes der letzten 7 Tage mit Antworten | submit_answer | 14 KB / 15 KB | |
| Wertungen und Themen | θ und wo das Kind angefangen hat; für jedes Thema: δ, die Antworten, die Fallen, in die es getappt ist, ob das Thema gemeistert ist und auf welcher Stufe | next_tasksubmit_tasksubmit_answer | 8 KB / 14 KB | |
| Letzte Antworten | Die letzten 20 Antworten und übersprungenen Aufgaben – und nie weniger als die 5 Antworten, die die Regel liest | next_tasksubmit_answer | 4 KB / 5 KB | |
| Aktuelle Aufgabe, versiegelt | Die Antwort, die Falle hinter jeder falschen Antwortmöglichkeit, die Lösung und das Lösungsprogramm – eine einzige undurchsichtige Zeichenkette | next_tasksubmit_tasksubmit_answer | 4 KB / 8 KB | |
| Aktuelle Aufgabe, offen | Was die Karte zeigt: der Text, das Bild, fünf Antwortmöglichkeiten, der Tipp – und nach der Antwort die Antwort selbst | next_tasksubmit_tasksubmit_answer | 2 KB / 4 KB | |
| Offene Anfrage | Während die Aufgabe geschrieben wird: ihre ID, der Auftrag, die verbrauchten Versuche und wer das Thema gewählt hat | next_taskprepare_tasksubmit_task | 1,5 KB / 2 KB | |
| Dienst und Kind | Die Version des Schemas, eine zufällige UUID, die Revision; das Pseudonym, die Klasse, die Interessen, die Notizen; die Zähler des Tages | save_profileedit_profilejeder Schreibvorgang | 0,6 KB / 1,5 KB |
Eine vorab geschriebene Aufgabe liegt neben der aktuellen und wiegt ungefähr gleich viel. Was die Datei nicht enthält: die Texte vergangener Aufgaben, die Antwort im Klartext, bevor das Kind geantwortet hat, irgendetwas, das die erwachsene Person benennt, und einen Rang – Ränge werden beim Anzeigen berechnet. Google Drive kennt kein bedingtes Schreiben, deshalb wird die Datei vor dem Speichern noch einmal gelesen, und das Schreiben wird abgelehnt, wenn sie sich geändert hat: Zwei Tabs überschreiben einander nie stillschweigend.
05
Versiegelt – und wie lange
Ein Mechanismus für zwei Zwecke: Er versiegelt die Anmeldung in die Tokens, die der Chat hält, und die Antwort einer Aufgabe – der aktuellen und der vorab geschriebenen – ins Profil. Jedes Siegel trägt seinen Zweck, sodass ein Zugriffstoken nicht als Aktualisierungstoken durchgehen kann und eine versiegelte Antwort als nichts anderes.
Ein Token, zerlegt
mt1.a.kQ7fWx.5rJ0Yb2mQ8vK1nT4pXc9…
- mt1
- Version des Formats
- Zweck
- c Code · a Zugriff · r Aktualisierung · d Client · s state · k Zustimmung · t Antwort einer Aufgabe
- Schlüssel-ID
- stammt aus dem Schlüssel selbst, daher fällt eine Rotation nicht auf
- Geheimtext
- XChaCha20-Poly1305, eine zufällige 192-Bit-Nonce
Kein JWT: Niemand außer dem Dienst liest es, und aus einer undurchsichtigen Zeichenkette kann kein Claim in ein Protokoll gelangen.
Gültigkeitsdauern, auf logarithmischer Skala
- Autorisierungscode
60 s - Laufende Anmeldung
10 min - Zugriffstoken
15 min - Aktualisierungstoken
30 Tage, gleitend, höchstens 90 - Siegelschlüssel
alle 90 Tage rotiert - Genehmigte Clients
180 Tage
Ein Server ohne Zustand kann einen Code nicht als benutzt markieren, deshalb bleibt das Zeitfenster des Codes klein. Nach einer Rotation gilt der vorige Schlüssel weiter, sodass beim Wechsel des Schlüssels weder ein Token noch eine versiegelte Antwort verloren geht.