Wilma logo

Wilma

Community
aikarjal
wilma

Access Finland's Wilma school system from AI agents, for parents (and students with their own login). Fetch schedules, homework, exams, grades and the gradebook, lesson notes (merkinnät) and absence summaries, messages with replies, news, printouts and linked news resources — through the WilmAI MCP tools (`wilma_*`) when connected, or the wilma CLI. Start with a summary, drill into messages and news, and fetch linked attachments. Requires wilma-cli 2.0+.

Overview

Publisheraikarjal
Repositorywilmai
Skill namewilma
Stars
81
Forks
18
Bundled files
1
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by aikarjal on GitHub. Read the source before you install it.

Installation

Install the Wilma AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/aikarjal/wilmai/tree/main/skills/wilma
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/aikarjal/wilmai.git /tmp/wilmai
mkdir -p .claude/skills
cp -r /tmp/wilmai/skills/wilma .claude/skills/wilma
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Wilma in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Wilma on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Wilma is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Wilma Skill

Overview

Wilma is the Finnish school information system used by schools and municipalities to share messages, news, exams, schedules, homework, and other student-related updates with parents/guardians. Students can use WilmAI with their own login too; it then covers just that student.

There are two ways to reach Wilma. Both return the same data, in the same JSON shape.

  1. MCP tools — if tools named wilma_* are available (the WilmAI connector, desktop extension or plugin), use them. No shell needed.
  2. CLI — otherwise use the wilma (or wilmai) command. It prints JSON when you run it (no --json needed), and wilma help <command> shows usage, examples and the JSON shape.

Both cover every child by default; pass student / --student <number|name> to narrow to one. Results come per child: { students: [{ student: { studentNumber, name, wilma? }, … }] }. Times are Finnish time with their offset (2026-10-02T13:37:00+03:00) — report them as they are, without converting. When you write dates for the user, follow the language you answer in: Finnish ke 7.10. (with the final period), English Wed 7 Oct. Wilma's own texts use the Finnish style; convert it instead of mixing the two (not Wed 7.10).

MCP toolCLI command
wilma_summarywilma summary [--since <date>]
wilma_schedulewilma schedule [today|tomorrow|week|next-week|<date>|<weekday>], wilma schedule --from <date> [--to <date>]
wilma_homeworkwilma homework
wilma_upcoming_examswilma exams
wilma_gradeswilma grades [--from <date> [--to <date>]]
wilma_gradebookwilma gradebook
wilma_contactswilma contacts
wilma_lesson_noteswilma notes [--date <date> | --days N | --from <date> [--to <date>]]
wilma_lesson_notes_summarywilma notes summary
wilma_list_messages / wilma_read_messagewilma messages / wilma messages <id>
wilma_list_news / wilma_read_newswilma news / wilma news <id>
wilma_get_news_attachmentwilma news <id> download <resource>
wilma_list_printouts / wilma_get_printoutwilma printouts / wilma printouts <id>
wilma_accountwilma students, wilma accounts
wilma_find_schoolwilma find-school <city>
—wilma home (WilmAI Home, the desktop app: where to download it)
wilma_loginwilma login
wilma_logoutwilma logout

Dates are YYYY-MM-DD, today, yesterday or tomorrow.

Logging in

The user logs in once; the login is saved on their computer.

  • On the user's own computer: run wilma login (or call wilma_login). It opens a page in the user's browser where they pick their school's Wilma and log in. Wait for the command to finish.
  • On a cloud computer the user can't see (no browser they can reach):
    1. Ask which city their children's school is in (or the school's name). Run wilma find-school <city> (or call wilma_find_school) and let the user pick from the results — one city often has several Wilmas (city schools, private schools, colleges).
    2. Ask the user to add WILMA_USERNAME and WILMA_PASSWORD to your secret or environment settings, plus WILMA_TOTP_SECRET if the account uses two-step verification, and set WILMA_TENANT to the Wilma address they picked. Every command then works without a saved login. Alternatively: wilma login --tenant <url> --username <name> --password-stdin.
  • Don't run wilma without arguments to look things up: in a terminal it opens a menu for people (from a program it prints the help).
  • Never ask the user to type their Wilma password into the chat.
  • Children on different Wilmas (e.g. a city school and a private school): the user adds each login on the login page ("Add another school's Wilma") or with another wilma login. All saved logins are used together; children then carry a wilma field naming their Wilma. wilma accounts lists the logins, with role: "student" for a student's own login. A child the user unticked on the login page (a profile Wilma lists twice) isn't read, and wilma accounts lists it under hidden. When a parent's and a child's own login on the same Wilma are both saved, the child is read through the parent's (wilma-cli 2.4+).

Commands continue the last Wilma session instead of logging in each time (each login would log the parent out of Wilma in their own browser), so running several commands in a row is fine.

If the user wants to use Wilma in their browser, or asks you to log out, run wilma logout or call wilma_logout (wilma-cli 2.2+). It ends WilmAI's Wilma session and keeps the saved login. Don't run other Wilma commands after that unless the user asks: the next one logs in again, and that logs them out of Wilma in the browser.

Install

bash
npm i -g @wilm-ai/wilma-cli

Core tasks

Daily briefing (start here)

bash
wilma summary
wilma summary --since yesterday

Per child: today's and the next school day's lessons, upcoming exams (with start times when given), recent homework, lesson notes since the previous school day (teachers' feedback and absences), recent bulletins, and recent and unread messages (unread, replyCount). --since <date> keeps only what is from that day on — use it for scheduled daily runs. If Wilma couldn't give a part, unavailable names it.

Schedule

bash
wilma schedule tomorrow
wilma schedule week
wilma schedule next-week
wilma schedule 2026-03-10
wilma schedule thu --student <number|name>

Default: this week. tomorrow is the next school day; a weekday (mon…sun, or Finnish ma…su) is its next occurrence. Lessons include the teacher(s) and, when Wilma gives one, the room. --from <date> reads any other week (to that week's Friday) or, with --to, a period of up to 31 days, e.g. a few weeks to see the usual school days (wilma-cli 2.4+).

Homework and exams

bash
wilma homework
wilma exams

Each exam has its date, subject, name, topic (what to study) and teacher; time is the start time when the school gives one (otherwise null).

Grades

bash
wilma grades                     # recent exam grades
wilma grades --from 2026-08-01   # every exam since then, graded or not yet
wilma gradebook                  # course and report-card grades

grades --from (wilma-cli 2.4+) reads Wilma's list of past exams for a period, as far back as the school keeps them; an exam still waiting for its grade has grade: null. The gradebook (Suoritukset) is a tree: subject → syllabus → course, each with grade, credits and completion date. Course codes ending in LV are usually school-year (report card) grades. An empty list means nothing has been graded yet.

Class and teachers

bash
wilma contacts

The child's class, class teacher and other supervisors (role as Wilma names it, e.g. Luokanvalvoja), and their own teachers with the subjects each teaches them in this and next week's timetable (wilma-cli 2.4+). Use it for "who is the class teacher" or "who teaches maths".

Lesson notes (merkinnät)

bash
wilma notes --date yesterday
wilma notes --days 14
wilma notes summary

Teachers' per-lesson notes: positive feedback, behavioural remarks, missing books or homework, and absence categorisations (medical, explained, unexplained). One day (default today), --days N for the last N days, or --from/--to (--to can be ahead of today: a teacher may note a make-up exam on a coming lesson). Teachers usually fill notes during or after class, so for a morning run use the previous day (the summary already does). When the teacher wrote something, it's in note.

notes summary counts the notes by typeLabel for this school year (or --from/--to), e.g. how many lessons were missed for health reasons.

Each note has start/end times from Wilma's hour grid (accurate to the lesson hour). subject is the course code (e.g. MA_8LV = math, 8th grade; empty when the note isn't about a lesson) and typeLabel is Wilma's Finnish label.

Messages and bulletins

bash
wilma messages
wilma messages --folder appointments
wilma messages <id>
wilma news
wilma news --older
wilma news <id>

Message lists mark unread messages and replyCount; wilma messages <id> returns the thread with its replies. The appointments folder holds event invitations (parents' evenings, learning discussions) with the family's answer as Wilma lists it (wilma-cli 2.4+): invitationStatus "Applied" (signed up, the time in invitationInfo), "ExpiredReg" (sign-up closed), or null; reading the invitation gives its time slots. The summary's invitations carry the same fields. wilma news returns the newest dated bulletins plus every pinned one (pinned: true, e.g. the school-year bulletin); --older adds older bulletins (archived: true, dated once read). Reading an item needs no --student unless several Wilmas are connected.

Printouts

bash
wilma printouts
wilma printouts <id> --output <directory>

PDF documents the school offers, such as report cards or absence reports (it varies by school).

Bulletin attachments

Always inspect the resources array returned by wilma news <id>. Each resource has:

  • id — stable within the bulletin (resource-1, resource-2, …); the download command also accepts the bare number (1).
  • label — the link text from the bulletin.
  • url — absolute URL.
  • authContext — "wilma": a download uses the Wilma session. "external": a download uses an isolated, unauthenticated fetch that never sends Wilma credentials (like opening the link in a signed-out browser).
  • fileName — naming hint when the URL path looks like a file; may be null even for real files.

Any resource can be attempted. There is no reliable way to know in advance whether a URL serves a file publicly, requires sign-in, or is a plain web page — so the CLI does not guess: it attempts the download and reports what actually happened. When a document is relevant to the user's request, attempt it:

bash
wilma news <news-id> download <resource-id> --output <directory>

Handle the returned status:

  • downloaded — the file was written. Use the returned absolute path, and trust contentType/sizeBytes over any guess from the bulletin label.
  • not_a_file — the link answered with a web page instead of a file. This usually means the document requires signing in (for example a private cloud-drive sharing link), or the link is simply a web page. Report this to the user; if access matters, open the url in a user-authorized browser session that has the external service's authentication. Never retry the download in a loop.
  • An error (see below) — the attempt itself failed (HTTP error, network problem, size limit). Report the message.

Keep downloads in a task-scoped directory via --output (defaults to the current working directory). Existing files are never overwritten — a numeric suffix is appended.

Prefer resource metadata over URLs embedded in content; content is prose and can be null for link-only bulletins.

With the MCP tools, wilma_get_news_attachment returns the file itself (text inline, images as images, other files such as PDFs as an embedded file) plus the same not_a_file handling. Pass save: true to also save a copy to the user's Downloads/WilmAI folder.

Choosing a child

--student takes a student number, the full name, or the start of a first or last name (Kiia for "Kiia Example"). An unknown or ambiguous name is an error (unknown_student / ambiguous_student) that lists the children, so pass a number or full name from wilma students when unsure.

Errors

Errors are JSON too: { "status": "error", "code": "…", "message": "…" } (sometimes with hint and cause). Exit code 2 means a usage error (invalid_argument, unknown_command — fix the command; wilma help <command>), 3 means not_logged_in (see Logging in), 1 anything else: unknown_student, ambiguous_student, not_found, login_failed (the saved password no longer works — ask the user to run wilma login again), mfa_required / mfa_failed, network, wilma_error.

MFA (two-step verification)

If the Wilma account has MFA/TOTP enabled, logins need the authenticator setup key (a base32 key or otpauth:// URI) so they can run unattended:

  • wilma login: the login page asks for the setup key when Wilma requires it and saves it with the login.
  • Environment: set WILMA_TOTP_SECRET.
  • One-off: --totp-secret <base32-key|otpauth://...> works on any command, but a key on the command line ends up in shell history; prefer the two above.

Notes

  • If the CLI says no saved login, run wilma login (logging in to the same Wilma again replaces that login). wilma accounts lists saved logins and wilma accounts remove <number> removes one; wilma config clear deletes every saved login.
  • This skill is for WilmAI CLI 2.x. If wilma --version prints 1.x, run wilma update (until then scripts/wilma-cli.sh runs 2.x through npx).
  • Updates. When a newer WilmAI is out, commands print Update available: <old> → <new> on stderr (at most once a day). Finish the user's request first, then run wilma update, or tell the user if you can't install software here.
  • Replying, signing up, reporting an absence. WilmAI only reads Wilma. When the user wants to reply to a message, sign up for an event or report an absence, say so and point them to Wilma, or to WilmAI Home, the desktop app from the same project, which does these (wilma home gives the download for their computer; without the CLI, https://wilm.ai/home). Mention Home only then, or when the user wants a Wilma app of their own; downloading and installing it is their step.
  • The 1.x spellings (kids list, tenants, attendance list, <command> list, messages read <id>, news read <id>, news resource download …, --all-students) still work.
  • TLS errors on managed machines. If a command fails with code network and a cause such as UNABLE_TO_GET_ISSUER_CERT_LOCALLY, the network is intercepting TLS and re-signing certificates with a private root CA that Node does not trust. Read the hint and report it rather than retrying. The fix is to run the CLI with NODE_USE_SYSTEM_CA=1 (Node >=22.19/>=24.6), or node --use-system-ca "$(command -v wilma)" (Node >=22.15). Never suggest NODE_TLS_REJECT_UNAUTHORIZED=0; it disables verification entirely. A working npm install does not prove TLS is healthy — the npm registry is commonly exempt from inspection.

Actionability guidance (for parents)

Wilma contains a mix of urgent items and general info. When summarizing for parents, prioritize actionable items:

Include items that:

  • Require action or preparation (forms, replies, permissions, materials to bring).
  • Are lesson notes about missing study materials or homework, behaviour, or unexplained absences.
  • Are replies in a thread the parent started (replyCount in message lists).
  • Announce a deadline or time-specific requirement.
  • Describe a schedule deviation or noteworthy event (trips, themed days, school closures, exams).
  • Mention homework, exams, or upcoming deadlines.

De-prioritize items that:

  • Are purely informational with no action, deadline, or schedule impact.
  • Are generic announcements unrelated to the target period.

When in doubt, include and let the parent decide. Prefer a short, structured summary with dates and IDs.

Scripts

scripts/wilma-cli.sh runs the installed wilma (or wilmai) when it is 2.x or newer, and otherwise the latest 2.x release through npx.

Links

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Wilma AI skill do?

Access Finland's Wilma school system from AI agents, for parents (and students with their own login). Fetch schedules, homework, exams, grades and the gradebook, lesson notes (merkinnät) and absence summaries, messages with replies, news, printouts and linked news resources — through the WilmAI MCP tools (`wilma_*`) when connected, or the wilma CLI. Start with a summary, drill into messages and news, and fetch linked attachments. Requires wilma-cli 2.0+.

Why use Wilma on TypingMind?

Because you install it once and use it with any model. Wilma is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Wilma in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/aikarjal/wilmai/tree/main/skills/wilma. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Wilma?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Wilma?

As many as you like. As long as a model supports skills, you can use Wilma with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Wilma AI skill free?

Yes. It is published on GitHub by aikarjal under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇