[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81578":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":15,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":14,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":37,"readmeContent":38,"aiSummary":39,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":40,"discoverSource":41},81578,"aula-mcp","Casperjuel\u002Faula-mcp","Casperjuel","MCP server for Denmark's Aula school platform — TypeScript MitID auth, no headless browser. Exposes profiles, calendar, messages, ugeplaner to AI agents (Claude\u002FCursor\u002Fetc) via Model Context Protocol.","https:\u002F\u002Fgithub.com\u002FCasperjuel\u002Faula-mcp",null,"TypeScript",28,5,3,2,0,1,4,45.73,"MIT License",false,"main",true,[25,26,27,28,29,30,31,32,33,34,35,36],"ai-agents","aula","bun","claude","denmark","hono","mcp","mcp-server","mitid","model-context-protocol","school","typescript","2026-06-12 04:01:34","# aula-mcp\n\n[![CI](https:\u002F\u002Fgithub.com\u002FCasperjuel\u002Faula-mcp\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002FCasperjuel\u002Faula-mcp\u002Factions)\n[![Licens: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicens-MIT-yellow.svg)](.\u002FLICENSE)\n[![Bun](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FBun-≥%201.3-black?logo=bun)](https:\u002F\u002Fbun.sh)\n[![pnpm](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fpnpm-≥%2010-F69220?logo=pnpm)](https:\u002F\u002Fpnpm.io)\n[![MCP](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FModel_Context_Protocol-Streamable_HTTP-6B5BFF)](https:\u002F\u002Fmodelcontextprotocol.io)\n[![Tests](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ftests-248%20pass-brightgreen)](#udvikling)\n\n**Hvad det her er — og hvad det *ikke* er:**\n\n`aula-mcp` er en server der sidder mellem en MCP-klient (LLM) og Aula — et interface, ikke meget mere. **LLM'en er ikke en del af projektet.** Du vælger selv klient (Claude Code, Claude Desktop, ChatGPT, Cursor, Ollama, LM Studio osv.), og den kører hvor den nu kører — i Anthropic\u002FOpenAI's cloud, eller lokalt hvis du bruger Ollama el.lign.\n\n**Projektet er altså ikke en garanti for at børnenes data kun bliver lokalt.** Om dataen forbliver lokal afhænger 100 % af hvilken klient du tilkobler — det er dit eget ansvar, ikke noget `aula-mcp` selv kan love.\n\n> ⚠️ **Brug det med omtanke**\n>\n> Hobby-eksperiment, ingen garantier. Det rør ved MitID og dine børns skoledata — kig koden igennem (eller få en udvikler-bekendt til det) før du kobler en LLM på. Eget ansvar.\n\n> ⚠️ **Det er klienten der får dataen at se — ikke serveren**\n>\n> Serveren her kører lokalt og sender intet videre på egen hånd. **Men den MCP-klient du tilkobler — Claude, ChatGPT, en anden cloud-LLM — får alt det den læser sendt videre til provideren (Anthropic, OpenAI osv.) for at kunne svare dig.** Det er ikke \"alt sammen lokalt\" bare fordi serveren er det. Sådan fungerer MCP: klienten ræsonnerer, serveren henter data.\n>\n> | | Hvor det går hen |\n> | --- | --- |\n> | MitID-credentials og OAuth-tokens | Forbliver lokalt — macOS Keychain eller AES-256-GCM-krypteret fil. Bruges kun til at hente data fra Aula. |\n> | Selve dataen (beskeder, ugeplaner, børnenavne osv.) | Sendes til den MCP-klient du vælger. Cloud-LLM → providerens servers (typisk USA). Lokal LLM → forbliver lokalt. |\n>\n> **Vil du have det 100 % lokalt?** Brug en lokal LLM som klient: [Ollama](https:\u002F\u002Follama.com), [LM Studio](https:\u002F\u002Flmstudio.ai), [llama.cpp](https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.cpp), Mistral via Hugging Face, etc. Alle taler MCP og kører på din egen hardware.\n\nTypeScript + Bun + Hono. Bygget på skuldrene af [`scaarup\u002Faula`](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula) (Python\u002FHome Assistant). Stadig i bevægelse — tools kan ændre signatur, kommandoer kan blive omdøbt, og enkelte vendor-integrationer er kun testet mod ét sæt skoler.\n\n![Claude Code spørger om næste uges ugeplan](.\u002Fdocs\u002Fdemos\u002Fclaude-code.gif)\n\n---\n\n## Indhold\n\n- [Hvad serveren rør ved](#hvad-serveren-rør-ved)\n- [Kom i gang](#kom-i-gang)\n- [Forbind til Claude Code (eller claude.ai)](#forbind-til-claude-code-eller-claudeai)\n- [Home Assistant (Assist + Voice)](.\u002Fdocs\u002Fhome-assistant.md)\n- [Self-hosting](#self-hosting)\n- [Hvad er der i manifestet](#hvad-er-der-i-manifestet)\n- [CLI-kommandoer](#cli-kommandoer)\n- [Konfiguration](#konfiguration)\n- [Arkitektur](#arkitektur)\n- [Rettelser fra Aula-issues](#rettelser-fra-aula-issues)\n- [Fejlfinding](#fejlfinding)\n- [Udvikling](#udvikling)\n- [Bidrag](#bidrag)\n- [Privatliv & jura](#privatliv--jura)\n\n---\n\n## Hvad serveren rør ved\n\nHvad selve `aula-mcp`-serveren gør (og ikke gør). Hvor dataen ender bagefter er klientens domæne — se disclaimeren øverst.\n\n- **MitID-credentials og OAuth-tokens bliver lokalt.** macOS: Keychain (`security` CLI). Linux\u002FWindows: AES-256-GCM-krypteret fil i `~\u002F.config\u002Faula-mcp\u002F`. De forlader ikke din computer.\n- **Serveren binder kun til `127.0.0.1`.** Den nægter at binde til en ikke-loopback adresse medmindre du sætter `AULA_MCP_ALLOW_REMOTE=1`. Default: kun programmer på din egen computer kan ramme den.\n- **Ingen telemetri, ingen tredjepart.** Programmet taler kun med Aula's egne servere (`api.aula.dk`, `login.aula.dk`), MitID (`nemlog-in.mitid.dk`) og vendor-API'erne (EasyIQ, Meebook m.fl. — hvis din skole har dem).\n- **MitID-godkendelsen går igennem MitID's egen infrastruktur.** Protokollen er skrevet om i TypeScript, men selve godkendelsen (QR-koden i MitID-appen) sker som altid mellem din enhed og nemlog-in.dk.\n- **`--debug`-tracen er opt-in og automatisk redaktet.** Cookies, OAuth-koder, MitID-payloads, M1-værdier, flowValueProof, adgangstokens osv. fjernes *før* noget skrives til disk. Trygt at vedhæfte en GitHub-issue.\n- **Loopback-only = familien kan ikke ramme serveren fra deres egen enhed.** Skal flere enheder i husstanden kunne spørge, så enten reverse proxy (se Self-hosting) eller en Home Assistant-integration på et tidspunkt.\n\n---\n\n## Kom i gang\n\nKræver **[Bun](https:\u002F\u002Fbun.sh) ≥ 1.3** og **[pnpm](https:\u002F\u002Fpnpm.io) ≥ 10**. macOS eller Linux.\n\n```sh\ngit clone git@github.com:Casperjuel\u002Faula-mcp.git\ncd aula-mcp\npnpm install\n\n# 1. Sanity-tjek\npnpm typecheck && pnpm lint && pnpm test\n\n# 2. Første-gangs MitID-login (QR-kode i MitID-appen)\npnpm login\n\n# 3. Health-check af alle Aula-endpoints\npnpm doctor\n\n# 4. Start MCP-serveren (http:\u002F\u002F127.0.0.1:7878\u002Fmcp)\npnpm mcp\n```\n\nDe fleste CLI-kommandoer har en kort genvej: `pnpm login`, `pnpm doctor`, `pnpm whoami`, `pnpm status`, `pnpm logout`. Til alt andet videresender `pnpm aula \u003Ckommando>` til CLI'en (fx `pnpm aula transcript list`, `pnpm aula log --last 5`).\n\n`doctor`-kommandoen kører hvert read-endpoint igennem og rapporterer status + svartid for hvert. Det er det hurtigste \"virker det her overhovedet?\"-tjek:\n\n![aula doctor kører igennem alle endpoints](.\u002Fdocs\u002Fdemos\u002Fdoctor.gif)\n\n`whoami` viser hvilken identitet dine tokens hører til, og hvilke børn der returneres af `getProfilesByLogin`:\n\n![aula whoami viser identitet + børn](.\u002Fdocs\u002Fdemos\u002Fwhoami.gif)\n\n---\n\n## Forbind til Claude Code (eller claude.ai)\n\n### Claude Code\n\n```sh\n# 1. Server kører i ét terminalvindue\npnpm mcp\n\n# 2. Registrér serveren med Claude Code (kun én gang)\nclaude mcp add --transport http aula http:\u002F\u002F127.0.0.1:7878\u002Fmcp\n\n# 3. I en hvilken som helst Claude Code-session, bekræft at den er forbundet\n\u002Fmcp\n```\n\nSå kan du bare spørge naturligt — børnenes navne bliver fuzzy-matched mod `discover`-manifestet, du behøver ikke kende deres ID:\n\n> *hvad står der på ugeplanen næste uge for theo*\n\nClaude kalder `aula.discover` én gang, vælger den rigtige ugeplan-vendor for din skole ud fra `detectedWidgets`, og svarer på dansk med dansk-formatterede datoer.\n\n### Claude Desktop\n\nDrop snippet'et fra [`examples\u002Fclaude-config\u002Fclaude-desktop.json`](.\u002Fexamples\u002Fclaude-config\u002Fclaude-desktop.json) ind i `~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json`.\n\n### claude.ai (web)\n\nWeb-UI'et kræver en offentlig HTTPS-URL — `127.0.0.1` virker ikke, fordi forbindelsen sker server-side fra Anthropic's cloud. Til en hurtig test:\n\n```sh\ncloudflared tunnel --url http:\u002F\u002F127.0.0.1:7878\n# → https:\u002F\u002F\u003Crandom>.trycloudflare.com — indsæt med `\u002Fmcp` på enden\n```\n\n> ⚠️ **Tunnel-URL'en er offentligt tilgængelig så længe den kører** — hvis nogen gætter den, kan de styre dine Aula-tokens. Fint til en hurtig demo, men lad den ikke stå åben. Til en permanent setup, se næste sektion.\n\n---\n\n## Self-hosting\n\nHvis du vil have serveren kørende uden at have din laptop åben, er der et par måder. Alle holder *serveren* lokalt — hvor klienten kører er stadig et separat valg (se top-disclaimeren).\n\n### Mulighed 1: Single binary på en Linux-boks (Pi, NAS, gammel laptop, billig VPS)\n\nDen simpleste vej. Compile-til-én-binary og kør den under systemd.\n\n```sh\n# Byg en standalone binary (~50 MB)\nbun build --compile --outfile dist\u002Faula-mcp packages\u002Fmcp-server\u002Fsrc\u002Fserver.ts\n\n# Kopiér til din server (Pi, NAS, VPS)\nscp dist\u002Faula-mcp aula:\u002Fusr\u002Flocal\u002Fbin\u002Faula-mcp\n```\n\n**Tokens på serveren** — to veje, vælg den der passer dig:\n\nA. *Log ind direkte på serveren via SSH.* QR-koderne renderes i SSH-sessionen, scan med MitID-appen på telefonen. Kører på enhver maskine du kan ssh'e ind på med en normal TTY.\n\n```sh\nssh aula\naula login\n```\n\nB. *Eksportér tokens fra din Mac (eller fra hvor du allerede er logget ind).* macOS Keychain kan ikke flyttes mellem maskiner, så `aula tokens export` re-krypterer dem til en bærbar fil-bundle.\n\n```sh\n# På din Mac\naula tokens export ~\u002Faula-bundle\n\n# Flyt til serveren — bundle indeholder live credentials, behandl som\n# adgangskoder. SSH krypterer i transit.\nscp ~\u002Faula-bundle\u002Ftokens.json ~\u002Faula-bundle\u002F.key \\\n    aula:\u002Fvar\u002Flib\u002Faula-mcp\u002F\n\n# Slet bundle på din Mac når du er færdig\nrm -rf ~\u002Faula-bundle\n```\n\nEller på serveren: `aula tokens import ~\u002Faula-bundle` hvis du vil have CLI'en til at lægge filerne det rigtige sted.\n\nEksempel `systemd`-unit i `\u002Fetc\u002Fsystemd\u002Fsystem\u002Faula-mcp.service`:\n\n```ini\n[Unit]\nDescription=aula-mcp server\nAfter=network.target\n\n[Service]\nType=simple\nUser=aula\nExecStart=\u002Fusr\u002Flocal\u002Fbin\u002Faula-mcp\nEnvironment=AULA_MCP_PORT=7878\nEnvironment=AULA_MCP_HOST=127.0.0.1\nEnvironment=AULA_MCP_DIR=\u002Fvar\u002Flib\u002Faula-mcp\nEnvironment=AULA_MCP_KEY=\u003Cen lang hex-streng eller passphrase>\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n```\n\nAktivér med `systemctl enable --now aula-mcp`. Tjek `journalctl -u aula-mcp -f` for logs.\n\n### Mulighed 2: Bag en authenticeret reverse proxy (familien tilgår fra mobilen)\n\nDefault binder serveren kun til `127.0.0.1`. For at familien (eller dig selv på telefonen via VPN) skal kunne ramme den, sæt en reverse proxy foran med authentication. Eksempel med [Caddy](https:\u002F\u002Fcaddyserver.com):\n\n```caddyfile\naula.dithjem.dk {\n    basicauth {\n        familie $2a$14$\u003Cbcrypt-hash>\n    }\n    reverse_proxy 127.0.0.1:7878\n}\n```\n\n`AULA_MCP_HOST` forbliver `127.0.0.1` — Caddy står for TLS, basic auth, rate limit. Det er Caddy der er på det offentlige internet, ikke selve MCP-serveren.\n\n> ⚠️ Hvis du *skal* exposé serveren direkte (springe proxy-laget over) skal du eksplicit sætte `AULA_MCP_ALLOW_REMOTE=1` — det er en kontrolleret, tilsigtet handling, ikke et uheld.\n\n### Mulighed 3: Home Assistant add-on\n\nDen nemmeste vej for HA-brugere. Add-on'en pakker `aula-mcp` ind så den kører som en del af din HA-installation og er tilgængelig fra HA's Voice\u002FAssist + alle dine HA-automatiseringer. Hvis du har **Nabu Casa**, åbner det også for sikker fjernadgang via deres tunnel.\n\n**👉 Fuld guide: [`docs\u002Fhome-assistant.md`](.\u002Fdocs\u002Fhome-assistant.md)** — installation, MitID-login, MCP-integration, valg af LLM, og stemmeopsætning.\n\nKorte stik:\n\n- Et-klik install: [![Open your Home Assistant instance and add the aula-mcp add-on repository.](https:\u002F\u002Fmy.home-assistant.io\u002Fbadges\u002Fsupervisor_add_addon_repository.svg)](https:\u002F\u002Fmy.home-assistant.io\u002Fredirect\u002Fsupervisor_add_addon_repository\u002F?repository_url=https:\u002F\u002Fgithub.com\u002FCasperjuel\u002Faula-mcp)\n- `aula-mcp` taler både Streamable HTTP (`\u002Fmcp`) og den ældre SSE-dialekt (`\u002Fsse`) — HA's officielle [`mcp` (client) integration](https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Fmcp\u002F) bruger SSE.\n- Login sker inde i add-on'ens egen sidebar-UI (MitID-QR + identitetsvalg).\n\n### Mulighed 4: VPS i Tyskland (Hetzner, Coolify)\n\nHvis du allerede har en europæisk VPS (Hetzner, Scaleway, OVH) og en domæne — så er det bare en `git clone` + `bun install` + systemd-unit som mulighed 1.\n\n### Backup & nøgle-håndtering\n\n- **Token-store**: gem en kryptert kopi af `~\u002F.config\u002Faula-mcp\u002Ftokens.json` + `.key` (eller Keychain-eksport på macOS). Mister du dem, skal du logge ind igen — ingen panik, men irriterende.\n- **AULA_MCP_KEY**: hvis du bruger fil-backenden i produktion, sæt en stærk `AULA_MCP_KEY` (env-var) og lad være med at committe den. Roterer du den, skal du re-loginne.\n- **Nye Aula-versioner**: Aula bumper deres API-version 1-2 gange om året. `aula-mcp` prober selv den nye version ved næste kald (intet manuelt arbejde), men hold et øje på release-noter for breaking changes der dukker op.\n\n---\n\n## Hvad er der i manifestet\n\nAgenter kalder `aula.discover` én gang og genbruger resultatet resten af sessionen. Manifestet fortæller agenten hvem brugeren er, hvilke børn man kan handle på vegne af, hvilke tredjeparts-widgets skolerne har konfigureret, og hvilke MCP-tools der skal kaldes:\n\n![aula.discover-manifest pretty-printet](.\u002Fdocs\u002Fdemos\u002Fdiscover.gif)\n\nForm:\n\n```ts\n{\n  user: { name, username, identityName? },\n  children: [{ id, name, userId?, institution: { id, name?, code? } }],\n  apiVersion: 23,\n  tokens: { expires_at, seconds_remaining },\n  detectedWidgets: ['0001', '0029', '0030'],   \u002F\u002F fra Aula's pageConfiguration\n  capabilities: {\n    profiles:      { summary, tools: ['aula.profiles.list'] },\n    presence:      { summary, tools: ['aula.presence.today', 'aula.presence.templates'] },\n    calendar:      { summary, tools: ['aula.calendar.events'] },\n    messages:      { summary, tools: ['aula.messages.list_threads', 'aula.messages.get_thread'] },\n    notifications: { summary, tools: ['aula.notifications.list'] },\n    posts:         { summary, tools: ['aula.posts.list'] },\n    ugeplan:       { summary, tools: ['aula.ugeplan.easyiq'] },          \u002F\u002F kun den detekterede vendor\n    opgaver:       { summary, tools: ['aula.opgaver.minuddannelse'] },\n    ugebrev:       { summary, tools: ['aula.ugebrev.minuddannelse'] },\n    huskelisten:   { summary, tools: ['aula.huskelisten.systematic'] }\n  },\n  usage: {\n    cache, nameResolution, pickOne, timeWindows, language\n  },\n  rawRequestEnabled: false,\n  writeEnabled: false\n}\n```\n\n`capabilities[area].tools[0]` er altid det rigtige tool at kalde — når en skoles widgets detekteres, listes kun den matchende vendor, så agenten ikke famler ud over flere providers. Det inline `usage`-blok fortæller agenten hvordan den skal opføre sig (cache manifestet, fuzzy-match børnenavne, default til Europe\u002FCopenhagen, svar på brugerens sprog).\n\n### Komme\u002Fgå — sæt afleverings- og hentetider\n\n`aula-mcp` er read-only som standard. Sætter du `AULA_MCP_WRITE=1`, registreres ét ekstra tool — `aula.presence.set_template` — der kan oprette eller overskrive et barns komme\u002Fgå-skabelon for én dag: afleveringstid, hentetid, henteform (`picked_up_by` \"Hentes af\", `self_decider` \"Selvbestemmer\", `send_home` \"Sendes hjem\", `go_home_with` \"Går hjem med\") og evt. en ugentlig gentagelse. Læse-toolet `aula.presence.templates` viser den nuværende plan og er altid slået til. Skrive-toolet rører rigtige data i Aula, så det sidder bevidst bag et flag — en agent skal ikke kunne omlægge dit barns hentetid uden at du har slået det til. `writeEnabled` i manifestet afspejler om flaget er sat.\n\n---\n\n## CLI-kommandoer\n\n```\naula login [--username \u003Cuser>] [--method APP|CODE_TOKEN] [--debug] [--transcript \u003Cfile>]\naula status [--json]\naula whoami [--json]\naula doctor [--json] [--verbose]\naula log [--last N] [--json]\naula transcript {list|view \u003Cfile>|prune} [--json] [--keep N] [--dry-run]\naula logout\naula --help\n```\n\n| Kommando | Hvad den gør |\n| -------- | ------------ |\n| `aula login` | Kører hele MitID-flowet (APP-metoden er default — scan QR med MitID-appen). Gemmer tokens. `--debug` opfanger en saneret wire-transcript så fejl er diagnoserbare. |\n| `aula status` | Viser om der er tokens, deres udløbstid og den aktive identitet. Kontakter ikke netværket. Exit-kode 1 hvis der ingen tokens er. |\n| `aula whoami` | Indlæser tokens (refresher hvis nødvendigt), kalder `getProfilesByLogin` + `getProfileContext`. Smoke-test af at hele auth + client-pipelinen virker. |\n| `aula doctor` | Kører hvert read-endpoint igennem og rapporterer per-call status med svartid. Det hurtigste \"virker det her?\"-tjek. `--verbose` dumper wire-transcripten inline ved fejl. |\n| `aula log` | Seneste login-forsøg (success\u002Ffailure, timestamps, fejlklasse). |\n| `aula transcript` | Inspicér opfangede `--debug`-transcripts; `prune` beholder de seneste N (default 10). |\n| `aula logout` | Sletter de gemte tokens. Krypteringsnøglen beholdes så næste login genbruger den. |\n\nKomplet hjælp med eksempler: `pnpm aula --help`\n\n![aula --help](.\u002Fdocs\u002Fdemos\u002Fhelp.gif)\n\n---\n\n## Konfiguration\n\n### Hvor tokens gemmes\n\n| Platform | Default | Override |\n| -------- | ------- | -------- |\n| macOS | Keychain (`security` CLI; service `aula-mcp`, account `tokens`) | `AULA_MCP_NO_KEYCHAIN=1` falder tilbage til fil-backenden |\n| Linux \u002F Windows | AES-256-GCM-krypteret fil i `~\u002F.config\u002Faula-mcp\u002Ftokens.json` | `AULA_MCP_KEY=\u003Chex|passphrase>` for krypteringsnøglen (ellers genereret i `~\u002F.config\u002Faula-mcp\u002F.key`, `chmod 600`) |\n\n### Server-miljøvariabler\n\n| Variabel | Default | Effekt |\n| -------- | ------- | ------ |\n| `AULA_MCP_PORT` | `7878` | Bind-port. |\n| `AULA_MCP_HOST` | `127.0.0.1` | Bind-interface. Nægter ikke-loopback medmindre `AULA_MCP_ALLOW_REMOTE=1`. |\n| `AULA_MCP_DIR` | `~\u002F.config\u002Faula-mcp` | Konfig-mappe (fil-backend + transcripts + login-log). |\n| `AULA_MCP_RAW=1` | off | Aktiverer `aula.raw_request` escape-hatch-toolet. |\n| `AULA_MCP_WRITE=1` | off | Aktiverer skrive-tools (`aula.presence.set_template` — sæt komme\u002Fgå-tider). Serveren er read-only uden den. |\n| `AULA_MCP_LOG=1` | off | Verbose console-logs fra auth\u002Fclient-lagene. |\n| `AULA_MCP_ALLOW_REMOTE=1` | off | Tillader at binde til ikke-loopback adresser (fx bag en reverse proxy). |\n\n### Wire-transcripts\n\n`--debug`-tilstand tee'r en JSONL-transcript af hvert HTTP-request\u002Fresponse til `~\u002F.config\u002Faula-mcp\u002Ftranscripts\u002Flogin-\u003Ctimestamp>.jsonl`. Cookies, OAuth\u002FSAML-payloads, MitID-auth-koder, adgangskoder, M1, flowValueProof, `access_token`-query-parameter og andre hemmelige felter bliver alle redaktet (`\u003Credacted N chars>`). Transcripten kan trygt vedhæftes en GitHub-issue.\n\n`aula transcript view \u003Cfile>` pretty-printer en af dem.\n\n---\n\n## Arkitektur\n\n```\npackages\u002F\n  aula-auth\u002F    — MitID + 3072-bit SRP-6a + OAuth\u002FSAML-kæde + token-store + wire-trace\n  aula-client\u002F  — Aula REST API + version-probing + integrations-plugins\n  mcp-server\u002F   — Hono + @modelcontextprotocol\u002Fsdk + aula.discover + 11 capability-tools\napps\u002F\n  cli\u002F          — aula login\u002Fstatus\u002Fwhoami\u002Fdoctor\u002Flog\u002Ftranscript\u002Flogout\n```\n\nCross-package-imports bruger workspace-navnet (`@aula-mcp\u002Faula-auth`); Bun resolver `.ts` direkte, så der er intet build-trin i dev. `tsc -p tsconfig.json --noEmit` kører i CI til ren type-checking.\n\n| Lag | Status | Noter |\n| --- | ------ | ----- |\n| `@aula-mcp\u002Faula-auth` | ✅ unit-testet + live-verificeret | MitID APP + CODE_TOKEN + PASSWORD; macOS Keychain eller AES-GCM-fil. |\n| `@aula-mcp\u002Faula-client` | ✅ unit-testet | Native Aula API + EasyIQ \u002F EasyIQ SkolePortal \u002F Meebook \u002F Min Uddannelse \u002F Systematic-plugins. |\n| `@aula-mcp\u002Fmcp-server` | ✅ unit-testet + live-verificeret med Claude Code | Streamable HTTP-transport, stateful session. Single-user, loopback by default. |\n| `apps\u002Fcli` | ✅ unit-testet | QR-rendering, debug-transcripts, JSONL login-log. |\n\n`@aula-mcp\u002Faula-auth` og `@aula-mcp\u002Faula-client` bruger kun Web-standarder + `node:crypto` + `node:child_process` — de kører på Node ≥ 20 såvel som Bun. MCP-serveren bruger `Bun.serve` og er Bun-only. CLI'en bruger Bun's TS-support og shipper via `bun build --compile`. For at bruge libraries fra et Node-script, se [`examples\u002Fscript\u002F`](.\u002Fexamples\u002Fscript\u002F).\n\nDetaljeret design-rationale: [docs\u002Farchitecture.md](.\u002Fdocs\u002Farchitecture.md).\n\n---\n\n## Rettelser fra Aula-issues\n\nEt par issues fra `scaarup\u002Faula`s tracker som jeg har taget højde for:\n\n| Upstream issue \u002F PR | Hvad koden gør |\n| ------------------- | ----------- |\n| [#311](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fissues\u002F311) — sensor dør når widget-JWT'en udløber | `WidgetTokenManager.withRetry` detekterer `{\"message\":\"JWT-Token expired...\"}` (samt 401\u002F403) og refresher én gang før retry. |\n| [#246, #248](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fissues\u002F246) — Aula API-version drifter (v22 → v23 mid-life) | `AulaClient` prober versioner lazily, retry én gang ved 410, kalder `onApiVersionChanged` ved bumps. |\n| [#310](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fissues\u002F310) — RelayState mangler i Level-3 SAML-svar | `extractSamlForm` returnerer `hadRelayState: false` og en tom string i stedet for at kaste. |\n| [#306, #287](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fissues\u002F306) — `post-broker-login` returnerer 200 med bekræftelses-form i stedet for 302 | `detectConfirmationForm` finder `button#confirmation-button`, submitter dens form og fortsætter. |\n| [#290, #351](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fissues\u002F351) — `password`\u002F`token` krævet for auth-metoder der ikke har brug for det | `AulaLoginOptions` kræver kun felter pr. valgt `method`. APP-metoden skal ikke have password. |\n| [PR #352](https:\u002F\u002Fgithub.com\u002Fscaarup\u002Faula\u002Fpull\u002F352) — EasyIQ SkolePortal (widget 0128) | Implementeret som `EasyIqSkoleportalClient` + `aula.ugeplan.easyiq_skoleportal` MCP-tool. Per-barn auth + dansk-entity-decode. |\n| Følsomme beskeder (`status.code` 403) | Surfaced som typed `AulaStepUpRequiredError`; MCP-tool returnerer struktureret `step_up_required` JSON i stedet for tomme data. |\n\n---\n\n## Fejlfinding\n\n| Symptom | Sandsynlig årsag \u002F fix |\n| ------- | ---------------------- |\n| `aula login` hænger efter username-prompt | MitID-appen er ikke åbnet endnu, eller QR-koderne er ikke renderet (terminal for smal). Sørg for at terminalen er ≥ 80 kolonner. |\n| `Login failed: MitID initialize failed (status …)` | nemlog-in.mitid.dk er ikke tilgængelig eller returnerede en fejl. Kør igen med `--debug` og inspicér transcripten. |\n| `Login failed: APP poll error: …` | MitID-appen afviste eller annullerede. Tjek at MitID-appen er logget ind på din konto. |\n| `Login failed: appProve failed (status …)` | Sjælden — MitID afviste SRP-proof'et. Kør igen med `--debug` og inspicér `~\u002F.config\u002Faula-mcp\u002Ftranscripts\u002Flogin-\u003Ctimestamp>.jsonl`. |\n| `aula whoami` → `step_up_required` for beskeder | En specifik tråd er følsom (Aula returnerer 403). Kør `aula login` igen for at re-etablere en step-up-session, prøv så igen. |\n| `aula doctor` siger `Aula API v22 → 410` | API-versionen er bumpet. Kør `aula doctor` igen — `AulaClient` prober frem og husker. |\n| `aula status` viser `expired N min ago` | Tokens er udløbet siden sidste brug. Et hvilket som helst read-kald (eller `aula doctor`) refresher dem automatisk. |\n| MCP-server: `Refusing to bind to non-loopback address` | Du har sat `AULA_MCP_HOST` til `0.0.0.0` eller lignende. Serveren er single-user; alle der kan ramme `\u002Fmcp` bliver dig. Sæt `AULA_MCP_ALLOW_REMOTE=1` hvis du forstår implikationerne. |\n\nNår noget fejler er JSONL-transcripten i `~\u002F.config\u002Faula-mcp\u002Ftranscripts\u002Flogin-\u003Ctimestamp>.jsonl` (efter `--debug`) det første sted at kigge. `aula transcript view \u003Cfile>` pretty-printer den.\n\n---\n\n## Udvikling\n\n```sh\npnpm install          # installér alt\npnpm typecheck        # tsc -p tsconfig.json --noEmit\npnpm lint             # biome check .\npnpm lint:fix         # biome check --write .\npnpm test             # bun:test-suiterne (248 cases)\npnpm test:watch       # re-run ved ændring\n```\n\nAlle andre top-level-scripts: `pnpm aula \u003Ccmd>`, `pnpm mcp`, plus per-kommando-genvejene (`pnpm login`, `pnpm doctor`, `pnpm whoami`, `pnpm status`, `pnpm logout`).\n\n---\n\n## Bidrag\n\nSe [CONTRIBUTING.md](.\u002FCONTRIBUTING.md) for repo-layout, konventioner og en guide til at tilføje integrations-plugins. Bidragydere accepterer at følge [Code of Conduct](.\u002FCODE_OF_CONDUCT.md). Sikkerhedsproblemer: skriv venligst til **info@casperjuel.dk** i stedet for at åbne en offentlig issue — se [SECURITY.md](.\u002FSECURITY.md).\n\n---\n\n## Privatliv & jura\n\nMitID-credentials og OAuth-tokens bliver på din maskine. Serveren binder kun til `localhost` som default. Ingen telemetri. Wire-tracen er opt-in (`--debug`) og hvert kendt-hemmeligt felt redaktes inden noget skrives til disk.\n\nSelve dataen (beskeder, ugeplaner, børnenavne osv.) går videre til den MCP-klient du tilkobler. Hvor klienten sender den hen er klientens sag, ikke serverens — se top-disclaimeren.\n\nBrug projektet til dine egne børns data — log ind som dig selv med din egen MitID; brug det ikke til at tilgå nogen andens konto.\n\n> **Forbehold.** Dette projekt er ikke tilknyttet, godkendt af eller sponsoreret af KMD A\u002FS, Netcompany A\u002FS eller Aula-konsortiet. *Aula* er et varemærke tilhørende sin respektive ejer; navnet bruges her udelukkende til at identificere hvad denne software taler med.\n\n---\n\n## Licens\n\n[MIT](.\u002FLICENSE).\n","`aula-mcp` 是一个为丹麦Aula学校平台设计的MCP服务器，通过TypeScript实现MitID认证，无需无头浏览器即可运行。它能够将用户的个人资料、日程、消息和周计划等信息暴露给AI代理（如Claude\u002FCursor等），支持Model Context Protocol协议进行数据传输。项目利用了TypeScript、Bun以及Hono框架构建，并基于`scaarup\u002Faula`项目进一步开发而成。适合于需要在本地环境或指定云服务中集成AI助手来辅助管理学生相关信息的场景使用，但需注意根据所选客户端的不同，部分敏感数据可能会被发送至第三方服务商处理。用户应当谨慎选择连接的AI客户端以确保数据隐私安全。","2026-06-11 04:05:34","CREATED_QUERY"]