hiski-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Juuso Lehtinen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # hiski-mcp
2
+
3
+ Tämän laajennuksen avulla Claude (tai muu tekoälyavustaja) osaa hakea tietoja **HisKi-tietokannasta**: seurakuntien historiakirjoista kastetuista, vihityistä, haudatuista sekä sisään- ja poismuuttaneista. Voit kysyä tavallisella kielellä, ja Claude tekee haut puolestasi ja kokoaa tulokset.
4
+
5
+ Esimerkkejä kysymyksistä:
6
+
7
+ - _Hae Orimattilan kastetuista Johan Hanssonin ja Ottilia Andersdotterin lapset._
8
+ - _Hae Orimattilan haudatuista Johan Hanssonit vuosilta 1850–1860 ja kerro heidän kuolinsyynsä._
9
+ - _Mitkä ovat Artjärven naapuriseurakunnat, ja miltä vuosilta Hiskissä on niiden vihityt?_
10
+ - _Etsi Johan Hanssonia kaikista Orimattilan ja Artjärven kirjoista vuosilta 1850–1855._
11
+
12
+ > Tämä on harrastajan tekemä epävirallinen työkalu. Se **ei ole Suomen Sukututkimusseuran** tekemä tai ylläpitämä. Tiedot haetaan seuran julkisesta HisKi-palvelusta osoitteesta [hiski.genealogia.fi](https://hiski.genealogia.fi/hiski?fi).
13
+
14
+ ## Asennus Claude Desktopiin
15
+
16
+ Tarvitset [Claude Desktop](https://claude.ai/download) -sovelluksen. Muuta ei tarvitse asentaa.
17
+
18
+ 1. **Lataa laajennus:** [hiski-mcp.mcpb](https://github.com/jleh/hiski-mcp/releases/latest/download/hiski-mcp.mcpb)
19
+ 2. **Avaa ladattu tiedosto** tuplaklikkaamalla. Claude Desktop avaa asennusikkunan. Paina **Install** (Asenna).
20
+ - Jos tuplaklikkaus ei toimi: avaa Claude Desktopissa **Settings → Extensions** (Asetukset → Laajennukset) ja vedä ladattu tiedosto ikkunaan.
21
+ 3. **Kokeile:** aloita uusi keskustelu ja kysy esimerkiksi _"Hae Orimattilan kastetuista Johan Hanssonin ja Ottilia Andersdotterin lapset."_ Claude saattaa kysyä luvan käyttää laajennusta; salli se.
22
+
23
+ ### Jos jokin ei toimi
24
+
25
+ - **Laajennus ei näy tai Claude ei käytä sitä:** tarkista Settings → Extensions -näkymästä, että _HisKi – sukututkimushaut_ on päällä. Käynnistä Claude Desktop tarvittaessa uudelleen.
26
+ - **"Hiski ei vastannut" tai "yritä hetken kuluttua":** HisKi-palvelu voi olla hetkellisesti ruuhkainen. Yritä hetken päästä uudelleen.
27
+ - **Tuloksia ei löydy:** kokeile väljempää hakua, esimerkiksi pelkkää etunimeä ja isän nimeä tai laajempaa vuosiväliä. Katso myös vinkit alta.
28
+ - **Päivitys:** lataa uusin versio samasta linkistä ja asenna se samalla tavalla.
29
+
30
+ ## Tietosuoja (Privacy Policy)
31
+
32
+ Laajennus ei kerää eikä tallenna tietoja, eikä siinä ole analytiikkaa tai telemetriaa. Hakuehdot, kuten nimet, seurakunnat ja vuodet, lähetetään omalta koneeltasi suoraan HisKi-palveluun (hiski.genealogia.fi), joka näkee myös IP-osoitteesi. Muualle tietoja ei lähetetä. Koko tietosuojaseloste suomeksi ja englanniksi: [PRIVACY.md](PRIVACY.md).
33
+
34
+ ---
35
+
36
+ ## Kehittäjille
37
+
38
+ ### Käyttö muissa MCP-asiakkaissa
39
+
40
+ Palvelin on npm-paketti `hiski-mcp`, joka toimii stdion yli. Se vaatii Node.js:n (≥ 22).
41
+
42
+ **Claude Code:**
43
+
44
+ ```bash
45
+ claude mcp add hiski -- npx -y hiski-mcp
46
+ ```
47
+
48
+ **Codex CLI:**
49
+
50
+ ```bash
51
+ codex mcp add hiski -- npx -y hiski-mcp
52
+ ```
53
+
54
+ tai `~/.codex/config.toml`:
55
+
56
+ ```toml
57
+ [mcp_servers.hiski]
58
+ command = "npx"
59
+ args = ["-y", "hiski-mcp"]
60
+ ```
61
+
62
+ **Muut asiakkaat** (esim. Cursor, VS Code, Claude Desktop ilman laajennusta):
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "hiski": { "command": "npx", "args": ["-y", "hiski-mcp"] }
68
+ }
69
+ }
70
+ ```
71
+
72
+ Työkaluja voi kokeilla käsin MCP Inspectorilla: `npx @modelcontextprotocol/inspector npx -y hiski-mcp`.
73
+
74
+ ### Työkalut
75
+
76
+ | Työkalu | Tehtävä |
77
+ | -------------------- | --------------------------------------------------------------------------------------------------------- |
78
+ | `etsi_seurakunta` | Seurakunnat nimellä, ruotsinkielisellä nimellä tai koodilla, indeksoidut vuodet kirjoittain (ei verkkoa). |
79
+ | `seurakunnan_tiedot` | Kirjat ja vuodet, naapuriseurakunnat koodeineen, maakunta, historia, kylät ja vanhat nimet. |
80
+ | `hae_kastetut` | Kastettujen haku: lapsi, isä, äiti, kylä/talo, vuodet. |
81
+ | `hae_vihityt` | Vihittyjen haku: mies ja vaimo asuinpaikkoineen. |
82
+ | `hae_haudatut` | Haudattujen haku: vainaja, kuolinsyy, ikä, syntymäaika ja omainen. |
83
+ | `hae_muuttaneet` | Sisään- tai poismuuttaneiden haku. |
84
+ | `hae_kaikki` | Henkilön haku kaikista kirjoista kerralla. |
85
+ | `hae_tapahtuma` | Yhden tapahtuman tarkat tiedot: eritellyt nimet, kommentit, digiarkiston linkki ja pysyvä linkki. |
86
+
87
+ Kaikki työkalut ovat vain lukevia. Hakuvinkit ovat palvelimen `instructions`-kentässä (`src/ohjeet.ts`).
88
+
89
+ ### Kehitys
90
+
91
+ ```bash
92
+ npm ci
93
+ npm run check # tyyppitarkistus, ESLint, Prettier ja testit
94
+ npm run build # dist/ (npm-paketti)
95
+ npm run paketoi # hiski-mcp.mcpb (Claude Desktop)
96
+ npm run testaa-paketti # purkaa paketin tyhjään kansioon ja kokeilee sitä
97
+ npm run test:live # testit oikeaa Hiskiä vasten (omalla koneella, ks. alla)
98
+ ```
99
+
100
+ - Parserit testataan Hiskistä tallennettuja sivuja vasten (`test/fixtures/`, luettelo `scripts/fixturet.ts`). Ne päivitetään komennolla `npm run tallenna-fixturet`.
101
+ - Live-testit ajetaan omalla koneella ennen jokaista julkaisua. Hiskin Cloudflare-suojaus estää GitHub Actionsin kaltaisten konesalien osoitteet, joten niitä ei voi ajaa CI:ssä.
102
+ - Seurakuntien nimet ja vuodet ovat tiedostossa `src/data/seurakunnat.json`. Ne päivitetään komennolla `npm run paivita-seurakunnat`, koska Hiskiin lisätään vuosia jatkuvasti.
103
+ - Hiskin erikoisuudet (latin-1-lomakkeet, satunnaiset virhesivut, otsikoton lohko ym.) ja rakenne on kuvattu tiedostossa [docs/toteutussuunnitelma.md](docs/toteutussuunnitelma.md). Julkaisuohje on tiedostossa [docs/julkaisu.md](docs/julkaisu.md).
104
+
105
+ Palaute ja virheilmoitukset: [GitHub Issues](https://github.com/jleh/hiski-mcp/issues).
106
+
107
+ ## Lisenssi
108
+
109
+ [MIT](LICENSE)
110
+
111
+ ---
112
+
113
+ **In English:** an unofficial [MCP](https://modelcontextprotocol.io) server for searching the [HisKi](https://hiski.genealogia.fi/hiski?fi) database of Finnish parish records (baptisms, marriages, burials and migrations). Install `hiski-mcp.mcpb` in Claude Desktop, or run `npx -y hiski-mcp` in any MCP client. Not affiliated with the Genealogical Society of Finland. It collects no data; searches go straight from your computer to HisKi. See the [privacy policy](PRIVACY.md#privacy-policy).
package/dist/client.js ADDED
@@ -0,0 +1,152 @@
1
+ import { decodeLatin1 } from "./encoding.js";
2
+ import { HISKI_URL, parseHakutulos } from "./hakutulos.js";
3
+ import { koodaaLatin1, LomakeVirhe, rakennaHakulomake } from "./lomake.js";
4
+ import { parseSeurakuntasivu, parseSeurakuntatiedot, } from "./seurakuntasivu.js";
5
+ import { parseTapahtumasivu } from "./tapahtumasivu.js";
6
+ import { SERVER_NAME, SERVER_VERSION } from "./version.js";
7
+ /** Hiski could not be reached or refused the request; the message says what to do. */
8
+ export class HiskiVirhe extends Error {
9
+ constructor(viesti, options) {
10
+ super(viesti, options);
11
+ this.name = "HiskiVirhe";
12
+ }
13
+ }
14
+ const USER_AGENT = `${SERVER_NAME}/${SERVER_VERSION} (+https://github.com/jleh/hiski-mcp)`;
15
+ /** Talks to Hiski using web-standard APIs only, so it also runs outside Node. */
16
+ export class HiskiClient {
17
+ #fetch;
18
+ #odota;
19
+ #yrityksia;
20
+ #uudelleenyritysMs;
21
+ #aikakatkaisuMs;
22
+ constructor(asetukset = {}) {
23
+ // Workers and browsers require fetch to be called without another `this`.
24
+ this.#fetch = asetukset.fetch ?? ((osoite, init) => fetch(osoite, init));
25
+ this.#odota = asetukset.odota ?? odota;
26
+ this.#yrityksia = asetukset.yrityksia ?? 5;
27
+ this.#uudelleenyritysMs = asetukset.uudelleenyritysMs ?? 1500;
28
+ this.#aikakatkaisuMs = asetukset.aikakatkaisuMs ?? 30_000;
29
+ }
30
+ /** Runs a search. Throws LomakeVirhe for an invalid request before contacting Hiski. */
31
+ async haku(pyynto, signal) {
32
+ const body = koodaaLatin1(rakennaHakulomake(pyynto));
33
+ const html = await this.#lataa(HISKI_URL, {
34
+ method: "POST",
35
+ headers: { "content-type": "application/x-www-form-urlencoded" },
36
+ body,
37
+ }, signal);
38
+ return parseHakutulos(html);
39
+ }
40
+ /**
41
+ * Fetches one event's page. Hiski answers about half of these requests with
42
+ * an error page, so that page is retried a few times before giving up.
43
+ */
44
+ async tapahtuma(koodi, kirja, numero, signal) {
45
+ if (!/^\d{4}$/.test(koodi))
46
+ throw new LomakeVirhe(`Seurakuntakoodi "${koodi}" ei kelpaa.`);
47
+ if (!Number.isSafeInteger(numero) || numero < 0) {
48
+ throw new LomakeVirhe(`Tapahtuman numero ${numero} ei kelpaa.`);
49
+ }
50
+ const url = `${HISKI_URL}?fi+${koodi}+${kirja}+${numero}`;
51
+ for (let yritys = 1;; yritys++) {
52
+ const sivu = parseTapahtumasivu(await this.#lataa(url, {}, signal));
53
+ if (sivu.tila === "ok")
54
+ return { tila: "ok", tapahtuma: { ...sivu.tapahtuma, numero, url } };
55
+ if (sivu.tila === "ei_loytynyt" || yritys >= this.#yrityksia)
56
+ return { tila: sivu.tila };
57
+ await this.#odota(this.#uudelleenyritysMs, signal);
58
+ }
59
+ }
60
+ /**
61
+ * Fetches a parish's page and then its info page. The info page is only
62
+ * background, so the answer is given without it if it cannot be fetched.
63
+ */
64
+ async seurakunta(koodi, signal) {
65
+ if (!/^\d{4}$/.test(koodi))
66
+ throw new LomakeVirhe(`Seurakuntakoodi "${koodi}" ei kelpaa.`);
67
+ const sivu = parseSeurakuntasivu(await this.#lataa(`${HISKI_URL}?fi+${koodi}`, {}, signal));
68
+ if (sivu.tila !== "ok")
69
+ return sivu;
70
+ if (!sivu.lisatiedot_url)
71
+ return { tila: "ok", sivu };
72
+ let tiedot;
73
+ try {
74
+ tiedot = parseSeurakuntatiedot(await this.#lataa(sivu.lisatiedot_url, {}, signal));
75
+ }
76
+ catch {
77
+ // A cancelled request stays cancelled; otherwise only background is missing.
78
+ signal?.throwIfAborted();
79
+ }
80
+ return { tila: "ok", sivu, ...(tiedot?.tila === "ok" && { tiedot }) };
81
+ }
82
+ /**
83
+ * Fetches a page and decodes it. Failures become HiskiVirhe with a message
84
+ * saying what to do; a cancellation by the caller is passed through as is.
85
+ */
86
+ async #lataa(url, init, signal) {
87
+ const headers = new Headers(init.headers);
88
+ headers.set("user-agent", USER_AGENT);
89
+ const aikaraja = AbortSignal.timeout(this.#aikakatkaisuMs);
90
+ const yhdistetty = signal ? AbortSignal.any([signal, aikaraja]) : aikaraja;
91
+ try {
92
+ const vastaus = await this.#fetch(url, { ...init, headers, signal: yhdistetty });
93
+ if (!vastaus.ok)
94
+ throw await vastausvirhe(vastaus);
95
+ return dekoodaa(vastaus.headers.get("content-type"), await vastaus.arrayBuffer());
96
+ }
97
+ catch (virhe) {
98
+ signal?.throwIfAborted();
99
+ if (virhe instanceof HiskiVirhe)
100
+ throw virhe;
101
+ if (aikaraja.aborted) {
102
+ throw new HiskiVirhe("Hiski ei vastannut ajoissa. Yritä hetken kuluttua uudelleen.", {
103
+ cause: virhe,
104
+ });
105
+ }
106
+ throw new HiskiVirhe("Hiskiin ei saatu yhteyttä. Tarkista verkkoyhteys ja yritä uudelleen.", {
107
+ cause: virhe,
108
+ });
109
+ }
110
+ }
111
+ }
112
+ /** Waits unless the signal aborts first. */
113
+ function odota(ms, signal) {
114
+ return new Promise((ratkaise, hylkaa) => {
115
+ signal?.throwIfAborted();
116
+ const ajastin = setTimeout(() => {
117
+ signal?.removeEventListener("abort", keskeyta);
118
+ ratkaise();
119
+ }, ms);
120
+ function keskeyta() {
121
+ clearTimeout(ajastin);
122
+ const syy = signal.reason;
123
+ hylkaa(syy instanceof Error ? syy : new DOMException("Peruttu", "AbortError"));
124
+ }
125
+ signal?.addEventListener("abort", keskeyta, { once: true });
126
+ });
127
+ }
128
+ /** Explains a failed response; Cloudflare's bot check gets its own message. */
129
+ async function vastausvirhe(vastaus) {
130
+ const teksti = await vastaus.text().catch(() => "");
131
+ if (vastaus.headers.has("cf-mitigated") || /Just a moment/i.test(teksti)) {
132
+ return new HiskiVirhe("Hiskin suojaus (Cloudflare) esti pyynnön. Yritä hetken kuluttua uudelleen.");
133
+ }
134
+ return new HiskiVirhe(`Hiski vastasi virheellä (HTTP ${vastaus.status}). Yritä hetken kuluttua uudelleen.`);
135
+ }
136
+ /**
137
+ * Hiski's own pages are ISO-8859-1 without saying so; others declare a charset,
138
+ * possibly quoted. An unknown charset falls back to ISO-8859-1.
139
+ */
140
+ function dekoodaa(sisaltotyyppi, tavut) {
141
+ const merkisto = /charset="?([\w-]+)/i.exec(sisaltotyyppi ?? "")?.[1];
142
+ if (merkisto) {
143
+ try {
144
+ return new TextDecoder(merkisto).decode(tavut);
145
+ }
146
+ catch {
147
+ // Not a charset TextDecoder knows.
148
+ }
149
+ }
150
+ return decodeLatin1(new Uint8Array(tavut));
151
+ }
152
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}