Skip to main content
Glama

fskintra-mcp

An MCP server for ForældreIntra (SkoleIntra) — logs in as a parent and exposes news, messages, weekly plans, homework, documents, photos, contacts and sign-ups as tools an AI agent can call.

What this is — and what it is not:

fskintra-mcp is a server that sits between an MCP client (an LLM) and ForældreIntra. It is an interface, not much more. The LLM is not part of this project. You pick the client yourself — Claude Code, Claude Desktop, ChatGPT, Cursor, Ollama, LM Studio — and it runs wherever it runs: in Anthropic's or OpenAI's cloud, or locally if you use Ollama or similar.

So this project is not a guarantee that your children's data stays local. Whether it does depends entirely on which client you connect. That is your call, not something fskintra-mcp can promise.

⚠️ Use it with care

A hobby experiment, no guarantees. It handles your ForældreIntra password and your children's school data — read the code (or have a developer friend read it) before you point an LLM at it. At your own risk.

⚠️ It's the client that sees the data — not the server

The server runs locally and forwards nothing on its own. But the MCP client you connect — Claude, ChatGPT, any other cloud LLM — gets everything it reads sent on to the provider (Anthropic, OpenAI, …) so it can answer you. It is not "all local" just because the server is. That is how MCP works: the client reasons, the server fetches.

Where it goes

ForældreIntra username and password

Stays local — macOS Keychain or an AES-256-GCM encrypted file. Used only to log in to ForældreIntra.

The data itself (messages, weekly plans, children's names, photos, …)

Goes to whichever MCP client you choose. Cloud LLM → that provider's servers (typically the US). Local LLM → stays local.

Want it 100 % local? Use a local LLM as the client: Ollama, LM Studio, llama.cpp. They all speak MCP and run on your own hardware.

Unofficial and third-party. Not affiliated with itslearning — see Privacy & legal.

Where this comes from

Two projects, two different debts:

  • svalgaard/fskintra — a long-running Python tool that turns ForældreIntra into email. None of its code is reused (it is Python 2 on mechanize), but it is the only written-down description of ForældreIntra's login flow, URL map and DOM selectors that exists. That reverse-engineering is what this project stands on.

  • Casperjuel/aula-mcp — the same problem solved for Aula. The architecture here is deliberately borrowed from it: layered packages, encrypted credential store, wire-tracing with redaction, a discover-first tool surface, and a doctor command. docs/architecture.md says which parts transferred unchanged and which ForældreIntra forced a different answer to.

Related MCP server: aula-mcp

Quick start

bun install
bun run fskintra login
bun run fskintra doctor        # does anything actually parse?

Then wire it into an agent — see examples/claude-config:

claude mcp add foraldreintra -- bun $PWD/packages/mcp-server/src/server-stdio.ts

Only ordinary ForældreIntra login ("alm login") is supported. If your school redirects to UNI-Login, the client stops with an explicit error rather than half-working.

Start with doctor

$ bun run fskintra doctor
• Store backend: macOS Keychain
✓ Logged in to minskole.skoleintra.dk as emil
• Message UI: conversations
• Children: Andrea 3A, Bertil 6B

──────────────────────────────────────── Andrea 3A
✓ news        child=Andrea 3A reminders=1 news=6 (412ms)
✓ messages    12 item(s) (233ms)
! weekplans   ForældreIntra says "ikke autoriseret".
✓ homework    2 item(s) (890ms)
✓ documents   14 item(s) (301ms)

Three outcomes, and the difference between the last two is the point:

Mark

Meaning

Parsed

!

Your school does not have this module

A parser bug — please file it

Add --debug for a sanitised wire transcript, and --dump <dir> to save what each section parsed.

Tools

Agents should call foraldreintra.discover once and work from the manifest: it returns the children, which sections this school actually has, and which message UI it runs.

Tool

What it does

foraldreintra.discover

Children, per-section availability, capability→tool map

foraldreintra.news

Front-page news with author, recipients, date, attachments; comments optional

foraldreintra.messages.list

Conversation list with thread/message ids

foraldreintra.messages.get

Every message in one conversation

foraldreintra.weekplans

Weekly plans, per day

foraldreintra.homework

Homework grouped by due date

foraldreintra.documents

Class documents including sub-folders

foraldreintra.photos

Photo albums and image URLs

foraldreintra.contacts

Contact cards for the class

foraldreintra.signups

Open sign-ups for conversations and events

foraldreintra.download

Saves an attachment to a local path

foraldreintra.reauthenticate

Fresh login, discarding the cached session

Behind flags: foraldreintra.messages.mark_read (FSKINTRA_MCP_WRITE=1) and foraldreintra.raw_request (FSKINTRA_MCP_RAW=1). The server is read-only without them.

CLI

fskintra login [--hostname H] [--username U] [--debug] [--no-store-password]
fskintra status | whoami | logout
fskintra doctor [--json] [--debug] [--dump DIR]
fskintra discover
fskintra fetch <section> [--child NAME] [--limit N] [--comments]
fskintra thread <message-id> [--thread THREAD_ID]
fskintra log [--last N]
fskintra transcript list | view <file> | prune [--keep N]

How it fits together

fskintra-auth  →  fskintra-client  →  mcp-server
                                          ↑
                                       apps/cli
  • packages/fskintra-auth — cookie-jar HTTP client with manual redirect walking, the login state machine, wire tracing, and the encrypted session store (Keychain on macOS, AES-256-GCM file elsewhere).

  • packages/fskintra-client — one module per section, plus the section-availability probe. Every parser has a pure Doc → data function.

  • packages/mcp-server — tools, discover, and both transports.

  • apps/clifskintra.

Three decisions worth knowing before you read the code:

Redirects are followed by hand. fetch doesn't expose Set-Cookie from intermediate hops, and the SSO relay sets cookies there.

The stored session includes your password. ForældreIntra has no refresh token; replaying the login form is the only way to renew a dead session. That is why the store is encrypted. --no-store-password opts out, at the cost of needing you present at every expiry.

"Unavailable" and "empty" are different types. A school without homework answers "ikke autoriseret". An agent handed an empty array tells a parent "no homework this week", which would be false.

Full reasoning in docs/architecture.md.

Configuration

Variable

Effect

FSKINTRA_MCP_DIR

Config dir (default ~/.config/fskintra-mcp)

FSKINTRA_MCP_KEY

Encryption key (64 hex chars) or passphrase for the file backend

FSKINTRA_MCP_NO_KEYCHAIN=1

Use the encrypted file instead of the macOS Keychain

FSKINTRA_MCP_LOG=1

Verbose logs (stderr under stdio)

FSKINTRA_MCP_WRITE=1

Register write tools

FSKINTRA_MCP_RAW=1

Register the raw page-fetch escape hatch

FSKINTRA_MCP_AUTO_CONFIRM_CONTACTS=1

Submit the "Bekræft kontaktoplysninger" form automatically

FSKINTRA_MCP_PORT / _HOST

HTTP server bind (default 127.0.0.1:7979)

FSKINTRA_MCP_ALLOW_REMOTE=1

Permit a non-loopback bind (refused otherwise)

FSKINTRA_HOSTNAME / _USERNAME / _PASSWORD

Credentials for headless installs, instead of fskintra login

Home Assistant

There is an add-on: homeassistant-addon/. Point HA's Model Context Protocol integration at http://homeassistant.local:7979/sse and Assist can answer questions about school out loud.

Known limits

  • Nothing here has run against a live ForældreIntra yet. Every selector is ported from a codebase that froze years ago. fskintra doctor is the first move.

  • UNI-Login is not supported.

  • Read-only. No sending messages, no signing up for events.

  • The old message UI is the less-tested of the two.

  • Selectors are a moving target. Sections fail independently; one broken parser doesn't take the others down.

Development

bun test          # 91 tests, no network
bun run typecheck
bun run lint

See CONTRIBUTING.md — particularly the part about keeping "unavailable" and "broken" apart when you touch a parser.

Your ForældreIntra username and password stay on your machine. The HTTP server binds to 127.0.0.1 by default and refuses a non-loopback bind unless you set FSKINTRA_MCP_ALLOW_REMOTE=1. No telemetry. The wire trace is opt-in (--debug), and every known-secret field is redacted before anything is written to disk.

The data itself — messages, weekly plans, children's names, photos — is passed on to whichever MCP client you connect. Where that client sends it is the client's business, not the server's; see the disclaimer at the top.

Use this for your own children's data — log in as yourself with your own ForældreIntra account. Do not use it to reach anyone else's account.

Disclaimer. This project is not affiliated with, endorsed by or sponsored by itslearning AS, UNI-C, or any Danish school or municipality. SkoleIntra and ForældreIntra are trademarks of their respective owners; the names are used here solely to identify what this software talks to. Provided "as is", without warranty of any kind — see LICENSE.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    This server enables MCP clients (LLMs) to access data from the Danish school platform Aula, such as messages, schedules, and child profiles, by authenticating via MitID and running locally.
    7
    44
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    A local MCP server for reading ParentSquare data (feeds, calendar, messages, directory, groups, and more) using undocumented internal APIs. It enables Claude, Cursor, and other MCP clients to access your ParentSquare account via stdio.
    23
    1
    -