@ansvar/ch-environmental-compliance-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/check-freshness.yml +49 -0
- package/.github/workflows/ci.yml +21 -0
- package/.github/workflows/codeql.yml +25 -0
- package/.github/workflows/ghcr-build.yml +45 -0
- package/.github/workflows/gitleaks.yml +18 -0
- package/.github/workflows/ingest.yml +59 -0
- package/.github/workflows/publish.yml +24 -0
- package/CHANGELOG.md +15 -0
- package/CODEOWNERS +1 -0
- package/COVERAGE.md +50 -0
- package/DISCLAIMER.md +48 -0
- package/Dockerfile +26 -0
- package/LICENSE +17 -0
- package/PRIVACY.md +23 -0
- package/README.md +116 -0
- package/SECURITY.md +25 -0
- package/TOOLS.md +142 -0
- package/data/coverage.json +24 -0
- package/data/database.db +0 -0
- package/data/sources.yml +36 -0
- package/dist/db.d.ts +25 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +197 -0
- package/dist/db.js.map +1 -0
- package/dist/http-server.d.ts +2 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +274 -0
- package/dist/http-server.js.map +1 -0
- package/dist/jurisdiction.d.ts +18 -0
- package/dist/jurisdiction.d.ts.map +1 -0
- package/dist/jurisdiction.js +16 -0
- package/dist/jurisdiction.js.map +1 -0
- package/dist/metadata.d.ts +10 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +22 -0
- package/dist/metadata.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +220 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/about.d.ts +15 -0
- package/dist/tools/about.d.ts.map +1 -0
- package/dist/tools/about.js +27 -0
- package/dist/tools/about.js.map +1 -0
- package/dist/tools/check-environmental-compliance.d.ts +30 -0
- package/dist/tools/check-environmental-compliance.d.ts.map +1 -0
- package/dist/tools/check-environmental-compliance.js +103 -0
- package/dist/tools/check-environmental-compliance.js.map +1 -0
- package/dist/tools/check-freshness.d.ts +15 -0
- package/dist/tools/check-freshness.d.ts.map +1 -0
- package/dist/tools/check-freshness.js +26 -0
- package/dist/tools/check-freshness.js.map +1 -0
- package/dist/tools/get-ammonia-rules.d.ts +24 -0
- package/dist/tools/get-ammonia-rules.d.ts.map +1 -0
- package/dist/tools/get-ammonia-rules.js +31 -0
- package/dist/tools/get-ammonia-rules.js.map +1 -0
- package/dist/tools/get-bff-requirements.d.ts +26 -0
- package/dist/tools/get-bff-requirements.d.ts.map +1 -0
- package/dist/tools/get-bff-requirements.js +36 -0
- package/dist/tools/get-bff-requirements.js.map +1 -0
- package/dist/tools/get-buffer-zone-rules.d.ts +23 -0
- package/dist/tools/get-buffer-zone-rules.d.ts.map +1 -0
- package/dist/tools/get-buffer-zone-rules.js +30 -0
- package/dist/tools/get-buffer-zone-rules.js.map +1 -0
- package/dist/tools/get-eip-requirements.d.ts +31 -0
- package/dist/tools/get-eip-requirements.d.ts.map +1 -0
- package/dist/tools/get-eip-requirements.js +40 -0
- package/dist/tools/get-eip-requirements.js.map +1 -0
- package/dist/tools/get-nutrient-loss-limits.d.ts +24 -0
- package/dist/tools/get-nutrient-loss-limits.d.ts.map +1 -0
- package/dist/tools/get-nutrient-loss-limits.js +31 -0
- package/dist/tools/get-nutrient-loss-limits.js.map +1 -0
- package/dist/tools/get-water-protection-zones.d.ts +32 -0
- package/dist/tools/get-water-protection-zones.d.ts.map +1 -0
- package/dist/tools/get-water-protection-zones.js +36 -0
- package/dist/tools/get-water-protection-zones.js.map +1 -0
- package/dist/tools/list-sources.d.ts +18 -0
- package/dist/tools/list-sources.d.ts.map +1 -0
- package/dist/tools/list-sources.js +61 -0
- package/dist/tools/list-sources.js.map +1 -0
- package/dist/tools/search-environmental-rules.d.ts +25 -0
- package/dist/tools/search-environmental-rules.d.ts.map +1 -0
- package/dist/tools/search-environmental-rules.js +26 -0
- package/dist/tools/search-environmental-rules.js.map +1 -0
- package/docker-compose.yml +12 -0
- package/eslint.config.js +26 -0
- package/package.json +54 -0
- package/scripts/ingest.ts +911 -0
- package/server.json +16 -0
- package/src/db.ts +238 -0
- package/src/http-server.ts +307 -0
- package/src/jurisdiction.ts +30 -0
- package/src/metadata.ts +32 -0
- package/src/server.ts +244 -0
- package/src/tools/about.ts +28 -0
- package/src/tools/check-environmental-compliance.ts +143 -0
- package/src/tools/check-freshness.ts +42 -0
- package/src/tools/get-ammonia-rules.ts +44 -0
- package/src/tools/get-bff-requirements.ts +52 -0
- package/src/tools/get-buffer-zone-rules.ts +43 -0
- package/src/tools/get-eip-requirements.ts +57 -0
- package/src/tools/get-nutrient-loss-limits.ts +44 -0
- package/src/tools/get-water-protection-zones.ts +50 -0
- package/src/tools/list-sources.ts +75 -0
- package/src/tools/search-environmental-rules.ts +35 -0
- package/tests/db.test.ts +80 -0
- package/tests/helpers/seed-db.ts +173 -0
- package/tests/jurisdiction.test.ts +35 -0
- package/tests/tools/about.test.ts +26 -0
- package/tests/tools/check-freshness.test.ts +50 -0
- package/tests/tools/list-sources.test.ts +61 -0
- package/tests/tools/search-environmental-rules.test.ts +47 -0
- package/tsconfig.json +19 -0
package/TOOLS.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Tools Reference
|
|
2
|
+
|
|
3
|
+
## Meta Tools
|
|
4
|
+
|
|
5
|
+
### `about`
|
|
6
|
+
|
|
7
|
+
Get server metadata: name, version, coverage, data sources, and links.
|
|
8
|
+
|
|
9
|
+
**Parameters:** None
|
|
10
|
+
|
|
11
|
+
**Returns:** Server name, version, jurisdiction list, data source names, tool count, homepage/repository links.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### `list_sources`
|
|
16
|
+
|
|
17
|
+
List all data sources with authority, URL, license, and freshness info.
|
|
18
|
+
|
|
19
|
+
**Parameters:** None
|
|
20
|
+
|
|
21
|
+
**Returns:** Array of data sources, each with `name`, `authority`, `official_url`, `retrieval_method`, `update_frequency`, `license`, `coverage`, `last_retrieved`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### `check_data_freshness`
|
|
26
|
+
|
|
27
|
+
Check when data was last ingested, staleness status, and how to trigger a refresh.
|
|
28
|
+
|
|
29
|
+
**Parameters:** None
|
|
30
|
+
|
|
31
|
+
**Returns:** `status` (fresh/stale/unknown), `last_ingest`, `days_since_ingest`, `staleness_threshold_days`, `refresh_command`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Domain Tools
|
|
36
|
+
|
|
37
|
+
### `search_environmental_rules`
|
|
38
|
+
|
|
39
|
+
Search Swiss environmental compliance rules across all topics: Gewaesserschutz, Ammoniak, BFF, Pufferstreifen, VBBo, UVP. Use for broad queries.
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Required | Description |
|
|
42
|
+
|-----------|------|----------|-------------|
|
|
43
|
+
| `query` | string | Yes | Free-text search query (German or English) |
|
|
44
|
+
| `topic` | string | No | Filter by topic (e.g. Gewaesserschutz, Ammoniak, BFF, UVP, VBBo) |
|
|
45
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
46
|
+
| `limit` | number | No | Max results (default: 20, max: 50) |
|
|
47
|
+
|
|
48
|
+
**Example:** `{ "query": "Schleppschlauch Ammoniak" }`
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### `get_water_protection_zones`
|
|
53
|
+
|
|
54
|
+
Get Grundwasserschutzzonen (S1, S2, S3, Sm, Zu) with restrictions and legal basis. Based on GSchG/GSchV.
|
|
55
|
+
|
|
56
|
+
| Parameter | Type | Required | Description |
|
|
57
|
+
|-----------|------|----------|-------------|
|
|
58
|
+
| `zone_type` | string | No | Zone type: S1, S2, S3, Sm, Zu (omit for all zones) |
|
|
59
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
60
|
+
|
|
61
|
+
**Example:** `{ "zone_type": "S2" }`
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### `get_buffer_zone_rules`
|
|
66
|
+
|
|
67
|
+
Get Pufferstreifen (buffer zone) distances and requirements along water bodies, hedges, and field boundaries. Based on OELN/ChemRRV.
|
|
68
|
+
|
|
69
|
+
| Parameter | Type | Required | Description |
|
|
70
|
+
|-----------|------|----------|-------------|
|
|
71
|
+
| `zone_type` | string | No | Buffer type filter (e.g. Gewaesser, Hecke, Nachbar, SPe) |
|
|
72
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
73
|
+
|
|
74
|
+
**Example:** `{ "zone_type": "Gewaesser" }`
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### `get_ammonia_rules`
|
|
79
|
+
|
|
80
|
+
Get Ammoniakemissionen rules: emission factors by technique, Schleppschlauch-Pflicht, Agrammon model parameters. Based on LRV.
|
|
81
|
+
|
|
82
|
+
| Parameter | Type | Required | Description |
|
|
83
|
+
|-----------|------|----------|-------------|
|
|
84
|
+
| `technique` | string | No | Application technique filter (e.g. Schleppschlauch, Prallteller, Injektion) |
|
|
85
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
86
|
+
|
|
87
|
+
**Example:** `{ "technique": "Schleppschlauch" }`
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### `get_bff_requirements`
|
|
92
|
+
|
|
93
|
+
Get Biodiversitaetsfoerderflaechen (BFF) types, QI/QII payment rates, minimum area requirements, and botanical criteria. Based on DZV.
|
|
94
|
+
|
|
95
|
+
| Parameter | Type | Required | Description |
|
|
96
|
+
|-----------|------|----------|-------------|
|
|
97
|
+
| `bff_type` | string | No | BFF type filter (e.g. extensiv-wiese, buntbrache, hecke) |
|
|
98
|
+
| `quality_level` | string | No | Quality level: QI or QII |
|
|
99
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
100
|
+
|
|
101
|
+
**Example:** `{ "quality_level": "QII" }`
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### `get_nutrient_loss_limits`
|
|
106
|
+
|
|
107
|
+
Get Pa.Iv. 19.475 nutrient loss reduction targets (Absenkpfad) for N and P through 2030. Yearly milestones and Suisse-Bilanz tolerance changes.
|
|
108
|
+
|
|
109
|
+
| Parameter | Type | Required | Description |
|
|
110
|
+
|-----------|------|----------|-------------|
|
|
111
|
+
| `nutrient` | string | No | Nutrient filter: N (Stickstoff) or P (Phosphor) |
|
|
112
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
113
|
+
|
|
114
|
+
**Example:** `{ "nutrient": "N" }`
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### `get_eip_requirements`
|
|
119
|
+
|
|
120
|
+
Get UVP (Umweltvertraeglichkeitspruefung) thresholds for agricultural buildings and VBBo Richtwerte for soil contamination.
|
|
121
|
+
|
|
122
|
+
| Parameter | Type | Required | Description |
|
|
123
|
+
|-----------|------|----------|-------------|
|
|
124
|
+
| `project_type` | string | No | Project type filter (e.g. Stallbau, Biogasanlage, Schweinehaltung) |
|
|
125
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
126
|
+
|
|
127
|
+
**Example:** `{ "project_type": "Stallbau" }`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### `check_environmental_compliance`
|
|
132
|
+
|
|
133
|
+
Check which environmental rules apply to a given agricultural operation: water protection, buffer zones, ammonia, BFF, UVP, nutrient loss limits.
|
|
134
|
+
|
|
135
|
+
| Parameter | Type | Required | Description |
|
|
136
|
+
|-----------|------|----------|-------------|
|
|
137
|
+
| `facility_type` | string | Yes | Type of operation (e.g. Milchwirtschaft, Schweinehaltung, Ackerbau, Gemuese) |
|
|
138
|
+
| `animal_count` | number | No | Number of animals (GVE) -- used for ammonia and UVP thresholds |
|
|
139
|
+
| `area_ha` | number | No | Farm area in hectares -- used for BFF minimum calculations |
|
|
140
|
+
| `jurisdiction` | string | No | ISO 3166-1 alpha-2 code (default: CH) |
|
|
141
|
+
|
|
142
|
+
**Example:** `{ "facility_type": "Milchwirtschaft", "animal_count": 60, "area_ha": 35 }`
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"server": "ch-environmental-compliance-mcp",
|
|
3
|
+
"jurisdiction": "CH",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"last_ingest": "2026-04-05",
|
|
6
|
+
"data": {
|
|
7
|
+
"water_protection_zones": 5,
|
|
8
|
+
"buffer_zones": 9,
|
|
9
|
+
"ammonia_rules": 10,
|
|
10
|
+
"bff_types": 18,
|
|
11
|
+
"nutrient_loss_limits": 8,
|
|
12
|
+
"environmental_rules": 16,
|
|
13
|
+
"fts_entries": 66
|
|
14
|
+
},
|
|
15
|
+
"tools": 11,
|
|
16
|
+
"sources": [
|
|
17
|
+
"BAFU — GSchG/GSchV (Gewaesserschutz)",
|
|
18
|
+
"BAFU — LRV (Luftreinhaltung, Ammoniak)",
|
|
19
|
+
"BLW — DZV (BFF, OELN)",
|
|
20
|
+
"Agroscope — Agrammon (Emissionsfaktoren)",
|
|
21
|
+
"Parlament — Pa.Iv. 19.475 (Absenkpfad)",
|
|
22
|
+
"BAFU — VBBo (Bodenbelastung)"
|
|
23
|
+
]
|
|
24
|
+
}
|
package/data/database.db
ADDED
|
Binary file
|
package/data/sources.yml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Data sources for ch-environmental-compliance-mcp
|
|
2
|
+
sources:
|
|
3
|
+
- name: Gewaesserschutzgesetz (GSchG) / Gewaesserschutzverordnung (GSchV)
|
|
4
|
+
authority: BAFU (Bundesamt fuer Umwelt)
|
|
5
|
+
url: https://www.bafu.admin.ch/bafu/de/home/themen/wasser/fachinformationen/gewaesserschutz.html
|
|
6
|
+
license: Swiss Federal Administration — free reuse
|
|
7
|
+
update_frequency: periodic (amendments as enacted)
|
|
8
|
+
last_retrieved: "2026-04-05"
|
|
9
|
+
|
|
10
|
+
- name: Luftreinhalte-Verordnung (LRV) / Agrammon
|
|
11
|
+
authority: BAFU / Agroscope
|
|
12
|
+
url: https://www.bafu.admin.ch/bafu/de/home/themen/luft/fachinformationen/luftschadstoffquellen/emissionen-der-landwirtschaft.html
|
|
13
|
+
license: Swiss Federal Administration — free reuse
|
|
14
|
+
update_frequency: periodic (Agrammon model updates)
|
|
15
|
+
last_retrieved: "2026-04-05"
|
|
16
|
+
|
|
17
|
+
- name: Direktzahlungsverordnung (DZV) — BFF und OELN
|
|
18
|
+
authority: BLW (Bundesamt fuer Landwirtschaft)
|
|
19
|
+
url: https://www.blw.admin.ch/blw/de/home/instrumente/direktzahlungen/biodiversitaetsbeitraege.html
|
|
20
|
+
license: Swiss Federal Administration — free reuse
|
|
21
|
+
update_frequency: annual (DZV revisions)
|
|
22
|
+
last_retrieved: "2026-04-05"
|
|
23
|
+
|
|
24
|
+
- name: Pa.Iv. 19.475 — Absenkpfad Naehrstoffverluste
|
|
25
|
+
authority: Parlament / BLW
|
|
26
|
+
url: https://www.blw.admin.ch/blw/de/home/nachhaltige-produktion/umwelt/naehrstoffe.html
|
|
27
|
+
license: Swiss Federal Administration — free reuse
|
|
28
|
+
update_frequency: annual targets through 2030
|
|
29
|
+
last_retrieved: "2026-04-05"
|
|
30
|
+
|
|
31
|
+
- name: VBBo — Verordnung ueber Belastungen des Bodens
|
|
32
|
+
authority: BAFU
|
|
33
|
+
url: https://www.bafu.admin.ch/bafu/de/home/themen/boden/fachinformationen/bodenschutz.html
|
|
34
|
+
license: Swiss Federal Administration — free reuse
|
|
35
|
+
update_frequency: periodic
|
|
36
|
+
last_retrieved: "2026-04-05"
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
export interface Database {
|
|
3
|
+
get<T>(sql: string, params?: unknown[]): T | undefined;
|
|
4
|
+
all<T>(sql: string, params?: unknown[]): T[];
|
|
5
|
+
run(sql: string, params?: unknown[]): void;
|
|
6
|
+
close(): void;
|
|
7
|
+
readonly instance: BetterSqlite3.Database;
|
|
8
|
+
}
|
|
9
|
+
export declare function createDatabase(dbPath?: string): Database;
|
|
10
|
+
export declare function ftsSearch(db: Database, query: string, limit?: number): {
|
|
11
|
+
title: string;
|
|
12
|
+
body: string;
|
|
13
|
+
topic: string;
|
|
14
|
+
jurisdiction: string;
|
|
15
|
+
rank: number;
|
|
16
|
+
}[];
|
|
17
|
+
/**
|
|
18
|
+
* Tiered FTS5 search with automatic fallback.
|
|
19
|
+
* Tiers: exact phrase -> AND -> prefix -> stemmed prefix -> OR -> LIKE
|
|
20
|
+
*/
|
|
21
|
+
export declare function tieredFtsSearch(db: Database, table: string, columns: string[], query: string, limit?: number): {
|
|
22
|
+
tier: string;
|
|
23
|
+
results: Record<string, unknown>[];
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAI3C,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;IACvD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC3C,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;CAC3C;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CA4BxD;AA2FD,wBAAgB,SAAS,CACvB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,GACjB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAGtF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,CA4DtD"}
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
export function createDatabase(dbPath) {
|
|
5
|
+
const resolvedPath = dbPath ??
|
|
6
|
+
join(dirname(fileURLToPath(import.meta.url)), '..', 'data', 'database.db');
|
|
7
|
+
const db = new BetterSqlite3(resolvedPath);
|
|
8
|
+
db.pragma('journal_mode = DELETE');
|
|
9
|
+
db.pragma('foreign_keys = ON');
|
|
10
|
+
initSchema(db);
|
|
11
|
+
return {
|
|
12
|
+
get(sql, params = []) {
|
|
13
|
+
return db.prepare(sql).get(...params);
|
|
14
|
+
},
|
|
15
|
+
all(sql, params = []) {
|
|
16
|
+
return db.prepare(sql).all(...params);
|
|
17
|
+
},
|
|
18
|
+
run(sql, params = []) {
|
|
19
|
+
db.prepare(sql).run(...params);
|
|
20
|
+
},
|
|
21
|
+
close() {
|
|
22
|
+
db.close();
|
|
23
|
+
},
|
|
24
|
+
get instance() {
|
|
25
|
+
return db;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function initSchema(db) {
|
|
30
|
+
db.exec(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS water_protection_zones (
|
|
32
|
+
id INTEGER PRIMARY KEY,
|
|
33
|
+
zone_type TEXT NOT NULL,
|
|
34
|
+
name TEXT NOT NULL,
|
|
35
|
+
restrictions TEXT NOT NULL,
|
|
36
|
+
description TEXT NOT NULL,
|
|
37
|
+
legal_basis TEXT,
|
|
38
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
39
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE IF NOT EXISTS buffer_zones (
|
|
43
|
+
id INTEGER PRIMARY KEY,
|
|
44
|
+
type TEXT NOT NULL,
|
|
45
|
+
distance_m REAL NOT NULL,
|
|
46
|
+
requirement TEXT NOT NULL,
|
|
47
|
+
source_law TEXT NOT NULL,
|
|
48
|
+
notes TEXT,
|
|
49
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
50
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE TABLE IF NOT EXISTS ammonia_rules (
|
|
54
|
+
id INTEGER PRIMARY KEY,
|
|
55
|
+
technique TEXT NOT NULL,
|
|
56
|
+
emission_factor REAL,
|
|
57
|
+
requirement TEXT NOT NULL,
|
|
58
|
+
legal_basis TEXT,
|
|
59
|
+
effective_date TEXT,
|
|
60
|
+
notes TEXT,
|
|
61
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
62
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS bff_types (
|
|
66
|
+
id TEXT PRIMARY KEY,
|
|
67
|
+
name TEXT NOT NULL,
|
|
68
|
+
quality_level TEXT NOT NULL,
|
|
69
|
+
payment_chf_ha REAL,
|
|
70
|
+
min_area_pct REAL,
|
|
71
|
+
botanical_criteria TEXT,
|
|
72
|
+
notes TEXT,
|
|
73
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
74
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS nutrient_loss_limits (
|
|
78
|
+
id INTEGER PRIMARY KEY,
|
|
79
|
+
nutrient TEXT NOT NULL,
|
|
80
|
+
year INTEGER NOT NULL,
|
|
81
|
+
limit_pct REAL NOT NULL,
|
|
82
|
+
target TEXT NOT NULL,
|
|
83
|
+
legal_basis TEXT,
|
|
84
|
+
notes TEXT,
|
|
85
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
86
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
CREATE TABLE IF NOT EXISTS environmental_rules (
|
|
90
|
+
id INTEGER PRIMARY KEY,
|
|
91
|
+
topic TEXT NOT NULL,
|
|
92
|
+
rule TEXT NOT NULL,
|
|
93
|
+
authority TEXT NOT NULL,
|
|
94
|
+
legal_basis TEXT NOT NULL,
|
|
95
|
+
threshold TEXT,
|
|
96
|
+
notes TEXT,
|
|
97
|
+
jurisdiction TEXT NOT NULL DEFAULT 'CH',
|
|
98
|
+
language TEXT NOT NULL DEFAULT 'DE'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
|
|
102
|
+
title, body, topic, jurisdiction
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS db_metadata (
|
|
106
|
+
key TEXT PRIMARY KEY,
|
|
107
|
+
value TEXT
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
INSERT OR IGNORE INTO db_metadata (key, value) VALUES ('schema_version', '1.0');
|
|
111
|
+
INSERT OR IGNORE INTO db_metadata (key, value) VALUES ('mcp_name', 'Switzerland Environmental Compliance MCP');
|
|
112
|
+
INSERT OR IGNORE INTO db_metadata (key, value) VALUES ('jurisdiction', 'CH');
|
|
113
|
+
`);
|
|
114
|
+
}
|
|
115
|
+
const FTS_COLUMNS = ['title', 'body', 'topic', 'jurisdiction'];
|
|
116
|
+
export function ftsSearch(db, query, limit = 20) {
|
|
117
|
+
const { results } = tieredFtsSearch(db, 'search_index', FTS_COLUMNS, query, limit);
|
|
118
|
+
return results;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Tiered FTS5 search with automatic fallback.
|
|
122
|
+
* Tiers: exact phrase -> AND -> prefix -> stemmed prefix -> OR -> LIKE
|
|
123
|
+
*/
|
|
124
|
+
export function tieredFtsSearch(db, table, columns, query, limit = 20) {
|
|
125
|
+
const sanitized = sanitizeFtsInput(query);
|
|
126
|
+
if (!sanitized.trim())
|
|
127
|
+
return { tier: 'empty', results: [] };
|
|
128
|
+
const columnList = columns.join(', ');
|
|
129
|
+
const select = `SELECT ${columnList}, rank FROM ${table}`;
|
|
130
|
+
const order = `ORDER BY rank LIMIT ?`;
|
|
131
|
+
// Tier 1: Exact phrase
|
|
132
|
+
const phrase = `"${sanitized}"`;
|
|
133
|
+
let results = tryFts(db, select, table, order, phrase, limit);
|
|
134
|
+
if (results.length > 0)
|
|
135
|
+
return { tier: 'phrase', results };
|
|
136
|
+
// Tier 2: AND
|
|
137
|
+
const words = sanitized.split(/\s+/).filter(w => w.length > 1);
|
|
138
|
+
if (words.length > 1) {
|
|
139
|
+
const andQuery = words.join(' AND ');
|
|
140
|
+
results = tryFts(db, select, table, order, andQuery, limit);
|
|
141
|
+
if (results.length > 0)
|
|
142
|
+
return { tier: 'and', results };
|
|
143
|
+
}
|
|
144
|
+
// Tier 3: Prefix
|
|
145
|
+
const prefixQuery = words.map(w => `${w}*`).join(' AND ');
|
|
146
|
+
results = tryFts(db, select, table, order, prefixQuery, limit);
|
|
147
|
+
if (results.length > 0)
|
|
148
|
+
return { tier: 'prefix', results };
|
|
149
|
+
// Tier 4: Stemmed prefix
|
|
150
|
+
const stemmed = words.map(w => stemWord(w) + '*');
|
|
151
|
+
const stemmedQuery = stemmed.join(' AND ');
|
|
152
|
+
if (stemmedQuery !== prefixQuery) {
|
|
153
|
+
results = tryFts(db, select, table, order, stemmedQuery, limit);
|
|
154
|
+
if (results.length > 0)
|
|
155
|
+
return { tier: 'stemmed', results };
|
|
156
|
+
}
|
|
157
|
+
// Tier 5: OR
|
|
158
|
+
if (words.length > 1) {
|
|
159
|
+
const orQuery = words.join(' OR ');
|
|
160
|
+
results = tryFts(db, select, table, order, orQuery, limit);
|
|
161
|
+
if (results.length > 0)
|
|
162
|
+
return { tier: 'or', results };
|
|
163
|
+
}
|
|
164
|
+
// Tier 6: LIKE fallback
|
|
165
|
+
const baseCols = ['topic', 'rule'];
|
|
166
|
+
const likeConditions = words.map(() => `(${baseCols.map(c => `${c} LIKE ?`).join(' OR ')})`).join(' AND ');
|
|
167
|
+
const likeParams = words.flatMap(w => baseCols.map(() => `%${w}%`));
|
|
168
|
+
try {
|
|
169
|
+
const likeResults = db.all(`SELECT topic as title, rule as body, topic, jurisdiction FROM environmental_rules WHERE ${likeConditions} LIMIT ?`, [...likeParams, limit]);
|
|
170
|
+
if (likeResults.length > 0)
|
|
171
|
+
return { tier: 'like', results: likeResults };
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// LIKE fallback failed
|
|
175
|
+
}
|
|
176
|
+
return { tier: 'none', results: [] };
|
|
177
|
+
}
|
|
178
|
+
function tryFts(db, select, table, order, matchExpr, limit) {
|
|
179
|
+
try {
|
|
180
|
+
return db.all(`${select} WHERE ${table} MATCH ? ${order}`, [matchExpr, limit]);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function sanitizeFtsInput(query) {
|
|
187
|
+
return query
|
|
188
|
+
.replace(/["""'',,,,]/g, '"')
|
|
189
|
+
.replace(/[^a-zA-Z0-9\s*"_\u00C0-\u024F-]/g, ' ')
|
|
190
|
+
.replace(/\s+/g, ' ')
|
|
191
|
+
.trim();
|
|
192
|
+
}
|
|
193
|
+
function stemWord(word) {
|
|
194
|
+
return word
|
|
195
|
+
.replace(/(ung|heit|keit|lich|isch|ieren|tion|ment|ness|able|ible|ous|ive|ing|ers|ed|es|er|en|ly|s)$/i, '');
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":""}
|