Skip to main content
Glama
troelskn

swehockey

by troelskn

swehockey

Look up Swedish ice-hockey data from stats.swehockey.se — schedules and results, standings, team rosters, and season-total scoring & goalie stats — from your AI assistant (via MCP) or the command line.

It's built for following youth series. Out of the box it covers Skåne U16P, U15P and U14P (boys, seasons 2024-25 and 2025-26); you can add any other league yourself — another district, age group, or the national/regional leagues.

What you can ask your assistant

Once it's connected, you can ask things like:

  • "How did the Skåne U16P teams do last weekend?"

  • "Show the current U14P standings."

  • "Who are Rögle's top scorers in U16P this season?"

  • "Which U15P goalies have the best save percentage?"

  • "List the teams in the U14P league."

It reads the official stats site and hands back clean, current data — no scraping or copy-pasting on your side.

Connect it to your AI assistant (MCP)

bin/swehockey-mcp is an MCP server — the standard way to give an assistant new abilities. Point your assistant's MCP configuration at it:

{
  "mcpServers": {
    "swehockey": {
      "command": "bun",
      "args": ["/abs/path/to/swehockey/bin/swehockey-mcp"]
    }
  }
}

Replace the path with where you put this repo. You'll need Bun installed (curl -fsSL https://bun.sh/install | bash), then run bun install once inside the repo.

Your assistant then gets read-only tools to:

  • see which leagues are set up, and the tournaments in each;

  • browse districts and their cups;

  • get schedules & results, standings, and team lists;

  • get season-total scoring and goalie stats, grouped by club (and filterable to a single club).

It only reads — it can't change anything on the site or your machine. (Adding leagues is done in the catalog file, below.)

New to MCP setup? See coworkerai.io/guide/mcp-setup.

The leagues it knows about

The tool only answers about leagues listed in its catalog, config/leagues.json. It ships with Skåne U16P / U15P / U14P for 2024-25 and 2025-26. Anything else, you add.

Add your own leagues

The catalog is a plain JSON file you can grow. The easiest way:

  1. Find the league with the built-in discover helper (on the command line):

    ./bin/swehockey discover-leagues            # districts, grouped by region
    ./bin/swehockey discover-leagues 21103      # drill into one district (Skåne)
    ./bin/swehockey discover-leagues national   # the national leagues (SHL, U20 Nationell, …)
    ./bin/swehockey discover-leagues regional   # the region-wide leagues
  2. Add it by appending --write to a league's id — it writes the entry for you:

    ./bin/swehockey discover-leagues 19001 --write

(Working with a coding assistant? You can also just ask it to "add the Stockholm U16 league" and let it run those for you.) Full details in Growing the catalog.

Prefer to edit by hand? Each entry is one league (a region + age-group + season) bundling the tournaments (the raw series on the site) it's made of:

{
  "key": "skane-u16p-2025",
  "region": "Skåne",
  "ageGroup": "U16P",
  "season": "2025-26",
  "tournaments": [
    { "id": 18433, "name": "U16P Fortsättning" },
    { "id": 18434, "name": "U16P Grund A" }
  ]
}
  • key is the short handle you'll use (e.g. "standings for skane-u16p-2025").

  • each tournament id is swehockey's own number for that series — it's in the page URL, …/Overview/18433; name is just a label.

Edits are picked up live — the file is re-read whenever it changes, so you don't have to restart the tool or the MCP server after adding a league. It's re-validated each time, so a typo (missing field, duplicate id) shows up as a clear error on the next request rather than misbehaving silently.

Command line (optional)

Everything the assistant can do is available directly too. Add --json to any command for machine-readable output.

./bin/swehockey leagues                       # the leagues that are set up
./bin/swehockey leagues skane-u14p-2025       # one league's tournaments
./bin/swehockey schedule skane-u14p-2025      # games + results, chronological
./bin/swehockey standings skane-u16p-2025     # standings, one table per tournament
./bin/swehockey playerstat skane-u16p-2025    # scoring, season totals by club
./bin/swehockey playerstat skane-u16p-2025 --team "Rögle BK"   # one club only
./bin/swehockey goaliestat skane-u16p-2025    # goalie stats by club
./bin/swehockey teams skane-u14p-2025         # the teams in a league
./bin/swehockey districts                     # ice-hockey districts, by region
./bin/swehockey cups 21103                    # cups in a district (id from `districts`)
./bin/swehockey schedule 18436                # a single tournament or cup, by id

Most commands take either a league key (from your catalog) or a raw numeric tournament/cup id from the site. Run ./bin/swehockey help for the full list.

Cups. swehockey holds cups in its stats system (a "Cups" section per district). districts then cups <district-id> finds them — the "Cups" section plus any cup-named entry mis-filed above the divider. A cup id then works with the other read commands — schedule especially (cups are knockouts, so they usually have no standings or roster).


The rest is reference for maintaining and extending the tool.

Concepts: tournaments and leagues

Meaning

Tournament

one raw series as the site presents it, keyed by its numeric tournamentID (e.g. 18433 "U16P Fortsättning")

League

a broad grouping — one region + age-group + season (e.g. skane-u16p-2025) — that bundles several tournaments

A youth season is often split into several tournaments (an autumn/spring split, re-seeded into strength groups), so one league holds ~4–7 tournaments. We don't model that autumn/spring split — swehockey doesn't mark it consistently, so we'd be guessing — tournaments are just listed in catalog order.

The catalog (config/leagues.json) is data, not code: swehockey ids are season-specific and turn over every year, so you edit JSON to add a season — no source changes. It's read on demand and cached against the file's modified-time (src/catalog.ts), so changes are picked up without a restart and each load re-validates — a bad entry (missing field, wrong type, duplicate id/key) fails loudly. config/clubs.json works the same way.

Growing the catalog

Two developer commands find ids to add to the catalog (both can write straight to it with --write — ids and names come verbatim from the source; neither is exposed over MCP):

discover-season <league-key> — another season of a league you already have. It follows that league's season-history links and reads the target season's sibling dropdown:

./bin/swehockey discover-season skane-u16p-2025            # any season newer than 2025-26
./bin/swehockey discover-season skane-u16p-2025 2026-27    # a specific season
./bin/swehockey discover-season skane-u16p-2025 2026-27 --write

Until the season exists it reports "not published yet".

discover-leagues [<id>|national|regional]other leagues you haven't mapped. Scopes are flat (district / national / regional), not a tree:

./bin/swehockey discover-leagues               # 1. districts, grouped by region
./bin/swehockey discover-leagues 21103         # 2. a district's series (Skåne)
./bin/swehockey discover-leagues 21103 U15P    #    ...narrowed by name
./bin/swehockey discover-leagues 19001         # 3. a league's tournaments (a leaf)
./bin/swehockey discover-leagues 19001 --write #    ...scaffold that league
./bin/swehockey discover-leagues national      # 4. the Sweden-wide leagues
./bin/swehockey discover-leagues regional      # 5. the region-wide leagues
  1. No arg → districts (entry points).

  2. A district id → every series in that region's dropdown, flagged mapped vs unmapped. It's unfiltered on purpose: swehockey mixes leagues, championships (DM), training series and cups with no reliable separator (the Overview/Schedule link kind doesn't track it, and the "Cups" divider leaks cups above it), so it shows everything and you pick (a name filter helps). Read-only.

  3. A league id → that league's tournaments, flagged mapped/unmapped.

  4. national / regional → the Sweden-wide leagues (from the "National" menu's Leagues column — SHL, U20 Nationell, SDHL, …) and the region-wide leagues (the "Regional" menu's Men/Women sections, by region — HockeyTvåan, U18 Regional, …). Both are listed directly in their nav menu and shown unfiltered (the sections also hold preseason/cup/district-team entries, with no reliable marker to separate them), so you pick. Drill into any league id like a leaf.

These three (district / national / regional) are siblings, not a hierarchy — the catalog's region is just a label ("Skåne", "National", "Region Syd"). The only real nesting is region → district, already reflected by grouping the district list under regions.

--write works on a league id (a leaf — a listing has nothing single to scaffold). It recovers what the leaf page doesn't carry:

  • District leagues — region + name come from the parent district's dropdown (--write scans the district dropdowns to find which one lists this id); season from the page's season-history links.

  • National / regional leagues — region is "National" / the region name ("Region Syd"); the age-group is taken from the league name (U18 NationellU18, else the name as-is, e.g. HockeyTvåan); season is the selected option of the page's season <select>. A league with no sub-divisions (SHL) is written as a single tournament. E.g. discover-leagues 19937 --writenational-u18-2025. Some regional pages expose no season picker, so --write reports "couldn't determine the season" rather than guess.

The catalog is validated before every write, so a scan can't corrupt it. Note the season/league conflation: a scaffold is the current season — for a new season of a newly-found league, run discover-season on it afterwards.

Club grouping (config/clubs.json)

A second config you can edit. It's a whitelist of multi-team clubs, used to group player/goalie stats by club: a club often fields several teams (e.g. Rögle BK Grön / Röd / Vit), and without this they'd be three separate rows for the same player.

["Rögle BK", "IF Malmö Redhawks", "Helsingborg HC Ungdom"]

If a team name contains a listed string, that club becomes the grouping key; otherwise the team stands on its own. swehockey exposes no authoritative club id, so this is a deliberate whitelist rather than a name-suffix guess (which could merge the wrong teams) — add new multi-team clubs here as they appear. It's only consulted by playerstat/goaliestat; the rest of the tool ignores it.

How player & goalie stats work

playerstat/goaliestat read the complete PlayersByTeam rosters and aggregate (swehockey's own leader pages cap at the top 25, so they can't be summed across a league — different point cutoffs each tournament would undercount). They sum every counting stat across the league's tournaments and recompute the rate columns (AVG = ΣTP/ΣGP, GAA = ΣGA·60/Σmin, SVS% = ΣSVS/ΣSOG, W% = ΣW/(ΣW+ΣL)). On a single tournament it gives the full, uncapped table.

Pass --team <name> (CLI) or the team argument (MCP) to keep only one club's rows — case-insensitive partial match on the club name (rögle, Rögle BK). Rank is assigned over the whole field first, so a filtered view still shows each player's league-wide standing (the RK/rank column keeps gaps). Each row carries an explicit rank in JSON.

Players are keyed by name + club, so a player's teams within one club collapse into a single row (Rögle BK Grön/Röd/Vit → Rögle BK) — via the whitelist in config/clubs.json. swehockey's trailing eligibility markers (Name**, a dispensation/affiliated flag it adds in some tournaments but not others) are stripped before keying too, so a player isn't split into marked/unmarked entries. Goalies are sorted by SVS%, so small samples can top the list; the GPI/MIP columns show how much they actually played.

Team "id": swehockey exposes no numeric team id on its league pages — a team's identifier is a short code (MIF, RBK G), read off the team scoring/goalkeeping page. It's a per-tournament display abbreviation, so it can vary between tournaments (RBK Grön vs RBK G) and is occasionally just the full name. Shown as when it can't be matched.

MCP tools

The server calls the same in-process query layer (src/queries.ts) the CLI uses — no subprocess — and returns each query's JSON. Tools: list_leagues, get_league, list_tournaments, list_districts, list_cups, get_teams, get_schedule, get_standings, get_player_stats, get_goalie_stats (the last two take an optional team filter). The discover-* commands are intentionally not exposed — they're catalog maintenance, not queries.

Project layout

File

Purpose

bin/swehockey

CLI entry point (#!/usr/bin/env bun)

bin/swehockey-mcp

MCP server entry point (read tools for an agent)

src/cli.ts

Argument parsing + command dispatch + output formatting

src/mcp.ts

MCP server (calls the query layer in-process)

src/queries.ts

Shared fetch + dispatch + JSON shaping (CLI & MCP)

src/catalog.ts

Leagues → tournaments catalog + resolvers

src/client.ts

HTTP client (HTTPS, retries on flaky/partial fetches)

src/html.ts

Generic table.tblContent parsing helpers

src/standings.ts

Standings view → typed rows (rank, W/T/L, GF:GA, points)

src/schedule.ts

Schedule view → typed games (date, teams, result, venue)

src/playersByTeam.ts

Complete rosters → aggregated player/goalie stats

src/clubs.ts

Club whitelist + team→club mapping (config/clubs.json)

src/discover.ts

Find not-yet-cataloged seasons / leagues (region scan)

src/teams.ts

Teams in a tournament, joined with their swehockey code

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/troelskn/swehockey'

If you have feedback or need assistance with the MCP directory API, please join our Discord server