From 3574c8133f4d3337b0110e4eb08e47c9b56f9bff Mon Sep 17 00:00:00 2001 From: Rihards Gailums Date: Sun, 11 Oct 2026 11:52:49 +0000 Subject: [PATCH] =?UTF-8?q?Architecture-as-Code=200.1.0:=20tikai=20dati=20?= =?UTF-8?q?un=20defin=C4=ABcijas;=20jomu=20katalogs=20(21=20VARAM=20joma),?= =?UTF-8?q?=20JSON=20sh=C4=93ma,=20B09=20datu=20l=C4=ABgums,=20p=C4=81rbau?= =?UTF-8?q?d=C4=ABt=C4=81js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - kultūras un valodu tehnoloģiju joma → domains/kultura-valoda, avota PDF → sources/kultura-valoda - izņemts KISC MCP servera kods, Docker un servera identitātes datnes (paliek KISC projekta repozitorijā) - domenas.yaml, schemas/arhitektura-0.1.schema.json, contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml, tools/validate.py Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016679RwmHsuTFfxt26wP6rk --- CHANGELOG.md | 8 + .../LICENSE => LICENSE | 0 README.md | 42 + ...digitalas-parvaldes-arhitekturas.odcs.yaml | 100 + .../diagrams/TODO-01-funkcijas.mmd | 0 .../diagrams/TODO-02-pakalpojumi.mmd | 0 .../diagrams/TODO-03-informacijas-resursi.mmd | 0 .../TODO-04-informacijas-sistemas.mmd | 0 .../diagrams/TODO-05-integracijas.mmd | 0 .../kultura-valoda}/docs/REGENERATION.md | 0 .../kultura-valoda}/docs/TRACEABILITY.md | 0 .../kultura-valoda/manifest.yaml | 0 .../registers/00-meta/abbreviations.yaml | 0 .../registers/00-meta/domain.yaml | 0 .../registers/00-meta/legal-acts.yaml | 0 .../registers/00-meta/principles-map.yaml | 0 .../registers/00-meta/principles.yaml | 0 .../registers/00-meta/related-documents.yaml | 0 .../registers/00-meta/terms.yaml | 0 .../registers/01-as-is/swot.yaml | 0 .../registers/02-goals/goal-m1.yaml | 0 .../registers/02-goals/goal-m2.yaml | 0 .../registers/02-goals/goal-m3.yaml | 0 .../registers/02-goals/goal-m4.yaml | 0 .../registers/02-goals/goal-m5.yaml | 0 .../registers/02-goals/goal-m6.yaml | 0 .../03-organizations/organizations.yaml | 0 .../registers/04-functions/functions.yaml | 0 .../05-services/kultura-services.yaml | 0 .../05-services/valoda-services.yaml | 0 .../kultura-info-resources.yaml | 0 .../valoda-info-resources.yaml | 0 .../registers/07-systems/kultura-systems.yaml | 0 .../registers/07-systems/valoda-systems.yaml | 0 .../registers/08-roadmap/interactions.yaml | 0 .../registers/08-roadmap/roadmap.yaml | 0 .../registers/09-risks/risks.yaml | 0 .../10-catalog/as-is-components.yaml | 0 .../registers/99-relations/edges.yaml | 0 .../KISC-merkarhitektura-apraksts-020.md | 0 .../kultura-valoda/views/01-ievads.md | 0 .../02-esosas-arhitekturas-novertejums.md | 0 .../views/03-merki-un-principi.md | 0 .../views/04-merk-arhitektura.md | 0 .../kultura-valoda/views/05-cela-karte.md | 0 .../views/06-pielikums-komponentu-katalogs.md | 0 domenas.yaml | 240 ++ .../.env.example | 37 - .../.github/placeholder.git | 1 - .../.github/workflows/qa.yml | 30 - .../.gitignore | 6 - ikt-arh-kultura-valodu-tehnologijas/MCP.md | 560 ----- ikt-arh-kultura-valodu-tehnologijas/README.md | 409 ---- .../docker-compose.yml | 13 - .../docs/mcp_best_practices_and_rationale.md | 266 --- .../domains/placeholder.git | 1 - .../mcp/Dockerfile | 19 - .../mcp/package-lock.json | 2119 ----------------- .../mcp/package.json | 27 - .../mcp/src/generateDoc.ts | 285 --- .../mcp/src/index.ts | 1730 -------------- .../mcp/src/repo/index.ts | 15 - .../mcp/src/repo/load.ts | 195 -- .../mcp/src/repo/types.ts | 44 - .../mcp/src/resources/get_resource.ts | 43 - .../mcp/src/resources/list_resources.ts | 8 - .../mcp/src/tools/get_entity.ts | 11 - .../mcp/src/tools/list_relations.ts | 16 - .../mcp/src/tools/search.ts | 29 - .../mcp/src/tools/subgraph.ts | 42 - .../mcp/tsconfig.json | 15 - .../mcpf/README.md | 506 ---- .../mcpf/scripts/validate-endpoints.sh | 138 -- .../mcpf/veritrust/README-VERITRUST.md | 247 -- .../mcpf/veritrust/install-credential.sh | 206 -- .../mcpf/veritrust/mcp-server-request.json | 66 - .../wellknown/credentials/mcp-server.json | 62 - .../mcpf/wellknown/did.json | 33 - .../mcpf/wellknown/jwks.json | 12 - .../mcpf/wellknown/mcp-trust-registry.json | 18 - .../mcpf/wellknown/mcp/manifest.json | 135 -- .../mcpf/wellknown/security.txt | 6 - .../releases/placeholder.git | 1 - .../scripts/MCPF_INITIALIZE_RESPONSE.md | 192 -- .../scripts/analyze_mcpf_response.py | 179 -- .../scripts/test-mcpf-init.sh | 74 - .../tools/placeholder.git | 1 - .../tools/qa/check_generated_doc_headings.sh | 29 - .../tools/qa/check_nonempty_registers.sh | 23 - .../tools/qa/check_stakeholder_count.sh | 21 - .../tools/qa/structure-checklist.md | 10 - schemas/arhitektura-0.1.schema.json | 166 ++ ...uras_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf | Bin ...ķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf | Bin tools/validate.py | 112 + 95 files changed, 668 insertions(+), 7880 deletions(-) create mode 100644 CHANGELOG.md rename ikt-arh-kultura-valodu-tehnologijas/LICENSE => LICENSE (100%) create mode 100644 README.md create mode 100644 contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/diagrams/TODO-01-funkcijas.mmd (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/diagrams/TODO-02-pakalpojumi.mmd (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/diagrams/TODO-03-informacijas-resursi.mmd (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/diagrams/TODO-04-informacijas-sistemas.mmd (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/diagrams/TODO-05-integracijas.mmd (100%) rename {ikt-arh-kultura-valodu-tehnologijas => domains/kultura-valoda}/docs/REGENERATION.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas => domains/kultura-valoda}/docs/TRACEABILITY.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/manifest.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/abbreviations.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/domain.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/legal-acts.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/principles-map.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/principles.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/related-documents.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/00-meta/terms.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/01-as-is/swot.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m1.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m2.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m3.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m4.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m5.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/02-goals/goal-m6.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/03-organizations/organizations.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/04-functions/functions.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/05-services/kultura-services.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/05-services/valoda-services.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/06-information-resources/kultura-info-resources.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/06-information-resources/valoda-info-resources.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/07-systems/kultura-systems.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/07-systems/valoda-systems.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/08-roadmap/interactions.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/08-roadmap/roadmap.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/09-risks/risks.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/10-catalog/as-is-components.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/registers/99-relations/edges.yaml (100%) rename {ikt-arh-kultura-valodu-tehnologijas => domains/kultura-valoda}/releases/KISC-merkarhitektura-apraksts-020.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/01-ievads.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/02-esosas-arhitekturas-novertejums.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/03-merki-un-principi.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/04-merk-arhitektura.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/05-cela-karte.md (100%) rename {ikt-arh-kultura-valodu-tehnologijas/domains => domains}/kultura-valoda/views/06-pielikums-komponentu-katalogs.md (100%) create mode 100644 domenas.yaml delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/.env.example delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/.github/placeholder.git delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/.github/workflows/qa.yml delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/.gitignore delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/MCP.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/README.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/docker-compose.yml delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/docs/mcp_best_practices_and_rationale.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/domains/placeholder.git delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/Dockerfile delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/package-lock.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/package.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/generateDoc.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/index.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/index.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/load.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/types.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/get_resource.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/list_resources.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/get_entity.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/list_relations.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/search.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/subgraph.ts delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcp/tsconfig.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/README.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/scripts/validate-endpoints.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/README-VERITRUST.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/install-credential.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/mcp-server-request.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/credentials/mcp-server.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/did.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/jwks.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp-trust-registry.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp/manifest.json delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/security.txt delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/releases/placeholder.git delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/scripts/MCPF_INITIALIZE_RESPONSE.md delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/scripts/analyze_mcpf_response.py delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/scripts/test-mcpf-init.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/tools/placeholder.git delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_generated_doc_headings.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_nonempty_registers.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_stakeholder_count.sh delete mode 100644 ikt-arh-kultura-valodu-tehnologijas/tools/qa/structure-checklist.md create mode 100644 schemas/arhitektura-0.1.schema.json rename {ikt-arh-kultura-valodu-tehnologijas/docs => sources/kultura-valoda}/ARH_kulturas_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf (100%) rename {ikt-arh-kultura-valodu-tehnologijas/docs => sources/kultura-valoda}/MērķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf (100%) create mode 100644 tools/validate.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8765473 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Izmaiņu žurnāls + +## 0.1.0 — 2026-10-11 + +- Repozitorijs pārdēvēts: Architekture-as-Code → Architecture-as-Code. +- Repozitorijā tikai dati un definīcijas: kultūras un valodu tehnoloģiju joma pārcelta uz `domains/kultura-valoda`, avota PDF uz `sources/kultura-valoda/`. KISC MCP servera kods, Docker datnes un servera identitātes datnes (DID, JWKS, akreditācijas pieprasījums) izņemtas — tās paliek KISC projekta repozitorijā. +- `domenas.yaml`: visas 21 VARAM arhitektūras jomas (6 horizontālās, 1 apakšjoma, 14 nozaru) ar izstrādes vadītāju iestādi, VARAM apstiprinājumu un dokumentu saitēm. +- Shēma `schemas/arhitektura-0.1.schema.json` (JSON Schema 2020-12), datu līgums `contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml`, pārbaudītājs `tools/validate.py`. diff --git a/ikt-arh-kultura-valodu-tehnologijas/LICENSE b/LICENSE similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/LICENSE rename to LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..0927b08 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Digitālās pārvaldes arhitektūras kā kods (Architecture-as-Code) + +VARAM digitālās pārvaldes arhitektūras horizontālās un nozaru jomas kā dati: viens jomu katalogs un katrai jomai — funkcijas, pakalpojumi, informācijas resursi, informācijas sistēmas, organizācijas, mērķi un saites starp tiem, visas pēc vienas shēmas. + +Autors: PPP Asociācija (PPPA), Valsts Pirmkods (VPK). Shēmas versija 0.1, melnraksts. + +> **Koncepcijas demonstrācija.** Šīs datnes nav oficiāls izdevums. Metodiski saistošs ir VARAM publicētais arhitektūras apraksts ([horizontālās jomas](https://www.varam.gov.lv/lv/horizontalo-jomu-arhitekturas), [nozaru jomas](https://www.varam.gov.lv/lv/nozaru-jomu-arhitekturas)). + +## Stāvoklis + +21 joma katalogā (6 horizontālās, 1 apakšjoma, 14 nozaru un starpnozaru). Kodā pārveidota 1: **Kultūra un valodu tehnoloģijas** (`domains/kultura-valoda`). Pārējās tiks pārveidotas pa vienai; katras jomas stāvoklis ir `domenas.yaml` laukā `as_code.status`. + +## Saturs + +| Datne / mape | Saturs | +|---|---| +| `domenas.yaml` | Visu jomu katalogs: nosaukums, veids, izstrādes vadītāja iestāde (ar VPK ID), VARAM apstiprinājums, dokumentu saites, stāvoklis kodā | +| `domains//manifest.yaml` | Jomas apraksts un tās reģistru atrašanās vieta | +| `domains//registers/` | Reģistri (YAML): organizācijas, funkcijas, pakalpojumi, informācijas resursi, sistēmas, mērķi, ceļa karte, riski, saites | +| `domains//views/` | Apraksta nodaļas (Markdown), ģenerētas no reģistriem | +| `sources//` | Avota dokumenti, no kuriem joma pārveidota | +| `schemas/arhitektura-0.1.schema.json` | Kopīga JSON shēma visām jomām; `x-roles` nosaka, pret kuru tipu pārbauda katru reģistru | +| `contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml` | ODCS v3 datu līgums (VDZP datu kopa B09) | +| `tools/validate.py` | Tās pašas pārbaudes, ko veic VDZP savienotājs | + +## Identifikatori + +Elementa identifikators jomā ir lokāls (`func.01`, `svc.k.03`, `org.kisc`). Pilnais identifikators ir `:`, piemēram `kultura-valoda:func.01`, tāpēc jomas var izmantot vienādus lokālos identifikatorus. Organizācijām var norādīt `vpk_id` (Valsts kancelejas organizāciju reģistrs, VDZP B01). + +## Jaunas jomas pievienošana + +1. `domenas.yaml`: jomai `as_code.status: in_progress`, vēlāk `transformed` un `path: domains/`. +2. `domains//manifest.yaml` ar `id: domain.` un reģistru sarakstu (lomas kā `domains/kultura-valoda/manifest.yaml`). +3. Reģistri `domains//registers/`; avota dokuments `sources//`. +4. `python3 tools/validate.py` — visām pārbaudēm jābūt OK. +5. Commit. VDZP nākamajā rītā (06:30 Rīgas laikā) ielādē jauno jomu automātiski. + +## Saistītās datu kopas + +- VDZP — Valsts datu un zināšanu platforma (demonstrācija): , datu kopa B09. +- Valdības deklarācija, organizāciju reģistrs (VPK ID): [Valdibas-Deklaracija-as-Code](https://processgit.org/Valsts-Pirmkods/Valdibas-Deklaracija-as-Code). +- Valsts budžets: [Budget-as-Code](https://processgit.org/Valsts-Pirmkods/Budget-as-Code). diff --git a/contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml b/contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml new file mode 100644 index 0000000..7f53e2e --- /dev/null +++ b/contracts/B09-digitalas-parvaldes-arhitekturas.odcs.yaml @@ -0,0 +1,100 @@ +# ODCS v3 datu līgums: B09 Digitālās pārvaldes arhitektūras (arhitektūra kā kods) +# Shēma: schemas/arhitektura-0.1.schema.json (JSON Schema 2020-12). Šī līguma schema daļa apraksta to, ko VDZP ielādē; +# pilnā struktūra un pieļaujamās vērtības ir JSON shēmā. +apiVersion: v3.0.2 +kind: DataContract +id: urn:pppa:cac:contract:B09 +name: Digitālās pārvaldes arhitektūras (arhitektūra kā kods) +version: 0.1.0 +status: active +domain: Valsts kā kods (Country as Code) +dataProduct: Digitālās pārvaldes arhitektūras +tenant: PPP Asociācija (PPPA) +description: + purpose: >- + VARAM digitālās pārvaldes arhitektūras horizontālās un nozaru jomas kā dati: viens jomu katalogs (domenas.yaml) ar visām jomām, + to izstrādes vadītāju iestādi, VARAM apstiprinājuma statusu un dokumentu saitēm, un katrai jomai, kas pārveidota kodā, — + funkcijas, pakalpojumi, informācijas resursi, informācijas sistēmas, organizācijas, mērķi un saites starp tiem. + usage: Mašīnlasāma datu apmaiņa; ielāde VDZP katru dienu; AI aģentu vaicājumi caur MCP. + limitations: >- + Koncepcijas demonstrācija, nav oficiāls izdevums. Juridiski un metodiski saistošs ir VARAM publicētais arhitektūras apraksts. + Kodā pārveidotas ne visas jomas — stāvoklis katrai jomai ir domenas.yaml (as_code.status). Identifikatori jomā ir lokāli; + pilnais identifikators ir :. +servers: +- server: processgit + type: custom + format: yaml + description: ProcessGit repozitorijs (Gitea API v1) + customProperties: + - {property: url, value: "https://processgit.org/Valsts-Pirmkods/Architecture-as-Code"} + - {property: ref, value: main} + - {property: catalogue, value: domenas.yaml} + - {property: path, value: "domains/*/manifest.yaml (un manifestā norādītie reģistri)"} + - {property: schema, value: "https://processgit.org/Valsts-Pirmkods/Architecture-as-Code/src/branch/main/schemas/arhitektura-0.1.schema.json"} +schema: +- name: Domain + logicalType: object + physicalType: yaml-object + physicalName: "domenas.yaml#/domains[]" + description: Viena VARAM arhitektūras joma (horizontālā, nozaru vai apakšjoma) un tās stāvoklis kodā. + customProperties: [{property: lakehouseTable, value: cac_bronze.arch_domain}, {property: jsonSchema, value: "#/$defs/CatalogueDomain"}] + properties: + - {name: slug, logicalType: string, required: true, primaryKey: true, unique: true, logicalTypeOptions: {pattern: "^[a-z0-9]+(-[a-z0-9]+)*$"}} + - {name: title, logicalType: string, required: true} + - {name: kind, logicalType: string, required: true, quality: [{type: library, rule: validValues, validValues: [horizontal, sectoral, subdomain]}]} + - {name: parent, logicalType: string, required: false, description: Apakšjomai — jomas slug} + - {name: lead_institution, logicalType: string, required: true, description: "Iestāde, kas vada arhitektūras apraksta izstrādi"} + - {name: lead_vpk_id, logicalType: string, required: false, description: Iestādes VPK ID (B01), logicalTypeOptions: {pattern: "^[0-9]{2}-[0-9]{4}$"}} + - {name: varam_status, logicalType: string, required: true, quality: [{type: library, rule: validValues, validValues: [approved, in_review, in_development, not_started]}]} + - {name: approved, logicalType: date, required: false} + - {name: description_url, logicalType: string, required: false, description: VARAM publicētais arhitektūras apraksts} + - {name: as_code.status, logicalType: string, required: true, quality: [{type: library, rule: validValues, validValues: [transformed, in_progress, not_started]}]} + - {name: as_code.path, logicalType: string, required: false, description: "Jomas mape domains/"} +- name: Element + logicalType: object + physicalType: yaml-object + physicalName: "domains//registers/**#/items[]" + description: Viens arhitektūras elements jomā — organizācija, funkcija, pakalpojums, informācijas resurss, informācijas sistēma, mērķis, ceļa kartes pasākums, mijiedarbība vai risks. + customProperties: [{property: lakehouseTable, value: cac_bronze.arch_register}, {property: jsonSchema, value: "#/$defs/Organization, Function, Component, Goal, Item"}] + properties: + - {name: uid, logicalType: string, required: true, primaryKey: true, unique: true, description: ":, piemēram kultura-valoda:func.01 (veido savienotājs)"} + - {name: id, logicalType: string, required: true, description: Lokālais identifikators jomā, logicalTypeOptions: {pattern: "^[a-z][a-z_]*(\\.[A-Za-z0-9_-]+)+$"}} + - {name: entity_type, logicalType: string, required: true, quality: [{type: library, rule: validValues, validValues: [organisation, function, service, information_resource, system, goal, roadmap_item, interaction, risk, as_is_component]}]} + - {name: name, logicalType: string, required: true, description: name vai title} + - {name: subdomain, logicalType: string, required: false} + - {name: status, logicalType: string, required: false, description: status vai change_status} + - {name: vpk_id, logicalType: string, required: false, description: Organizācijai — VPK ID (B01)} + - {name: attributes, logicalType: object, required: false, description: Pārējie elementa lauki, kā autorēti} +- name: Edge + logicalType: object + physicalType: yaml-object + physicalName: "domains//registers/99-relations/edges.yaml#/edges[]" + description: Saite starp diviem vienas jomas elementiem. + customProperties: [{property: lakehouseTable, value: cac_bronze.arch_edge}, {property: jsonSchema, value: "#/$defs/Edge"}] + properties: + - {name: from, logicalType: string, required: true} + - {name: type, logicalType: string, required: true, description: "has_goal, has_function, has_service, has_information_resource, has_system, performed_by …"} + - {name: to, logicalType: string, required: true} +quality: +- {name: schema_valid, type: custom, engine: json-schema, implementation: schemas/arhitektura-0.1.schema.json, + description: "Katalogs, jomu manifesti un reģistri atbilst JSON shēmai (katra datne — savas lomas tipam, x-roles)", dimension: conformity} +- {name: catalogue_valid, type: custom, engine: cac-lakehouse, implementation: "app.cac_processgit:arch_catalogue_valid", + description: "Katalogā katra joma unikāla; katrai kodā pārveidotajai jomai ir mape ar manifestu; katra mape ir katalogā", dimension: consistency} +- {name: ids_unique, type: custom, engine: cac-lakehouse, implementation: "app.cac_processgit:arch_ids_unique", description: Lokālie identifikatori jomā ir unikāli, dimension: uniqueness} +- {name: edges_resolve, type: custom, engine: cac-lakehouse, implementation: "app.cac_processgit:arch_edges_resolve", description: "Katras saites abi gali ir šīs jomas elementi", dimension: consistency} +- {name: organization_has_vpk_id, type: custom, engine: cac-lakehouse, implementation: "app.cac_processgit:arch_org_vpk", + description: "Organizācija sasaistīta ar VPK ID — norādīts reģistrā (vpk_id) vai atrasts pēc nosaukuma", dimension: completeness} +- {name: lead_has_vpk_id, type: custom, engine: cac-lakehouse, implementation: "app.cac_processgit:arch_lead_vpk", + description: "Jomas izstrādes vadītājai iestādei norādīts VPK ID", dimension: completeness} +slaProperties: +- {property: frequency, value: 1, unit: d, element: "pull ingest katru dienu 06:30 Rīgas laikā"} +- {property: retention, value: "visas versijas (git vēsture)"} +team: +- {role: owner, name: "VARAM (digitālās pārvaldes arhitektūras ietvars); jomu arhitektūru turētāji — jomu vadošās iestādes"} +- {role: data steward, name: PPP Asociācija (PPPA)} +support: +- {channel: issues, url: "https://processgit.org/Valsts-Pirmkods/Architecture-as-Code/issues"} +customProperties: +- {property: sourceId, value: B09} +- {property: identifiers, value: "joma ; elements :"} +- {property: validator, value: tools/validate.py} diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-01-funkcijas.mmd b/domains/kultura-valoda/diagrams/TODO-01-funkcijas.mmd similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-01-funkcijas.mmd rename to domains/kultura-valoda/diagrams/TODO-01-funkcijas.mmd diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-02-pakalpojumi.mmd b/domains/kultura-valoda/diagrams/TODO-02-pakalpojumi.mmd similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-02-pakalpojumi.mmd rename to domains/kultura-valoda/diagrams/TODO-02-pakalpojumi.mmd diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-03-informacijas-resursi.mmd b/domains/kultura-valoda/diagrams/TODO-03-informacijas-resursi.mmd similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-03-informacijas-resursi.mmd rename to domains/kultura-valoda/diagrams/TODO-03-informacijas-resursi.mmd diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-04-informacijas-sistemas.mmd b/domains/kultura-valoda/diagrams/TODO-04-informacijas-sistemas.mmd similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-04-informacijas-sistemas.mmd rename to domains/kultura-valoda/diagrams/TODO-04-informacijas-sistemas.mmd diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-05-integracijas.mmd b/domains/kultura-valoda/diagrams/TODO-05-integracijas.mmd similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/diagrams/TODO-05-integracijas.mmd rename to domains/kultura-valoda/diagrams/TODO-05-integracijas.mmd diff --git a/ikt-arh-kultura-valodu-tehnologijas/docs/REGENERATION.md b/domains/kultura-valoda/docs/REGENERATION.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/docs/REGENERATION.md rename to domains/kultura-valoda/docs/REGENERATION.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/docs/TRACEABILITY.md b/domains/kultura-valoda/docs/TRACEABILITY.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/docs/TRACEABILITY.md rename to domains/kultura-valoda/docs/TRACEABILITY.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/manifest.yaml b/domains/kultura-valoda/manifest.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/manifest.yaml rename to domains/kultura-valoda/manifest.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/abbreviations.yaml b/domains/kultura-valoda/registers/00-meta/abbreviations.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/abbreviations.yaml rename to domains/kultura-valoda/registers/00-meta/abbreviations.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/domain.yaml b/domains/kultura-valoda/registers/00-meta/domain.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/domain.yaml rename to domains/kultura-valoda/registers/00-meta/domain.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/legal-acts.yaml b/domains/kultura-valoda/registers/00-meta/legal-acts.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/legal-acts.yaml rename to domains/kultura-valoda/registers/00-meta/legal-acts.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/principles-map.yaml b/domains/kultura-valoda/registers/00-meta/principles-map.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/principles-map.yaml rename to domains/kultura-valoda/registers/00-meta/principles-map.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/principles.yaml b/domains/kultura-valoda/registers/00-meta/principles.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/principles.yaml rename to domains/kultura-valoda/registers/00-meta/principles.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/related-documents.yaml b/domains/kultura-valoda/registers/00-meta/related-documents.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/related-documents.yaml rename to domains/kultura-valoda/registers/00-meta/related-documents.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/terms.yaml b/domains/kultura-valoda/registers/00-meta/terms.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/00-meta/terms.yaml rename to domains/kultura-valoda/registers/00-meta/terms.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/01-as-is/swot.yaml b/domains/kultura-valoda/registers/01-as-is/swot.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/01-as-is/swot.yaml rename to domains/kultura-valoda/registers/01-as-is/swot.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m1.yaml b/domains/kultura-valoda/registers/02-goals/goal-m1.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m1.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m1.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m2.yaml b/domains/kultura-valoda/registers/02-goals/goal-m2.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m2.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m2.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m3.yaml b/domains/kultura-valoda/registers/02-goals/goal-m3.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m3.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m3.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m4.yaml b/domains/kultura-valoda/registers/02-goals/goal-m4.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m4.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m4.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m5.yaml b/domains/kultura-valoda/registers/02-goals/goal-m5.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m5.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m5.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m6.yaml b/domains/kultura-valoda/registers/02-goals/goal-m6.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/02-goals/goal-m6.yaml rename to domains/kultura-valoda/registers/02-goals/goal-m6.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/03-organizations/organizations.yaml b/domains/kultura-valoda/registers/03-organizations/organizations.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/03-organizations/organizations.yaml rename to domains/kultura-valoda/registers/03-organizations/organizations.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/04-functions/functions.yaml b/domains/kultura-valoda/registers/04-functions/functions.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/04-functions/functions.yaml rename to domains/kultura-valoda/registers/04-functions/functions.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/05-services/kultura-services.yaml b/domains/kultura-valoda/registers/05-services/kultura-services.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/05-services/kultura-services.yaml rename to domains/kultura-valoda/registers/05-services/kultura-services.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/05-services/valoda-services.yaml b/domains/kultura-valoda/registers/05-services/valoda-services.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/05-services/valoda-services.yaml rename to domains/kultura-valoda/registers/05-services/valoda-services.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/06-information-resources/kultura-info-resources.yaml b/domains/kultura-valoda/registers/06-information-resources/kultura-info-resources.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/06-information-resources/kultura-info-resources.yaml rename to domains/kultura-valoda/registers/06-information-resources/kultura-info-resources.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/06-information-resources/valoda-info-resources.yaml b/domains/kultura-valoda/registers/06-information-resources/valoda-info-resources.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/06-information-resources/valoda-info-resources.yaml rename to domains/kultura-valoda/registers/06-information-resources/valoda-info-resources.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/07-systems/kultura-systems.yaml b/domains/kultura-valoda/registers/07-systems/kultura-systems.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/07-systems/kultura-systems.yaml rename to domains/kultura-valoda/registers/07-systems/kultura-systems.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/07-systems/valoda-systems.yaml b/domains/kultura-valoda/registers/07-systems/valoda-systems.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/07-systems/valoda-systems.yaml rename to domains/kultura-valoda/registers/07-systems/valoda-systems.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/08-roadmap/interactions.yaml b/domains/kultura-valoda/registers/08-roadmap/interactions.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/08-roadmap/interactions.yaml rename to domains/kultura-valoda/registers/08-roadmap/interactions.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/08-roadmap/roadmap.yaml b/domains/kultura-valoda/registers/08-roadmap/roadmap.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/08-roadmap/roadmap.yaml rename to domains/kultura-valoda/registers/08-roadmap/roadmap.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/09-risks/risks.yaml b/domains/kultura-valoda/registers/09-risks/risks.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/09-risks/risks.yaml rename to domains/kultura-valoda/registers/09-risks/risks.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/10-catalog/as-is-components.yaml b/domains/kultura-valoda/registers/10-catalog/as-is-components.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/10-catalog/as-is-components.yaml rename to domains/kultura-valoda/registers/10-catalog/as-is-components.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/99-relations/edges.yaml b/domains/kultura-valoda/registers/99-relations/edges.yaml similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/registers/99-relations/edges.yaml rename to domains/kultura-valoda/registers/99-relations/edges.yaml diff --git a/ikt-arh-kultura-valodu-tehnologijas/releases/KISC-merkarhitektura-apraksts-020.md b/domains/kultura-valoda/releases/KISC-merkarhitektura-apraksts-020.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/releases/KISC-merkarhitektura-apraksts-020.md rename to domains/kultura-valoda/releases/KISC-merkarhitektura-apraksts-020.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/01-ievads.md b/domains/kultura-valoda/views/01-ievads.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/01-ievads.md rename to domains/kultura-valoda/views/01-ievads.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/02-esosas-arhitekturas-novertejums.md b/domains/kultura-valoda/views/02-esosas-arhitekturas-novertejums.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/02-esosas-arhitekturas-novertejums.md rename to domains/kultura-valoda/views/02-esosas-arhitekturas-novertejums.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/03-merki-un-principi.md b/domains/kultura-valoda/views/03-merki-un-principi.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/03-merki-un-principi.md rename to domains/kultura-valoda/views/03-merki-un-principi.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/04-merk-arhitektura.md b/domains/kultura-valoda/views/04-merk-arhitektura.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/04-merk-arhitektura.md rename to domains/kultura-valoda/views/04-merk-arhitektura.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/05-cela-karte.md b/domains/kultura-valoda/views/05-cela-karte.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/05-cela-karte.md rename to domains/kultura-valoda/views/05-cela-karte.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/06-pielikums-komponentu-katalogs.md b/domains/kultura-valoda/views/06-pielikums-komponentu-katalogs.md similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/domains/kultura-valoda/views/06-pielikums-komponentu-katalogs.md rename to domains/kultura-valoda/views/06-pielikums-komponentu-katalogs.md diff --git a/domenas.yaml b/domenas.yaml new file mode 100644 index 0000000..31b8ae9 --- /dev/null +++ b/domenas.yaml @@ -0,0 +1,240 @@ +# Digitālās pārvaldes arhitektūras jomas (VARAM) un to stāvoklis šajā repozitorijā. +# Avots: VARAM lapas „Horizontālo jomu arhitektūras” un „Nozaru jomu arhitektūras” (pārbaudīts 2026-10-11). +# Tiek glabāta tikai iestāde, kas vada apraksta izstrādi; personu kontaktdati netiek glabāti. +# as_code.status: transformed — joma pārveidota kodā (domains/); not_started — vēl tikai VARAM publicētais dokuments. +# Shēma: schemas/arhitektura-0.1.schema.json (#/$defs/Catalogue). +version: "0.1.0" +sources: + - {title: "VARAM: Horizontālo jomu arhitektūras", url: "https://www.varam.gov.lv/lv/horizontalo-jomu-arhitekturas", checked: "2026-10-11"} + - {title: "VARAM: Nozaru jomu arhitektūras", url: "https://www.varam.gov.lv/lv/nozaru-jomu-arhitekturas", checked: "2026-10-11"} + - {title: "VARAM: Digitālās pārvaldes arhitektūra", url: "https://www.varam.gov.lv/lv/digitalas-parvaldes-arhitektura", checked: "2026-10-11"} + +domains: + # ---------------------------------------------------------------- horizontālās jomas + - slug: valsts-pakalpojumi + title: "Valsts pakalpojumi" + kind: horizontal + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija" + lead_vpk_id: "21-0000" + varam_status: approved + approved: "2026-02-18" + approved_versions: [{version: "1", approved: "2025-06-25"}, {version: "2", approved: "2026-02-18"}] + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51282/download?attachment" + presentation_url: "https://www.varam.gov.lv/media/44522" + as_code: {status: not_started} + - slug: datu-parvaldiba + title: "Datu pārvaldība un koplietošana" + kind: horizontal + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija" + lead_vpk_id: "21-0000" + varam_status: approved + approved: "2024-11-27" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51756/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51717/download?attachment" + as_code: {status: not_started} + - slug: datu-analize + title: "Datu analīze" + kind: horizontal + lead_institution: "Centrālā statistikas pārvalde" + lead_vpk_id: "12-0039" + varam_status: approved + approved: "2025-04-23" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51750/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51753/download?attachment" + as_code: {status: not_started} + - slug: uzticamiba-identifikacija + title: "Uzticamība un personu identifikācija" + kind: horizontal + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija" + lead_vpk_id: "21-0000" + varam_status: approved + approved: "2025-01-22" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51687/download?attachment" + presentation_url: "https://www.varam.gov.lv/media/42702" + as_code: {status: not_started} + - slug: ikt-infrastruktura-kiberdrosiba + title: "IKT infrastruktūra un kiberdrošība" + kind: horizontal + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija" + lead_vpk_id: "21-0000" + varam_status: approved + approved: "2025-03-19" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51128/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51131/download?attachment" + as_code: {status: not_started} + - slug: parvaldes-modernizacija + title: "Valsts pārvaldes modernizācija (t. sk. resursi un dokumenti)" + kind: horizontal + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija" + lead_vpk_id: "21-0000" + varam_status: approved + approved: "2025-08-27" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51071/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51711/download?attachment" + as_code: {status: not_started} + - slug: mi-produktivitatei + title: "Mākslīgais intelekts pārvaldes produktivitātei" + kind: subdomain + parent: parvaldes-modernizacija + lead_institution: "Valsts digitālās attīstības aģentūra" + varam_status: approved + approved: "2025-10-29" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51122/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51119/download?attachment" + as_code: {status: not_started} + + # ---------------------------------------------------------------- nozaru un starpnozaru jomas + - slug: kultura-valoda + title: "Kultūra un valodu tehnoloģijas" + kind: sectoral + lead_institution: "Kultūras informācijas sistēmu centrs" + lead_vpk_id: "22-0558" + varam_status: approved + approved: "2025-07-07" + approved_by: IKT forums (rakstiskā procedūra) + description_url: "https://www.varam.gov.lv/lv/media/51729/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51699/download?attachment" + as_code: {status: transformed, path: domains/kultura-valoda, source: "sources/kultura-valoda/"} + - slug: izglitiba-zinatne + title: "Izglītība un zinātne" + kind: sectoral + lead_institution: "Izglītības un zinātnes ministrija" + lead_vpk_id: "15-0000" + varam_status: approved + approved: "2026-02-18" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/49004/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/49007/download?attachment" + as_code: {status: not_started} + - slug: labklajiba + title: "Labklājība" + kind: sectoral + lead_institution: "Labklājības ministrija" + lead_vpk_id: "18-0000" + varam_status: approved + approved: "2025-08-27" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51068/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51089/download?attachment" + as_code: {status: not_started} + - slug: veseliba + title: "Veselība" + kind: sectoral + lead_institution: "Latvijas Digitālās veselības centrs" + varam_status: approved + approved: "2025-09-25" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51765/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51588/download?attachment" + as_code: {status: not_started} + - slug: nodokli + title: "Nodokļi" + kind: sectoral + lead_institution: "Valsts ieņēmumu dienests" + lead_vpk_id: "13-0056" + varam_status: approved + approved: "2025-07-07" + approved_by: IKT forums (rakstiskā procedūra) + description_url: "https://www.varam.gov.lv/lv/media/51732/download?attachment" + presentation_url: "https://www.varam.gov.lv/media/44489" + as_code: {status: not_started} + - slug: muita + title: "Muita" + kind: sectoral + lead_institution: "Valsts ieņēmumu dienests" + lead_vpk_id: "13-0056" + varam_status: approved + approved: "2025-04-23" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51744/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51747/download?attachment" + as_code: {status: not_started} + - slug: transports + title: "Transports" + kind: sectoral + lead_institution: "Satiksmes ministrija" + lead_vpk_id: "17-0000" + varam_status: approved + approved: "2025-07-07" + approved_by: IKT forums (rakstiskā procedūra) + description_url: "https://www.varam.gov.lv/lv/media/51735/download?attachment" + presentation_url: "https://www.varam.gov.lv/media/44495" + as_code: {status: not_started} + - slug: e-lieta + title: "E-lieta" + kind: sectoral + lead_institution: "Tiesu administrācija" + lead_vpk_id: "19-0458" + varam_status: approved + approved: "2025-04-23" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51738/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51741/download?attachment" + as_code: {status: not_started} + - slug: buvnieciba + title: "Būvniecība" + kind: sectoral + lead_institution: "Valsts zemes dienests" + lead_vpk_id: "19-0485" + varam_status: approved + approved: "2025-06-12" + approved_by: IKT forums (rakstiskā procedūra) + description_url: "https://www.varam.gov.lv/lv/media/51720/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51723/download?attachment" + as_code: {status: not_started} + - slug: energetika + title: "Enerģētika" + kind: sectoral + lead_institution: "Būvniecības valsts kontroles birojs" + lead_vpk_id: "12-0686" + varam_status: approved + approved: "2025-08-04" + approved_by: IKT forums (rakstiskā procedūra) + description_url: "https://www.varam.gov.lv/lv/media/51708/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51726/download?attachment" + as_code: {status: not_started} + - slug: vide-regioni + title: "Vide un reģionālā attīstība" + kind: sectoral + lead_institution: "Valsts digitālās attīstības aģentūra" + varam_status: approved + approved: "2025-09-25" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51083/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51086/download?attachment" + as_code: {status: not_started} + - slug: demokratija-lidzdaliba + title: "Demokrātija, līdzdalība un tiesiskā informācija" + kind: sectoral + lead_institution: "Valsts digitālās attīstības aģentūra" + varam_status: approved + approved: "2026-01-21" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51768/download?attachment" + as_code: {status: not_started} + - slug: zemkopiba + title: "Zemkopība" + kind: sectoral + lead_institution: "Zemkopības ministrija" + lead_vpk_id: "16-0000" + varam_status: approved + approved: "2025-08-27" + approved_by: IKT forums + description_url: "https://www.varam.gov.lv/lv/media/51077/download?attachment" + presentation_url: "https://www.varam.gov.lv/lv/media/51080/download?attachment" + as_code: {status: not_started} + - slug: pasvaldibas + title: "Pašvaldības" + kind: sectoral + lead_institution: "Viedās administrācijas un reģionālās attīstības ministrija (līdz kompetences centra izveidei)" + lead_vpk_id: "21-0000" + varam_status: in_review + description_url: "https://www.varam.gov.lv/lv/media/50924/download?attachment" + as_code: {status: not_started} diff --git a/ikt-arh-kultura-valodu-tehnologijas/.env.example b/ikt-arh-kultura-valodu-tehnologijas/.env.example deleted file mode 100644 index a941821..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/.env.example +++ /dev/null @@ -1,37 +0,0 @@ -# Server -MCP_PORT=8787 -MCP_HOST=0.0.0.0 - -# IMPORTANT: point this to the folder that contains registers/ and views/ -# In this repo that's likely the domain folder, e.g. /app/domains/kultura-valoda -REPO_ROOT=/app/domains/kultura-valoda - -# Public URL of the server (used in OAuth discovery metadata) -SERVER_PUBLIC_URL=https://llm.kis.gov.lv:8787 - -# Auth switch -AUTH_REQUIRED=true - -# Auth mode: "both" (default) | "jwks" | "static" -# both — accepts either a valid static token OR a valid OAuth JWT -# jwks — only OAuth/OIDC JWT tokens (validated via JWKS) -# static — only the fixed bearer token -AUTH_MODE=both - -# ── Static bearer token ────────────────────────────────────────────── -STATIC_BEARER_TOKEN=change-me - -# ── Built-in OAuth (Authorization Code + PKCE & client_credentials) ── -# Login credentials for the /authorize page (browser-based flow) -OAUTH_LOGIN_USERNAME=admin -OAUTH_LOGIN_PASSWORD=change-me - -# Pre-registered machine-to-machine client (client_credentials grant) -OAUTH_CLIENT_ID=mcp-service-account -OAUTH_CLIENT_SECRET=change-me-to-a-strong-secret - -# ── External JWKS / OIDC settings (optional, for external IDP) ────── -# Leave blank to use the built-in OAuth server only. -# OAUTH_JWKS_URL=https://YOUR-IDP/.well-known/jwks.json -# OAUTH_ISSUER=https://YOUR-IDP/ -# OAUTH_AUDIENCE=YOUR_API_AUDIENCE diff --git a/ikt-arh-kultura-valodu-tehnologijas/.github/placeholder.git b/ikt-arh-kultura-valodu-tehnologijas/.github/placeholder.git deleted file mode 100644 index 8b13789..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/.github/placeholder.git +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ikt-arh-kultura-valodu-tehnologijas/.github/workflows/qa.yml b/ikt-arh-kultura-valodu-tehnologijas/.github/workflows/qa.yml deleted file mode 100644 index 48ea6fc..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/.github/workflows/qa.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: QA - -on: - push: - pull_request: - -jobs: - qa: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install MCP deps - working-directory: mcp - run: npm ci - - - name: Generate doc - run: | - cd mcp - REPO_ROOT="$GITHUB_WORKSPACE" DOMAIN_DIR="domains/kultura-valoda" OUT_FILE="KISC-merkarhitektura-apraksts.md" npm run gen:doc - - - name: Non-empty required registers - run: tools/qa/check_nonempty_registers.sh - - - name: Required headings exist in regenerated doc - run: tools/qa/check_generated_doc_headings.sh KISC-merkarhitektura-apraksts.md - - - name: Stakeholder presence sanity check - run: tools/qa/check_stakeholder_count.sh KISC-merkarhitektura-apraksts.md diff --git a/ikt-arh-kultura-valodu-tehnologijas/.gitignore b/ikt-arh-kultura-valodu-tehnologijas/.gitignore deleted file mode 100644 index e030f3b..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/ -dist/ -.env -.DS_Store -*.log -site/ diff --git a/ikt-arh-kultura-valodu-tehnologijas/MCP.md b/ikt-arh-kultura-valodu-tehnologijas/MCP.md deleted file mode 100644 index 8bac557..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/MCP.md +++ /dev/null @@ -1,560 +0,0 @@ -# MCP serveris — Kultūras un valodu tehnoloģiju arhitektūras interfeiss - -## Pārskats - -Šis dokuments apraksta KISC MCP (Model Context Protocol) serveri, kas nodrošina AI aģentiem un automatizācijas rīkiem strukturētu piekļuvi Latvijas kultūras un valodu tehnoloģiju jomas mērķarhitektūras datiem. - -**Servera adrese:** `https://llm.kis.gov.lv/mcp` -**Versija:** 0.3.0 -**Operators:** Kultūras informācijas sistēmu centrs (KISC) -**Identitāte:** `did:web:llm.kis.gov.lv` - -MCP serveris ir **tikai-lasīšanas interfeiss** — tas nemaina SSOT saturu, bet gan ļauj to meklēt, analizēt un ģenerēt dokumentus no tā. - ---- - -## 1) Kas ir šī arhitektūra un ko MCP serveris dara - -Kultūras un valodu tehnoloģiju jomas mērķarhitektūra apraksta Latvijas valsts stratēģiju kultūras mantojuma digitalizācijai un latviešu valodas tehnoloģiju attīstībai. Tā ietver divus apakšdomēnus: - -- **Kultūras apakšjoma** (`kultura`) — muzeju, bibliotēku, arhīvu un kultūras pieminekļu digitalizācija, datu pārvaldība, publiskā pieejamība -- **Valodu tehnoloģiju apakšjoma** (`valoda`) — Lielais latviešu valodas modelis (LVM-LV), mašīntulkošana, runas sintēze/atpazīšana, semantiskā meklēšana - -Arhitektūra satur šādus elementu tipus: - -| Entītiju tips | Piemēri | Skaits | -|---|---|---| -| **Mērķi** (`goal`) | M1 "Kultūras mantojuma saglabāšana", M4 "Latviešu valoda digitālajā laikmetā" | 6 | -| **Organizācijas** (`org`) | KISC, LNB, LU MII, Kultūras ministrija | ~40 | -| **Funkcijas** (`func`) | Mantojuma digitalizācija, valodu platformas uzturēšana | 6 | -| **Pakalpojumi** (`svc`) | Mašīntulkošana, runas sintēze, DOM, statistikas portāls | 30 | -| **Informācijas resursi** (`ir`) | Valodu korpusi, muzeja dati, terminoloģija | 23 | -| **Sistēmas** (`sys`) | LVM-LV, VBK, valodu platformas, DOM | 32 | -| **Ceļa kartes pasākumi** (`rm`) | Modernizācijas iniciatīvas ar laika grafiku | ~10 | -| **Riski** (`risk`) | Datu kvalitāte, kiberdrošība, AI Act atbilstība | ~8 | -| **Dokumenti** (`doc`) | GDPR, AI Act, VDAR, nozares regulējums | ~15 | -| **Principi** (`principle`) | Arhitektūras pamatprincipi | 4 | - ---- - -## 2) MCP rīki — ko AI aģents saņem - -Kad AI aģents (piemēram, Claude) pieslēdzas MCP serverim, tam kļūst pieejami **9 rīki**. Šajā sadaļā aprakstīts katrs rīks — tā nolūks, parametri un kas tiek atgriezts. - -### 2.1 `identify` — servera identitātes verificēšana - -**Nolūks:** Atgriež informāciju par servera operatoru, identitāti un uzticamības pierādījumiem. Ļauj AI aģentam pārliecināties, ka serveris pieder KISC un ir verificēts. - -**Parametri:** nav obligātu parametru. - -**Atgriež:** -- Servera nosaukums, versija, operators (KISC) -- DID identitāte: `did:web:llm.kis.gov.lv` -- MCPF (MCP Trust Framework) atbilstības informācija -- VeriTrust izdotas verifikācijas akreditācijas URL -- JWKS, DID dokumenta, MCP manifesta un uzticamības reģistra URL - -**Kad lietot:** Pirms uzticēšanās servera datiem — lai pārliecinātos, ka datu avots ir leģitīms. - ---- - -### 2.2 `describe_model` — arhitektūras metamodelis - -**Nolūks:** Atgriež pilnu arhitektūras datu modeļa aprakstu — kādi entītiju tipi pastāv, kādi atribūti katram tipam, kādi attiecību tipi, kādi apakšdomēni un skati. - -**Parametri:** nav. - -**Atgriež:** -- 11 entītiju tipu saraksts ar to ID shēmām un atribūtiem -- 16 attiecību tipu saraksts (has_goal, depends_on, owns, u.c.) -- 2 apakšdomēni (kultura, valoda) -- 6 dokumenta skatu saraksts - -**Kad lietot:** Kā pirmo soli — lai AI aģents saprastu, kāda veida dati ir pieejami un kā tie ir strukturēti. Šis rīks ir "karte" visiem pārējiem rīkiem. - -**Piemērs — ko AI aģents uzzina:** -> "Šajā arhitektūrā ir 6 stratēģiskie mērķi ar ID formātā `goal.m1`–`goal.m6`. Sistēmas ir ar ID `sys.kultura.01` vai `sys.valoda.01`. Attiecības ietver `has_system`, `depends_on`, `owns`. Ir 2 apakšdomēni: kultūra un valoda." - ---- - -### 2.3 `search` — meklēšana pēc atslēgvārdiem - -**Nolūks:** Meklēt jebkādu arhitektūras entītiju vai dokumenta fragmentu pēc atslēgvārdiem. Atbalsta gan latviešu, gan angļu valodas vaicājumus. - -**Parametri:** -- `query` (obligāts) — meklēšanas frāze, piemēram, "valodu tehnoloģijas", "KISC", "digitālais mantojums", "AI risks" -- `kind` (neobligāts) — filtrēt pēc tipa: goal, org, sys, svc, ir, func, principle u.c. -- `subdomain` (neobligāts) — filtrēt pēc apakšdomēna: "kultura" vai "valoda" -- `limit` (neobligāts) — rezultātu skaits (noklusējums 25) - -**Atgriež:** Sakārtots saraksts ar atrastajām entītijām, katrai norādot ID, piemērotības novērtējumu (score), tipu un pilnus datus. - -**Kad lietot:** Kad zināms aptuvens jautājums, bet nav precīzs entītijas ID. - -**Piemēri:** -``` -search(query="mašīntulkošana") → atrod svc.valoda.04, sys.valoda.03 -search(query="LNB", kind="org") → atrod org.lnb -search(query="risks", kind="risk") → atrod visus riskus -search(query="korpuss", subdomain="valoda") → atrod ir.valoda.01, ir.valoda.02 -``` - ---- - -### 2.4 `get_entity` — konkrētas entītijas izgūšana - -**Nolūks:** Atgriež pilnu informāciju par vienu konkrētu entītiju pēc tās ID. - -**Parametri:** -- `id` (obligāts) — entītijas ID, piemēram: `goal.m4`, `org.kisc`, `sys.valoda.02`, `risk.001` - -**Atgriež:** Visu entītijas informāciju — nosaukums, apraksts, statuss, izmaiņu apraksts, VIRSIS ID atsauces un avota faila ceļš. - -**Kad lietot:** Kad precīzi zināms, kuru entītiju vajag apskatīt. - -**Piemēri:** -``` -get_entity(id="goal.m4") → "Latviešu valoda digitālajā laikmetā" — pilns apraksts -get_entity(id="sys.valoda.02") → "Lielais latviešu valodas modelis (LVM-LV)" — statuss, resursi -get_entity(id="org.kisc") → KISC organizācijas pilna informācija -``` - ---- - -### 2.5 `list_entities` — entītiju saraksts pēc tipa - -**Nolūks:** Atgriež visu entītiju ID sarakstu, iespējams filtrētu pēc tipa un/vai apakšdomēna. Ļauj ātri uzzināt, kas pastāv. - -**Parametri:** -- `type` (neobligāts) — entītiju tipa filtrs: "goal", "org", "sys", "svc", "ir", "func", "principle", "rm", "risk", "doc", "int" -- `subdomain` (neobligāts) — "kultura" vai "valoda" - -**Atgriež:** ID saraksts ar katras entītijas pamata informāciju (nosaukums, tips). - -**Kad lietot:** Kad vajag pārskatu par visām noteikta tipa entītijām. - -**Piemēri:** -``` -list_entities(type="goal") → 6 mērķi (goal.m1–goal.m6) -list_entities(type="sys", subdomain="valoda") → 11 valodu tehnoloģiju sistēmas -list_entities(type="risk") → visi identificētie riski -list_entities() → pilns visu entītiju saraksts -``` - ---- - -### 2.6 `list_relations` — attiecību meklēšana - -**Nolūks:** Atrast visas attiecības (šķautnes), kas saistītas ar konkrētu entītiju — ienākošās, izejošās vai abas. - -**Parametri:** -- `id` (obligāts) — entītijas ID, piemēram: `domain.kultura-valoda`, `goal.m4` -- `direction` (neobligāts) — "in" (ienākošās), "out" (izejošās), "both" (abas, noklusējums) - -**Atgriež:** Saraksts ar šķautnēm, katrai norādot `from`, `type`, `to` un kopējo skaitu. - -**Kad lietot:** Ietekmes analīzē ("kuri pakalpojumi ir saistīti ar mērķi M4?"), atkarību kartēšanā, audita nolūkos. - -**Piemērs:** -``` -list_relations(id="domain.kultura-valoda", direction="out") -→ 95 šķautnes: has_goal→goal.m1..m6, has_system→sys.kultura.01..sys.valoda.11, u.c. -``` - -> **Piezīme:** Pašreizējā versijā visas šķautnes iziet no `domain.kultura-valoda`. Lai atrastu, piemēram, visas valodu apakšjomas sistēmas, meklējiet `list_relations(id="domain.kultura-valoda")` un filtrējiet pēc tipa `has_system` un `to` prefiksa `sys.valoda.*`. - ---- - -### 2.7 `subgraph` — apakšgrafa izgūšana - -**Nolūks:** Sākot no vienas vai vairākām sēklas entītijām, apstaigāt attiecību grafu un atgriezt visas saistītās entītijas līdz noteiktam dziļumam. - -**Parametri:** -- `seed_ids` (obligāts) — sākuma entītiju ID masīvs, piemēram: `["goal.m4"]` vai `["org.kisc", "org.lumii"]` -- `depth` (neobligāts) — apstaigāšanas dziļums (noklusējums 1, maks. 3) - -**Atgriež:** Pilns mezglu un šķautņu saraksts — katra mezgla pilna informācija un visas savienojošās šķautnes. - -**Kad lietot:** Lai iegūtu "apkārtnes karti" — visas entītijas, kas saistītas ar noteiktu komponentu. - -**Piemērs:** -``` -subgraph(seed_ids=["domain.kultura-valoda"], depth=1) -→ 96 mezgli, 95 šķautnes — viss domēna saturs -``` - ---- - -### 2.8 `get_view` — dokumenta skata izgūšana - -**Nolūks:** Atgriež viena konkrēta dokumenta skata saturu Markdown formātā. Skati ir cilvēklasāmas dokumenta nodaļas. - -**Parametri:** -- `view_id` (obligāts) — skata identifikators. Iespējamās vērtības: - - `01-ievads` — Ievads - - `02-esosas-arhitekturas-novertejums` — Esošās arhitektūras novērtējums - - `03-merki-un-principi` — Mērķi un principi - - `04-merk-arhitektura` — Mērķarhitektūra - - `05-cela-karte` — Ceļa karte - - `06-pielikums-komponentu-katalogs` — Komponentu katalogs - -**Atgriež:** Markdown saturs ar virsrakstiem, tekstiem un atsaucēm uz reģistriem. - -**Kad lietot:** Lai izlasītu noteiktu dokumenta nodaļu bez pilna dokumenta ģenerēšanas. - -**Piemērs:** -``` -get_view(view_id="03-merki-un-principi") -→ Markdown ar M1–M6 mērķu aprakstiem un arhitektūras principiem -``` - ---- - -### 2.9 `generate_document` — pilna dokumenta ģenerēšana - -**Nolūks:** Reģenerēt pilnu cilvēklasāmo arhitektūras dokumentu no YAML reģistriem. Apvieno visus skatus ar inline reģistru paplašinājumiem vienotā Markdown vai JSON dokumentā. - -**Parametri:** -- `format` (neobligāts) — "markdown" (noklusējums) vai "json" -- `sections` (neobligāts) — konkrētu sadaļu saraksts. Ja nav norādīts, ģenerē pilnu dokumentu. - - Pieejamās vērtības: `ievads`, `esosa`, `merki`, `arhitektura`, `celakarte`, `katalogs` - -**Atgriež:** Pilns Markdown vai JSON dokuments, kas satur visas vai izvēlētās sadaļas ar iekļautiem reģistru datiem. - -**Kad lietot:** -- Lai iegūtu aktuālu, pilnu mērķarhitektūras dokumentu -- Lai sagatavotu materiālu prezentācijai vai pārskatam -- Lai pārbaudītu, ka visi reģistri ir korekti un savstarpēji saskanīgi - -**Piemēri:** -``` -generate_document() -→ Pilns dokuments Markdown formātā (~50+ lappuses) - -generate_document(format="json") -→ Strukturēts JSON ar katras sadaļas datiem - -generate_document(sections=["merki", "arhitektura"]) -→ Tikai mērķi/principi un mērķarhitektūras sadaļas -``` - ---- - -## 3) Tipisko jautājumu un atbilžu piemēri - -Šajā sadaļā parādīts, kādus jautājumus var uzdot AI aģentam un kādus rīkus tas izmantos, lai atbildētu. - -### 3.1 Vispārīgs pārskats - -**Jautājums:** "Pastāsti par šo arhitektūru — kas tajā ir un kā tā ir organizēta?" - -**AI aģenta darbība:** Izsauc `describe_model`, pēc tam `list_entities` vispārīgam pārskatam. - -**Sagaidāmā atbilde:** Skaidrojums, ka tā ir Latvijas kultūras un valodu tehnoloģiju jomas mērķarhitektūra ar 2 apakšdomēniem, 6 mērķiem, ~30 pakalpojumiem, ~30 sistēmām u.c. - ---- - -### 3.2 Konkrēta mērķa izpēte - -**Jautājums:** "Kas ir M4 mērķis un kā tas attiecas uz latviešu valodas AI?" - -**AI aģenta darbība:** Izsauc `get_entity(id="goal.m4")`. - -**Sagaidāmā atbilde:** M4 ir "Latviešu valoda digitālajā laikmetā" — mērķis nodrošināt latviešu valodas klātbūtni MI risinājumos, attīstot valodas resursus, modeļus un pakalpojumus. - ---- - -### 3.3 Sistēmu saraksts valodu jomā - -**Jautājums:** "Kādas informācijas sistēmas ir valodu tehnoloģiju apakšjomā?" - -**AI aģenta darbība:** Izsauc `list_entities(type="sys", subdomain="valoda")`. - -**Sagaidāmā atbilde:** 11 sistēmu saraksts — valodu tehnoloģiju platforma, LVM-LV, mašīntulkošana, runas atpazīšana, runas sintēze, virtuālie asistenti, semantiskā meklēšana, teksta ģenerēšana, vieglās valodas rīki, valodu resursu pārvaldība, jaunu tehnoloģiju izmitināšanas vide. - ---- - -### 3.4 Konkrētas sistēmas detaļas - -**Jautājums:** "Pastāsti par Lielo latviešu valodas modeli — kas tas ir, kāds ir tā statuss?" - -**AI aģenta darbība:** Izsauc `search(query="latviešu valodas modelis")` vai `get_entity(id="sys.valoda.02")`. - -**Sagaidāmā atbilde:** sys.valoda.02 — "Lielais latviešu valodas modelis (LVM-LV)", statuss "Jauns", tiks izveidots un uzturēts kā valsts mēroga valodas infrastruktūras pamatelements. - ---- - -### 3.5 Ietekmes analīze - -**Jautājums:** "Kuras sistēmas un pakalpojumi ir saistīti ar kultūras domēnu?" - -**AI aģenta darbība:** Izsauc `list_relations(id="domain.kultura-valoda", direction="out")`, filtrē pēc `has_system` un `has_service`. - -**Sagaidāmā atbilde:** 21 kultūras sistēma un 16 kultūras pakalpojumi, katrs ar savu ID un saiti uz domēnu. - ---- - -### 3.6 Risku analīze - -**Jautājums:** "Kādi riski ir identificēti šajā arhitektūrā?" - -**AI aģenta darbība:** Izsauc `list_entities(type="risk")`, pēc tam `get_entity` katram riskam. - -**Sagaidāmā atbilde:** Risku saraksts ar aprakstiem, iespējamību, ietekmi un mazināšanas pasākumiem. - ---- - -### 3.7 Pilna dokumenta ģenerēšana - -**Jautājums:** "Saģenerē pilnu mērķarhitektūras dokumentu." - -**AI aģenta darbība:** Izsauc `generate_document(format="markdown")`. - -**Sagaidāmā atbilde:** Pilns Markdown dokuments ar visām 6 sadaļām — ievads, esošā situācija, mērķi un principi, mērķarhitektūra, ceļa karte, komponentu katalogs. - ---- - -### 3.8 Organizāciju meklēšana - -**Jautājums:** "Kas ir KISC un kāda ir tā loma?" - -**AI aģenta darbība:** Izsauc `search(query="KISC", kind="org")` vai `get_entity(id="org.kisc")`. - -**Sagaidāmā atbilde:** Kultūras informācijas sistēmu centrs — domēna īstenotājs, atbildīgs par kultūras IS uzturēšanu un attīstību. - ---- - -### 3.9 Ceļa kartes izpēte - -**Jautājums:** "Kādi ir plānotie pasākumi un to laika grafiks?" - -**AI aģenta darbība:** Izsauc `get_view(view_id="05-cela-karte")` vai `list_entities(type="rm")`. - -**Sagaidāmā atbilde:** Ceļa kartes sadaļa ar pasākumu sarakstu, termiņiem un atbildīgajām organizācijām. - ---- - -### 3.10 Servera uzticamības pārbaude - -**Jautājums:** "Vai šis serveris ir uzticams? Kas to pārvalda?" - -**AI aģenta darbība:** Izsauc `identify`. - -**Sagaidāmā atbilde:** Serveris pieder KISC (Latvijas valsts iestāde), ir verificēts ar VeriTrust akreditāciju, DID identitāte ir `did:web:llm.kis.gov.lv`, atbilst MCPF Layer 1. - ---- - -## 4) Kā AI aģents saprot arhitektūras kontekstu - -Kad AI aģents (piemēram, Claude) pieslēdzas šim MCP serverim, tas **automātiski saņem** šādu konteksta informāciju: - -1. **Servera apraksts:** "MCP server for Latvian cultural heritage and language technology architecture documentation" — tas uzreiz norāda, ka dati ir par Latvijas kultūras un valodu tehnoloģiju jomu. - -2. **Rīku apraksti:** Katrs rīks satur detalizētu `description` lauku, kas palīdz AI aģentam saprast, kad un kā to lietot. Piemēram, `search` rīka aprakstā ir minēts: "Search KISC architecture documentation for Latvian government cultural digitalization. Contains: strategic goals (M1-M6), organizations (KISC, LNB, LUMII, ministries, museums)..." — tas dod aģentam bagātu kontekstu. - -3. **Entītiju ID shēmas:** Rīku parametru aprakstos ir norādīti piemēri (`goal.m4`, `org.kisc`, `sys.valoda.01`), kas aģentam palīdz konstruēt pareizus vaicājumus. - -4. **Uzticamības metadati:** Inicializācijas laikā serveris atgriež MCPF metadatus ar DID, akreditācijas un verifikācijas URL — AI aģents var novērtēt datu avota uzticamību. - -Tādējādi AI aģentam **nav nepieciešama ārēja apmācība** — konteksts tiek saņemts tieši no servera, un aģents var sākt atbildēt uz jautājumiem par arhitektūru nekavējoties. - ---- - -## 5) Dokumenta ģenerēšanas detalizēta procedūra - -### 5.1 Ģenerēšanas mehānisms - -Dokumenta ģenerēšana apvieno divus avotus: - -``` -views/ (Markdown struktūra) + registers/ (YAML dati) → Pilns dokuments -``` - -Process: - -1. Tiek nolasīts katrs skats (`01-ievads.md`, `02-esosas-...md`, ..., `06-pielikums-...md`) -2. Skatā atrastās reģistru atsauces (backtick formātā) tiek aizstātas ar attiecīgo YAML reģistru saturu, formatētu kā tabulas vai saraksti -3. Rezultāts tiek apvienots vienotā dokumentā - -### 5.2 Ģenerēšana caur komandrindu - -```bash -cd mcp -REPO_ROOT="$(pwd)/.." \ -DOMAIN_DIR="domains/kultura-valoda" \ -OUT_FILE="KISC-merkarhitektura-apraksts.md" \ -npm run gen:doc -``` - -### 5.3 Ģenerēšana caur MCP (AI aģents) - -``` -# Pilns dokuments (visas sadaļas) -generate_document(format="markdown") - -# Tikai ievads un mērķi -generate_document(format="markdown", sections=["ievads", "merki"]) - -# JSON formāts mašīnapstrādei -generate_document(format="json") - -# Tikai mērķarhitektūra un katalogs -generate_document(format="markdown", sections=["arhitektura", "katalogs"]) -``` - -### 5.4 Sadaļu identifikatori - -| Sadaļas ID | Skata fails | Saturs | -|---|---|---| -| `ievads` | `01-ievads.md` | Ievads, tvērums, termini, saīsinājumi | -| `esosa` | `02-esosas-arhitekturas-novertejums.md` | Esošās situācijas novērtējums | -| `merki` | `03-merki-un-principi.md` | Mērķi M1–M6 un 4 arhitektūras principi | -| `arhitektura` | `04-merk-arhitektura.md` | Funkcijas, pakalpojumi, IR, sistēmas | -| `celakarte` | `05-cela-karte.md` | Ceļa karte, riski, mijiedarbības | -| `katalogs` | `06-pielikums-komponentu-katalogs.md` | Pilns komponentu katalogs | - -### 5.5 Ģenerētā dokumenta struktūra - -Pilns dokuments satur aptuveni šādu struktūru: - -``` -1. Ievads - 1.1 Dokumenta nolūks un mērķauditorija - 1.2 Domēna arhitektūras tvērums - 1.3 Termini un saīsinājumi - 1.4 Saistītie dokumenti - -2. Esošās arhitektūras novērtējums - 2.1 Kultūras apakšjomas esošā situācija - 2.2 Valodu tehnoloģiju esošā situācija - 2.3 Esošo sistēmu novērtējums - -3. Mērķi un principi - 3.1 Stratēģiskie mērķi (M1–M6) - 3.2 Arhitektūras principi (P1–P4) - -4. Mērķarhitektūra - 4.1 Funkcijas (6 gab.) - 4.2 Pakalpojumi (30 gab. — kultūra + valoda) - 4.3 Informācijas resursi (23 gab.) - 4.4 Sistēmas (32 gab.) - -5. Ceļa karte - 5.1 Pasākumu plāns - 5.2 Attiecības un atkarības - 5.3 Riski - 5.4 Mijiedarbības ar citām jomām - -6. Pielikums — komponentu katalogs - 6.1 Pilns sistēmu saraksts - 6.2 Pilns pakalpojumu saraksts - 6.3 Pilns IR saraksts -``` - ---- - -## 6) MCPF uzticamības ietvars - -Serveris implementē **MCPF (MCP Trust Framework) Layer 1** — uzticamības atklāšanu sesijas līmenī. - -### 6.1 Inicializācijas metadati - -Katras MCP sesijas sākumā serveris atgriež šādus metadatus: - -```json -{ - "_meta": { - "identity": { - "id": "did:web:llm.kis.gov.lv", - "service": { "mcp": "https://llm.kis.gov.lv/mcp" }, - "keys": { "jwks_uri": "https://llm.kis.gov.lv/.well-known/jwks.json" } - }, - "mcpf": { - "version": "0.1", - "entrypoint": { - "type": "manifest", - "url": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json" - } - }, - "trust": { - "verifications": [{ - "verifier": "did:web:veritrust.vc", - "type": ["VerifiableCredential", "MCPServerVerification"], - "covers": "did:web:llm.kis.gov.lv" - }] - } - } -} -``` - -### 6.2 Verifikācijas galapunkti - -| URL | Saturs | -|---|---| -| `https://llm.kis.gov.lv/.well-known/jwks.json` | Publiskā atslēga JWT verifikācijai | -| `https://llm.kis.gov.lv/.well-known/did.json` | DID dokuments | -| `https://llm.kis.gov.lv/.well-known/mcp/manifest.json` | MCP manifests | -| `https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json` | Uzticamības reģistrs | -| `https://veritrust.vc/portal/mcp/credentials/...` | VeriTrust akreditācija | - ---- - -## 7) Tehniskā informācija - -### 7.1 Servera versija un konfigurācija - -| Parametrs | Vērtība | -|---|---| -| Versija | 0.3.0 | -| Protokols | MCP 2024-11-05 | -| Transports | Streamable HTTP (`/mcp`) | -| Iespējas (capabilities) | `tools`, `resources` | -| Rīku skaits | 9 | -| Vide | Docker (Node.js 20, TypeScript) | - -### 7.2 Palaišana lokāli (izstrādei) - -```bash -cd mcp -npm install -npm run dev -``` - -Serveris būs pieejams: `http://localhost:8787/mcp` - -### 7.3 Palaišana ar Docker - -```bash -cd mcp -docker compose up -d -``` - -Vai no POC izvietojuma: - -```bash -cd /opt/kisc-llm/poc/deploy -docker compose build --no-cache arch-mcp -./scripts/start.sh -``` - -### 7.4 Veselības pārbaude - -```bash -curl https://llm.kis.gov.lv/health -# → {"status":"ok"} -``` - -### 7.5 MCP inicializācijas tests - -```bash -curl -sS https://llm.kis.gov.lv/mcp \ - -H "content-type: application/json" \ - -H "accept: application/json, text/event-stream" \ - -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{ - "protocolVersion":"2024-11-05", - "capabilities":{}, - "clientInfo":{"name":"test","version":"1.0"} - }}' -``` - -Sagaidāmā atbilde: servera informācija ar versiju 0.3.0, 9 rīkiem un MCPF metadatiem. diff --git a/ikt-arh-kultura-valodu-tehnologijas/README.md b/ikt-arh-kultura-valodu-tehnologijas/README.md deleted file mode 100644 index 3ab56a6..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/README.md +++ /dev/null @@ -1,409 +0,0 @@ -# IKT arhitektūra — Kultūras un valodu tehnoloģiju joma (SSOT) - -Šis repozitorijs uztur jomas mērķarhitektūru kā kodu (**"Architecture as Code"**), kur visa informācija tiek glabāta strukturēti, versēti un atkārtojami ģenerējama. - -Repozitorijs ir veidots kā **SSOT (Single Source of Truth)** — autoritatīvais saturs ir mašīnlasāmos YAML reģistros, bet cilvēklasāmais dokuments tiek ģenerēts no tiem. AI aģenti un automatizācijas rīki piekļūst šiem datiem caur MCP serveri. - ---- - -## 1) Pamatideja (Architecture as Code) - -Šajā repozitorijā arhitektūra tiek uzturēta šādi: - -- **Skati (`views/`)** — cilvēklasāms dokuments (Markdown), kas nosaka dokumenta struktūru un stāstījumu -- **Reģistri (`registers/`)** — mašīnlasāmi dati (YAML), kas ir autoritatīvais avots visām entītijām -- **Diagrammas (`diagrams/`)** — Mermaid (šobrīd TODO vietturi) -- **Attiecības (`registers/99-relations/edges.yaml`)** — grafiks starp entītijām (95 šķautnes) - -Repozitorijā visas entītijas tiek uzturētas kā **vienumi** (*items*), kuriem ir stabili identifikatori (ID) un atribūti. Piemēram, `sys.valoda.02` ir "Lielais latviešu valodas modelis (LVM-LV)", `goal.m4` ir "Latviešu valoda digitālajā laikmetā". - ---- - -## 2) Repozitorija struktūra - -``` -. -├── domains/ -│ └── kultura-valoda/ -│ ├── manifest.yaml # Domēna manifests -│ ├── views/ # Dokumenta skati (Markdown) -│ │ ├── 01-ievads.md -│ │ ├── 02-esosas-arhitekturas-novertejums.md -│ │ ├── 03-merki-un-principi.md -│ │ ├── 04-merk-arhitektura.md -│ │ ├── 05-cela-karte.md -│ │ └── 06-pielikums-komponentu-katalogs.md -│ ├── registers/ # SSOT reģistri (YAML) -│ │ ├── 00-meta/ # Termini, saīsinājumi, saistītie dokumenti -│ │ ├── 02-goals/ # Stratēģiskie mērķi (M1–M6) -│ │ ├── 03-organizations/ # Institūcijas, lomas, atbildības -│ │ ├── 04-functions/ # Jomas funkcijas -│ │ ├── 05-services/ # Pakalpojumi (kultūra + valoda) -│ │ ├── 06-information-resources/ # Informācijas resursi -│ │ ├── 07-systems/ # Informācijas sistēmas -│ │ ├── 08-roadmap/ # Ceļa karte un mijiedarbības -│ │ ├── 09-risks/ # Riski un mazināšanas pasākumi -│ │ └── 99-relations/ # Attiecību grafiks (edges.yaml) -│ └── diagrams/ # Mermaid diagrammas (TODO) -├── mcp/ # MCP serveris (AI aģentu interfeiss) -├── tools/ -│ └── qa/ # Kvalitātes pārbaudes skripti -└── docs/ - ├── MCP.md # MCP servera dokumentācija - ├── TRACEABILITY.md - └── REGENERATION.md -``` - ---- - -## 3) Skati (views/) — cilvēklasāms dokuments - -**Atrašanās vieta:** `domains/kultura-valoda/views/` - -Skati satur dokumenta nodaļu struktūru, virsrakstus un paskaidrojošu tekstu. Skatos ir atsauces uz reģistriem (backtick formātā), piemēram: - -```md -`registers/03-organizations/organizations.yaml` -``` - -Svarīgi: skatos nav jāuztur manuālas tabulas ar daudz vienumiem. Skati norāda "ko rādīt", bet saturs tiek ņemts no reģistriem. Dokumenta ģenerēšanas laikā atsauces tiek aizstātas ar reģistru saturu. - -Pieejamie skati: - -| Skats | Apraksts | -|---|---| -| `01-ievads.md` | Ievads, tvērums, termini | -| `02-esosas-arhitekturas-novertejums.md` | Esošās arhitektūras novērtējums | -| `03-merki-un-principi.md` | Mērķi (M1–M6) un arhitektūras principi | -| `04-merk-arhitektura.md` | Mērķarhitektūra (funkcijas, pakalpojumi, sistēmas, IR) | -| `05-cela-karte.md` | Ceļa karte, riski, mijiedarbības | -| `06-pielikums-komponentu-katalogs.md` | Komponentu katalogs (pilns saraksts) | - -## 4) Reģistri (registers/) — mašīnlasāmi dati - -**Atrašanās vieta:** `domains/kultura-valoda/registers/` - -Reģistri ir YAML faili, kas satur vienumus. Šie reģistri ir SSOT un ir autoritatīvais avots. - -| Reģistrs | Saturs | -|---|---| -| `00-meta/abbreviations.yaml` | Saīsinājumi | -| `00-meta/terms.yaml` | Termini un definīcijas | -| `00-meta/related-documents.yaml` | Saistītie dokumenti | -| `00-meta/legal-acts.yaml` | Juridiskais regulējums (grupēts pa tēmām) | -| `02-goals/goal-m1.yaml` … `goal-m6.yaml` | Stratēģiskie mērķi | -| `03-organizations/organizations.yaml` | Institūcijas, lomas, atbildības | -| `04-functions/functions.yaml` | Jomas funkcijas (6 gab.) | -| `05-services/kultura-services.yaml` | Kultūras apakšjomas pakalpojumi (16 gab.) | -| `05-services/valoda-services.yaml` | Valodu tehnoloģiju pakalpojumi (14 gab.) | -| `06-information-resources/kultura-info-resources.yaml` | Kultūras informācijas resursi (15 gab.) | -| `06-information-resources/valoda-info-resources.yaml` | Valodu tehnoloģiju IR (8 gab.) | -| `07-systems/kultura-systems.yaml` | Kultūras IS (21 gab.) | -| `07-systems/valoda-systems.yaml` | Valodu tehnoloģiju IS (11 gab.) | -| `08-roadmap/roadmap.yaml` | Ceļa kartes pasākumi | -| `08-roadmap/interactions.yaml` | Mijiedarbība ar citām jomām | -| `09-risks/risks.yaml` | Riski un mazināšanas pasākumi | -| `99-relations/edges.yaml` | Attiecību grafiks (95 šķautnes) | - -## 5) Attiecības (edges.yaml) — grafiks starp vienumiem - -**Atrašanās vieta:** `domains/kultura-valoda/registers/99-relations/edges.yaml` - -Šis fails satur attiecības starp vienumiem. Katrai attiecībai ir `from`, `type` un `to` lauki. - -Pieejamie attiecību tipi: - -| Tips | Nozīme | -|---|---| -| `has_goal` | Domēnam ir mērķis | -| `has_function` | Domēnam ir funkcija | -| `has_service` | Domēnam ir pakalpojums | -| `has_system` | Domēnam ir sistēma | -| `has_information_resource` | Domēnam ir informācijas resurss | -| `owns` / `manages` / `operates` | Organizācija ir īpašnieks / pārvaldītājs / operators | -| `depends_on` | Atkarība starp komponentēm | -| `implements` / `supports` / `enables` | Realizācijas un atbalsta saites | -| `regulates` / `complies_with` | Regulējuma saites | - -Pašlaik visas 95 šķautnes ir tipa `domain.kultura-valoda → {entītija}`, veidojot zvaigznes topoloģiju. Nākotnē grafiks tiks papildināts ar organizāciju, sistēmu un pakalpojumu savstarpējām saitēm. - -## 6) Diagrammas (diagrams/) — Mermaid TODO vietturi - -**Atrašanās vieta:** `domains/kultura-valoda/diagrams/` - -Diagrammas tiek uzturētas Mermaid formātā. Šobrīd tās ir vietturi (TODO), kas nākotnē tiks aizstātas ar ģenerētām vai manuāli veidotām vizualizācijām. - ---- - -## 7) Kā ģenerēt cilvēklasāmo dokumentu no SSOT - -Repozitorijs nodrošina pilna mērķarhitektūras dokumenta automātisku ģenerēšanu no reģistriem un skatiem. - -### 7.1 Priekšnosacījumi - -- Node.js 18+ (ieteicams Node 20) -- npm - -### 7.2 Uzstādīšana - -No repozitorija saknes: - -```bash -cd mcp -npm install -``` - -### 7.3 Dokumenta ģenerēšana (komandrinda) - -No repozitorija saknes: - -```bash -cd mcp -REPO_ROOT="$(pwd)/.." DOMAIN_DIR="domains/kultura-valoda" OUT_FILE="KISC-merkarhitektura-apraksts.md" npm run gen:doc -``` - -Rezultāts: `KISC-merkarhitektura-apraksts.md` (repo saknē). - -### 7.4 Dokumenta ģenerēšana caur MCP serveri (AI aģents) - -Ja MCP serveris darbojas, jebkurš AI aģents var ģenerēt dokumentu, izsaucot rīku `generate_document`: - -``` -Rīks: generate_document -Parametri: - format: "markdown" — Markdown formāts cilvēklasāmam dokumentam - format: "json" — JSON formāts mašīnapstrādei - sections: ["ievads", "arhitektura", "celakarte"] — konkrētas sadaļas (neobligāts) -``` - -Pilna dokumenta ģenerēšana (visas sadaļas): -``` -generate_document(format="markdown") -``` - -Atsevišķu sadaļu ģenerēšana: -``` -generate_document(format="markdown", sections=["ievads", "merki"]) -``` - -Pieejamās sadaļas: `ievads`, `esosa`, `merki`, `arhitektura`, `celakarte`, `katalogs`. - -Ģenerēšanas laikā tiek apvienoti visi skati (`views/`) ar tajā atsaucēto reģistru (`registers/`) saturu, veidojot vienotu, pilnu dokumentu. - -### 7.5 Svarīgi par ģenerēto dokumentu - -- Ģenerētais dokuments ir **build artifact** — tas netiek rediģēts manuāli. -- Izmaiņas vienmēr veic **reģistros** vai **skatos**, pēc tam atkārtoti ģenerē. -- Katru reizi ģenerējot, dokuments atspoguļo aktuālo SSOT stāvokli. - ---- - -## 8) Kvalitātes kontrole (QA) - -Repo ietver QA pārbaudes, lai nepieļautu satura zudumu. - -No repozitorija saknes: - -```bash -tools/qa/check_nonempty_registers.sh -tools/qa/check_generated_doc_headings.sh KISC-merkarhitektura-apraksts.md -tools/qa/check_stakeholder_count.sh KISC-merkarhitektura-apraksts.md -``` - -Ja QA neiziet — jālabo reģistri vai skati. Nedrīkst "saīsināt" saturu, lai tikai testi izietu. - ---- - -## 9) MCP serveris — AI aģentu interfeiss - -MCP (Model Context Protocol) serveris nodrošina AI aģentiem strukturētu piekļuvi arhitektūras SSOT datiem. Serveris darbojas kā tikai-lasīšanas slānis — tas nemaina SSOT saturu. - -Detalizēta MCP servera dokumentācija, rīku apraksti, lietošanas scenāriji un piemēri ir pieejami atsevišķā dokumentā: - -📖 **[docs/MCP.md](docs/MCP.md)** — pilna MCP servera dokumentācija - -### 9.1 Īsumā par iespējām - -MCP serveris piedāvā **9 rīkus**, kas ļauj AI aģentiem: - -- Meklēt un izgūt jebkuru arhitektūras entītiju (mērķi, sistēmas, pakalpojumus, IR, riskus u.c.) -- Analizēt attiecības starp entītijām (grafu vaicājumi) -- Ģenerēt pilnu mērķarhitektūras dokumentu tieši no SSOT -- Pārlūkot dokumenta skatus un metamodeli -- Verificēt servera identitāti un uzticamību (MCPF trust framework) - -### 9.2 Kā pieslēgties - -MCP serveris ir pieejams adresē: - -``` -https://llm.kis.gov.lv/mcp -``` - -Pieslēgšanās no Claude.ai: **Settings** → **Connectors** → **Add** → ievadiet URL. - ---- - -## 10) MCP autentifikācija - -MCP serveris atbalsta **trīs autentifikācijas metodes**, kas var darboties vienlaicīgi (`AUTH_MODE=both`): - -1. **Statiskais Bearer tokens** — vienkārša fiksēta atslēga -2. **OAuth 2.1 Authorization Code + PKCE** — interaktīva pieeja (Claude Desktop, pārlūkprogramma) -3. **OAuth 2.0 client_credentials** — mašīna-pret-mašīnu (API, skripti) - -### 10.1 Statiskais Bearer tokens (vienkāršā pieeja) - -Iestatiet `.env`: - -```env -AUTH_REQUIRED=true -AUTH_MODE=static # vai "both", lai darbotos visi veidi -STATIC_BEARER_TOKEN=my-secret-token-here -``` - -Pieprasījuma piemērs: - -```bash -curl -H "Authorization: Bearer my-secret-token-here" \ - http://localhost:8787/mcp -``` - -Šī metode ir piemērota iekšējai testēšanai un vienkāršiem integrācijas gadījumiem. - -### 10.2 OAuth 2.1 — Authorization Code + PKCE (Claude Desktop) - -Šo plūsmu automātiski izmanto **Claude Desktop** un citi MCP klienti, kas atbalsta OAuth 2.1. -Serveris implementē pilnu MCP autorizācijas specifikāciju: - -- **RFC 9728** — Protected Resource Metadata (`/.well-known/oauth-protected-resource`) -- **RFC 8414** — Authorization Server Metadata (`/.well-known/oauth-authorization-server`) -- **RFC 7591** — Dynamic Client Registration (`/register`) -- **PKCE S256** — obligāts drošības mehānisms - -#### Konfigurācija (.env) - -```env -AUTH_REQUIRED=true -AUTH_MODE=both -SERVER_PUBLIC_URL=https://llm.kis.gov.lv:8787 - -# Pieteikšanās dati autorizācijas lapā -OAUTH_LOGIN_USERNAME=admin -OAUTH_LOGIN_PASSWORD=change-me -``` - -#### Pieslēgšanās no Claude Desktop - -1. Claude Desktop → **Settings** → **Connectors** → **Add** -2. Ievadiet MCP servera URL: `https://llm.kis.gov.lv:8787/mcp` -3. Claude automātiski atklās OAuth galapunktus un atvērs pieteikšanās lapu pārlūkprogrammā -4. Ievadiet `OAUTH_LOGIN_USERNAME` / `OAUTH_LOGIN_PASSWORD` -5. Pēc autorizācijas Claude saņem JWT tokenu un izmanto to turpmākajos pieprasījumos - -#### Plūsmas secība (tehniski) - -``` -Claude Desktop MCP Server - │ │ - ├── POST /mcp (bez tokena) ──────────►│ - │◄── 401 + WWW-Authenticate ─────────┤ - │ │ - ├── GET /.well-known/ │ - │ oauth-protected-resource ───────►│ - │◄── { authorization_servers: [...] } │ - │ │ - ├── GET /.well-known/ │ - │ oauth-authorization-server ─────►│ - │◄── { endpoints, PKCE, ... } │ - │ │ - ├── POST /register ─────────────────►│ - │◄── { client_id, client_secret } │ - │ │ - ├── 🌐 Opens browser → /authorize ──►│ - │ (user logs in with credentials) │ - │◄── 302 redirect with ?code=... ────┤ - │ │ - ├── POST /token (code + PKCE) ──────►│ - │◄── { access_token, refresh_token } │ - │ │ - ├── POST /mcp + Bearer token ────────►│ - │◄── MCP response ──────────────────┤ -``` - -### 10.3 OAuth 2.0 — client_credentials (mašīna-pret-mašīnu) - -Skriptiem un API integrācijām, kas neizmanto pārlūkprogrammu. - -#### Konfigurācija (.env) - -```env -OAUTH_CLIENT_ID=mcp-service-account -OAUTH_CLIENT_SECRET=change-me-to-a-strong-secret -``` - -#### Tokena iegūšana - -```bash -# 1. Iegūt JWT tokenu -TOKEN=$(curl -s -X POST https://llm.kis.gov.lv:8787/token \ - -d grant_type=client_credentials \ - -d client_id=mcp-service-account \ - -d client_secret=change-me-to-a-strong-secret \ - | jq -r .access_token) - -# 2. Lietot tokenu MCP pieprasījumos -curl -H "Authorization: Bearer $TOKEN" \ - https://llm.kis.gov.lv:8787/mcp -``` - -Tokens ir derīgs 1 stundu (3600 s). Pēc termiņa beigām iegūstiet jaunu. - -### 10.4 Galapunkti - -| Galapunkts | Metode | Auth | Apraksts | -|---|---|---|---| -| `/health` | GET | Nē | Veselības pārbaude | -| `/.well-known/oauth-protected-resource` | GET | Nē | RFC 9728 — resursa metadati | -| `/.well-known/oauth-authorization-server` | GET | Nē | RFC 8414 — autorizācijas servera metadati | -| `/.well-known/jwks.json` | GET | Nē | Publiskā atslēga JWT verifikācijai | -| `/register` | POST | Nē | RFC 7591 — klienta dinamiskā reģistrācija | -| `/authorize` | GET/POST | Nē | Autorizācijas lapa (login + consent) | -| `/token` | POST | Nē | Tokenu izsniegšana (auth code, client_credentials, refresh) | -| `/mcp` | POST/GET/DELETE | **Jā** | MCP servera galapunkts | - -### 10.5 AUTH_MODE vērtības - -| Vērtība | Apraksts | -|---|---| -| `both` (noklusējums) | Pieņem statisko tokenu, OAuth JWT un auth code tokenu | -| `static` | Tikai statiskais Bearer tokens | -| `jwks` | Tikai OAuth/OIDC JWT (validēts ar JWKS) | - -### 10.6 Ārējais identitātes nodrošinātājs (neobligāts) - -Ja izmantojat ārēju IDP (Auth0, Keycloak, Azure AD) papildus iebūvētajam OAuth: - -```env -OAUTH_JWKS_URL=https://your-idp/.well-known/jwks.json -OAUTH_ISSUER=https://your-idp/ -OAUTH_AUDIENCE=your-api-audience -``` - ---- - -## 11) Darba kārtība (ieteicamais process) - -1. Veic izmaiņas reģistros (`registers/`) -2. Ja vajag — koriģē struktūru skatos (`views/`) -3. Ģenerē dokumentu (`npm run gen:doc` vai `generate_document` caur MCP) -4. Palaid QA (`tools/qa/`) -5. Commit - ---- - -## 12) Papildu materiāli - -- **[docs/MCP.md](docs/MCP.md)** — MCP servera dokumentācija (rīku apraksti, lietošanas scenāriji, piemēri) -- `docs/TRACEABILITY.md` — izsekojamība starp sākotnējo dokumentu, reģistriem un ģenerēto rezultātu -- `docs/REGENERATION.md` — regenerācijas procedūra un noteikumi diff --git a/ikt-arh-kultura-valodu-tehnologijas/docker-compose.yml b/ikt-arh-kultura-valodu-tehnologijas/docker-compose.yml deleted file mode 100644 index 5cbdfb0..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: "3.9" -services: - arch-mcp: - build: - context: . - dockerfile: mcp/Dockerfile - ports: - - "${MCP_PORT:-8787}:8787" - env_file: - - .env - environment: - - ARCH_ROOT=/app/domains - - REPO_ROOT=${REPO_ROOT:-/app} diff --git a/ikt-arh-kultura-valodu-tehnologijas/docs/mcp_best_practices_and_rationale.md b/ikt-arh-kultura-valodu-tehnologijas/docs/mcp_best_practices_and_rationale.md deleted file mode 100644 index a5f07f1..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/docs/mcp_best_practices_and_rationale.md +++ /dev/null @@ -1,266 +0,0 @@ -# MCP Best Practices, Market Reality, and Cached Knowledge Rationale - -## Executive Summary - -MCP (Model Context Protocol) has rapidly evolved from Anthropic's November 2024 release to become the de-facto standard for AI-to-tool integration, with adoption by OpenAI in March 2025 and donation to the Linux Foundation's Agentic AI Foundation in December 2025. However, its effectiveness for document-centric use cases depends heavily on implementation patterns. This analysis addresses whether MCP with cached/pre-computed knowledge layers makes sense versus alternatives. - ---- - -## 1. Market Best Practices and Real Examples - -### 1.1 Production MCP Document Implementations - -| Implementation | Approach | Key Features | -|----------------|----------|--------------| -| **Chroma MCP Server** | Vector-native semantic search | Sets the standard for semantic document management using vector search, supporting both ephemeral and persistent storage | -| **Knowledge-Base-MCP (Puran)** | Production-grade RAG | Agent-directed hybrid retrieval with auto mode choosing among dense, hybrid, sparse, and rerank routes; when scores are low, returns abstain so client can decide whether to retry | -| **AWS Bedrock KB + MCP** | Enterprise knowledge bases | Connects to Amazon Bedrock Knowledge Bases for semantic search capabilities, providing unified access pattern regardless of underlying AWS service | -| **Basic Memory** | Local-first knowledge graphs | Local-first knowledge management system that builds a semantic graph from Markdown files, enabling persistent memory across conversations | -| **AtScale MCP** | Semantic layer for BI | Exposes semantic models to any MCP-compatible AI agent with real-time model discovery where new models become queryable instantly after deployment | - -### 1.2 Dominant Architecture Patterns - -**Pattern A: MCP + Vector Store (Most Common)** -``` -Documents → Chunking → Embeddings → Vector DB → MCP Server → LLM Client -``` - -Prepare the knowledge base by collecting and preprocessing data, chunk into reasonably sized pieces, embed using an embedding model, and load into a vector database like FAISS, Weaviate, or Pinecone - -**Pattern B: MCP + Knowledge Graph** -``` -Documents → Entity Extraction → Graph DB → MCP Server → LLM Client -``` - -The MCP server exposes Graphiti's core capabilities including episode management, entity management, search capabilities with semantic and hybrid search for facts and node summaries - -**Pattern C: MCP + Hybrid (Best Practice)** -``` -Documents → [Vectors + Graph + BM25] → Unified MCP Interface → LLM Client -``` - -Combines vector search plus BM25 lexical search using RRF, then reranks. Best for complex queries with both conceptual and specific keyword requirements - ---- - -## 2. When MCP Beats Alternatives - -### 2.1 MCP vs. Direct Document Attachment - -| Scenario | Winner | Rationale | -|----------|--------|-----------| -| **One-off Q&A on small docs** | Direct Attachment | Zero setup, full context visible | -| **Repeated queries on same corpus** | MCP | Avoid re-processing, selective retrieval | -| **Multi-user access** | MCP | AI applications can seamlessly access up-to-date information and context as needed through unified protocol | -| **Agentic workflows** | MCP | Enables "agentic" AI systems that can autonomously interact with multiple systems, retrieve the latest information, and even take actions | -| **Version-controlled knowledge** | MCP | Git-based updates, deterministic ingestion | - -### 2.2 MCP vs. Traditional RAG API - -| Aspect | Traditional RAG | MCP-wrapped RAG | Advantage | -|--------|-----------------|-----------------|-----------| -| **Standardization** | Custom per-service | Universal protocol | MCP | -| **Tool Discovery** | Manual documentation | Auto-discovery | MCP | -| **Multi-source** | N×M integrations | M+N integrations | MCP flips the N×M problem to an M+N model: tool providers implement one standard MCP server, and AI app developers implement MCP client support once | -| **Agent autonomy** | Fixed pipeline | LLM itself makes contextual decisions about how to interact with the data, determining query strategy and prompt formulation | MCP | - -### 2.3 When NOT to Use MCP - -1. **Simple, one-time document analysis** - Direct attachment wins -2. **Highly dynamic real-time data** - Direct API calls may be simpler -3. **No multi-client requirement** - Overhead not justified -4. **Prototype/exploratory phase** - Start simple, add MCP later - ---- - -## 3. Cached/Pre-computed Knowledge: The Rationale - -### 3.1 What is "Cached Knowledge" in MCP Context? - -Three tiers of pre-computation that MCP servers can provide: - -| Tier | What's Cached | When Generated | Example | -|------|---------------|----------------|---------| -| **1. Embeddings** | Vector representations | At ingestion | Semantic search index | -| **2. Summaries** | Condensed content | At ingestion or scheduled | "This document describes X" | -| **3. Knowledge Graph** | Entity-relationship extractions | At ingestion | "Goal M1 → implemented by → System X" | - -### 3.2 Rationale FOR Cached Knowledge Layers - -**A. Token Economics** - -Simply stuffing all potentially relevant data into the prompt is inefficient and sometimes impossible. MCP enables dynamically retrieving just-in-time context from external sources as needed instead of front-loading everything - -Cost comparison (200-page document): -- Direct attachment: ~100K tokens every query = $0.30-3.00/query -- MCP with embeddings: ~2K tokens retrieved = $0.006-0.06/query -- MCP with summaries: ~500 tokens = $0.0015-0.015/query - -**B. Response Quality** - -Tune the number of retrieved documents included in the prompt - often 3-5 good snippets are better than 10 - sometimes using too many can overwhelm the model - -Pre-computed summaries ensure the LLM gets: -- Condensed, relevant context -- Pre-extracted key facts -- Relationship context from knowledge graphs - -**C. Caching Benefits** - -Implement caching at multiple levels. Cache the results of common retrieval queries — for example, if many users ask "What is the refund policy?", you can cache the answer or at least the retrieved document so the agent doesn't vector-search the same question repeatedly - -Implementing advanced caching (exact, semantic, task-aware) to avoid redundant API calls, tracking and optimizing costs across providers - -**D. Offline/Latency Benefits** - -Pre-computed knowledge enables: -- Faster response times (no embedding at query time) -- Offline capability (no API calls for embeddings) -- Deterministic behavior (same query = same retrieval) - -### 3.3 Implementation: Hierarchical Memory - -Provides hierarchical memory storage with three-tier compression (chunks, micro-summaries, meta-summaries) - -Example architecture: -```yaml -# Pre-computed knowledge layers -raw_chunks: - - content: "Full text chunk" - - embedding: [0.1, 0.2, ...] - -micro_summaries: - - chunk_ids: [1, 2, 3] - - summary: "These chunks describe the cultural heritage preservation goals" - - keywords: ["heritage", "preservation", "M1"] - -meta_summaries: - - scope: "02-goals subdomain" - - summary: "Six strategic goals (M1-M6) covering preservation through cybersecurity" - - entity_count: 6 -``` - -### 3.4 Rationale AGAINST Over-caching - -| Risk | Description | Mitigation | -|------|-------------|------------| -| **Staleness** | Summaries out of sync with source | Git-triggered regeneration | -| **Loss of nuance** | Summarization loses detail | Keep raw chunks accessible | -| **Hallucination risk** | LLM-generated summaries may be wrong | Human review for critical content | -| **Storage cost** | Multiple representations | Tiered storage, compress cold data | - ---- - -## 4. Recommended Architecture for KISC Use Case - -### 4.1 Current State Analysis - -Your Architecture-as-Code repo has: -- ✅ Structured YAML entities (good for knowledge graph) -- ✅ Explicit relationships in `edges.yaml` -- ❌ No vector embeddings -- ❌ No pre-computed summaries -- ❌ Primitive substring search (not semantic) - -### 4.2 Proposed Enhanced Architecture - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ KISC Architecture MCP Server │ -├─────────────────────────────────────────────────────────────────┤ -│ Layer 1: Raw Data │ -│ ├── YAML entities (goals, systems, services, etc.) │ -│ └── Markdown documentation │ -├─────────────────────────────────────────────────────────────────┤ -│ Layer 2: Pre-computed Knowledge (NEW) │ -│ ├── embeddings.index (vector search via FAISS/Qdrant) │ -│ ├── summaries.yaml (entity-level summaries in Latvian) │ -│ ├── knowledge_graph.json (Neo4j-style graph export) │ -│ └── glossary.yaml (term definitions for natural language) │ -├─────────────────────────────────────────────────────────────────┤ -│ Layer 3: MCP Tools (ENHANCED) │ -│ ├── semantic_search(query) → vector similarity │ -│ ├── get_entity(id) → full entity + related context │ -│ ├── explain_concept(term) → natural language explanation │ -│ ├── find_relationships(entity) → graph traversal │ -│ ├── summarize_domain(domain) → pre-computed summary │ -│ └── natural_query(latvian_question) → LLM-friendly response │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### 4.3 Pre-computation Pipeline - -```bash -# On every git commit to Architecture-as-Code: -1. Load all YAML entities -2. Generate embeddings for each entity (title + description) -3. Generate micro-summaries for each subdomain -4. Build/update knowledge graph from edges.yaml -5. Create glossary from all entity titles/codes -6. Store in /mcp/cache/ directory -``` - -### 4.4 Query Flow (Enhanced) - -**User asks:** "Kādas sistēmas realizē valodas tehnoloģiju mērķi?" -(What systems implement the language technology goal?) - -**Current behavior:** Returns `[]` (no match for Latvian natural language) - -**Enhanced behavior:** -1. `natural_query` tool receives Latvian question -2. Extracts intent: "systems implementing language technology goal" -3. Maps to goal.m4 (Latviešu valoda digitālajā laikmetā) -4. Traverses knowledge graph: goal.m4 → implemented_by → [sys.valoda.01, sys.valoda.02, ...] -5. Returns pre-computed summary + entity list - ---- - -## 5. Decision Framework: When to Add Cached Knowledge - -| Question | If YES | If NO | -|----------|--------|-------| -| Will multiple users query the same corpus? | Add caching | Skip | -| Is query latency critical (<1s)? | Add embeddings | Direct retrieval OK | -| Do users ask in natural language (not IDs)? | Add semantic search | ID-based lookup OK | -| Is the corpus >100 entities? | Add summaries | Full scan OK | -| Do queries require cross-entity reasoning? | Add knowledge graph | Flat search OK | -| Is the corpus updated less than daily? | Pre-compute aggressively | Real-time generation | - ---- - -## 6. Conclusion - -### Is MCP with Cached Knowledge Worth It? - -**YES, when:** -- You have a stable, structured knowledge base (like Architecture-as-Code) -- Multiple consumers need consistent access -- Natural language queries are required -- Token cost optimization matters -- Cross-entity reasoning is needed - -**NO, when:** -- One-time document analysis -- Rapidly changing data (real-time feeds) -- Simple keyword lookup suffices -- No multi-client requirement - -### For KISC Specifically: - -Your Architecture-as-Code approach is **fundamentally correct** but needs: -1. **Semantic search layer** (embeddings for Latvian content) -2. **Pre-computed summaries** (domain/subdomain level) -3. **Natural language interface** (Latvian query handling) -4. **Enhanced MCP tools** (beyond primitive substring search) - -The investment in these layers will pay off as the architecture grows and more stakeholders (internal teams, external auditors, automated agents) need to query it. - ---- - -## References - -- Model Context Protocol Specification: https://modelcontextprotocol.io/specification -- MCP Server Registry: https://github.com/modelcontextprotocol/servers -- Agentic RAG + MCP Integration Guide: https://becomingahacker.org/integrating-agentic-rag-with-mcp-servers -- AWS MCP Implementation: https://aws.amazon.com/blogs/machine-learning/unlocking-the-power-of-model-context-protocol-mcp-on-aws/ diff --git a/ikt-arh-kultura-valodu-tehnologijas/domains/placeholder.git b/ikt-arh-kultura-valodu-tehnologijas/domains/placeholder.git deleted file mode 100644 index f6e8391..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/domains/placeholder.git +++ /dev/null @@ -1 +0,0 @@ -placeholder.git diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/Dockerfile b/ikt-arh-kultura-valodu-tehnologijas/mcp/Dockerfile deleted file mode 100644 index 103d20b..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM node:20-alpine - -WORKDIR /app - -# Copy MCP server code -COPY mcp/package.json mcp/tsconfig.json /app/mcp/ -COPY mcp/src /app/mcp/src - -# Copy architecture data (SSOT) -COPY domains /app/domains - -WORKDIR /app/mcp -RUN npm ci || npm install -RUN npm run build - -ENV ARCH_ROOT=/app/domains -EXPOSE 8787 - -CMD ["npm", "run", "start"] diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/package-lock.json b/ikt-arh-kultura-valodu-tehnologijas/mcp/package-lock.json deleted file mode 100644 index 4bed118..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/package-lock.json +++ /dev/null @@ -1,2119 +0,0 @@ -{ - "name": "kisc-arch-mcp", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "kisc-arch-mcp", - "version": "0.1.0", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.0.4", - "dotenv": "^16.6.1", - "express": "^4.21.2", - "fast-glob": "^3.3.2", - "jose": "^5.10.0", - "js-yaml": "^4.1.0", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/js-yaml": "^4.0.9", - "@types/node": "^20.17.0", - "ts-node": "^10.9.2", - "typescript": "^5.6.3" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz", - "integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==", - "license": "MIT", - "dependencies": { - "@hono/node-server": "^1.19.7", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "jose": "^6.1.1", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "^1" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", - "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", - "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", - "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hono": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", - "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16.9.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jose": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", - "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkce-challenge": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/router/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/router/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/router/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - } - } -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/package.json b/ikt-arh-kultura-valodu-tehnologijas/mcp/package.json deleted file mode 100644 index 926ff0b..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "kisc-arch-mcp", - "version": "0.1.0", - "private": true, - "type": "module", - "scripts": { - "build": "tsc -p tsconfig.json", - "start": "node dist/index.js", - "gen:doc": "ts-node src/generateDoc.ts" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.0.4", - "dotenv": "^16.6.1", - "express": "^4.21.2", - "fast-glob": "^3.3.2", - "jose": "^5.10.0", - "js-yaml": "^4.1.0", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/js-yaml": "^4.0.9", - "@types/node": "^20.17.0", - "ts-node": "^10.9.2", - "typescript": "^5.6.3" - } -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/generateDoc.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/generateDoc.ts deleted file mode 100644 index df8aa89..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/generateDoc.ts +++ /dev/null @@ -1,285 +0,0 @@ -import fs from "node:fs/promises"; -import path from "node:path"; -import yaml from "js-yaml"; - -type AnyObj = Record; - -const REPO_ROOT = process.env.REPO_ROOT || path.resolve(__dirname, "../../"); -const DOMAIN_DIR = process.env.DOMAIN_DIR || "domains/kultura-valoda"; -const OUT_FILE = process.env.OUT_FILE || "KISC-merkarhitektura-apraksts.md"; - -async function readText(rel: string): Promise { - const abs = path.join(REPO_ROOT, rel); - return fs.readFile(abs, "utf-8"); -} - -async function readYaml(rel: string): Promise { - const abs = path.join(REPO_ROOT, rel); - const raw = await fs.readFile(abs, "utf-8"); - const obj = yaml.load(raw); - if (!obj || typeof obj !== "object") return {}; - return obj as AnyObj; -} - -function mdEscape(s: any): string { - const t = (s ?? "").toString(); - return t.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim(); -} - -function mdTable(headers: string[], rows: string[][]): string { - const h = `| ${headers.join(" | ")} |`; - const sep = `| ${headers.map(() => "---").join(" | ")} |`; - const body = rows.map((r) => `| ${r.join(" | ")} |`).join("\n"); - return [h, sep, body].filter(Boolean).join("\n"); -} - -function isRegisterRefLine(line: string): string | null { - // Matches: `registers/..../file.yaml` or `registers/..../file.yml` - const m = line.match(/`(registers\/[^`]+\.(yaml|yml))`/i); - return m?.[1] ?? null; -} - -function isDiagramRefLine(line: string): string | null { - const m = line.match(/`(diagrams\/[^`]+\.(mmd|png|svg|pdf))`/i); - return m?.[1] ?? null; -} - -function renderAbbreviations(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - const rows = items.map((it: any) => [mdEscape(it.abbr), mdEscape(it.meaning)]); - return mdTable(["Saīsinājums", "Nozīme"], rows); -} - -function renderTerms(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - // Accept both {term, meaning} and {name, definition} - const rows = items.map((it: any) => [ - mdEscape(it.term ?? it.name ?? it.id ?? ""), - mdEscape(it.meaning ?? it.definition ?? it.description ?? ""), - ]); - return mdTable(["Termins", "Skaidrojums"], rows); -} - -function renderRelatedDocs(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - const rows = items.map((it: any) => [ - mdEscape(it.id), - mdEscape(it.title), - mdEscape(it.date ?? ""), - mdEscape(it.type ?? ""), - mdEscape(it.relevance ?? ""), - ]); - return mdTable(["ID", "Nosaukums", "Datums", "Veids", "Saistība / nozīme"], rows); -} - -function renderOrganizations(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - const rows = items.map((it: any) => [ - mdEscape(it.id), - mdEscape(it.title), - mdEscape(it.role ?? ""), - mdEscape(Array.isArray(it.responsibilities) ? it.responsibilities.join("; ") : ""), - mdEscape(it.status ?? ""), - ]); - return mdTable(["ID", "Organizācija", "Loma", "Atbildība (kopsavilkums)", "Statuss"], rows); -} - -function renderGenericItemsTable(obj: AnyObj, preferredCols: string[] | null = null): string { - const items = Array.isArray(obj.items) ? obj.items : []; - if (!items.length) return "_(tukšs reģistrs)_"; - - const allKeys = new Set(); - for (const it of items) { - if (it && typeof it === "object") Object.keys(it).forEach((k) => allKeys.add(k)); - } - - const cols = preferredCols - ? preferredCols.filter((c) => allKeys.has(c)).concat([...allKeys].filter((k) => !preferredCols.includes(k))) - : [...allKeys]; - - const headers = cols.map((c) => c); - const rows = items.map((it: any) => - cols.map((c) => { - const v = it?.[c]; - if (Array.isArray(v)) return mdEscape(v.join(", ")); - if (v && typeof v === "object") return mdEscape(JSON.stringify(v)); - return mdEscape(v ?? ""); - }) - ); - - return mdTable(headers, rows); -} - -function renderLegalActs(obj: AnyObj): string { - const groups = Array.isArray(obj.groups) ? obj.groups : []; - if (!groups.length) return "_(tukšs reģistrs)_"; - - const out: string[] = []; - for (const g of groups) { - out.push(`### ${mdEscape(g.title ?? g.id ?? "")}`.trim()); - const items = Array.isArray(g.items) ? g.items : []; - if (!items.length) { - out.push("_(nav ierakstu)_"); - out.push(""); - continue; - } - for (const it of items) { - const ref = mdEscape(it.ref ?? ""); - const notes = mdEscape(it.notes ?? ""); - out.push(`- **${ref}** — ${notes}`.trim()); - } - out.push(""); - } - return out.join("\n").trim(); -} - -function renderRoadmap(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - if (!items.length) return "_(tukšs reģistrs)_"; - - const rows = items.map((it: any) => [ - mdEscape(it.id), - mdEscape(it.title), - mdEscape(it.timeframe ?? ""), - mdEscape(Array.isArray(it.owner_orgs) ? it.owner_orgs.join(", ") : ""), - mdEscape(Array.isArray(it.depends_on) ? it.depends_on.join(", ") : ""), - mdEscape(Array.isArray(it.outputs) ? it.outputs.join("; ") : ""), - ]); - - return mdTable(["ID", "Pasākums", "Laika posms", "Atbildīgie", "Atkarības", "Rezultāti"], rows); -} - -function renderRisks(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - if (!items.length) return "_(tukšs reģistrs)_"; - - const rows = items.map((it: any) => [ - mdEscape(it.id), - mdEscape(it.risk), - mdEscape(it.likelihood ?? ""), - mdEscape(it.impact ?? ""), - mdEscape(Array.isArray(it.mitigation) ? it.mitigation.join("; ") : it.mitigation ?? ""), - mdEscape(Array.isArray(it.owner_orgs) ? it.owner_orgs.join(", ") : ""), - ]); - - return mdTable(["ID", "Risks", "Varbūtība", "Ietekme", "Mazināšana", "Atbildīgie"], rows); -} - -function renderInteractions(obj: AnyObj): string { - const items = Array.isArray(obj.items) ? obj.items : []; - if (!items.length) return "_(tukšs reģistrs)_"; - - const out: string[] = []; - for (const it of items) { - out.push(`### ${mdEscape(it.title ?? it.id ?? "")}`.trim()); - out.push(`- **ID:** ${mdEscape(it.id)}`); - out.push(`- **Tips:** ${mdEscape(it.interaction_type ?? "")}`); - out.push(`- **Pretējā puse:** ${mdEscape(it.counterpart ?? "")}`); - if (Array.isArray(it.components) && it.components.length) { - out.push(`- **Komponentes:** ${mdEscape(it.components.join(", "))}`); - } - if (it.why_it_matters) { - out.push(`- **Kāpēc svarīgi:** ${mdEscape(it.why_it_matters)}`); - } - if (Array.isArray(it.requirements) && it.requirements.length) { - out.push(`- **Prasības:** ${mdEscape(it.requirements.join("; "))}`); - } - out.push(""); - } - return out.join("\n").trim(); -} - -function renderRegisterByPath(relRegisterPathFromDomain: string, domainRoot: string): Promise { - const fullRel = path.posix.join(domainRoot, relRegisterPathFromDomain); - return (async () => { - const obj = await readYaml(fullRel); - - // Render by filename conventions - const file = relRegisterPathFromDomain.replace(/\\/g, "/"); - - if (file.endsWith("abbreviations.yaml") || file.endsWith("abbreviations.yml")) return renderAbbreviations(obj); - if (file.endsWith("terms.yaml") || file.endsWith("terms.yml")) return renderTerms(obj); - if (file.endsWith("related-documents.yaml") || file.endsWith("related-documents.yml")) return renderRelatedDocs(obj); - if (file.endsWith("organizations.yaml") || file.endsWith("organizations.yml")) return renderOrganizations(obj); - if (file.endsWith("legal-acts.yaml") || file.endsWith("legal-acts.yml")) return renderLegalActs(obj); - if (file.endsWith("roadmap.yaml") || file.endsWith("roadmap.yml")) return renderRoadmap(obj); - if (file.endsWith("risks.yaml") || file.endsWith("risks.yml")) return renderRisks(obj); - if (file.endsWith("interactions.yaml") || file.endsWith("interactions.yml")) return renderInteractions(obj); - - // Known item-heavy registers: functions/services/info-resources/systems: use a nicer preferred order - const preferred = (() => { - if (file.includes("/04-functions/")) return ["id", "subdomain", "name", "change_status", "change_description"]; - if (file.includes("/05-services/")) return ["id", "subdomain", "name", "change_status", "change_description"]; - if (file.includes("/06-information-resources/")) - return ["id", "subdomain", "name", "type", "owner", "change_status", "change_description"]; - if (file.includes("/07-systems/")) return ["id", "subdomain", "name", "owner", "change_status", "change_description"]; - return null; - })(); - - return renderGenericItemsTable(obj, preferred); - })(); -} - -async function build() { - const domainRoot = DOMAIN_DIR.replace(/\\/g, "/"); - const manifestRel = path.posix.join(domainRoot, "manifest.yaml"); - const manifest = await readYaml(manifestRel); - - if (!Array.isArray(manifest.views)) { - throw new Error(`manifest.yaml missing 'views' array: ${manifestRel}`); - } - - const pieces: string[] = []; - pieces.push(`# ${manifest.title ?? "Dokuments"}`); - if (manifest.version) pieces.push(`\n_Versija: ${manifest.version}_\n`); - - for (const v of manifest.views) { - const viewFile = v?.file; - if (!viewFile) continue; - - const viewRel = path.posix.join(domainRoot, viewFile); - let md = await readText(viewRel); - - const outLines: string[] = []; - const lines = md.split(/\r?\n/); - - for (const line of lines) { - const regRef = isRegisterRefLine(line); - const diagramRef = isDiagramRefLine(line); - - if (regRef) { - // keep the original line, then render the register below it - outLines.push(line); - const rendered = await renderRegisterByPath(regRef, domainRoot); - outLines.push(""); - outLines.push(rendered); - outLines.push(""); - continue; - } - - if (diagramRef) { - // keep diagram placeholder as-is (per your rule) - outLines.push(line); - continue; - } - - outLines.push(line); - } - - pieces.push(outLines.join("\n").trimEnd()); - pieces.push(""); // spacing between views - } - - const finalMd = pieces.join("\n").replace(/\n{3,}/g, "\n\n").trim() + "\n"; - const outAbs = path.join(REPO_ROOT, OUT_FILE); - await fs.writeFile(outAbs, finalMd, "utf-8"); - - // eslint-disable-next-line no-console - console.log(`OK: generated ${OUT_FILE}`); -} - -build().catch((e) => { - // eslint-disable-next-line no-console - console.error(e); - process.exit(1); -}); diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/index.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/index.ts deleted file mode 100644 index fef00f9..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/index.ts +++ /dev/null @@ -1,1730 +0,0 @@ -import "dotenv/config"; -import express from "express"; -import { randomUUID, createHash, createPrivateKey, createPublicKey } from "node:crypto"; -import fs from "node:fs/promises"; -import path from "node:path"; -import fg from "fast-glob"; -import yaml from "js-yaml"; -import { createRemoteJWKSet, jwtVerify, SignJWT, exportJWK, generateKeyPair, importJWK, type KeyLike, type JWK } from "jose"; -import { z } from "zod"; - -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { isInitializeRequest, InitializeRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js"; - -import { getIndex, reload } from "./repo/index.js"; -import type { RepoIndex } from "./repo/types.js"; - -const PORT = Number(process.env.MCP_PORT || process.env.PORT || 8787); -const HOST = process.env.MCP_HOST || "0.0.0.0"; - -// Repo root is mounted at /repo inside docker (see Dockerfile); allow override for local dev. -const REPO_ROOT = process.env.REPO_ROOT || "/repo"; -const AUTH_REQUIRED = (process.env.AUTH_REQUIRED || "false").toLowerCase() === "true"; -const AUTH_MODE = (process.env.AUTH_MODE || "both").toLowerCase(); // "both" | "jwks" | "static" -const OAUTH_JWKS_URL = process.env.OAUTH_JWKS_URL || ""; -const OAUTH_ISSUER = process.env.OAUTH_ISSUER || ""; -const OAUTH_AUDIENCE = process.env.OAUTH_AUDIENCE || ""; -const STATIC_BEARER_TOKEN = process.env.STATIC_BEARER_TOKEN || ""; - -// Built-in OAuth settings -const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENT_ID || ""; -const OAUTH_CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET || ""; - -// Fixed login credentials for the built-in authorization page -const OAUTH_LOGIN_USERNAME = process.env.OAUTH_LOGIN_USERNAME || "admin"; -const OAUTH_LOGIN_PASSWORD = process.env.OAUTH_LOGIN_PASSWORD || "change-me"; - -// Server public URL (used for OAuth metadata discovery) -const SERVER_PUBLIC_URL = process.env.SERVER_PUBLIC_URL || `http://localhost:${PORT}`; - -const useJwks = AUTH_MODE === "jwks" || AUTH_MODE === "both"; -const useStatic = AUTH_MODE === "static" || AUTH_MODE === "both"; - -// ============================================================================= -// Repository Index - Loads entities, edges, and text index at startup -// ============================================================================= -let repoIndex: RepoIndex; - -function initRepoIndex() { - console.log("Loading repository index..."); - try { - repoIndex = getIndex(REPO_ROOT); - console.log(`✅ Index loaded: ${repoIndex.entitiesById.size} entities, ${repoIndex.edges.length} edges`); - } catch (err) { - console.error("❌ Failed to load repository index:", err); - // Initialize empty index as fallback - repoIndex = { - entitiesById: new Map(), - edges: [], - textIndex: [], - resources: [], - }; - } -} - -// Initialize repository index synchronously at startup -initRepoIndex(); - -// --- Signing key (shared by all OAuth flows) --- -let signingKey: KeyLike | null = null; -let publicJwk: JWK; -const TOKEN_TTL_SECONDS = 3600; - -async function initSigningKey() { - const { privateKey, publicKey } = await generateKeyPair("RS256"); - signingKey = privateKey; - publicJwk = await exportJWK(publicKey); - publicJwk.kid = "mcp-builtin-1"; - publicJwk.alg = "RS256"; - publicJwk.use = "sig"; -} - -// ============================================================================= -// MCPF Trust Framework - Private Key for Response Signing -// ============================================================================= -let mcpfPrivateKey: KeyLike | null = null; -let mcpfPublicKey: JWK | null = null; - -async function initMcpfKeys() { - const privateKeyPem = process.env.MCPF_PRIVATE_KEY; - if (!privateKeyPem) { - console.warn("⚠️ MCPF_PRIVATE_KEY not found in .env - response signing disabled"); - return; - } - - try { - // Import PEM directly via node:crypto, then use as KeyLike with jose - mcpfPrivateKey = createPrivateKey(privateKeyPem); - - const publicKeyPem = process.env.MCPF_PUBLIC_KEY; - if (publicKeyPem) { - mcpfPublicKey = await exportJWK(createPublicKey(publicKeyPem)); - } - - console.log("✅ MCPF signing key initialized"); - } catch (error) { - console.error("❌ Failed to initialize MCPF signing key:", error); - } -} - -// Always init signing key when auth is required (needed for auth code flow) -if (AUTH_REQUIRED) { - initSigningKey().catch((err) => - console.error("Failed to init signing key:", err) - ); -} - -// Init MCPF keys if configured -if (AUTH_REQUIRED || process.env.MCPF_PRIVATE_KEY) { - initMcpfKeys().catch((err) => console.error("Failed to init MCPF keys:", err)); -} - -// --- External JWKS (for external IDP tokens) --- -let externalJwks: ReturnType | null = null; -if (AUTH_REQUIRED && useJwks && OAUTH_JWKS_URL) { - externalJwks = createRemoteJWKSet(new URL(OAUTH_JWKS_URL)); -} - -// --- Dynamic Client Registration store (in-memory, RFC 7591) --- -interface RegisteredClient { - client_id: string; - client_secret?: string; - client_name?: string; - redirect_uris: string[]; - grant_types: string[]; - response_types: string[]; - token_endpoint_auth_method: string; - created_at: number; -} -const registeredClients = new Map(); - -// Pre-register the fixed machine-to-machine client if configured -if (OAUTH_CLIENT_ID && OAUTH_CLIENT_SECRET) { - registeredClients.set(OAUTH_CLIENT_ID, { - client_id: OAUTH_CLIENT_ID, - client_secret: OAUTH_CLIENT_SECRET, - client_name: "MCP Service Account", - redirect_uris: [], - grant_types: ["client_credentials"], - response_types: [], - token_endpoint_auth_method: "client_secret_post", - created_at: Date.now(), - }); -} - -// --- Authorization code store (in-memory) --- -interface AuthCode { - code: string; - client_id: string; - redirect_uri: string; - code_challenge: string; - code_challenge_method: string; - scope: string; - resource?: string; - expires_at: number; -} -const authCodes = new Map(); - -// --- Refresh token store --- -interface RefreshEntry { - refresh_token: string; - client_id: string; - scope: string; - created_at: number; -} -const refreshTokens = new Map(); - -type AnyObj = Record; - -function extractBearerToken(authHeader: string | undefined): string | null { - if (!authHeader) return null; - const match = authHeader.match(/^Bearer\s+(.+)$/i); - return match ? match[1] : null; -} - -async function authMiddleware( - req: express.Request, - res: express.Response, - next: express.NextFunction -) { - try { - if (!AUTH_REQUIRED) return next(); - - const token = extractBearerToken(req.headers.authorization); - if (!token) { - res - .status(401) - .setHeader( - "WWW-Authenticate", - `Bearer resource_metadata="${SERVER_PUBLIC_URL}/.well-known/oauth-protected-resource"` - ) - .json({ error: "missing_bearer_token" }); - return; - } - - // --- Try static token first (fast path) --- - if (useStatic && STATIC_BEARER_TOKEN && token === STATIC_BEARER_TOKEN) { - return next(); - } - - // --- Try built-in signing key (auth code + client_credentials tokens) --- - if (publicJwk) { - try { - const key = await importJWK(publicJwk, "RS256"); - await jwtVerify(token, key as KeyLike, { - issuer: "kisc-arch-mcp", - audience: "kisc-arch-mcp", - }); - return next(); - } catch { - // Not a built-in token — fall through to external JWKS - } - } - - // --- Try external JWKS --- - if (useJwks && externalJwks) { - const verifyOpts: { issuer?: string; audience?: string } = {}; - if (OAUTH_ISSUER) verifyOpts.issuer = OAUTH_ISSUER; - if (OAUTH_AUDIENCE) verifyOpts.audience = OAUTH_AUDIENCE; - await jwtVerify(token, externalJwks, verifyOpts); - return next(); - } - - res - .status(401) - .setHeader( - "WWW-Authenticate", - `Bearer resource_metadata="${SERVER_PUBLIC_URL}/.well-known/oauth-protected-resource"` - ) - .json({ error: "invalid_token" }); - } catch (error) { - res - .status(401) - .setHeader( - "WWW-Authenticate", - `Bearer resource_metadata="${SERVER_PUBLIC_URL}/.well-known/oauth-protected-resource"` - ) - .json({ error: "unauthorized" }); - } -} - -async function loadYamlFile(filePath: string): Promise { - const raw = await fs.readFile(filePath, "utf-8"); - const obj = yaml.load(raw); - if (!obj || typeof obj !== "object") return {}; - return obj as AnyObj; -} - -async function listRegisterFiles(): Promise { - const patterns = ["domains/**/registers/**/*.yml", "domains/**/registers/**/*.yaml", "domains/**/views/**/*.md"]; - return fg(patterns, { cwd: REPO_ROOT, dot: false, onlyFiles: true }); -} - -function normalizeText(value: string): string { - return (value || "").toString().toLowerCase(); -} - -async function searchInRepo( - query: string, - limit = 25, - kind?: string, - subdomain?: string -) { - const q = normalizeText(query); - if (!q) return []; - - const hits: Array<{ - id: string; - score: number; - kind: string; - subdomain?: string; - entity?: Record; - }> = []; - - // Search using the repo index's textIndex - for (const row of repoIndex.textIndex) { - if (kind && row.kind !== kind) continue; - if (subdomain && row.subdomain !== subdomain) continue; - - // Score by substring occurrence count - let score = 0; - let pos = row.haystack.indexOf(q); - while (pos !== -1) { - score++; - pos = row.haystack.indexOf(q, pos + q.length); - } - - if (score > 0) { - const entity = repoIndex.entitiesById.get(row.id); - hits.push({ - id: row.id, - score, - kind: row.kind, - subdomain: row.subdomain, - entity: entity as Record | undefined, - }); - } - } - - // Also search markdown views for fallback - const viewFiles = await fg(["domains/**/views/*.md"], { - cwd: REPO_ROOT, - onlyFiles: true, - }); - for (const rel of viewFiles) { - const abs = path.join(REPO_ROOT, rel); - try { - const txt = await fs.readFile(abs, "utf-8"); - const idx = normalizeText(txt).indexOf(q); - if (idx >= 0) { - const start = Math.max(0, idx - 80); - const end = Math.min(txt.length, idx + 160); - hits.push({ - id: `view:${rel}`, - score: 1, - kind: "markdown", - subdomain: undefined, - entity: { - id: `view:${rel}`, - file: rel, - snippet: txt.slice(start, end).replace(/\s+/g, " ").trim(), - }, - }); - } - } catch { - // Skip unreadable files - } - } - - hits.sort((a, b) => b.score - a.score); - return hits.slice(0, limit); -} - -async function getEntityById(entityId: string) { - // First try the indexed lookup (fast O(1)) - const entity = repoIndex.entitiesById.get(entityId); - if (entity) { - return { - id: entityId, - file: entity._source_path, - entity, - }; - } - - // Fallback to file scanning for entities not in index - const files = await fg( - ["domains/**/registers/**/*.yml", "domains/**/registers/**/*.yaml", "registers/**/*.yml", "registers/**/*.yaml"], - { cwd: REPO_ROOT, onlyFiles: true } - ); - - for (const rel of files) { - const abs = path.join(REPO_ROOT, rel); - const data = await loadYamlFile(abs); - - const tryMatch = (obj: unknown): unknown | null => { - if (!obj) return null; - - if (Array.isArray(obj)) { - return obj.find( - (item) => item && typeof item === "object" && (item as { id?: string }).id === entityId - ); - } - - if (typeof obj === "object") { - const record = obj as Record; - if ((record.id as string | undefined) === entityId) return record; - if (Array.isArray(record.items)) { - return record.items.find( - (item) => item && typeof item === "object" && (item as { id?: string }).id === entityId - ); - } - if (Object.prototype.hasOwnProperty.call(record, entityId)) { - return record[entityId]; - } - } - - return null; - }; - - const found = tryMatch(data); - if (found) { - return { id: entityId, file: rel, entity: found }; - } - } - - return null; -} - -// ============================================================================= -// MCPF Trust Framework - Response Attestation -// ============================================================================= - -async function createResponseAttestation( - textContent: string, - requestId?: string | number -): Promise<{ type: "resource"; resource: Record } | null> { - if (!mcpfPrivateKey) { - return null; - } - - try { - const contentHash = createHash("sha256").update(textContent, "utf-8").digest("hex"); - - const payload = { - did: "did:web:llm.kis.gov.lv", - contentHash: `sha256:${contentHash}`, - signedAt: new Date().toISOString(), - }; - - const signature = await new SignJWT(payload) - .setProtectedHeader({ alg: "EdDSA", typ: "JWT" }) - .setIssuedAt() - .setIssuer("did:web:llm.kis.gov.lv") - .sign(mcpfPrivateKey); - - return { - type: "resource", - resource: { - uri: `did:web:llm.kis.gov.lv#attestation/${requestId || Date.now()}`, - mimeType: "application/json", - text: JSON.stringify({ - did: payload.did, - contentHash: payload.contentHash, - signature, - signedAt: payload.signedAt, - }), - annotations: { - audience: ["assistant"], - priority: 0.1, - }, - }, - }; - } catch (error) { - console.error("Failed to create response attestation:", error); - return null; - } -} - -function createMcpServer() { - const server = new McpServer( - { - name: "kisc-arch-kultura-valodu-mcp", - version: "0.3.0", - }, - { - capabilities: { - resources: {}, - tools: { listChanged: true }, - }, - } - ); - - // ============================================================================= - // MCPF Layer 1: Session-Level Trust Metadata in _meta - // Inject _meta into initialize response without breaking SDK internals - // ============================================================================= - const lowLevelServer = server.server; - const originalOnInitialize = (lowLevelServer as unknown as { _oninitialize: (req: unknown) => Promise> })._oninitialize.bind(lowLevelServer); - - lowLevelServer.setRequestHandler( - InitializeRequestSchema, - async (request) => { - // Call the SDK's original handler to preserve session state - const baseResponse = await originalOnInitialize(request); - - const _meta = { - identity: { - id: "did:web:llm.kis.gov.lv", - service: { - mcp: "https://llm.kis.gov.lv/mcp", - }, - keys: { - jwks_uri: "https://llm.kis.gov.lv/.well-known/jwks.json", - }, - }, - mcpf: { - version: "0.1", - spec: { - repository: "https://github.com/MCPTrustFramework/MCPF-specification", - }, - entrypoint: { - type: "manifest", - url: "https://llm.kis.gov.lv/.well-known/mcp/manifest.json", - }, - artifacts: { - trust_registry: "https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json", - credential: "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json", - }, - }, - trust: { - verifications: [ - { - verifier: "did:web:veritrust.vc", - type: ["VerifiableCredential", "MCPServerVerification"], - credential: "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json", - covers: "did:web:llm.kis.gov.lv", - proof_hint: { - verificationMethod: "did:web:veritrust.vc#key-1", - created: "2026-01-29T10:12:41Z", - }, - }, - ], - }, - }; - - return { ...baseResponse, _meta }; - } - ); - - server.registerTool( - "search", - { - title: "Search Latvian cultural heritage and language technology architecture", - description: - "Search KISC architecture documentation for Latvian government cultural digitalization. " + - "Contains: strategic goals (M1-M6), organizations (KISC, LNB, LUMII, ministries, museums), " + - "information systems (kultūras IS, valodu tehnoloģiju platformas), services, " + - "information resources, roadmap initiatives, risks, and legal/regulatory mappings. " + - "Use for: kultūras mantojums digitalizācija, valodu tehnoloģijas, AI Act compliance, " + - "GDPR, open data, cultural heritage architecture. " + - "Returns matching entities with full details.", - inputSchema: { - query: z.string().describe( - "Search query - use Latvian or English terms. Examples: 'valodu tehnoloģijas', " + - "'KISC', 'digitālais mantojums', 'language model', 'AI risks'" - ), - limit: z.number().optional().describe("Max results (default 25)"), - kind: z.string().optional().describe( - "Filter by entity kind: goal, org, sys, svc, ir, func, principle, etc." - ), - subdomain: z.string().optional().describe( - "Filter by subdomain: 'kultura' or 'valoda'" - ), - }, - }, - async ({ query, limit, kind, subdomain }) => { - const results = await searchInRepo( - query, - Number.isFinite(limit) ? Number(limit) : 25, - kind, - subdomain - ); - const textContent = JSON.stringify({ query, kind, subdomain, count: results.length, results }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - server.registerTool( - "get_entity", - { - title: "Get architecture entity by ID", - description: - "Retrieve a specific entity from KISC architecture registers by its canonical ID. " + - "Entity types: " + - "goal.m1-m6 (strategic goals like 'Latviešu valoda digitālajā laikmetā'), " + - "org.* (organizations: org.kisc, org.lnb, org.lumii, org.varam, org.km), " + - "sys.kultura.*, sys.valoda.* (information systems), " + - "svc.kultura.*, svc.valoda.* (services), " + - "ir.kultura.*, ir.valoda.* (information resources), " + - "func.01-06 (domain functions), " + - "principle.p1-p4 (architecture principles), " + - "rm.001-010 (roadmap items), " + - "risk.001-008 (identified risks), " + - "doc.* (legal/reference documents like GDPR, AI Act). " + - "Use list_entities first if you don't know the exact ID.", - inputSchema: { - id: z.string().describe( - "Entity ID, e.g.: goal.m4, org.kisc, sys.valoda.01, svc.kultura.05, risk.002.data-quality" - ), - }, - }, - async ({ id }) => { - const found = await getEntityById(id); - const textContent = found - ? JSON.stringify(found, null, 2) - : JSON.stringify({ id, found: false }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // MCPF Trust Discovery Tool: "identify" - // Allows AI agents to verify server identity and trust credentials. - // This tool exposes the same trust metadata as _meta in initialize response, - // solving the bootstrapping problem when MCP hosts strip _meta. - // ============================================================================= - server.registerTool( - "identify", - { - title: "Server identity and trust credentials", - description: - "Returns server identity, ownership proof, and trust credentials. " + - "Call this to verify who operates this MCP server before trusting its data.", - inputSchema: { - // No required inputs - this is a self-describing identity endpoint - include_verification_urls: z - .boolean() - .optional() - .describe("Include URLs for external verification (default: true)"), - }, - }, - async ({ include_verification_urls }) => { - const includeUrls = include_verification_urls !== false; - - // Server identity - who is this? - const identity = { - server: { - name: "kisc-arch-kultura-valodu-mcp", - version: "0.2.0", - description: - "MCP server for Latvian cultural heritage and language technology architecture documentation", - }, - operator: { - name: "Kultūras informācijas sistēmu centrs (KISC)", - name_en: "Cultural Information Systems Centre", - jurisdiction: "Latvia", - sector: "Government", - }, - did: "did:web:llm.kis.gov.lv", - }; - - // Trust framework compliance - const framework = { - name: "MCPF", - version: "0.1", - layer: 1, - compliance: ["session-trust-metadata", "tool-based-discovery"], - spec: includeUrls - ? "https://github.com/MCPTrustFramework/MCPF-specification" - : undefined, - }; - - // Verifiable credentials and trust verification - const credentials = { - issuer: "did:web:veritrust.vc", - type: ["VerifiableCredential", "MCPServerVerification"], - subject: "did:web:llm.kis.gov.lv", - issued: "2026-01-29T10:12:41Z", - credential_url: includeUrls - ? "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json" - : undefined, - }; - - // Verification endpoints for agents that want to verify cryptographically - const verification = includeUrls - ? { - jwks_uri: "https://llm.kis.gov.lv/.well-known/jwks.json", - did_document: "https://llm.kis.gov.lv/.well-known/did.json", - mcp_manifest: "https://llm.kis.gov.lv/.well-known/mcp/manifest.json", - trust_registry: "https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json", - } - : undefined; - - const identityResponse = { - identity, - framework, - credentials, - ...(verification && { verification }), - _note: - "This server implements MCPF Layer 1 trust discovery. " + - "Verify credentials at the provided URLs or trust this self-attestation based on your policy.", - }; - - const textContent = JSON.stringify(identityResponse, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - // Add cryptographic attestation if signing key is available - const attestation = await createResponseAttestation(textContent, "identify"); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // list_entities: List available entity IDs by type - // ============================================================================= - server.registerTool( - "list_entities", - { - title: "List available entity IDs by type", - description: - "List all entity IDs in KISC architecture, optionally filtered by type. " + - "Use this to discover what entities exist before calling get_entity. " + - "Types: goal, org, sys, svc, ir, func, principle, rm (roadmap), risk, doc, int (interaction), legal, domain, subdomain.", - inputSchema: { - type: z.string().optional().describe( - "Filter by entity type prefix: 'goal', 'org', 'sys', 'svc', 'ir', 'func', " + - "'principle', 'rm', 'risk', 'doc', 'int', 'legal'. Omit to list all." - ), - subdomain: z.string().optional().describe( - "Filter by subdomain: 'kultura' or 'valoda'" - ), - }, - }, - async ({ type, subdomain }) => { - const files = await fg(["domains/**/registers/**/*.yml", "domains/**/registers/**/*.yaml"], { - cwd: REPO_ROOT, - onlyFiles: true, - }); - - const ids: string[] = []; - - for (const rel of files) { - const abs = path.join(REPO_ROOT, rel); - const data = await loadYamlFile(abs); - - const extractIds = (obj: unknown): void => { - if (!obj) return; - if (Array.isArray(obj)) { - obj.forEach(item => { - if (item?.id) ids.push(item.id); - }); - } else if (typeof obj === "object") { - const record = obj as Record; - if (record.id) ids.push(record.id as string); - if (Array.isArray(record.items)) { - record.items.forEach((item: any) => { - if (item?.id) ids.push(item.id); - }); - } - } - }; - - extractIds(data); - } - - let filtered = [...new Set(ids)].sort(); - - if (type) { - const prefix = type.toLowerCase(); - filtered = filtered.filter(id => id.toLowerCase().startsWith(prefix + ".")); - } - - if (subdomain) { - const sub = subdomain.toLowerCase(); - filtered = filtered.filter(id => id.toLowerCase().includes(`.${sub}.`)); - } - - const textContent = JSON.stringify({ - count: filtered.length, - type: type || "all", - subdomain: subdomain || "all", - ids: filtered, - }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // list_relations: List entity relationships - // ============================================================================= - server.registerTool( - "list_relations", - { - title: "List entity relationships", - description: - "Find all relationships (edges) connected to an entity. " + - "Returns incoming and/or outgoing edges from the architecture graph. " + - "Relationship types include: has_goal, has_function, has_service, " + - "has_system, has_info_resource, owns, manages, depends_on, implements, etc.", - inputSchema: { - id: z.string().describe("Entity ID to find relations for, e.g. goal.m4 or org.kisc"), - direction: z.enum(["in", "out", "both"]).optional().describe( - "Direction: 'in' (incoming), 'out' (outgoing), 'both' (default)" - ), - }, - }, - async ({ id, direction = "both" }) => { - const edgesFile = path.join(REPO_ROOT, "domains/kultura-valoda/registers/99-relations/edges.yaml"); - let allEdges: Array<{ from: string; type: string; to: string }> = []; - - try { - const data = await loadYamlFile(edgesFile); - if (Array.isArray((data as any).edges)) { - allEdges = (data as any).edges; - } - } catch { - // No edges file - } - - const filtered = allEdges.filter(e => { - if (direction === "out") return e.from === id; - if (direction === "in") return e.to === id; - return e.from === id || e.to === id; - }); - - const textContent = JSON.stringify({ - entity: id, - direction, - count: filtered.length, - edges: filtered, - }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // subgraph: Extract connected subgraph - // ============================================================================= - server.registerTool( - "subgraph", - { - title: "Extract connected subgraph", - description: - "Starting from one or more seed entities, traverse the relationship graph " + - "and return all connected entities up to a specified depth. " + - "Useful for understanding what systems, services, and organizations " + - "are related to a goal or function.", - inputSchema: { - seed_ids: z.array(z.string()).describe( - "Starting entity IDs, e.g. ['goal.m4'] or ['org.kisc', 'org.lumii']" - ), - depth: z.number().optional().describe( - "Traversal depth (default 1, max 3). Depth 1 = direct connections only." - ), - }, - }, - async ({ seed_ids, depth = 1 }) => { - const safeDepth = Math.min(Math.max(depth, 0), 3); - - // Load edges - const edgesFile = path.join(REPO_ROOT, "domains/kultura-valoda/registers/99-relations/edges.yaml"); - let allEdges: Array<{ from: string; type: string; to: string }> = []; - try { - const data = await loadYamlFile(edgesFile); - if (Array.isArray((data as any).edges)) { - allEdges = (data as any).edges; - } - } catch {} - - // Traverse - const nodes = new Set(seed_ids); - const collectedEdges: Array<{ from: string; type: string; to: string }> = []; - let frontier = new Set(seed_ids); - - for (let d = 0; d < safeDepth; d++) { - const next = new Set(); - for (const e of allEdges) { - if (frontier.has(e.from)) { - collectedEdges.push(e); - if (!nodes.has(e.to)) next.add(e.to); - nodes.add(e.to); - } - if (frontier.has(e.to)) { - collectedEdges.push(e); - if (!nodes.has(e.from)) next.add(e.from); - nodes.add(e.from); - } - } - frontier = next; - if (!frontier.size) break; - } - - // Fetch entity details for all nodes - const nodeDetails: Array> = []; - for (const nodeId of nodes) { - const entity = await getEntityById(nodeId); - if (entity) { - nodeDetails.push(entity.entity as Record); - } else { - nodeDetails.push({ id: nodeId, _kind: "unknown" }); - } - } - - const textContent = JSON.stringify({ - seeds: seed_ids, - depth: safeDepth, - node_count: nodeDetails.length, - edge_count: collectedEdges.length, - nodes: nodeDetails, - edges: collectedEdges, - }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // get_view: Get architecture view document - // ============================================================================= - server.registerTool( - "get_view", - { - title: "Get architecture view document", - description: - "Retrieve a human-readable markdown view from the architecture documentation. " + - "Views: " + - "01-ievads (introduction), " + - "02-esosas-arhitekturas-novertejums (current state assessment), " + - "03-merki-un-principi (goals and principles), " + - "04-merk-arhitektura (target architecture), " + - "05-cela-karte (roadmap), " + - "06-pielikums-komponentu-katalogs (component catalog). " + - "Returns raw markdown content for the specified view.", - inputSchema: { - view_id: z.enum([ - "01-ievads", - "02-esosas-arhitekturas-novertejums", - "03-merki-un-principi", - "04-merk-arhitektura", - "05-cela-karte", - "06-pielikums-komponentu-katalogs" - ]).describe("View identifier"), - }, - }, - async ({ view_id }) => { - const viewPath = path.join(REPO_ROOT, `domains/kultura-valoda/views/${view_id}.md`); - - let markdown: string; - try { - markdown = await fs.readFile(viewPath, "utf-8"); - } catch { - return { - content: [{ type: "text" as const, text: JSON.stringify({ error: `View not found: ${view_id}` }) }], - }; - } - - const textContent = JSON.stringify({ - view_id, - title: view_id.replace(/-/g, " ").replace(/^\d+-/, ""), - content: markdown, - }, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // generate_document: Generate full architecture document - // ============================================================================= - server.registerTool( - "generate_document", - { - title: "Generate full architecture document", - description: - "Regenerate the complete human-readable architecture document from YAML registers. " + - "Combines all views (01-ievads through 06-pielikums) with inline register expansions " + - "into a single comprehensive Markdown document. " + - "Use this when you need the full formatted document rather than individual entities.", - inputSchema: { - format: z.enum(["markdown", "json"]).optional().describe( - "Output format: 'markdown' (default) for human reading, 'json' for structured data" - ), - sections: z.array(z.string()).optional().describe( - "Specific sections to include. Omit for full document. " + - "Options: 'ievads', 'esosa', 'merki', 'arhitektura', 'celakarte', 'katalogs'" - ), - }, - }, - async ({ format = "markdown", sections }) => { - const domainRoot = "domains/kultura-valoda"; - const manifestRel = path.posix.join(domainRoot, "manifest.yaml"); - const manifest = await loadYamlFile(path.join(REPO_ROOT, manifestRel)); - - if (!Array.isArray((manifest as any).views)) { - return { - content: [{ type: "text" as const, text: JSON.stringify({ error: "manifest.yaml missing views" }) }], - }; - } - - const sectionMap: Record = { - "ievads": "01-ievads", - "esosa": "02-esosas-arhitekturas-novertejums", - "merki": "03-merki-un-principi", - "arhitektura": "04-merk-arhitektura", - "celakarte": "05-cela-karte", - "katalogs": "06-pielikums-komponentu-katalogs", - }; - - const views = (manifest as any).views as Array<{ id: number; title: string; file: string }>; - let filteredViews = views; - - if (sections && sections.length > 0) { - const allowedFiles = sections.map(s => sectionMap[s]).filter(Boolean); - filteredViews = views.filter(v => - allowedFiles.some(af => v.file.includes(af)) - ); - } - - const pieces: string[] = []; - pieces.push(`# ${(manifest as any).title || "Dokuments"}`); - if ((manifest as any).version) pieces.push(`\n_Versija: ${(manifest as any).version}_\n`); - - for (const v of filteredViews) { - const viewFile = v?.file; - if (!viewFile) continue; - - const viewRel = path.posix.join(domainRoot, viewFile); - let md: string; - try { - md = await fs.readFile(path.join(REPO_ROOT, viewRel), "utf-8"); - } catch { - continue; - } - - // Expand register references inline - const lines = md.split(/\r?\n/); - const outLines: string[] = []; - - for (const line of lines) { - const regMatch = line.match(/`(registers\/[^`]+\.(yaml|yml))`/i); - if (regMatch) { - outLines.push(line); - try { - const regRel = path.posix.join(domainRoot, regMatch[1]); - const regData = await loadYamlFile(path.join(REPO_ROOT, regRel)); - const items = Array.isArray((regData as any).items) ? (regData as any).items : []; - if (items.length > 0) { - // Simple table rendering - const keys = Object.keys(items[0]).filter(k => !k.startsWith("_")); - outLines.push(""); - outLines.push(`| ${keys.join(" | ")} |`); - outLines.push(`| ${keys.map(() => "---").join(" | ")} |`); - for (const item of items.slice(0, 50)) { - const row = keys.map(k => { - const val = item[k]; - if (Array.isArray(val)) return val.join(", "); - return String(val || "").replace(/\|/g, "\\|").replace(/\n/g, " "); - }); - outLines.push(`| ${row.join(" | ")} |`); - } - outLines.push(""); - } - } catch { - // Skip failed register expansion - } - continue; - } - outLines.push(line); - } - - pieces.push(outLines.join("\n").trimEnd()); - pieces.push(""); - } - - const finalMd = pieces.join("\n").replace(/\n{3,}/g, "\n\n").trim(); - - let textContent: string; - if (format === "json") { - textContent = JSON.stringify({ - title: (manifest as any).title, - version: (manifest as any).version, - sections: filteredViews.map(v => v.title), - content: finalMd, - }, null, 2); - } else { - textContent = finalMd; - } - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // describe_model: Describe architecture metamodel - // ============================================================================= - server.registerTool( - "describe_model", - { - title: "Describe architecture metamodel", - description: - "Returns documentation about the KISC architecture data model: " + - "entity types, their attributes, and relationship types. " + - "Use this to understand how the architecture data is structured.", - inputSchema: {}, - }, - async () => { - const metamodel = { - entity_types: { - "goal": { - description: "Strategic goals (M1-M6) for the domain", - id_pattern: "goal.m{1-6}", - attributes: ["id", "code", "title", "description", "status"], - }, - "org": { - description: "Organizations involved in the domain", - id_pattern: "org.{abbreviation}", - attributes: ["id", "title", "role", "responsibilities", "status"], - examples: ["org.kisc", "org.lnb", "org.lumii", "org.km"], - }, - "sys": { - description: "Information systems", - id_pattern: "sys.{subdomain}.{nn}", - subdomains: ["kultura", "valoda"], - attributes: ["id", "subdomain", "name", "owner", "change_status", "change_description"], - }, - "svc": { - description: "Services provided by systems", - id_pattern: "svc.{subdomain}.{nn}", - subdomains: ["kultura", "valoda"], - attributes: ["id", "subdomain", "name", "change_status", "change_description"], - }, - "ir": { - description: "Information resources (data assets)", - id_pattern: "ir.{subdomain}.{nn}", - subdomains: ["kultura", "valoda"], - attributes: ["id", "subdomain", "name", "type", "owner", "change_status"], - }, - "func": { - description: "Domain functions", - id_pattern: "func.{nn}", - attributes: ["id", "name", "subdomain", "change_status", "change_description"], - }, - "principle": { - description: "Architecture principles", - id_pattern: "principle.p{1-4}", - attributes: ["id", "name", "rationale", "implications"], - }, - "rm": { - description: "Roadmap initiatives", - id_pattern: "rm.{nnn}.*", - attributes: ["id", "title", "timeframe", "owner_orgs", "depends_on", "outputs"], - }, - "risk": { - description: "Identified risks", - id_pattern: "risk.{nnn}.*", - attributes: ["id", "risk", "likelihood", "impact", "mitigation", "owner_orgs"], - }, - "doc": { - description: "Reference documents and legal acts", - id_pattern: "doc.*", - attributes: ["id", "title", "date", "type", "relevance"], - }, - "int": { - description: "External interactions (interfaces with other domains)", - id_pattern: "int.{nnn}.*", - attributes: ["id", "title", "interaction_type", "counterpart", "requirements"], - }, - }, - relationship_types: [ - "has_goal", "has_function", "has_service", "has_system", "has_info_resource", - "owns", "manages", "operates", "develops", "governs", - "depends_on", "implements", "supports", "enables", - "regulates", "complies_with", - ], - subdomains: { - "kultura": "Kultūras mantojums - cultural heritage digitalization", - "valoda": "Valodu tehnoloģijas - language technology and AI", - }, - views: [ - { id: "01-ievads", title: "Ievads (Introduction)" }, - { id: "02-esosas-arhitekturas-novertejums", title: "Esošās arhitektūras novērtējums (Current State)" }, - { id: "03-merki-un-principi", title: "Mērķi un principi (Goals and Principles)" }, - { id: "04-merk-arhitektura", title: "Mērķarhitektūra (Target Architecture)" }, - { id: "05-cela-karte", title: "Ceļa karte (Roadmap)" }, - { id: "06-pielikums-komponentu-katalogs", title: "Komponentu katalogs (Component Catalog)" }, - ], - }; - - const textContent = JSON.stringify(metamodel, null, 2); - - const content: Array> = [ - { type: "text" as const, text: textContent }, - ]; - - const attestation = await createResponseAttestation(textContent); - if (attestation) { - content.push(attestation as unknown as Record); - } - - return { content } as { content: Array<{ type: "text"; text: string }> }; - } - ); - - // ============================================================================= - // MCP Resources Protocol - Expose index resources - // ============================================================================= - const lowLevelServerResources = server.server; - - lowLevelServerResources.setRequestHandler( - ListResourcesRequestSchema, - async () => { - return { - resources: repoIndex.resources.map(r => ({ - uri: r.uri, - name: r.title, - mimeType: r.mimeType, - })), - }; - } - ); - - lowLevelServerResources.setRequestHandler( - ReadResourceRequestSchema, - async (request) => { - const uri = request.params?.uri as string; - if (!uri) { - return { contents: [] }; - } - - // Handle different URI schemes - if (uri.startsWith("views://")) { - const match = uri.match(/views:\/\/([^/]+)\/(.+)/); - if (match) { - const [, domain, viewName] = match; - const viewPath = path.join(REPO_ROOT, `domains/${domain}/views/${viewName}.md`); - try { - const content = await fs.readFile(viewPath, "utf-8"); - return { - contents: [{ uri, mimeType: "text/markdown", text: content }], - }; - } catch { - return { contents: [] }; - } - } - } - - if (uri.startsWith("register://")) { - const match = uri.match(/register:\/\/([^/]+)\/(.+)/); - if (match) { - const [, domain, regPath] = match; - const filePath = path.join(REPO_ROOT, `domains/${domain}/${regPath}`); - try { - const content = await fs.readFile(filePath, "utf-8"); - return { - contents: [{ uri, mimeType: "application/yaml", text: content }], - }; - } catch { - return { contents: [] }; - } - } - } - - if (uri.startsWith("manifest://")) { - const domain = uri.replace("manifest://", ""); - const manifestPath = path.join(REPO_ROOT, `domains/${domain}/manifest.yaml`); - try { - const content = await fs.readFile(manifestPath, "utf-8"); - return { - contents: [{ uri, mimeType: "application/yaml", text: content }], - }; - } catch { - return { contents: [] }; - } - } - - return { contents: [] }; - } - ); - - return server; -} - -const transports: Record = {}; - -const app = express(); -app.use( - express.json({ - verify: (req, _res, buf) => { - (req as { rawBody?: string }).rawBody = buf?.toString() ?? ""; - }, - }) -); - -app.use((req, res, next) => { - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Access-Control-Allow-Headers", "content-type, mcp-session-id, authorization"); - res.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,OPTIONS"); - res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id"); - if (req.method === "OPTIONS") return res.status(204).end(); - next(); -}); - -// ============================================================ -// OAuth 2.1 / MCP Authorization endpoints -// ============================================================ - -// --- RFC 9728: Protected Resource Metadata --- -app.get("/.well-known/oauth-protected-resource", (_req, res) => { - res.json({ - resource: SERVER_PUBLIC_URL, - authorization_servers: [SERVER_PUBLIC_URL], - scopes_supported: ["mcp:access"], - bearer_methods_supported: ["header"], - }); -}); - -// --- RFC 8414: Authorization Server Metadata --- -app.get("/.well-known/oauth-authorization-server", (_req, res) => { - res.json({ - issuer: "kisc-arch-mcp", - authorization_endpoint: `${SERVER_PUBLIC_URL}/authorize`, - token_endpoint: `${SERVER_PUBLIC_URL}/token`, - registration_endpoint: `${SERVER_PUBLIC_URL}/register`, - jwks_uri: `${SERVER_PUBLIC_URL}/.well-known/jwks.json`, - response_types_supported: ["code"], - grant_types_supported: ["authorization_code", "client_credentials", "refresh_token"], - code_challenge_methods_supported: ["S256"], - token_endpoint_auth_methods_supported: ["client_secret_post", "none"], - scopes_supported: ["mcp:access"], - }); -}); - -// --- JWKS endpoint --- -app.get("/.well-known/jwks.json", async (_req, res) => { - if (!publicJwk) { - res.status(503).json({ error: "signing_key_not_ready" }); - return; - } - res.json({ keys: [publicJwk] }); -}); - -// --- RFC 7591: Dynamic Client Registration --- -app.post("/register", express.json(), (req, res) => { - const body = req.body || {}; - const clientId = `client_${randomUUID()}`; - const clientSecret = randomUUID(); - const redirectUris: string[] = Array.isArray(body.redirect_uris) ? body.redirect_uris : []; - const grantTypes: string[] = Array.isArray(body.grant_types) - ? body.grant_types - : ["authorization_code"]; - const responseTypes: string[] = Array.isArray(body.response_types) - ? body.response_types - : ["code"]; - const authMethod = body.token_endpoint_auth_method || "client_secret_post"; - - const client: RegisteredClient = { - client_id: clientId, - client_secret: clientSecret, - client_name: body.client_name || "Unknown", - redirect_uris: redirectUris, - grant_types: grantTypes, - response_types: responseTypes, - token_endpoint_auth_method: authMethod, - created_at: Date.now(), - }; - registeredClients.set(clientId, client); - - res.status(201).json({ - client_id: clientId, - client_secret: clientSecret, - client_name: client.client_name, - redirect_uris: redirectUris, - grant_types: grantTypes, - response_types: responseTypes, - token_endpoint_auth_method: authMethod, - }); -}); - -// --- Authorization endpoint (GET — shows login form, POST — processes it) --- -app.get("/authorize", (req, res) => { - const { response_type, client_id, redirect_uri, state, code_challenge, code_challenge_method, scope, resource } = req.query as Record; - - if (response_type !== "code") { - res.status(400).send("unsupported_response_type"); - return; - } - if (!client_id || !registeredClients.has(client_id)) { - res.status(400).send("invalid_client_id"); - return; - } - if (!code_challenge || code_challenge_method !== "S256") { - res.status(400).send("PKCE S256 required"); - return; - } - - // Render a minimal login form - const html = ` - -MCP — Authorize - -
-

KISC Architecture MCP

-

Authorize ${client_id.slice(0, 16)}… to access architecture data.

-
- - - - - - - - - - - -
-
`; - res.type("html").send(html); -}); - -app.post("/authorize", express.urlencoded({ extended: false }), (req, res) => { - const { client_id, redirect_uri, state, code_challenge, code_challenge_method, scope, resource, username, password } = req.body; - - // Validate credentials - if (username !== OAUTH_LOGIN_USERNAME || password !== OAUTH_LOGIN_PASSWORD) { - res.status(401).type("html").send(` -Login failed - -

Invalid credentials

Try again

-`); - return; - } - - if (!client_id || !registeredClients.has(client_id)) { - res.status(400).send("invalid_client_id"); - return; - } - - // Generate authorization code - const code = randomUUID(); - const entry: AuthCode = { - code, - client_id, - redirect_uri: redirect_uri || "", - code_challenge: code_challenge || "", - code_challenge_method: code_challenge_method || "S256", - scope: scope || "mcp:access", - resource: resource || undefined, - expires_at: Date.now() + 10 * 60 * 1000, // 10 minutes - }; - authCodes.set(code, entry); - - // Redirect back with code - const target = new URL(redirect_uri); - target.searchParams.set("code", code); - if (state) target.searchParams.set("state", state); - res.redirect(302, target.toString()); -}); - -// --- Token endpoint (auth code exchange, client_credentials, refresh) --- -app.post("/token", express.urlencoded({ extended: false }), async (req, res) => { - if (!signingKey) { - res.status(503).json({ error: "signing_key_not_ready" }); - return; - } - - const { grant_type } = req.body; - - // ---- Authorization Code exchange ---- - if (grant_type === "authorization_code") { - const { code, client_id, redirect_uri, code_verifier } = req.body; - - const entry = authCodes.get(code); - if (!entry) { - res.status(400).json({ error: "invalid_grant", error_description: "Invalid or expired code" }); - return; - } - authCodes.delete(code); - - if (entry.client_id !== client_id) { - res.status(400).json({ error: "invalid_grant", error_description: "client_id mismatch" }); - return; - } - if (entry.redirect_uri && entry.redirect_uri !== redirect_uri) { - res.status(400).json({ error: "invalid_grant", error_description: "redirect_uri mismatch" }); - return; - } - if (Date.now() > entry.expires_at) { - res.status(400).json({ error: "invalid_grant", error_description: "Code expired" }); - return; - } - - // Verify PKCE - if (entry.code_challenge) { - const { createHash } = await import("node:crypto"); - const expected = createHash("sha256") - .update(code_verifier || "") - .digest("base64url"); - if (expected !== entry.code_challenge) { - res.status(400).json({ error: "invalid_grant", error_description: "PKCE verification failed" }); - return; - } - } - - const now = Math.floor(Date.now() / 1000); - const accessToken = await new SignJWT({ sub: client_id, scope: entry.scope }) - .setProtectedHeader({ alg: "RS256", kid: "mcp-builtin-1" }) - .setIssuedAt(now) - .setExpirationTime(now + TOKEN_TTL_SECONDS) - .setIssuer("kisc-arch-mcp") - .setAudience("kisc-arch-mcp") - .sign(signingKey); - - const refreshToken = randomUUID(); - refreshTokens.set(refreshToken, { - refresh_token: refreshToken, - client_id, - scope: entry.scope, - created_at: Date.now(), - }); - - res.json({ - access_token: accessToken, - token_type: "Bearer", - expires_in: TOKEN_TTL_SECONDS, - refresh_token: refreshToken, - scope: entry.scope, - }); - return; - } - - // ---- Client Credentials ---- - if (grant_type === "client_credentials") { - const { client_id, client_secret } = req.body; - const client = registeredClients.get(client_id); - if (!client || client.client_secret !== client_secret) { - res.status(401).json({ error: "invalid_client" }); - return; - } - - const now = Math.floor(Date.now() / 1000); - const accessToken = await new SignJWT({ sub: client_id, scope: "mcp:access" }) - .setProtectedHeader({ alg: "RS256", kid: "mcp-builtin-1" }) - .setIssuedAt(now) - .setExpirationTime(now + TOKEN_TTL_SECONDS) - .setIssuer("kisc-arch-mcp") - .setAudience("kisc-arch-mcp") - .sign(signingKey); - - res.json({ - access_token: accessToken, - token_type: "Bearer", - expires_in: TOKEN_TTL_SECONDS, - }); - return; - } - - // ---- Refresh Token ---- - if (grant_type === "refresh_token") { - const { refresh_token, client_id } = req.body; - const entry = refreshTokens.get(refresh_token); - if (!entry || entry.client_id !== client_id) { - res.status(400).json({ error: "invalid_grant" }); - return; - } - refreshTokens.delete(refresh_token); - - const now = Math.floor(Date.now() / 1000); - const accessToken = await new SignJWT({ sub: client_id, scope: entry.scope }) - .setProtectedHeader({ alg: "RS256", kid: "mcp-builtin-1" }) - .setIssuedAt(now) - .setExpirationTime(now + TOKEN_TTL_SECONDS) - .setIssuer("kisc-arch-mcp") - .setAudience("kisc-arch-mcp") - .sign(signingKey); - - const newRefresh = randomUUID(); - refreshTokens.set(newRefresh, { - refresh_token: newRefresh, - client_id, - scope: entry.scope, - created_at: Date.now(), - }); - - res.json({ - access_token: accessToken, - token_type: "Bearer", - expires_in: TOKEN_TTL_SECONDS, - refresh_token: newRefresh, - scope: entry.scope, - }); - return; - } - - res.status(400).json({ error: "unsupported_grant_type" }); -}); - -// --- Health --- -app.get("/health", (_req, res) => { - res.json({ ok: true, name: "kisc-arch-mcp", transport: "streamable-http" }); -}); - -// --- Admin endpoint to reload index without restart --- -app.post("/admin/reload", async (_req, res) => { - try { - repoIndex = reload(REPO_ROOT); - res.json({ - ok: true, - entities: repoIndex.entitiesById.size, - edges: repoIndex.edges.length, - resources: repoIndex.resources.length, - }); - } catch (error) { - res.status(500).json({ ok: false, error: String(error) }); - } -}); - -// Normalise /MCP → /mcp (case-insensitive path) -app.use((req, _res, next) => { - if (req.path.toLowerCase() === "/mcp" && req.path !== "/mcp") { - req.url = "/mcp" + req.url.slice(req.path.length); - } - next(); -}); - -app.use("/mcp", authMiddleware); - -app.post("/mcp", async (req, res) => { - const sessionId = req.headers["mcp-session-id"] as string | undefined; - - let transport: StreamableHTTPServerTransport | undefined; - - if (sessionId && transports[sessionId]) { - transport = transports[sessionId]; - } else if (!sessionId && isInitializeRequest(req.body)) { - transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => randomUUID(), - onsessioninitialized: (newSessionId) => { - transports[newSessionId] = transport!; - }, - }); - - transport.onclose = () => { - if (transport?.sessionId) delete transports[transport.sessionId]; - }; - - const server = createMcpServer(); - await server.connect(transport); - } else { - res.status(400).json({ - jsonrpc: "2.0", - error: { code: -32000, message: "Bad Request: No valid session ID provided" }, - id: null, - }); - return; - } - - await transport.handleRequest(req, res, req.body); -}); - -async function handleSessionRequest(req: express.Request, res: express.Response) { - const sessionId = req.headers["mcp-session-id"] as string | undefined; - if (!sessionId || !transports[sessionId]) { - res.status(400).send("Invalid or missing session ID"); - return; - } - const transport = transports[sessionId]; - await transport.handleRequest(req, res); -} - -app.get("/mcp", handleSessionRequest); -app.delete("/mcp", handleSessionRequest); - -app.listen(PORT, HOST, () => { - // eslint-disable-next-line no-console - console.log(`MCP Streamable HTTP server listening on http://${HOST}:${PORT}/mcp`); -}); diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/index.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/index.ts deleted file mode 100644 index a1e6c60..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { loadRepo } from "./load.js"; -import { RepoIndex } from "./types.js"; - -let cached: RepoIndex | null = null; - -export function getIndex(archRoot: string): RepoIndex { - if (!cached) cached = loadRepo(archRoot); - return cached; -} - -// Optional: reload endpoint support (not exposed yet) -export function reload(archRoot: string): RepoIndex { - cached = loadRepo(archRoot); - return cached; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/load.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/load.ts deleted file mode 100644 index 6f7a1a6..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/load.ts +++ /dev/null @@ -1,195 +0,0 @@ -import fg from "fast-glob"; -import fs from "node:fs"; -import path from "node:path"; -import yaml from "js-yaml"; -import { Entity, Edge, RepoIndex } from "./types.js"; - -function toKindFromId(id: string): Entity["_kind"] { - if (id.startsWith("goal.")) return "goal"; - if (id.startsWith("func.")) return "function"; - if ( - id.startsWith("svc.kultura.") || - id.startsWith("svc.valoda.") || - id.startsWith("svc.") - ) - return "service"; - if ( - id.startsWith("ir.kultura.") || - id.startsWith("ir.valoda.") || - id.startsWith("ir.") - ) - return "information_resource"; - if ( - id.startsWith("sys.kultura.") || - id.startsWith("sys.valoda.") || - id.startsWith("sys.") - ) - return "system"; - if (id.startsWith("principle.")) return "principle"; - if (id.startsWith("org.")) return "org"; - if (id.startsWith("domain.")) return "domain"; - if (id.startsWith("subdomain.")) return "subdomain"; - if (id.startsWith("rm.")) return "roadmap"; - if (id.startsWith("risk.")) return "risk"; - if (id.startsWith("int.")) return "interaction"; - if (id.startsWith("doc.")) return "document"; - if (id.startsWith("legal.")) return "legal"; - return "unknown"; -} - -function safeString(x: unknown): string { - if (x === null || x === undefined) return ""; - if (typeof x === "string") return x; - if (typeof x === "number" || typeof x === "boolean") return String(x); - try { - return JSON.stringify(x); - } catch { - return ""; - } -} - -function indexEntity(idx: RepoIndex, e: Entity) { - idx.entitiesById.set(e.id, e); - const hay = [ - e.id, - e._kind, - e._domain ?? "", - e._subdomain ?? "", - safeString(e.title), - safeString(e.name), - safeString(e.description), - safeString(e.change_description), - safeString(e.status), - ] - .join(" ") - .toLowerCase(); - - idx.textIndex.push({ - id: e.id, - haystack: hay, - kind: e._kind, - domain: e._domain, - subdomain: e._subdomain, - }); -} - -function readYaml(filePath: string): any { - const raw = fs.readFileSync(filePath, "utf-8"); - return yaml.load(raw); -} - -export function loadRepo(archRoot: string): RepoIndex { - const idx: RepoIndex = { - entitiesById: new Map(), - edges: [], - textIndex: [], - resources: [], - }; - - // Register resource URIs for views and manifest and raw registers. - const domainManifests = fg.sync(["**/manifest.yaml"], { - cwd: archRoot, - dot: false, - absolute: true, - }); - for (const mf of domainManifests) { - const mfObj: any = readYaml(mf); - const domainFolder = path.dirname(mf); - const domainSlug = path.basename(domainFolder); - - idx.resources.push({ - uri: `manifest://${domainSlug}`, - title: `Manifest: ${domainSlug}`, - mimeType: "application/yaml", - }); - - // Views resources - const views = (mfObj?.views ?? []) as Array<{ - file: string; - title?: string; - id?: string | number; - }>; - for (const v of views) { - const name = path.basename(v.file, path.extname(v.file)); - idx.resources.push({ - uri: `views://${domainSlug}/${name}`, - title: `View: ${domainSlug} / ${v.title ?? name}`, - mimeType: "text/markdown", - }); - } - - // Register raw YAML registers as resources - const regFiles = fg.sync(["registers/**/*.yaml"], { - cwd: domainFolder, - absolute: true, - }); - for (const rf of regFiles) { - const rel = path.relative(domainFolder, rf).replaceAll("\\", "/"); - idx.resources.push({ - uri: `register://${domainSlug}/${rel}`, - title: `Register: ${domainSlug}/${rel}`, - mimeType: "application/yaml", - }); - } - - // Load domain entity (if present) - const domainYaml = path.join(domainFolder, "registers/00-meta/domain.yaml"); - if (fs.existsSync(domainYaml)) { - const d = readYaml(domainYaml); - const ent: Entity = { - ...(d ?? {}), - id: d?.id ?? `domain.${domainSlug}`, - _kind: "domain", - _domain: `domain.${domainSlug}`, - _source_path: path.relative(archRoot, domainYaml).replaceAll("\\", "/"), - }; - indexEntity(idx, ent); - } - - // Load YAML registers with "items" - const regList = fg.sync(["registers/**/*.yaml"], { - cwd: domainFolder, - absolute: true, - }); - for (const file of regList) { - const obj = readYaml(file); - - // edges file - if (file.endsWith("registers/99-relations/edges.yaml") && obj?.edges) { - const edges = obj.edges as Edge[]; - idx.edges.push(...edges); - continue; - } - - // Single-entity YAML (has id but no items) - if (obj?.id && !obj?.items) { - const ent: Entity = { - ...(obj ?? {}), - _kind: toKindFromId(obj.id), - _domain: `domain.${domainSlug}`, - _subdomain: obj?.subdomain ?? undefined, - _source_path: path.relative(archRoot, file).replaceAll("\\", "/"), - }; - indexEntity(idx, ent); - continue; - } - - // List YAML (items) - if (Array.isArray(obj?.items)) { - for (const it of obj.items) { - if (!it?.id) continue; - const ent: Entity = { - ...(it ?? {}), - _kind: toKindFromId(it.id), - _domain: `domain.${domainSlug}`, - _subdomain: it?.subdomain ?? undefined, - _source_path: path.relative(archRoot, file).replaceAll("\\", "/"), - }; - indexEntity(idx, ent); - } - } - } - } - - return idx; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/types.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/types.ts deleted file mode 100644 index 387e7c6..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/repo/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -export type DomainId = string; - -export type Edge = { - from: string; - type: string; - to: string; -}; - -export type Entity = { - id: string; - _kind: - | "goal" - | "function" - | "service" - | "information_resource" - | "system" - | "principle" - | "org" - | "domain" - | "subdomain" - | "roadmap" - | "risk" - | "interaction" - | "document" - | "legal" - | "unknown"; - _domain?: DomainId; - _subdomain?: string; - _source_path?: string; - [k: string]: any; -}; - -export type RepoIndex = { - entitiesById: Map; - edges: Edge[]; - textIndex: Array<{ - id: string; - haystack: string; - kind: string; - domain?: string; - subdomain?: string; - }>; - resources: Array<{ uri: string; title: string; mimeType: string }>; -}; diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/get_resource.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/get_resource.ts deleted file mode 100644 index a903390..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/get_resource.ts +++ /dev/null @@ -1,43 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import fg from "fast-glob"; - -function readFileSafe(filePath: string): string { - return fs.readFileSync(filePath, "utf-8"); -} - -export function getResource(archRoot: string, uri: string) { - // manifest:// - if (uri.startsWith("manifest://")) { - const domain = uri.replace("manifest://", ""); - const mf = path.join(archRoot, domain, "manifest.yaml"); - if (!fs.existsSync(mf)) return { ok: false, error: `Not found: ${uri}` }; - return { ok: true, uri, mimeType: "application/yaml", content: readFileSafe(mf) }; - } - - // views:/// - if (uri.startsWith("views://")) { - const rest = uri.replace("views://", ""); - const [domain, viewName] = rest.split("/", 2); - const domainDir = path.join(archRoot, domain); - const matches = fg.sync([`views/${viewName}.md`], { cwd: domainDir, absolute: true }); - if (!matches.length) return { ok: false, error: `Not found: ${uri}` }; - return { - ok: true, - uri, - mimeType: "text/markdown", - content: readFileSafe(matches[0]!), - }; - } - - // register:/// - if (uri.startsWith("register://")) { - const rest = uri.replace("register://", ""); - const [domain, rel] = rest.split("/", 2); - const filePath = path.join(archRoot, domain, rel); - if (!fs.existsSync(filePath)) return { ok: false, error: `Not found: ${uri}` }; - return { ok: true, uri, mimeType: "application/yaml", content: readFileSafe(filePath) }; - } - - return { ok: false, error: `Unsupported uri: ${uri}` }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/list_resources.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/list_resources.ts deleted file mode 100644 index e35456a..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/resources/list_resources.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RepoIndex } from "../repo/types.js"; - -export function listResources(idx: RepoIndex) { - return { - ok: true, - resources: idx.resources, - }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/get_entity.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/get_entity.ts deleted file mode 100644 index 28b792e..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/get_entity.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RepoIndex } from "../repo/types.js"; - -export function getEntityTool(idx: RepoIndex, args: any) { - const id = String(args?.id ?? "").trim(); - if (!id) return { ok: false, error: "Missing args.id" }; - - const ent = idx.entitiesById.get(id); - if (!ent) return { ok: false, error: `Entity not found: ${id}` }; - - return { ok: true, entity: ent }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/list_relations.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/list_relations.ts deleted file mode 100644 index 38fc7cd..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/list_relations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RepoIndex } from "../repo/types.js"; - -export function listRelationsTool(idx: RepoIndex, args: any) { - const id = String(args?.id ?? "").trim(); - if (!id) return { ok: false, error: "Missing args.id" }; - - const direction = String(args?.direction ?? "both"); // in|out|both - - const edges = idx.edges.filter((e) => { - if (direction === "out") return e.from === id; - if (direction === "in") return e.to === id; - return e.from === id || e.to === id; - }); - - return { ok: true, edges }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/search.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/search.ts deleted file mode 100644 index ec1b0fd..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/search.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { RepoIndex } from "../repo/types.js"; - -export function searchTool(idx: RepoIndex, args: any) { - const q = String(args?.query ?? "").toLowerCase().trim(); - if (!q) return { ok: false, error: "Missing args.query" }; - - const kind = args?.kind ? String(args.kind) : null; - const subdomain = args?.subdomain ? String(args.subdomain) : null; - - const hits: Array<{ id: string; score: number; kind: string; subdomain?: string }> = []; - - for (const row of idx.textIndex) { - if (kind && row.kind !== kind) continue; - if (subdomain && row.subdomain !== subdomain) continue; - - // Very simple scoring: count substring occurrences - const hay = row.haystack; - let score = 0; - let pos = hay.indexOf(q); - while (pos !== -1) { - score++; - pos = hay.indexOf(q, pos + q.length); - } - if (score > 0) hits.push({ id: row.id, score, kind: row.kind, subdomain: row.subdomain }); - } - - hits.sort((a, b) => b.score - a.score); - return { ok: true, results: hits.slice(0, 50) }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/subgraph.ts b/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/subgraph.ts deleted file mode 100644 index 4d5ab45..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/src/tools/subgraph.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { RepoIndex } from "../repo/types.js"; - -export function subgraphTool(idx: RepoIndex, args: any) { - const seeds = Array.isArray(args?.seed_ids) - ? args.seed_ids.map((x: any) => String(x)) - : []; - const depth = Number.isFinite(args?.depth) ? Number(args.depth) : 1; - - if (!seeds.length) return { ok: false, error: "Missing args.seed_ids[]" }; - if (depth < 0 || depth > 5) return { ok: false, error: "depth must be 0..5" }; - - const nodes = new Set(seeds); - const edgesOut: any[] = []; - - let frontier = new Set(seeds); - - for (let d = 0; d < depth; d++) { - const next = new Set(); - - for (const e of idx.edges) { - if (frontier.has(e.from)) { - edgesOut.push(e); - if (!nodes.has(e.to)) next.add(e.to); - nodes.add(e.to); - } - if (frontier.has(e.to)) { - edgesOut.push(e); - if (!nodes.has(e.from)) next.add(e.from); - nodes.add(e.from); - } - } - - frontier = next; - if (!frontier.size) break; - } - - const nodeObjs = Array.from(nodes).map( - (id) => idx.entitiesById.get(id) ?? { id, _kind: "unknown" } - ); - - return { ok: true, nodes: nodeObjs, edges: edgesOut }; -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcp/tsconfig.json b/ikt-arh-kultura-valodu-tehnologijas/mcp/tsconfig.json deleted file mode 100644 index 5c4f7d0..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcp/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "Bundler", - "outDir": "dist", - "rootDir": "src", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "types": ["node"] - }, - "include": ["src/**/*.ts"] -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/README.md b/ikt-arh-kultura-valodu-tehnologijas/mcpf/README.md deleted file mode 100644 index 5e17323..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/README.md +++ /dev/null @@ -1,506 +0,0 @@ -# KISC MCP Server - MCPF Integration Deployment Guide - -**Project:** MCPF (MCP Trust Framework) Integration for KISC MCP Server -**Target Server:** llm.kis.gov.lv -**DID:** `did:web:llm.kis.gov.lv` -**Date:** 2026-01-30 - ---- - -## 📋 Table of Contents - -1. [Overview](#overview) -2. [Package Contents](#package-contents) -3. [Prerequisites](#prerequisites) -4. [Part A: Local Implementation](#part-a-local-implementation) -5. [Part B: VeriTrust Integration](#part-b-veritrust-integration) -6. [Validation & Testing](#validation--testing) -7. [Troubleshooting](#troubleshooting) - ---- - -## Overview - -This package integrates MCPF (MCP Trust Framework) into the KISC MCP Server, providing: - -✅ **Cryptographic Identity** — `did:web:llm.kis.gov.lv` with Ed25519 signing -✅ **Verifiable Credentials** — VeriTrust-issued MCPServerCredential -✅ **Trust Metadata** — Discoverable at `/.well-known/mcp-trust-registry.json` -✅ **Standards Compliance** — W3C DID Core, VC Data Model, MCPF Specification - ---- - -## Package Contents - -``` -kisc-mcpf-deploy/ -├── README.md # This file -├── keypair-SECURE.json # ⚠️ PRIVATE KEY (secure handling!) -├── public-key.json # Public key reference -│ -├── wellknown/ # .well-known files for nginx -│ ├── did.json # DID Document -│ ├── jwks.json # JWK Set (public keys) -│ ├── mcp-trust-registry.json # MCPF registry discovery -│ ├── security.txt # RFC 9116 security contact -│ ├── mcp/ -│ │ └── manifest.json # MCP server capabilities -│ └── credentials/ -│ └── mcp-server.json # VC placeholder (VeriTrust will replace) -│ -├── scripts/ # Deployment automation -│ ├── deploy-wellknown.sh # Deploy .well-known to server -│ ├── validate-endpoints.sh # Test all endpoints -│ └── update-env.sh # Add MCPF_PRIVATE_KEY to .env -│ -├── nginx/ # nginx configuration -│ └── wellknown.conf # nginx config for .well-known -│ -├── docs/ # Documentation -│ ├── DEPLOYMENT.md # Step-by-step deployment -│ ├── INTEGRATION.md # start.sh/status.sh updates -│ └── TESTING.md # Validation procedures -│ -└── veritrust/ # VeriTrust submission - ├── README-VERITRUST.md # Instructions for VeriTrust - ├── mcp-server-request.json # Credential request payload - └── install-credential.sh # Install received VC -``` - ---- - -## Prerequisites - -Before deployment, ensure: - -- [ ] SSH access to `llm.kis.gov.lv` (10.20.30.96) -- [ ] Sudo privileges or ownership of `/opt/kisc-llm/` -- [ ] POC stack running (`/opt/kisc-llm/poc/deploy/`) -- [ ] nginx container (kisc-nginx) operational -- [ ] Let's Encrypt certificates valid -- [ ] Git access to `kisc-gov-lv/MCP-KISC-architecture` - ---- - -## Part A: Local Implementation - -### Step 1: Secure Private Key Storage - -**⚠️ CRITICAL: Handle `keypair-SECURE.json` securely!** - -```bash -# On your local machine (NOT on server yet) -cat keypair-SECURE.json -# Contains: private_key_pem, public_key_pem, multibase, jwk_x - -# Verify integrity -sha256sum keypair-SECURE.json -``` - -**DO NOT:** -- ❌ Commit to Git -- ❌ Send via unencrypted email -- ❌ Store in Slack/Teams -- ❌ Print to logs - -**DO:** -- ✅ Transfer via encrypted channel (scp with key auth, 1Password, etc.) -- ✅ Store in `/opt/kisc-llm/poc/deploy/.env` only -- ✅ Backup offline (encrypted USB/vault) -- ✅ Document who has access - ---- - -### Step 2: Deploy .well-known Files to Server - -```bash -# On llm.kis.gov.lv server - -# 1. Create .well-known directory -sudo mkdir -p /opt/kisc-llm/poc/deploy/.well-known/{mcp,credentials} -sudo chown -R "$USER":"$USER" /opt/kisc-llm/poc/deploy/.well-known - -# 2. Copy .well-known files -cd /opt/kisc-llm/poc/deploy -rsync -av /path/to/kisc-mcpf-deploy/wellknown/ .well-known/ - -# 3. Verify structure -tree .well-known/ -# Expected: -# .well-known/ -# ├── did.json -# ├── jwks.json -# ├── mcp-trust-registry.json -# ├── security.txt -# ├── mcp/ -# │ └── manifest.json -# └── credentials/ -# └── mcp-server.json - -# 4. Set permissions (read-only for nginx) -chmod -R 644 .well-known/**/* -find .well-known -type d -exec chmod 755 {} \; -``` - ---- - -### Step 3: Add Private Key to .env - -```bash -# On llm.kis.gov.lv server -cd /opt/kisc-llm/poc/deploy - -# Extract private key from keypair-SECURE.json -PRIVATE_KEY_PEM=$(cat /path/to/keypair-SECURE.json | jq -r '.private_key_pem') - -# Add to .env (replace newlines with \n) -echo "MCPF_PRIVATE_KEY=\"$PRIVATE_KEY_PEM\"" >> .env - -# Verify (should show -----BEGIN PRIVATE KEY-----) -grep MCPF_PRIVATE_KEY .env | head -c 100 - -# Secure the .env file -chmod 600 .env -``` - ---- - -### Step 4: Update nginx Configuration - -```bash -# On llm.kis.gov.lv server -cd /opt/kisc-llm/poc/deploy/nginx/conf.d - -# Backup existing config -cp default.conf default.conf.backup-$(date +%Y%m%d) - -# Add .well-known location block (insert after line 30, before "location /") -cat >> default.conf << 'EOF' - - # ========================================================================== - # MCPF .well-known endpoints - # ========================================================================== - location /.well-known/ { - alias /opt/kisc-llm/poc/deploy/.well-known/; - - # CORS headers for trust framework discovery - add_header Access-Control-Allow-Origin * always; - add_header Access-Control-Allow-Methods "GET, OPTIONS" always; - add_header Access-Control-Allow-Headers "Content-Type" always; - - # Cache DID documents for 1 hour (they rarely change) - add_header Cache-Control "public, max-age=3600" always; - - # Serve JSON files - location ~ \.(json)$ { - add_header Content-Type application/json; - } - - # Serve text files - location ~ \.(txt)$ { - add_header Content-Type text/plain; - } - - # No directory listing - autoindex off; - } - -EOF - -# Validate nginx config -docker exec kisc-nginx nginx -t - -# If validation passes, reload -docker exec kisc-nginx nginx -s reload -``` - ---- - -### Step 5: Update start.sh Script - -Add MCPF integration steps to `/opt/kisc-llm/poc/deploy/scripts/start.sh`: - -```bash -# Insert after Step 3 (TLS cert handling), before Step 4 (OpenGateLLM start) - -# ============================================================================= -# Step 3.5: MCPF .well-known Files -# ============================================================================= -log_step "Step 3.5: Checking MCPF .well-known files..." - -if [[ ! -f "$DEPLOY_DIR/.well-known/did.json" ]]; then - log_error "MCPF .well-known files not found!" - log_error "Run: rsync -av /path/to/wellknown/ $DEPLOY_DIR/.well-known/" - exit 1 -fi - -# Verify critical files exist -REQUIRED_FILES=( - ".well-known/did.json" - ".well-known/jwks.json" - ".well-known/mcp-trust-registry.json" - ".well-known/mcp/manifest.json" - ".well-known/credentials/mcp-server.json" -) - -for file in "${REQUIRED_FILES[@]}"; do - if [[ ! -f "$DEPLOY_DIR/$file" ]]; then - log_warn "Missing: $file" - fi -done - -log_info "MCPF .well-known files OK" -``` - ---- - -### Step 6: Update status.sh Script - -Add MCPF health checks to `/opt/kisc-llm/poc/deploy/scripts/status.sh`: - -```bash -# Insert at the end, before final completion message - -# ============================================================================= -# MCPF ENDPOINTS STATUS -# ============================================================================= -echo -e "${CYAN}MCPF Endpoints:${NC}" -echo "----------------------------------------" - -check_wellknown() { - local endpoint=$1 - local name=$2 - local response - response=$(curl -sS -k --connect-timeout 2 "https://localhost$endpoint" 2>/dev/null || echo "") - - if [[ -n "$response" ]] && echo "$response" | grep -q "@context\|keys\|mcpfVersion"; then - echo -e " ${GREEN}✅${NC} $name" - else - echo -e " ${RED}❌${NC} $name (HTTP error or empty response)" - fi -} - -check_wellknown "/.well-known/did.json" "DID Document" -check_wellknown "/.well-known/jwks.json" "JWKS" -check_wellknown "/.well-known/mcp-trust-registry.json" "MCPF Registry Discovery" -check_wellknown "/.well-known/mcp/manifest.json" "MCP Manifest" -check_wellknown "/.well-known/credentials/mcp-server.json" "MCP Credential" - -echo "" -``` - ---- - -### Step 7: Validate Deployment - -```bash -# On llm.kis.gov.lv server -cd /opt/kisc-llm/poc/deploy - -# Test .well-known endpoints -./scripts/validate-endpoints.sh - -# Expected output: -# ✅ DID Document: https://llm.kis.gov.lv/.well-known/did.json -# ✅ JWKS: https://llm.kis.gov.lv/.well-known/jwks.json -# ✅ MCPF Registry: https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json -# ✅ MCP Manifest: https://llm.kis.gov.lv/.well-known/mcp/manifest.json -# ✅ MCP Credential: https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json -``` - ---- - -## Part B: VeriTrust Integration - -### Step 8: Submit to VeriTrust for Credential Issuance - -**KISC already has a VeriTrust profile!** - -Existing KISC DID in VeriTrust: -- `did:key:z6Mkuwv1z6y2yorbBf4LEkNzJCg16ERVfWE3bJEPKXtQm7a9` (Holder DID) -- `did:web:veritrust.vc:portal:company:df0684bd-b54a-4684-b3d6-93a3b1c4bcb9` (Public Alias) - -**Action Required:** - -1. **Contact VeriTrust** via existing relationship -2. **Request MCPServerCredential** for `did:web:llm.kis.gov.lv` -3. **Provide:** - - DID: `did:web:llm.kis.gov.lv` - - Public Key (multibase): `z6MkjWGNnJsdyvutfbsytFJhkwDwyHkMkfWVL8X1fS1yBm2w` - - MCP Endpoint: `https://llm.kis.gov.lv/mcp` - - Manifest URL: `https://llm.kis.gov.lv/.well-known/mcp/manifest.json` - - Organization: KISC (Kultūras informācijas sistēmu centrs) - - Owner: Kultūras ministrija - - Compliance: GDPR, NIS2, Latvian Data Protection Act - -**Submission Payload:** See `veritrust/mcp-server-request.json` - ---- - -### Step 9: Install VeriTrust-Issued Credential - -Once VeriTrust issues the credential: - -```bash -# On llm.kis.gov.lv server -cd /opt/kisc-llm/poc/deploy - -# Backup placeholder -cp .well-known/credentials/mcp-server.json .well-known/credentials/mcp-server.json.placeholder - -# Install VeriTrust credential (replace PLACEHOLDER with actual credential) -cat > .well-known/credentials/mcp-server.json << 'EOF' -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://mcpf.dev/credentials/v1" - ], - "id": "https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json", - ... (VeriTrust-provided credential JSON) ... -} -EOF - -# Verify signature (use MCPF-python or manual verification) -# The credential MUST be signed by did:web:veritrust.vc - -# Reload nginx to pick up new credential -docker exec kisc-nginx nginx -s reload -``` - ---- - -## Validation & Testing - -### Local Tests (from server) - -```bash -# Test DID Document -curl https://llm.kis.gov.lv/.well-known/did.json | jq - -# Test MCPF Registry Discovery -curl https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json | jq - -# Test MCP Manifest -curl https://llm.kis.gov.lv/.well-known/mcp/manifest.json | jq - -# Test credential (placeholder until VeriTrust issues) -curl https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json | jq -``` - -### External Tests (from any machine) - -```bash -# DID Resolution (W3C standard) -curl https://llm.kis.gov.lv/.well-known/did.json - -# Should return: -# { -# "@context": [...], -# "id": "did:web:llm.kis.gov.lv", -# "verificationMethod": [...], -# ... -# } -``` - -### AI Agent Discovery Test - -```python -import requests - -# Agent discovers MCPF-enabled MCP server -registry_response = requests.get("https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json") -print(registry_response.json()) - -# Agent fetches credential for verification -credential_url = registry_response.json()["services"]["mcp"]["credential"] -credential = requests.get(credential_url).json() - -# Agent verifies signature against did:web:veritrust.vc -# (Use MCPF-python for full verification) -``` - ---- - -## Troubleshooting - -### Issue: 404 on .well-known endpoints - -**Cause:** nginx not serving .well-known directory - -**Fix:** -```bash -# Check nginx volume mount -docker inspect kisc-nginx | grep .well-known - -# If missing, update docker-compose.yml: -volumes: - - ./.well-known:/opt/kisc-llm/poc/deploy/.well-known:ro - -# Restart -docker restart kisc-nginx -``` - ---- - -### Issue: CORS errors when agents try to fetch DID - -**Cause:** Missing CORS headers - -**Fix:** Ensure nginx config has: -```nginx -add_header Access-Control-Allow-Origin * always; -``` - ---- - -### Issue: Private key not found in .env - -**Cause:** MCPF_PRIVATE_KEY not set - -**Fix:** -```bash -# Check .env -grep MCPF_PRIVATE_KEY /opt/kisc-llm/poc/deploy/.env - -# If missing, extract from keypair-SECURE.json and add -``` - ---- - -## Security Checklist - -Before going to production: - -- [ ] `keypair-SECURE.json` deleted from server (only in `.env`) -- [ ] `.env` has permissions `600` (read/write by owner only) -- [ ] `.well-known` files have permissions `644` (world-readable) -- [ ] Private key backed up offline (encrypted) -- [ ] Access control documented (who has private key) -- [ ] VeriTrust credential installed (not placeholder) -- [ ] All endpoints accessible via HTTPS only -- [ ] nginx TLS configured correctly (Let's Encrypt) - ---- - -## Next Steps - -1. **Deploy locally** (Part A) — Complete Steps 1-7 -2. **Submit to VeriTrust** (Part B) — Step 8 -3. **Install credential** — Step 9 (after VeriTrust response) -4. **Test with AI agents** — Validate MCPF discovery workflow -5. **Monitor** — Check logs, status.sh output -6. **Document** — Update KISC internal documentation - ---- - -## Support - -- **MCPF Specification:** https://github.com/MCPTrustFramework/MCPF-specification -- **VeriTrust:** https://veritrust.vc -- **Questions:** Contact Rihards (Veritrust relationship) or KISC IT team - ---- - -**Version:** 1.0 -**Last Updated:** 2026-01-30 -**Status:** Ready for Deployment diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/scripts/validate-endpoints.sh b/ikt-arh-kultura-valodu-tehnologijas/mcpf/scripts/validate-endpoints.sh deleted file mode 100644 index d3d3565..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/scripts/validate-endpoints.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash -############################################################################### -# KISC MCPF - Validate .well-known Endpoints -# -# Tests all MCPF endpoints are accessible and return valid JSON -# -# Usage: ./validate-endpoints.sh [domain] -# Default domain: localhost (for local testing) -# Production: ./validate-endpoints.sh llm.kis.gov.lv -############################################################################### -set -euo pipefail - -DOMAIN="${1:-localhost}" -BASE_URL="https://$DOMAIN" - -# Use -k for localhost self-signed certs -CURL_OPTS="-sS --connect-timeout 5 --max-time 10" -if [[ "$DOMAIN" == "localhost" ]]; then - CURL_OPTS="$CURL_OPTS -k" -fi - -# Colors -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' - -PASSED=0 -FAILED=0 - -echo "============================================" -echo "MCPF Endpoint Validation" -echo "============================================" -echo "Target: $BASE_URL" -echo "" - -test_endpoint() { - local path=$1 - local name=$2 - local required_field=$3 - - echo -n "Testing $name... " - - local url="$BASE_URL$path" - local response - response=$(curl $CURL_OPTS "$url" 2>/dev/null || echo "") - - if [[ -z "$response" ]]; then - echo -e "${RED}❌ FAIL${NC} (No response)" - echo " URL: $url" - ((FAILED++)) - return 1 - fi - - # Check if valid JSON - if ! echo "$response" | jq empty 2>/dev/null; then - echo -e "${RED}❌ FAIL${NC} (Invalid JSON)" - echo " URL: $url" - echo " Response: ${response:0:100}..." - ((FAILED++)) - return 1 - fi - - # Check for required field - if [[ -n "$required_field" ]]; then - if ! echo "$response" | jq -e "$required_field" >/dev/null 2>&1; then - echo -e "${YELLOW}⚠️ WARN${NC} (Missing field: $required_field)" - echo " URL: $url" - fi - fi - - echo -e "${GREEN}✅ PASS${NC}" - echo " URL: $url" - ((PASSED++)) -} - -# ============================================================================= -# Test Suite -# ============================================================================= - -# Test 1: DID Document -test_endpoint "/.well-known/did.json" "DID Document" ".id" - -# Test 2: JWKS -test_endpoint "/.well-known/jwks.json" "JWKS" ".keys" - -# Test 3: MCPF Registry Discovery -test_endpoint "/.well-known/mcp-trust-registry.json" "MCPF Registry Discovery" ".mcpfVersion" - -# Test 4: Security.txt -echo -n "Testing Security.txt... " -response=$(curl $CURL_OPTS "$BASE_URL/.well-known/security.txt" 2>/dev/null || echo "") -if echo "$response" | grep -q "Contact:"; then - echo -e "${GREEN}✅ PASS${NC}" - echo " URL: $BASE_URL/.well-known/security.txt" - ((PASSED++)) -else - echo -e "${RED}❌ FAIL${NC}" - ((FAILED++)) -fi - -# Test 5: MCP Manifest -test_endpoint "/.well-known/mcp/manifest.json" "MCP Manifest" ".capabilities" - -# Test 6: MCP Credential -test_endpoint "/.well-known/credentials/mcp-server.json" "MCP Credential" ".credentialSubject" - -# ============================================================================= -# Results -# ============================================================================= - -echo "" -echo "============================================" -echo "Results" -echo "============================================" -echo "Passed: $PASSED" -echo "Failed: $FAILED" -echo "" - -if [[ $FAILED -eq 0 ]]; then - echo -e "${GREEN}✅ All tests passed!${NC}" - echo "" - echo "MCPF integration is working correctly." - echo "Next steps:" - echo " 1. Submit to VeriTrust for credential issuance" - echo " 2. Replace placeholder credential in /.well-known/credentials/mcp-server.json" - echo " 3. Test with AI agents (Claude Desktop, ChatGPT, etc.)" - exit 0 -else - echo -e "${RED}❌ Some tests failed${NC}" - echo "" - echo "Troubleshooting:" - echo " 1. Check nginx is serving .well-known directory" - echo " 2. Verify .well-known files exist in /opt/kisc-llm/poc/deploy/.well-known/" - echo " 3. Check nginx logs: docker logs kisc-nginx" - echo " 4. Verify TLS certificates are valid" - exit 1 -fi diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/README-VERITRUST.md b/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/README-VERITRUST.md deleted file mode 100644 index ad5546d..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/README-VERITRUST.md +++ /dev/null @@ -1,247 +0,0 @@ -# VeriTrust MCPF Credential Submission - -## Overview - -KISC already has a VeriTrust organization profile. This submission requests a **MCPServerCredential** for the new `did:web:llm.kis.gov.lv` identity. - ---- - -## Existing KISC Profile in VeriTrust - -**Holder DID:** `did:key:z6Mkuwv1z6y2yorbBf4LEkNzJCg16ERVfWE3bJEPKXtQm7a9` -**Public Alias:** `did:web:veritrust.vc:portal:company:df0684bd-b54a-4684-b3d6-93a3b1c4bcb9` -**Status:** Verified - ---- - -## New Identity for MCP Server - -**DID:** `did:web:llm.kis.gov.lv` -**Public Key (multibase):** `z6MkjWGNnJsdyvutfbsytFJhkwDwyHkMkfWVL8X1fS1yBm2w` -**Service Endpoint:** `https://llm.kis.gov.lv/mcp` -**Manifest:** `https://llm.kis.gov.lv/.well-known/mcp/manifest.json` - ---- - -## Submission Process - -### Step 1: Verify Local Deployment - -Before submitting to VeriTrust, ensure: - -```bash -# All .well-known endpoints accessible -curl https://llm.kis.gov.lv/.well-known/did.json -curl https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json -curl https://llm.kis.gov.lv/.well-known/mcp/manifest.json - -# MCP server operational -curl https://llm.kis.gov.lv/mcp-health -``` - ---- - -### Step 2: Submit Credential Request - -**Method 1: Via VeriTrust Portal (Recommended)** - -1. Log into VeriTrust portal: https://veritrust.vc/portal -2. Navigate to your KISC organization profile -3. Click "Request Credential" → "MCP Server Credential" -4. Fill in form with data from `mcp-server-request.json` -5. Upload or paste: - - DID: `did:web:llm.kis.gov.lv` - - Public key (multibase): `z6MkjWGNnJsdyvutfbsytFJhkwDwyHkMkfWVL8X1fS1yBm2w` - - Endpoint: `https://llm.kis.gov.lv/mcp` - - Manifest URL: `https://llm.kis.gov.lv/.well-known/mcp/manifest.json` -6. Submit for review - -**Method 2: Via API (If Available)** - -```bash -# POST to VeriTrust credential issuance API -curl -X POST https://veritrust.vc/api/v1/credentials/issue \ - -H "Authorization: Bearer $VERITRUST_API_KEY" \ - -H "Content-Type: application/json" \ - -d @mcp-server-request.json -``` - -**Method 3: Email Submission** - -Send `mcp-server-request.json` to: credentials@veritrust.vc - -Include: -- Subject: "KISC MCP Server Credential Request - did:web:llm.kis.gov.lv" -- Body: Reference existing KISC profile (did:key:z6Mkuwv1z6y2yorbBf4LEkNzJCg16ERVfWE3bJEPKXtQm7a9) -- Attach: mcp-server-request.json - ---- - -### Step 3: Verification by VeriTrust - -VeriTrust will verify: - -1. ✅ KISC organization profile exists and is verified -2. ✅ `llm.kis.gov.lv` domain is controlled by KISC -3. ✅ DID document accessible at `https://llm.kis.gov.lv/.well-known/did.json` -4. ✅ MCP manifest valid at `https://llm.kis.gov.lv/.well-known/mcp/manifest.json` -5. ✅ Public key matches DID document -6. ✅ Compliance claims are accurate (GDPR, NIS2) - -**Timeline:** 1-5 business days (typically 1-2 days for verified organizations) - ---- - -### Step 4: Receive Credential - -VeriTrust will provide: - -```json -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://mcpf.dev/credentials/v1" - ], - "id": "https://veritrust.vc/credentials/[UUID]", - "type": ["VerifiableCredential", "MCPServerCredential"], - "issuer": { - "id": "did:web:veritrust.vc", - "name": "VeriTrust" - }, - "issuanceDate": "2026-01-30T10:00:00Z", - "expirationDate": "2027-01-30T10:00:00Z", - "credentialSubject": { - "id": "did:web:llm.kis.gov.lv#mcp-server", - ... - }, - "credentialStatus": { - "id": "https://veritrust.vc/status/2026#94567", - "type": "StatusList2021Entry", - ... - }, - "proof": { - "type": "Ed25519Signature2020", - "created": "2026-01-30T10:00:00Z", - "verificationMethod": "did:web:veritrust.vc#key-1", - "proofPurpose": "assertionMethod", - "proofValue": "z5vgK8B..." // VeriTrust's cryptographic signature - } -} -``` - ---- - -### Step 5: Install Credential - -Use the provided `install-credential.sh` script: - -```bash -# On llm.kis.gov.lv server -cd /opt/kisc-llm/poc/deploy - -# Save VeriTrust credential to temporary file -cat > /tmp/veritrust-credential.json << 'EOF' -{ - ... (paste VeriTrust-provided credential JSON) ... -} -EOF - -# Run install script -./veritrust/install-credential.sh /tmp/veritrust-credential.json - -# Verify installation -curl https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json | jq -``` - ---- - -### Step 6: Register in MCPF Registry - -VeriTrust will automatically register the MCP server in their MCPF registry at `https://mcp.veritrust.vc`. - -Verify registration: - -```bash -# Search by country -curl "https://mcp.veritrust.vc/mcp/search?country=LV" - -# Get specific server -curl "https://mcp.veritrust.vc/mcp/servers/did:web:llm.kis.gov.lv" -``` - -Expected response: -```json -{ - "did": "did:web:llm.kis.gov.lv", - "endpoint": "https://llm.kis.gov.lv/mcp", - "manifest": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json", - "credentials": [ - "https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json" - ], - "metadata": { - "organization": "Kultūras informācijas sistēmu centrs", - "country": "LV", - "tags": ["architecture", "government", "latvia", "culture"], - "status": "active" - } -} -``` - ---- - -## Troubleshooting - -### VeriTrust cannot verify domain ownership - -**Solution:** Add DNS TXT record: - -``` -_veritrust.llm.kis.gov.lv TXT "did=did:web:llm.kis.gov.lv" -``` - -### VeriTrust cannot fetch DID document - -**Solution:** Verify HTTPS and CORS: - -```bash -curl -I https://llm.kis.gov.lv/.well-known/did.json -# Should show: -# HTTP/2 200 -# access-control-allow-origin: * -# content-type: application/json -``` - -### Credential issuance delayed - -**Solution:** Contact VeriTrust support with: -- Organization: KISC -- Existing DID: did:key:z6Mkuwv1z6y2yorbBf4LEkNzJCg16ERVfWE3bJEPKXtQm7a9 -- New DID: did:web:llm.kis.gov.lv -- Request ID: (if provided) - ---- - -## Contact - -**VeriTrust Support:** -- Website: https://veritrust.vc -- Email: support@veritrust.vc (or credentials@veritrust.vc) -- Portal: https://veritrust.vc/portal - -**KISC Contact:** -- Rihards (VeriTrust relationship) -- KISC IT operations team - ---- - -## Credential Renewal - -**Expiration:** 1 year from issuance -**Renewal Process:** 30 days before expiration, VeriTrust will notify KISC via email -**Action Required:** Confirm renewal (usually automatic for verified organizations) - ---- - -**Version:** 1.0 -**Last Updated:** 2026-01-30 -**Status:** Ready for Submission diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/install-credential.sh b/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/install-credential.sh deleted file mode 100644 index 910842d..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/install-credential.sh +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env bash -############################################################################### -# KISC MCPF - Install VeriTrust-Issued Credential -# -# Installs the VeriTrust-signed MCP Server Credential and validates it -# -# Usage: ./install-credential.sh -############################################################################### -set -euo pipefail - -CREDENTIAL_FILE="${1:-}" -DEPLOY_DIR="/opt/kisc-llm/poc/deploy" -TARGET="$DEPLOY_DIR/.well-known/credentials/mcp-server.json" - -# Colors -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' - -log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -log_error() { echo -e "${RED}[ERROR]${NC} $1"; } - -# ============================================================================= -# Validation -# ============================================================================= - -if [[ -z "$CREDENTIAL_FILE" ]]; then - log_error "Usage: $0 " - echo "" - echo "Example:" - echo " $0 /tmp/veritrust-credential.json" - exit 1 -fi - -if [[ ! -f "$CREDENTIAL_FILE" ]]; then - log_error "File not found: $CREDENTIAL_FILE" - exit 1 -fi - -if [[ ! -d "$DEPLOY_DIR" ]]; then - log_error "Deploy directory not found: $DEPLOY_DIR" - exit 1 -fi - -echo "============================================" -echo "Install VeriTrust Credential" -echo "============================================" -echo "Source: $CREDENTIAL_FILE" -echo "Target: $TARGET" -echo "" - -# ============================================================================= -# Validate Credential Format -# ============================================================================= - -log_info "Step 1: Validating credential format..." - -# Check if valid JSON -if ! jq empty "$CREDENTIAL_FILE" 2>/dev/null; then - log_error "Invalid JSON in credential file" - exit 1 -fi - -# Check required fields -REQUIRED_FIELDS=( - "@context" - "type" - "issuer.id" - "credentialSubject.id" - "proof.type" - "proof.proofValue" -) - -for field in "${REQUIRED_FIELDS[@]}"; do - if ! jq -e ".$field" "$CREDENTIAL_FILE" >/dev/null 2>&1; then - log_error "Missing required field: $field" - exit 1 - fi -done - -# Verify issuer is VeriTrust -ISSUER=$(jq -r '.issuer.id' "$CREDENTIAL_FILE") -if [[ "$ISSUER" != "did:web:veritrust.vc" ]]; then - log_error "Invalid issuer: $ISSUER (expected: did:web:veritrust.vc)" - exit 1 -fi - -# Verify subject is KISC MCP server -SUBJECT=$(jq -r '.credentialSubject.id' "$CREDENTIAL_FILE") -if [[ "$SUBJECT" != "did:web:llm.kis.gov.lv#mcp-server" ]]; then - log_warn "Subject mismatch: $SUBJECT (expected: did:web:llm.kis.gov.lv#mcp-server)" -fi - -# Check expiration -EXPIRATION=$(jq -r '.expirationDate' "$CREDENTIAL_FILE") -log_info "Credential expires: $EXPIRATION" - -log_info "✅ Credential format valid" -echo "" - -# ============================================================================= -# Backup Existing Credential -# ============================================================================= - -log_info "Step 2: Backing up existing credential..." - -if [[ -f "$TARGET" ]]; then - BACKUP="$TARGET.backup-$(date +%Y%m%d-%H%M%S)" - cp "$TARGET" "$BACKUP" - log_info "Backup created: $BACKUP" -else - log_warn "No existing credential to backup" -fi - -echo "" - -# ============================================================================= -# Install New Credential -# ============================================================================= - -log_info "Step 3: Installing new credential..." - -# Copy credential to target location -cp "$CREDENTIAL_FILE" "$TARGET" - -# Set permissions (world-readable) -chmod 644 "$TARGET" - -log_info "✅ Credential installed: $TARGET" -echo "" - -# ============================================================================= -# Reload nginx -# ============================================================================= - -log_info "Step 4: Reloading nginx..." - -if docker ps --format '{{.Names}}' | grep -q "kisc-nginx"; then - docker exec kisc-nginx nginx -s reload - log_info "✅ nginx reloaded" -else - log_warn "nginx container not found, skipping reload" -fi - -echo "" - -# ============================================================================= -# Verify Installation -# ============================================================================= - -log_info "Step 5: Verifying installation..." - -# Test endpoint -RESPONSE=$(curl -sS -k https://localhost/.well-known/credentials/mcp-server.json 2>/dev/null || echo "") - -if [[ -z "$RESPONSE" ]]; then - log_error "Endpoint not accessible" - exit 1 -fi - -if ! echo "$RESPONSE" | jq empty 2>/dev/null; then - log_error "Endpoint returned invalid JSON" - exit 1 -fi - -# Check proof value matches -INSTALLED_PROOF=$(echo "$RESPONSE" | jq -r '.proof.proofValue') -SOURCE_PROOF=$(jq -r '.proof.proofValue' "$CREDENTIAL_FILE") - -if [[ "$INSTALLED_PROOF" != "$SOURCE_PROOF" ]]; then - log_error "Proof value mismatch! Installation may be corrupted." - exit 1 -fi - -log_info "✅ Installation verified" -echo "" - -# ============================================================================= -# Success Summary -# ============================================================================= - -echo "============================================" -echo "Installation Complete" -echo "============================================" -echo "" -echo "Credential Details:" -echo " Issuer: $(jq -r '.issuer.name' "$TARGET")" -echo " Subject: $(jq -r '.credentialSubject.id' "$TARGET")" -echo " Issued: $(jq -r '.issuanceDate' "$TARGET")" -echo " Expires: $(jq -r '.expirationDate' "$TARGET")" -echo " Status URL: $(jq -r '.credentialStatus.statusListCredential' "$TARGET")" -echo "" -echo "Endpoint:" -echo " https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json" -echo "" -echo "Next Steps:" -echo " 1. Test external access:" -echo " curl https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json | jq" -echo "" -echo " 2. Verify in MCPF Registry:" -echo " curl https://mcp.veritrust.vc/mcp/servers/did:web:llm.kis.gov.lv" -echo "" -echo " 3. Test with AI agent (Claude Desktop, ChatGPT, etc.)" -echo "" diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/mcp-server-request.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/mcp-server-request.json deleted file mode 100644 index 74b808b..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/veritrust/mcp-server-request.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "credentialType": "MCPServerCredential", - "subject": { - "did": "did:web:llm.kis.gov.lv", - "type": "MCPServer", - "endpoint": "https://llm.kis.gov.lv/mcp", - "manifest": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json" - }, - "controller": { - "id": "org.kisc", - "name": "Kultūras informācijas sistēmu centrs", - "registrationNumber": "KISC-REG-NUMBER", - "country": "LV", - "website": "https://kis.gov.lv", - "existingDID": "did:key:z6Mkuwv1z6y2yorbBf4LEkNzJCg16ERVfWE3bJEPKXtQm7a9" - }, - "owner": { - "id": "org.km", - "name": "Kultūras ministrija", - "country": "LV", - "website": "https://km.gov.lv" - }, - "capabilities": [ - { - "name": "search", - "description": "Search YAML registers and Markdown documentation", - "riskLevel": "low" - }, - { - "name": "get_entity", - "description": "Retrieve entity by canonical ID", - "riskLevel": "low" - } - ], - "governance": { - "assuranceLevel": "substantial", - "compliance": [ - "GDPR", - "NIS2", - "Latvian-Data-Protection-Act" - ], - "dataClassification": "public", - "certifications": [], - "auditTrail": true - }, - "publicKey": { - "type": "Ed25519VerificationKey2020", - "multibase": "z6MkjWGNnJsdyvutfbsytFJhkwDwyHkMkfWVL8X1fS1yBm2w", - "jwk": { - "kty": "OKP", - "crv": "Ed25519", - "x": "Sw-NGiVKSYj0zsrL7ceP6EMV673IuL2bYzHEypuojvA" - } - }, - "validityPeriod": { - "notBefore": "2026-01-30T00:00:00Z", - "notAfter": "2027-01-30T00:00:00Z" - }, - "metadata": { - "purpose": "MCPF Trust Framework integration for KISC MCP Server", - "environment": "production", - "poc": "POC-AI-LLM-1", - "technicalContact": "support@kis.gov.lv", - "requestDate": "2026-01-30" - } -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/credentials/mcp-server.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/credentials/mcp-server.json deleted file mode 100644 index 53c3cbf..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/credentials/mcp-server.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://mcpf.dev/credentials/v1" - ], - "id": "https://llm.kis.gov.lv/.well-known/credentials/mcp-server.json", - "type": [ - "VerifiableCredential", - "MCPServerCredential" - ], - "issuer": { - "id": "did:web:veritrust.vc", - "name": "VeriTrust" - }, - "issuanceDate": "2026-01-30T00:00:00Z", - "expirationDate": "2027-01-30T00:00:00Z", - "credentialSubject": { - "id": "did:web:llm.kis.gov.lv#mcp-server", - "type": "MCPServer", - "endpoint": "https://llm.kis.gov.lv/mcp", - "manifest": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json", - "controller": { - "id": "org.kisc", - "name": "Kultūras informācijas sistēmu centrs", - "country": "LV" - }, - "owner": { - "id": "org.km", - "name": "Kultūras ministrija", - "country": "LV" - }, - "capabilities": [ - "search", - "get_entity" - ], - "governance": { - "assuranceLevel": "substantial", - "compliance": [ - "GDPR", - "NIS2", - "Latvian-Data-Protection-Act" - ], - "dataClassification": "public", - "certifications": [] - } - }, - "credentialStatus": { - "id": "https://veritrust.vc/status/2026#PLACEHOLDER", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "PLACEHOLDER", - "statusListCredential": "https://veritrust.vc/status/2026" - }, - "proof": { - "type": "Ed25519Signature2020", - "created": "2026-01-30T00:00:00Z", - "verificationMethod": "did:web:veritrust.vc#key-1", - "proofPurpose": "assertionMethod", - "proofValue": "PLACEHOLDER_WILL_BE_REPLACED_BY_VERITRUST_SIGNATURE" - }, - "_comment": "⚠️ PLACEHOLDER: This credential will be replaced by VeriTrust-issued credential. DO NOT use in production until replaced." -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/did.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/did.json deleted file mode 100644 index a512c89..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/did.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "id": "did:web:llm.kis.gov.lv", - "controller": "did:web:llm.kis.gov.lv", - "verificationMethod": [ - { - "id": "did:web:llm.kis.gov.lv#key-1", - "type": "Ed25519VerificationKey2020", - "controller": "did:web:llm.kis.gov.lv", - "publicKeyMultibase": "z6MkjWGNnJsdyvutfbsytFJhkwDwyHkMkfWVL8X1fS1yBm2w" - } - ], - "authentication": [ - "did:web:llm.kis.gov.lv#key-1" - ], - "assertionMethod": [ - "did:web:llm.kis.gov.lv#key-1" - ], - "service": [ - { - "id": "did:web:llm.kis.gov.lv#mcp-server", - "type": "MCPServer", - "serviceEndpoint": "https://llm.kis.gov.lv/mcp" - } - ], - "alsoKnownAs": [ - "https://llm.kis.gov.lv", - "urn:kisc:llm-platform" - ] -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/jwks.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/jwks.json deleted file mode 100644 index 8ee4f98..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/jwks.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "keys": [ - { - "kty": "OKP", - "crv": "Ed25519", - "x": "Sw-NGiVKSYj0zsrL7ceP6EMV673IuL2bYzHEypuojvA", - "use": "sig", - "kid": "did:web:llm.kis.gov.lv#key-1", - "alg": "EdDSA" - } - ] -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp-trust-registry.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp-trust-registry.json deleted file mode 100644 index 1fb34a5..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp-trust-registry.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "@context": "https://mcpf.dev/registry/v1", - "id": "did:web:llm.kis.gov.lv#trust-registry", - "type": "MCPTrustRegistry", - "version": "1.0", - "publisher": { - "id": "did:web:llm.kis.gov.lv", - "name": "KISC - Kultūras informācijas sistēmu centrs" - }, - "services": { - "mcp": { - "id": "did:web:llm.kis.gov.lv#mcp-server", - "endpoint": "https://llm.kis.gov.lv/mcp", - "manifest": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json", - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json" - } - } -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp/manifest.json b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp/manifest.json deleted file mode 100644 index 86e8cb7..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/mcp/manifest.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "@context": "https://modelcontextprotocol.io/schema/2025-03-26", - "@type": "MCPServer", - "id": "did:web:llm.kis.gov.lv#mcp-server", - "name": "KISC MCP Server (IKT Architecture)", - "version": "1.0.0", - "description": "Target architecture for Latvian Cultural and Language Technology domain (Kultūras un valodas tehnoloģiju apakšjomas mērķarhitektūra)", - "author": { - "name": "Kultūras informācijas sistēmu centrs (KISC)", - "url": "https://kis.gov.lv", - "did": "did:web:llm.kis.gov.lv" - }, - "capabilities": { - "tools": true, - "resources": false, - "prompts": false, - "sampling": false - }, - "server": { - "endpoint": "https://llm.kis.gov.lv/mcp", - "transport": "sse", - "authentication": { - "required": false, - "methods": [] - } - }, - "tools": [ - { - "name": "search", - "description": "Search YAML registers and Markdown documentation across the target architecture", - "inputSchema": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "Search query string (searches across all registers and views)" - }, - "limit": { - "type": "number", - "description": "Maximum number of results to return", - "default": 25, - "maximum": 100 - } - }, - "required": ["query"] - } - }, - { - "name": "get_entity", - "description": "Retrieve a specific entity by its canonical ID from the architecture registers", - "inputSchema": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Entity canonical ID (e.g., 'goal.m1', 'org.kisc', 'sys.kultura.01')", - "pattern": "^[a-z]+\\.[a-z0-9_-]+$" - } - }, - "required": ["id"] - } - } - ], - "mcpf": { - "version": "0.1", - "spec": { - "repository": "https://github.com/MCPTrustFramework/MCPF-specification" - }, - "entrypoint": { - "type": "manifest", - "url": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json" - }, - "artifacts": { - "trust_registry": "https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json", - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json" - } - }, - "trust": { - "verifications": [ - { - "verifier": "did:web:veritrust.vc", - "type": ["VerifiableCredential", "MCPServerVerification"], - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json", - "covers": "did:web:llm.kis.gov.lv", - "proof_hint": { - "verificationMethod": "did:web:veritrust.vc#key-1", - "created": "2026-01-29T10:12:41Z" - } - } - ] - }, - "metadata": { - "organization": "Kultūras informācijas sistēmu centrs", - "organizationType": "government", - "country": "LV", - "domain": "kultura-valoda", - "tags": [ - "architecture", - "government", - "latvia", - "culture", - "language", - "ikta", - "enterprise-architecture", - "target-architecture" - ], - "dataClassification": "public", - "compliance": [ - "GDPR", - "NIS2", - "Latvian-Data-Protection-Act" - ], - "languages": [ - "lv", - "en" - ], - "status": "production" - }, - "security": { - "tlsRequired": true, - "minTlsVersion": "1.3", - "signedRequestsRequired": false, - "rateLimits": { - "requestsPerMinute": 60, - "requestsPerHour": 1000 - } - }, - "links": { - "documentation": "https://kis.gov.lv/architecture", - "support": "mailto:support@kis.gov.lv", - "source": "https://github.com/kisc-gov-lv/MCP-KISC-architecture", - "terms": "https://kis.gov.lv/terms", - "privacy": "https://kis.gov.lv/privacy" - } -} diff --git a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/security.txt b/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/security.txt deleted file mode 100644 index ddb6079..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/mcpf/wellknown/security.txt +++ /dev/null @@ -1,6 +0,0 @@ -Contact: mailto:security@kis.gov.lv -Contact: https://kis.gov.lv/security -Expires: 2027-12-31T23:59:59Z -Preferred-Languages: lv, en -Canonical: https://llm.kis.gov.lv/.well-known/security.txt -Policy: https://kis.gov.lv/security-policy diff --git a/ikt-arh-kultura-valodu-tehnologijas/releases/placeholder.git b/ikt-arh-kultura-valodu-tehnologijas/releases/placeholder.git deleted file mode 100644 index 8b13789..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/releases/placeholder.git +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ikt-arh-kultura-valodu-tehnologijas/scripts/MCPF_INITIALIZE_RESPONSE.md b/ikt-arh-kultura-valodu-tehnologijas/scripts/MCPF_INITIALIZE_RESPONSE.md deleted file mode 100644 index 238fce8..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/scripts/MCPF_INITIALIZE_RESPONSE.md +++ /dev/null @@ -1,192 +0,0 @@ -# MCPF Initialize Response - Expected Format - -## Request Format -```bash -curl -X POST http://localhost:8787/mcp \ - -H "Content-Type: application/json" \ - -H "Accept: application/json, text/event-stream" \ - -d '{ - "jsonrpc": "2.0", - "method": "initialize", - "params": { - "protocolVersion": "2025-03-26", - "capabilities": {}, - "clientInfo": { - "name": "test-client", - "version": "1.0" - } - }, - "id": 1 - }' -``` - -## Response Format (SSE Stream) - -The server returns Server-Sent Events (SSE) format: - -``` -event: message -data: {JSON_RESPONSE} -``` - -## Complete JSON Response Structure - -```json -{ - "result": { - "protocolVersion": "2025-03-26", - "capabilities": { - "tools": { - "listChanged": true - } - }, - "serverInfo": { - "name": "kisc-arch-kultura-valodu-mcp", - "version": "0.2.0" - }, - "_meta": { - "identity": { - "id": "did:web:llm.kis.gov.lv", - "service": { - "mcp": "https://llm.kis.gov.lv/mcp" - }, - "keys": { - "jwks_uri": "https://llm.kis.gov.lv/.well-known/jwks.json" - } - }, - "mcpf": { - "version": "0.1", - "spec": { - "repository": "https://github.com/MCPTrustFramework/MCPF-specification" - }, - "entrypoint": { - "type": "manifest", - "url": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json" - }, - "artifacts": { - "trust_registry": "https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json", - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json" - } - }, - "trust": { - "verifications": [ - { - "verifier": "did:web:veritrust.vc", - "type": [ - "VerifiableCredential", - "MCPServerVerification" - ], - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json", - "covers": "did:web:llm.kis.gov.lv", - "proof_hint": { - "verificationMethod": "did:web:veritrust.vc#key-1", - "created": "2026-01-29T10:12:41Z" - } - } - ] - } - } - }, - "jsonrpc": "2.0", - "id": 1 -} -``` - -## MCPF Layer 1: Session-Level Trust Metadata - -The `_meta` field contains three key sections: - -### 1. Identity -```json -{ - "id": "did:web:llm.kis.gov.lv", - "service": { - "mcp": "https://llm.kis.gov.lv/mcp" - }, - "keys": { - "jwks_uri": "https://llm.kis.gov.lv/.well-known/jwks.json" - } -} -``` - -**Purpose:** Establishes the server's decentralized identity (DID) and key material location. - -### 2. MCPF Metadata -```json -{ - "version": "0.1", - "spec": { - "repository": "https://github.com/MCPTrustFramework/MCPF-specification" - }, - "entrypoint": { - "type": "manifest", - "url": "https://llm.kis.gov.lv/.well-known/mcp/manifest.json" - }, - "artifacts": { - "trust_registry": "https://llm.kis.gov.lv/.well-known/mcp-trust-registry.json", - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json" - } -} -``` - -**Purpose:** Links to MCPF specification and trust artifacts for agent discovery. - -### 3. Trust Verifications -```json -{ - "verifications": [ - { - "verifier": "did:web:veritrust.vc", - "type": ["VerifiableCredential", "MCPServerVerification"], - "credential": "https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json", - "covers": "did:web:llm.kis.gov.lv", - "proof_hint": { - "verificationMethod": "did:web:veritrust.vc#key-1", - "created": "2026-01-29T10:12:41Z" - } - } - ] -} -``` - -**Purpose:** Documents third-party verification by VeriTrust credential service. - -## Verification Steps - -An MCPF-aware agent should: - -1. **Extract DID** from `_meta.identity.id` -2. **Resolve DID document** at `https://llm.kis.gov.lv/.well-known/did.json` -3. **Fetch verification credential** from VeriTrust -4. **Verify credential signature** using VeriTrust's public key -5. **Check credential subject** matches server DID -6. **Optionally fetch** trust registry and manifest for additional context - -## Test with jq - -Extract specific fields: - -```bash -# Get the DID -curl ... | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.identity.id' -# Output: did:web:llm.kis.gov.lv - -# Get MCPF version -curl ... | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.mcpf.version' -# Output: 0.1 - -# Get verifier DID -curl ... | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.trust.verifications[0].verifier' -# Output: did:web:veritrust.vc - -# Get credential URL -curl ... | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.trust.verifications[0].credential' -# Output: https://veritrust.vc/portal/mcp/credentials/aec9930b-9139-4b33-ac6f-ad3bd3d91da0.json -``` - -## Notes - -- The `_meta` field is **in addition to** standard MCP initialize response fields -- Backward compatible: non-MCPF clients ignore `_meta` -- Layer 1 (session-level) + Layer 2 (per-response attestations) = Dual-layer trust -- Session ID returned in `Mcp-Session-Id` response header (not shown in JSON) diff --git a/ikt-arh-kultura-valodu-tehnologijas/scripts/analyze_mcpf_response.py b/ikt-arh-kultura-valodu-tehnologijas/scripts/analyze_mcpf_response.py deleted file mode 100644 index 14acca5..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/scripts/analyze_mcpf_response.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python3 -""" -MCPF Initialize Response Analyzer -Tests MCPF dual-layer trust integration and displays the response in a structured format. -""" - -import json -import requests -import sys -from typing import Dict, Any - -def test_mcpf_initialize(endpoint: str = "http://localhost:8787/mcp") -> Dict[str, Any]: - """ - Send initialize request to MCP server and return parsed response. - """ - payload = { - "jsonrpc": "2.0", - "method": "initialize", - "params": { - "protocolVersion": "2025-03-26", - "capabilities": {}, - "clientInfo": { - "name": "mcpf-analyzer", - "version": "1.0" - } - }, - "id": 1 - } - - headers = { - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream" - } - - print(f"🔍 Testing endpoint: {endpoint}") - print(f"📤 Sending initialize request...\n") - - try: - response = requests.post(endpoint, json=payload, headers=headers, timeout=5) - response.raise_for_status() - - # Parse SSE response (format: "event: message\ndata: {json}\n") - lines = response.text.strip().split('\n') - data_line = None - for line in lines: - if line.startswith('data: '): - data_line = line[6:] # Remove "data: " prefix - break - - if not data_line: - print("❌ No data line found in SSE response") - return {} - - return json.loads(data_line) - - except requests.exceptions.RequestException as e: - print(f"❌ Request failed: {e}") - return {} - except json.JSONDecodeError as e: - print(f"❌ JSON parsing failed: {e}") - return {} - -def print_section(title: str, content: str): - """Print a formatted section.""" - print("=" * 80) - print(f" {title}") - print("=" * 80) - print(content) - print() - -def analyze_response(response: Dict[str, Any]): - """Analyze and display MCPF response structure.""" - - if not response: - print("❌ No response received") - return - - # Check for result - result = response.get("result", {}) - if not result: - print("❌ No result in response") - print_section("Full Response", json.dumps(response, indent=2)) - return - - print("✅ Initialize successful\n") - - # Display basic server info - server_info = result.get("serverInfo", {}) - print_section( - "Server Information", - f"Name: {server_info.get('name', 'Unknown')}\n" - f"Version: {server_info.get('version', 'Unknown')}\n" - f"Protocol: {result.get('protocolVersion', 'Unknown')}" - ) - - # Display _meta (MCPF Layer 1) - meta = result.get("_meta") - if not meta: - print("⚠️ No _meta field found - MCPF integration not present") - return - - print("✅ MCPF Layer 1 (Session-Level Trust Metadata) present\n") - - # Identity section - identity = meta.get("identity", {}) - if identity: - print_section( - "🆔 Identity (DID & Keys)", - f"DID: {identity.get('id', 'Not found')}\n" - f"MCP Service: {identity.get('service', {}).get('mcp', 'Not found')}\n" - f"JWKS URI: {identity.get('keys', {}).get('jwks_uri', 'Not found')}" - ) - - # MCPF section - mcpf = meta.get("mcpf", {}) - if mcpf: - print_section( - "📋 MCPF Metadata", - f"Version: {mcpf.get('version', 'Not found')}\n" - f"Spec Repository: {mcpf.get('spec', {}).get('repository', 'Not found')}\n" - f"Manifest URL: {mcpf.get('entrypoint', {}).get('url', 'Not found')}\n" - f"Trust Registry: {mcpf.get('artifacts', {}).get('trust_registry', 'Not found')}\n" - f"Credential: {mcpf.get('artifacts', {}).get('credential', 'Not found')}" - ) - - # Trust verifications section - trust = meta.get("trust", {}) - verifications = trust.get("verifications", []) - if verifications: - print_section("🔐 Trust Verifications", "") - for i, verification in enumerate(verifications, 1): - print(f" Verification #{i}:") - print(f" Verifier: {verification.get('verifier', 'Not found')}") - print(f" Type: {', '.join(verification.get('type', []))}") - print(f" Covers: {verification.get('covers', 'Not found')}") - print(f" Credential: {verification.get('credential', 'Not found')}") - - proof = verification.get('proof_hint', {}) - if proof: - print(f" Proof:") - print(f" Method: {proof.get('verificationMethod', 'Not found')}") - print(f" Created: {proof.get('created', 'Not found')}") - print() - - # Summary - print_section( - "✅ MCPF Integration Summary", - f"• Identity DID present: {'✅' if identity else '❌'}\n" - f"• MCPF metadata present: {'✅' if mcpf else '❌'}\n" - f"• Trust verifications: {len(verifications)}\n" - f"• VeriTrust verified: {'✅' if any(v.get('verifier') == 'did:web:veritrust.vc' for v in verifications) else '❌'}\n" - f"\n🎯 This server implements MCPF v{mcpf.get('version', '?')} dual-layer trust" - ) - - # Full JSON for reference - print_section("📄 Complete _meta JSON", json.dumps(meta, indent=2)) - -def main(): - endpoint = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8787/mcp" - - print(""" -╔════════════════════════════════════════════════════════════════════════════╗ -║ MCPF Initialize Response Analyzer ║ -║ Testing Dual-Layer Trust Integration ║ -╚════════════════════════════════════════════════════════════════════════════╝ -""") - - response = test_mcpf_initialize(endpoint) - analyze_response(response) - - print(""" -╔════════════════════════════════════════════════════════════════════════════╗ -║ Next: Test Layer 2 (per-response attestations) by calling a tool ║ -║ Example: {"method": "tools/call", "params": {"name": "search", ...}} ║ -╚════════════════════════════════════════════════════════════════════════════╝ -""") - -if __name__ == "__main__": - main() diff --git a/ikt-arh-kultura-valodu-tehnologijas/scripts/test-mcpf-init.sh b/ikt-arh-kultura-valodu-tehnologijas/scripts/test-mcpf-init.sh deleted file mode 100644 index e5c5b59..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/scripts/test-mcpf-init.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -# Test MCPF dual-layer trust integration - Initialize stage -# Shows the complete initialize response including _meta - -set -e - -echo "==========================================" -echo "MCPF Initialize Response Test" -echo "==========================================" -echo "" - -# Test against localhost (assuming server is running) -MCP_ENDPOINT="${MCP_ENDPOINT:-http://localhost:8787/mcp}" - -echo "Testing endpoint: $MCP_ENDPOINT" -echo "" - -# Send initialize request -echo "Sending initialize request..." -RESPONSE=$(curl -s -X POST "$MCP_ENDPOINT" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json, text/event-stream" \ - -d '{ - "jsonrpc": "2.0", - "method": "initialize", - "params": { - "protocolVersion": "2025-03-26", - "capabilities": {}, - "clientInfo": { - "name": "mcpf-test-client", - "version": "1.0" - } - }, - "id": 1 - }' \ - --max-time 5) - -echo "" -echo "==========================================" -echo "Raw Response (SSE format):" -echo "==========================================" -echo "$RESPONSE" -echo "" - -# Extract and pretty-print the JSON from SSE data line -echo "==========================================" -echo "Parsed JSON Response:" -echo "==========================================" -echo "$RESPONSE" | grep '^data:' | sed 's/^data: //' | jq '.' - -echo "" -echo "==========================================" -echo "MCPF _meta Section:" -echo "==========================================" -echo "$RESPONSE" | grep '^data:' | sed 's/^data: //' | jq '.result._meta' - -echo "" -echo "==========================================" -echo "Layer 1 Trust Metadata Details:" -echo "==========================================" -echo "" -echo "Identity DID:" -echo "$RESPONSE" | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.identity.id' -echo "" -echo "MCPF Version:" -echo "$RESPONSE" | grep '^data:' | sed 's/^data: //' | jq -r '.result._meta.mcpf.version' -echo "" -echo "Trust Verifications:" -echo "$RESPONSE" | grep '^data:' | sed 's/^data: //' | jq '.result._meta.trust.verifications[]' -echo "" - -echo "==========================================" -echo "Test Complete" -echo "==========================================" diff --git a/ikt-arh-kultura-valodu-tehnologijas/tools/placeholder.git b/ikt-arh-kultura-valodu-tehnologijas/tools/placeholder.git deleted file mode 100644 index 8b13789..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/tools/placeholder.git +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_generated_doc_headings.sh b/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_generated_doc_headings.sh deleted file mode 100644 index 67d2887..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_generated_doc_headings.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -set -eu - -DOC_PATH="${1:-KISC-merkarhitektura-apraksts.md}" - -if [ ! -f "$DOC_PATH" ]; then - echo "ERROR: regenerated doc not found: $DOC_PATH" - echo "Hint: pass path as argument, e.g.: tools/qa/check_generated_doc_headings.sh docs/output.md" - exit 1 -fi - -need_heading() { - h="$1" - if ! grep -Eq "^[#]{1,6}[[:space:]]+$h([[:space:]]|\$)" "$DOC_PATH"; then - echo "ERROR: missing heading: $h" - exit 1 - fi -} - -# Required headings (core completeness gates) -need_heading "1.2 Iesaistītās puses" -need_heading "1.3 Saīsinājumi" -need_heading "1.4 Saistītie dokumenti" -need_heading "4.1 Juridiskais skats" -need_heading "5.1 Pasākumu plāns" -need_heading "5.2 Mijiedarbība ar citām jomām" -need_heading "5.3 Riski" - -echo "OK: required headings exist in $DOC_PATH" diff --git a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_nonempty_registers.sh b/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_nonempty_registers.sh deleted file mode 100644 index 17325ad..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_nonempty_registers.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -set -eu - -REQ_FILES=" -domains/kultura-valoda/registers/08-roadmap/roadmap.yaml -domains/kultura-valoda/registers/08-roadmap/interactions.yaml -domains/kultura-valoda/registers/09-risks/risks.yaml -" - -for f in $REQ_FILES; do - if [ ! -f "$f" ]; then - echo "ERROR: missing required register: $f" - exit 1 - fi - - # fail if file contains only an empty items list - if grep -Eq '^\s*items:\s*\[\s*\]\s*$' "$f"; then - echo "ERROR: register is empty (items: []): $f" - exit 1 - fi -done - -echo "OK: required registers exist and are non-empty" diff --git a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_stakeholder_count.sh b/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_stakeholder_count.sh deleted file mode 100644 index 63072c4..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/check_stakeholder_count.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -eu - -DOC_PATH="${1:-KISC-merkarhitektura-apraksts.md}" - -if [ ! -f "$DOC_PATH" ]; then - echo "ERROR: regenerated doc not found: $DOC_PATH" - exit 1 -fi - -# Naive but effective: count organization list lines containing "org." OR common abbreviations. -# Adjust pattern if your doc renders differently. -COUNT="$(grep -Eo '(org\.[a-z0-9_]+)|\b(KM|KISC|LNB|LNA|NKMP|LNKC|NKC|VKKF|VDAA|VARAM|TA|TM|VVC|LVA|LUMII)\b' "$DOC_PATH" | wc -l | tr -d ' ')" - -# Require at least 10 hits to ensure it isn't collapsed to just 3 orgs. -if [ "$COUNT" -lt 10 ]; then - echo "ERROR: stakeholder content appears too small (hits=$COUNT). Likely collapsed list." - exit 1 -fi - -echo "OK: stakeholder presence check passed (hits=$COUNT)" diff --git a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/structure-checklist.md b/ikt-arh-kultura-valodu-tehnologijas/tools/qa/structure-checklist.md deleted file mode 100644 index 1fd4e14..0000000 --- a/ikt-arh-kultura-valodu-tehnologijas/tools/qa/structure-checklist.md +++ /dev/null @@ -1,10 +0,0 @@ -# Regenerated document completeness checklist (kultura-valoda) - -The regenerated architecture document MUST include: - -- [ ] Abbreviations/terms (1.3) -- [ ] Related documents (1.4) -- [ ] Stakeholders/organizations list (1.2 scope) -- [ ] Legal view section "Juridiskais skats" (4.1) sourced from `registers/00-meta/legal-acts.yaml` - -If any item is missing, the generator/template must be updated (do not delete content from re diff --git a/schemas/arhitektura-0.1.schema.json b/schemas/arhitektura-0.1.schema.json new file mode 100644 index 0000000..3a24322 --- /dev/null +++ b/schemas/arhitektura-0.1.schema.json @@ -0,0 +1,166 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://processgit.org/Valsts-Pirmkods/Architecture-as-Code/schemas/arhitektura-0.1.schema.json", + "title": "Digitālās pārvaldes arhitektūras kā kods — shēma v0.1", + "description": "Kopīga shēma visām VARAM digitālās pārvaldes arhitektūras jomām (horizontālajām un nozaru). Jomas reģistri ir YAML datnes; katru datni pārbauda pret vienu no šeit definētajiem tipiem (sk. x-roles). Identifikatori jomā ir lokāli (func.01); pilnais identifikators ir :, piemēram kultura-valoda:func.01. PPPA, Valsts Pirmkods. Melnraksts.", + "x-roles": { + "catalogue": "Catalogue", + "manifest": "Manifest", + "organizations": "OrganizationsFile", + "functions": "FunctionsFile", + "services": "ComponentsFile", + "information_resources": "ComponentsFile", + "systems": "ComponentsFile", + "goals": "Goal", + "roadmap": "ItemsFile", + "interactions": "ItemsFile", + "risks": "ItemsFile", + "as_is_catalog": "ItemsFile", + "relations": "EdgesFile", + "meta": "AnyObject", + "as_is": "AnyObject" + }, + "$defs": { + "LocalId": { + "description": "Lokālais identifikators jomā: prefikss un segmenti, piemēram func.01, svc.k.03, org.kisc, goal.m1, domain.kultura-valoda.", + "type": "string", + "pattern": "^[a-z][a-z_]*(\\.[A-Za-z0-9_-]+)+$" + }, + "Slug": {"type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"}, + "VpkId": {"description": "VPK ID no Valsts kancelejas organizāciju reģistra (B01): SS-IIII.", "type": "string", "pattern": "^[0-9]{2}-[0-9]{4}$"}, + "Text": {"type": "string", "minLength": 1}, + "AnyObject": {"type": "object"}, + + "Catalogue": { + "description": "domenas.yaml — visu VARAM digitālās pārvaldes arhitektūras jomu saraksts un katras jomas statuss šajā repozitorijā.", + "type": "object", + "required": ["version", "sources", "domains"], + "properties": { + "version": {"type": "string"}, + "sources": {"type": "array", "items": {"type": "object", "required": ["url", "title"], "properties": {"url": {"type": "string", "format": "uri"}, "title": {"type": "string"}, "checked": {"type": "string", "format": "date"}}}}, + "domains": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/CatalogueDomain"}} + } + }, + "CatalogueDomain": { + "type": "object", + "required": ["slug", "title", "kind", "lead_institution", "varam_status", "as_code"], + "properties": { + "slug": {"$ref": "#/$defs/Slug"}, + "title": {"$ref": "#/$defs/Text"}, + "kind": {"enum": ["horizontal", "sectoral", "subdomain"], "description": "horizontal — horizontālā joma; sectoral — nozaru vai starpnozaru joma; subdomain — apakšjoma (parent norāda jomu)"}, + "parent": {"$ref": "#/$defs/Slug"}, + "lead_institution": {"$ref": "#/$defs/Text", "description": "Iestāde, kuras pārstāvis vada arhitektūras apraksta izstrādi (kā VARAM lapā). Personu kontaktdati netiek glabāti."}, + "lead_vpk_id": {"$ref": "#/$defs/VpkId"}, + "varam_status": {"enum": ["approved", "in_review", "in_development", "not_started"]}, + "approved": {"type": "string", "format": "date"}, + "approved_versions": {"type": "array", "items": {"type": "object", "required": ["version", "approved"], "properties": {"version": {"type": "string"}, "approved": {"type": "string", "format": "date"}}}}, + "approved_by": {"type": "string"}, + "description_url": {"type": "string", "format": "uri"}, + "presentation_url": {"type": "string", "format": "uri"}, + "as_code": { + "type": "object", + "required": ["status"], + "properties": { + "status": {"enum": ["transformed", "in_progress", "not_started"]}, + "path": {"type": "string", "pattern": "^domains/[a-z0-9-]+$"}, + "source": {"type": "string", "description": "No kura dokumenta pārveidots (datne mapē sources/)"} + }, + "if": {"properties": {"status": {"const": "transformed"}}}, + "then": {"required": ["status", "path"]} + } + } + }, + + "Manifest": { + "description": "domains//manifest.yaml — jomas apraksts un tās reģistru atrašanās vieta.", + "type": "object", + "required": ["id", "title", "version", "status", "registers"], + "properties": { + "id": {"type": "string", "pattern": "^domain\\.[a-z0-9]+(-[a-z0-9]+)*$"}, + "title": {"$ref": "#/$defs/Text"}, + "version": {"type": "string"}, + "status": {"enum": ["draft", "approved", "superseded"]}, + "template_compliance": {"type": "boolean"}, + "views": {"type": "array"}, + "registers": { + "type": "object", + "description": "Reģistra loma → datne vai mape (relatīvi pret jomas mapi). Lomas: sk. x-roles.", + "properties": { + "organizations": {"type": "string"}, "functions": {"type": "string"}, "services": {"type": "string"}, + "information_resources": {"type": "string"}, "systems": {"type": "string"}, "relations": {"type": "string"} + }, + "required": ["organizations", "functions", "relations"], + "additionalProperties": {"type": "string"} + } + } + }, + + "ItemsFile": {"type": "object", "required": ["items"], "properties": {"items": {"type": "array", "items": {"$ref": "#/$defs/Item"}}}}, + "Item": {"type": "object", "required": ["id"], "properties": {"id": {"$ref": "#/$defs/LocalId"}}}, + + "OrganizationsFile": {"type": "object", "required": ["items"], "properties": {"items": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/Organization"}}}}, + "Organization": { + "type": "object", + "required": ["id", "title", "role", "status"], + "properties": { + "id": {"type": "string", "pattern": "^org\\.[A-Za-z0-9_.-]+$"}, + "title": {"$ref": "#/$defs/Text"}, + "role": {"$ref": "#/$defs/Text"}, + "responsibilities": {"type": "array", "items": {"type": "string"}}, + "status": {"type": "string"}, + "vpk_id": {"$ref": "#/$defs/VpkId", "description": "Iestādes VPK ID (B01). Ja nav norādīts, VDZP to meklē pēc nosaukuma un saiti atzīmē kā izgūtu."} + } + }, + + "FunctionsFile": {"type": "object", "required": ["items"], "properties": {"items": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/Function"}}}}, + "Function": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": {"type": "string", "pattern": "^func\\.[A-Za-z0-9_.-]+$"}, + "subdomain": {"$ref": "#/$defs/LocalId"}, + "name": {"$ref": "#/$defs/Text"}, + "change_status": {"type": "string"}, + "change_description": {"type": "string"}, + "performed_by": {"type": "array", "items": {"$ref": "#/$defs/LocalId"}, "description": "Organizācijas (org.*), kas veic funkciju"} + } + }, + + "ComponentsFile": {"type": "object", "required": ["items"], "properties": {"items": {"type": "array", "items": {"$ref": "#/$defs/Component"}}}}, + "Component": { + "description": "Pakalpojums, informācijas resurss vai informācijas sistēma.", + "type": "object", + "required": ["id", "name", "status"], + "properties": { + "id": {"$ref": "#/$defs/LocalId"}, + "number": {}, + "subdomain": {"$ref": "#/$defs/LocalId"}, + "name": {"$ref": "#/$defs/Text"}, + "status": {"type": "string"}, + "description": {"type": "string"}, + "change_description": {"type": "string"}, + "resources": {} + } + }, + + "Goal": { + "type": "object", + "required": ["id", "title"], + "properties": {"id": {"type": "string", "pattern": "^goal\\.[A-Za-z0-9_.-]+$"}, "code": {"type": "string"}, "title": {"$ref": "#/$defs/Text"}, + "description": {"type": "string"}, "status": {"type": "string"}} + }, + + "EdgesFile": {"type": "object", "required": ["edges"], "properties": {"edges": {"type": "array", "items": {"$ref": "#/$defs/Edge"}}}}, + "Edge": { + "type": "object", + "required": ["from", "type", "to"], + "properties": { + "from": {"$ref": "#/$defs/LocalId"}, + "type": {"type": "string", "pattern": "^[a-z]+(_[a-z]+)*$", + "description": "Saites veids, piemēram has_goal, has_function, has_service, has_information_resource, has_system, performed_by, uses, provides"}, + "to": {"$ref": "#/$defs/LocalId"} + }, + "additionalProperties": false + } + } +} diff --git a/ikt-arh-kultura-valodu-tehnologijas/docs/ARH_kulturas_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf b/sources/kultura-valoda/ARH_kulturas_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/docs/ARH_kulturas_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf rename to sources/kultura-valoda/ARH_kulturas_valodu_jomu_PREZENT_V0.4_4.06 (1).pdf diff --git a/ikt-arh-kultura-valodu-tehnologijas/docs/MērķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf b/sources/kultura-valoda/MērķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf similarity index 100% rename from ikt-arh-kultura-valodu-tehnologijas/docs/MērķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf rename to sources/kultura-valoda/MērķARH_Kultura_Valoda_V02_18.06.2025 (1).pdf diff --git a/tools/validate.py b/tools/validate.py new file mode 100644 index 0000000..253af8b --- /dev/null +++ b/tools/validate.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""Pārbauda repozitoriju pret shēmu un datu līguma noteikumiem (tās pašas pārbaudes, ko veic VDZP savienotājs). + + python3 tools/validate.py (no repozitorija saknes; pip install pyyaml jsonschema) + +Pārbaudes: + catalogue_valid domenas.yaml atbilst #/$defs/Catalogue; slug unikāli; katrai pārveidotajai jomai mape eksistē + schema_valid katrs jomas reģistrs atbilst savas lomas tipam (schemas/arhitektura-0.1.schema.json, x-roles) + ids_unique lokālie identifikatori jomā ir unikāli + edges_resolve katras saites from un to ir šīs jomas elements +""" +import json +import sys +from pathlib import Path + +import yaml +from jsonschema import Draft202012Validator + +ROOT = Path(__file__).resolve().parents[1] +SCHEMA = json.loads((ROOT / "schemas/arhitektura-0.1.schema.json").read_text(encoding="utf-8")) + +ENTITY = {"organizations": "organisation", "functions": "function", "services": "service", + "information_resources": "information_resource", "systems": "system", "goals": "goal", + "roadmap": "roadmap_item", "interactions": "interaction", "risks": "risk", "as_is_catalog": "as_is_component"} + + +def validator(defname): + return Draft202012Validator({"$schema": SCHEMA["$schema"], "$defs": SCHEMA["$defs"], "$ref": f"#/$defs/{defname}"}) + + +def errors(defname, doc, where): + return [f"{where}: {'/'.join(map(str, e.absolute_path)) or '(sakne)'}: {e.message[:200]}" + for e in sorted(validator(defname).iter_errors(doc), key=lambda e: list(e.absolute_path))][:20] + + +def files_of(domain_dir, rel): + p = domain_dir / rel + if p.is_dir(): + return sorted(p.rglob("*.yaml")) + return [p] if p.exists() else [] + + +def domain(domain_dir): + """-> (entities, edges, problems) for one domain folder.""" + slug = domain_dir.name + problems = {"schema_valid": [], "ids_unique": [], "edges_resolve": []} + man_path = domain_dir / "manifest.yaml" + man = yaml.safe_load(man_path.read_text(encoding="utf-8")) + problems["schema_valid"] += errors("Manifest", man, f"{slug}/manifest.yaml") + if man.get("id") != f"domain.{slug}": + problems["schema_valid"].append(f"{slug}/manifest.yaml: id {man.get('id')} ≠ domain.{slug}") + entities, edges = [], [] + for role, rel in (man.get("registers") or {}).items(): + defname = SCHEMA["x-roles"].get(role, "AnyObject") + for f in files_of(domain_dir, rel): + doc = yaml.safe_load(f.read_text(encoding="utf-8")) or {} + where = f"{slug}/{f.relative_to(domain_dir)}" + problems["schema_valid"] += errors(defname, doc, where) + if role == "relations": + edges += [dict(e, file=where) for e in doc.get("edges", []) if isinstance(e, dict)] + elif role in ENTITY: + items = [doc] if defname == "Goal" else doc.get("items", []) if isinstance(doc, dict) else [] + entities += [dict(it, _type=ENTITY[role], _file=where) for it in items if isinstance(it, dict) and it.get("id")] + # the domain and its subdomains are nodes too (edges start from domain.) + meta = domain_dir / "registers/00-meta/domain.yaml" + nodes = {f"domain.{slug}"} + if meta.exists(): + m = yaml.safe_load(meta.read_text(encoding="utf-8")) or {} + nodes |= {s["id"] for s in m.get("subdomains", []) if isinstance(s, dict) and s.get("id")} + seen = {} + for e in entities: + if e["id"] in seen: + problems["ids_unique"].append(f"{slug}: {e['id']} ({seen[e['id']]} un {e['_file']})") + seen[e["id"]] = e["_file"] + known = set(seen) | nodes + for e in edges: + for k in ("from", "to"): + if e.get(k) not in known: + problems["edges_resolve"].append(f"{e['file']}: {e.get('from')} -{e.get('type')}-> {e.get('to')} ({k} nav jomā)") + return man, entities, edges, problems + + +def main(): + cat = yaml.safe_load((ROOT / "domenas.yaml").read_text(encoding="utf-8")) + bad = errors("Catalogue", cat, "domenas.yaml") + slugs = [d.get("slug") for d in cat.get("domains", [])] + bad += [f"domenas.yaml: slug {s} atkārtojas" for s in set(slugs) if slugs.count(s) > 1] + for d in cat.get("domains", []): + if d.get("as_code", {}).get("status") == "transformed" and not (ROOT / d["as_code"].get("path", "-") / "manifest.yaml").exists(): + bad.append(f"domenas.yaml: {d['slug']}: {d['as_code'].get('path')}/manifest.yaml nav") + for p in sorted((ROOT / "domains").glob("*/manifest.yaml")): + if p.parent.name not in slugs: + bad.append(f"domains/{p.parent.name}: jomas nav domenas.yaml") + total = {"catalogue_valid": bad} + for p in sorted((ROOT / "domains").glob("*/manifest.yaml")): + man, ents, edges, prob = domain(p.parent) + print(f"{p.parent.name}: {len(ents)} elementi, {len(edges)} saites") + for k, v in prob.items(): + total.setdefault(k, []).extend(v) + ok = True + for k, v in total.items(): + print(f"{k}: {'OK' if not v else f'{len(v)} kļūdas'}") + for x in v[:10]: + print(" ", x) + ok &= not v + n = sum(1 for d in cat["domains"] if d["as_code"]["status"] == "transformed") + print(f"Jomas: {len(cat['domains'])}, pārveidotas kodā: {n}") + sys.exit(0 if ok else 1) + + +if __name__ == "__main__": + main()