Helmet
v0.3.1Manage your Helmet library accounts (Helsinki capital region) from an AI agent — renew loans, place and cancel holds, view fines, and search the catalog acro...
Like a lobster shell, security has layers — review code before you run it.
Runtime requirements
Install
npm i -g @helmet-ai/helmetHelmet Library Skill
Access the Helmet library system (Helsinki Metropolitan Area libraries) from an AI agent. Query loans, renew books, check holds and fines — for one account or many family accounts at once, via the helmet CLI with --json output.
What you can ask your agent
- "Is anything overdue for me or the kids at the library?"
- "Renew everything that's due this week."
- "How many holds do I have, and which are ready for pickup?"
- "Place a hold on Dog Man Scarlet Shedder."
- "Cancel my hold on the Finnish language audiobook."
- "Do I owe any fines right now?"
- "Search the Helmet catalog for 'Tove Jansson'."
Quick Start
# Install
npm install -g @helmet-ai/helmet
# First-time login — run once per library card (saves credentials locally)
helmet login
# Full account overview (single account)
helmet summary --json
# Family overview (all saved profiles)
helmet summary --all-profiles --json
Preflight (recommended before bulk queries)
helmet status --json
helmet status --all-profiles --json
Verifies each saved profile's session is live. Exit code 0 means authenticated; 2 means the user needs to run helmet login. Run this once at the start of a multi-step task to avoid partial failures halfway through.
Profiles
The CLI stores one or more library accounts as profiles. Use these when acting for a user or a family:
- One profile saved — commands run against it by default.
- Multiple profiles saved — commands run against the last-used profile unless you pass
--profileor--all-profiles.
Profile selection flags
| Flag | Purpose |
|---|---|
--profile <selector> | Target one profile. Selector is any of: display name (Alice), unique display-name prefix (al), card number, or full id (helmet|<card>). |
--all-profiles | Fan out across every saved profile. Works on summary and loans list. Mutually exclusive with --profile. |
--all-profiles is not supported on loans renew, holds place, holds cancel (all destructive — always target one profile), search (unauthenticated), or login.
Fan-out JSON shape
--all-profiles --json wraps output in a per-profile array. Each row is independent: one profile failing does not block others.
[
{"profile": {"id": "helmet|...", "displayName": "Alice"}, "ok": true, "data": { /* same shape as single-profile --json */ }},
{"profile": {"id": "helmet|...", "displayName": "Bob"}, "ok": false, "error": "AuthenticationError: ...", "errorCode": "AUTH_REQUIRED"}
]
Exit code is 0 if at least one profile succeeded, 1 if all failed. When reporting partial failures to the user, key on errorCode (stable) rather than the free-form error string (which embeds the raw exception message).
helmet profiles list --json
Enumerate saved profiles. Returns id, displayName, cardNumber, and lastUsedAt for each.
helmet profiles rename <selector> <new-name> / helmet profiles remove <selector>
Local-only management of saved profiles (no Helmet API calls).
Session cache
Each profile's authenticated cookie jar is persisted at ~/.config/helmet/sessions/<id>.json (mode 0600). The first command per profile walks the full Finna login handshake (~2.7s); subsequent commands skip straight to the data request (~1.3s). Stale cookies are handled transparently — if Finna redirects to the login page, the session auto-re-authenticates using the stored PIN. You do not need to manage the cache explicitly; it is cleared automatically on helmet login, helmet profiles remove, or an unrecoverable AuthenticationError.
Commands
All commands accept --json for machine-readable output. Always use --json when calling from an agent.
helmet summary --json
Returns a complete account snapshot: loans (with overdue/due-soon flags), holds (with pickup-ready status), and fines. Start here — one call gives you everything needed for triage. Add --profile <selector> for a specific person or --all-profiles for a whole family.
helmet loans list --json
List checked-out items with title, author, due date, due status (ok, due, overdue), and whether renewable. Supports --profile and --all-profiles.
helmet loans renew <id> --json
Renew a specific loan by its ID. Returns success/failure with new due date or error code. Requires --profile <selector> when multiple profiles exist (the CLI will not auto-pick a profile for destructive operations).
helmet loans renew --all --json
Renew every renewable item on one profile. Same profile-targeting rule as above — pass --profile <selector> explicitly.
helmet holds list --json
List current holds. helmet holds (no subcommand) is an alias. Each row has shape:
{
"id": "12345678",
"title": "Sample Book Title",
"author": "Last, First",
"pickupLocation": "Sample Branch",
"queuePosition": null,
"pickupDeadline": "15.1.2026",
"createdDate": "1.1.2026",
"shelfLocation": "A 000",
"status": "available_for_pickup",
"cancelable": true,
"fetchedAt": "2026-01-01T12:00:00.000Z"
}
Field meanings:
status:"pending"(in queue) /"in_transit"(on its way to the pickup branch) /"available_for_pickup"(arrived — the user must go collect it).pickupDeadline(Finnish "nouto viimeistään"): the date the hold will be cancelled and passed to the next patron if uncollected. Only populated whenstatus === "available_for_pickup"—nullotherwise. Date is in Finnish formatD.M.YYYY. If the user misses this date, Helmet charges a no-pickup fee and may suspend borrowing for a period — agents should surface this date prominently in reminders.shelfLocation(Finnish "Varaushylly"): the exact shelf code inside the branch where the arrived hold is waiting (e.g.A 000). Only populated for arrived holds. Include it in pickup reminders so the user can walk straight to the shelf.createdDate(Finnish "Luotu"): when the hold was placed. Informational, not actionable.queuePosition: for pending holds, the user's position in the reservation queue (nullonce the hold is in transit or arrived).
helmet holds place <record-id> [--comment <text>] --json
Place a title-level hold on a Helmet catalog record. The record-id comes from helmet search (e.g. helmet.2613471). The CLI uses the default pickup location preselected by Helmet in the hold form. The --comment field is used only for bookmobile pickup stops. Returns { success, message }; on success, message is typically "Varauspyyntö onnistui.". Requires --profile <selector> when multiple profiles exist — the CLI will not auto-pick a profile for a destructive/state-changing operation.
helmet holds cancel <hold-id> --json
Cancel an active hold. The hold-id is the id field from helmet holds list --json. Returns { success, message }; on success, message is typically "1 varaus(ta) poistettu.". Same profile-targeting rule as place — pass --profile <selector> explicitly.
helmet fines --json
List individual fines and total amount owed.
helmet search <query> --json
Search the Helmet catalog. Unauthenticated — --profile has no effect and --all-profiles is rejected.
helmet status [--json] [--all-profiles]
Lightweight preflight: reports whether the selected profile (or all profiles) has a live authenticated session. Exit code 0 if authenticated, 2 if helmet login is needed. With --json:
{
"version": "0.3.0",
"profile": {"id": "helmet|...", "displayName": "Alice"},
"authenticated": true,
"checkedAt": "2026-04-14T12:34:56Z"
}
On --all-profiles, emits {version, checkedAt, profiles: [...]} where each row carries authenticated: bool and, on failure, errorCode: "AUTH_REQUIRED". Run this before multi-step tasks.
helmet version / helmet --version / helmet -V
Print the CLI version (e.g. 0.3.0). No auth, no network. Useful when an agent needs to record which helmet build produced a report.
Handling authentication errors
When the stored session expires, the PIN has changed, or Finna invalidates the session for any reason, helmet exits with code 2 and (with --json) emits:
{
"ok": false,
"errorCode": "AUTH_REQUIRED",
"errorName": "AuthenticationError",
"message": "Helmet session expired and re-authentication failed — run `helmet login` to refresh credentials.",
"recovery": "Run `helmet login` to refresh your library credentials."
}
On --all-profiles, one row may fail with errorCode: "AUTH_REQUIRED" while others succeed. Report the affected person by displayName and continue with the rest.
Agent recovery procedure
- Detect — check the exit code (2 = auth) or the
errorCode: "AUTH_REQUIRED"field in the JSON output. - Report — tell the user which profile(s) need re-authentication, referring to them by
displayName(not card number). - Do NOT run
helmet loginyourself. It is interactive and prompts for a PIN that only the user knows. Ask the user to runhelmet loginlocally. - Retry the original command after the user confirms they've re-logged in. The CLI's stale session cache is cleared automatically when auth fails, so the retry will attempt a fresh login.
- Do NOT loop. If the retry still returns
AUTH_REQUIRED, stop and ask the user — do not keep retrying. The most likely cause is a changed PIN that the stored credential no longer matches, which only a freshhelmet logincan fix.
Triage Guidance
When reporting to the user, prioritize items by actionability:
| Priority | Condition | Action |
|---|---|---|
| URGENT | Overdue items (dueStatus: "overdue") | Renew immediately or alert user |
| URGENT | Fines > 0 EUR | Alert user — fines block borrowing |
| URGENT | Ready-for-pickup hold whose pickupDeadline is today, tomorrow, or in the past | Alert user to collect today — missing the deadline cancels the hold and charges a no-pickup fee |
| HIGH | Loans due within 3 days (dueStatus: "due" or in loansDueSoon) | Renew if possible, otherwise warn |
| HIGH | Ready-for-pickup hold (status: "available_for_pickup") with pickupDeadline within 7 days | Remind user to pick up. Always cite the exact deadline date and the shelfLocation so they can go straight to the shelf at pickupLocation. |
| MEDIUM | Loans due within 7 days | Mention in summary |
| LOW | Holds with queue position ≤ 2 | Mention — pickup may come soon |
Formatting hold reminders for users
When surfacing a ready-for-pickup hold, prefer a line like:
"Pick up Sample Book Title at Sample Branch (shelf A 000) by 15.1.2026 — otherwise the hold is cancelled and a no-pickup fee applies."
Always include: title, pickupLocation, shelfLocation (when present), and pickupDeadline. Dates are already in Finnish D.M.YYYY format — keep them as-is; do not reformat to ISO unless the user is clearly a developer consuming JSON.
Recommended workflow (single person)
- Run
helmet summary --json(or with--profile <name>). - Check for URGENT items first — auto-renew overdue loans if renewable.
- Surface HIGH items prominently.
- Mention MEDIUM/LOW items briefly.
- If renewals fail, include the error code in your report.
Recommended workflow (family)
- Run
helmet summary --all-profiles --json— one call covers everyone. - Flatten rows where
ok: true. For eachok: falserow, mention the person's name and that their data could not be fetched (don't abort the whole report). - Group findings by urgency first, by person second — one overdue book across any family member is more actionable than "here is Alice's summary, here is Bob's summary".
- For renewals, issue one
helmet loans renew --all --profile <name>per person that has renewable overdue loans. Never fan out renew. - Prefer referring to people by
displayNamein user-facing output; keepidfor internal routing.
Scripts
The wrapper script at scripts/helmet-cli.sh can be used as a fallback when the helmet binary is not on PATH:
bash skills/helmet/scripts/helmet-cli.sh summary --all-profiles --json
Troubleshooting
Empty holds / loans / fines arrays with no error — this was a bug in helmet ≤ 0.1.1 where an expired session was parsed as "no data" instead of raising an auth error. Upgrade to 0.2.0+:
npm install -g @helmet-ai/helmet@latest
helmet --version # must be ≥ 0.2.0
If you still see empty results after upgrading, run helmet status --json — if it reports authenticated: false, the user needs to run helmet login. Real empty results (no holds, no loans) after 0.2.0 are legitimate; only pre-0.2.0 masked auth failures as empty.
Comments
Loading comments...
