@honua/mcp-server 0.1.13 → 0.1.15
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/README.md +18 -2
- package/certification/geospatial-mcp-schemas/PROVENANCE.md +14 -8
- package/certification/geospatial-mcp-schemas/common/controls.schema.json +81 -0
- package/certification/geospatial-mcp-schemas/common/exploration.schema.json +165 -0
- package/certification/geospatial-mcp-schemas/common/interactions.schema.json +263 -0
- package/certification/geospatial-mcp-schemas/index.json +155 -4
- package/certification/geospatial-mcp-schemas/resources/app-package.schema.json +17 -1
- package/certification/geospatial-mcp-schemas/resources/map-package.schema.json +17 -1
- package/certification/geospatial-mcp-schemas/resources/ops-findings.schema.json +61 -0
- package/certification/geospatial-mcp-schemas/resources/ops-health.schema.json +138 -0
- package/certification/geospatial-mcp-schemas/tools/add_control.schema.json +107 -0
- package/certification/geospatial-mcp-schemas/tools/alert_events.result.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/alert_events.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/bind_component_context.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/bind_interaction.schema.json +97 -0
- package/certification/geospatial-mcp-schemas/tools/create_exploration_context.schema.json +35 -0
- package/certification/geospatial-mcp-schemas/tools/deploy_operations.result.schema.json +109 -0
- package/certification/geospatial-mcp-schemas/tools/deploy_operations.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/operate_events.result.schema.json +67 -0
- package/certification/geospatial-mcp-schemas/tools/operate_events.schema.json +46 -0
- package/certification/geospatial-mcp-schemas/tools/ops_findings.schema.json +25 -0
- package/certification/geospatial-mcp-schemas/tools/ops_health.schema.json +10 -0
- package/certification/geospatial-mcp-schemas/tools/platform_release_status.result.schema.json +53 -0
- package/certification/geospatial-mcp-schemas/tools/platform_release_status.schema.json +10 -0
- package/certification/geospatial-mcp-schemas/tools/propose_operation.schema.json +7 -2
- package/certification/geospatial-mcp-schemas/tools/propose_rollback.schema.json +29 -0
- package/certification/geospatial-mcp-schemas/tools/remove_control.schema.json +114 -0
- package/certification/geospatial-mcp-schemas/tools/remove_exploration_context.schema.json +42 -0
- package/certification/geospatial-mcp-schemas/tools/remove_interaction.schema.json +99 -0
- package/dist/src/certification/certifier.d.ts +17 -2
- package/dist/src/certification/certifier.d.ts.map +1 -1
- package/dist/src/certification/certifier.js +69 -11
- package/dist/src/certification/cli.js +4 -1
- package/dist/src/certification/lifecycle.d.ts +2 -0
- package/dist/src/certification/lifecycle.d.ts.map +1 -1
- package/dist/src/certification/lifecycle.js +3 -0
- package/dist/src/certification/operator-catalog.d.ts.map +1 -1
- package/dist/src/certification/operator-catalog.js +0 -12
- package/dist/src/certification/report.d.ts.map +1 -1
- package/dist/src/certification/report.js +2 -1
- package/dist/src/certification/roster.d.ts +99 -0
- package/dist/src/certification/roster.d.ts.map +1 -0
- package/dist/src/certification/roster.js +233 -0
- package/dist/src/certification/round-trip-target.d.ts +45 -0
- package/dist/src/certification/round-trip-target.d.ts.map +1 -0
- package/dist/src/certification/round-trip-target.js +122 -0
- package/dist/src/certification/run.d.ts.map +1 -1
- package/dist/src/certification/run.js +7 -0
- package/dist/src/eval/northstar-corpus.d.ts.map +1 -1
- package/dist/src/eval/northstar-corpus.js +12 -10
- package/dist/src/eval/operator-corpus.d.ts.map +1 -1
- package/dist/src/eval/operator-corpus.js +11 -6
- package/dist/src/eval/proposal-tools.d.ts +14 -0
- package/dist/src/eval/proposal-tools.d.ts.map +1 -0
- package/dist/src/eval/proposal-tools.js +20 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +15 -6
- package/dist/src/neutral/query.js +1 -1
- package/dist/src/neutral/result.d.ts +2 -2
- package/dist/src/neutral/result.d.ts.map +1 -1
- package/dist/src/neutral/result.js +11 -4
- package/dist/src/release/zero-to-map-cli.d.ts.map +1 -1
- package/dist/src/release/zero-to-map-cli.js +8 -2
- package/dist/src/release/zero-to-map-provision.d.ts +6 -0
- package/dist/src/release/zero-to-map-provision.d.ts.map +1 -1
- package/dist/src/release/zero-to-map-provision.js +6 -0
- package/dist/src/tools/apply-style-preset.d.ts +1 -1
- package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
- package/dist/src/tools/apply-style-preset.js +4 -2
- package/dist/src/tools/control-plane-command.d.ts +1 -1
- package/dist/src/tools/control-plane-command.d.ts.map +1 -1
- package/dist/src/tools/control-plane-command.js +3 -2
- package/dist/src/tools/count-features.d.ts +1 -1
- package/dist/src/tools/count-features.d.ts.map +1 -1
- package/dist/src/tools/count-features.js +2 -1
- package/dist/src/tools/docs-search.d.ts +2 -2
- package/dist/src/tools/docs-search.d.ts.map +1 -1
- package/dist/src/tools/docs-search.js +15 -4
- package/dist/src/tools/get-extent.d.ts +1 -1
- package/dist/src/tools/get-extent.d.ts.map +1 -1
- package/dist/src/tools/get-extent.js +2 -1
- package/dist/src/tools/query-features.d.ts +11 -2
- package/dist/src/tools/query-features.d.ts.map +1 -1
- package/dist/src/tools/query-features.js +18 -5
- package/dist/src/tools/statistics.d.ts +1 -1
- package/dist/src/tools/statistics.d.ts.map +1 -1
- package/dist/src/tools/statistics.js +2 -1
- package/package.json +2 -2
- package/release/zero-to-map/configs/claude-code.mcp.json +1 -1
- package/release/zero-to-map/configs/claude-desktop.json +1 -1
- package/release/zero-to-map/configs/cursor.json +1 -1
package/README.md
CHANGED
|
@@ -113,6 +113,15 @@ The tool requires `confirm: true`. Stop and remove this bootstrap server from
|
|
|
113
113
|
the MCP client configuration after installation; the normal platform-free
|
|
114
114
|
server remains read-only and cannot invoke the installer.
|
|
115
115
|
|
|
116
|
+
Choose a new directory for each installation. The installer checks Docker Engine
|
|
117
|
+
and Compose before writing files, refuses existing compose/bootstrap files it
|
|
118
|
+
cannot recognize, and preserves unrelated keys in an installer-owned private
|
|
119
|
+
`.env`. New installations keep an isolated Compose project identity in that file;
|
|
120
|
+
keep `.env` with the installation to retain its credentials and volumes. Existing
|
|
121
|
+
legacy installations keep their original project after verifying that its
|
|
122
|
+
containers belong to the chosen directory. Restore the original files when that
|
|
123
|
+
check refuses a conflicting or incomplete legacy installation.
|
|
124
|
+
|
|
116
125
|
## Environment Variables
|
|
117
126
|
|
|
118
127
|
- `HONUA_BASE_URL` (required in normal mode): absolute base URL — a public ArcGIS folder
|
|
@@ -136,12 +145,12 @@ All read-only:
|
|
|
136
145
|
- `honua_list_sources` — **protocol-neutral discovery.** Returns `<protocol>:<address>` source references plus each source's real capabilities. Every protocol family degrades independently with a reason.
|
|
137
146
|
- `honua_list_services` — GeoServices-specific service catalog (prefer `honua_list_sources`)
|
|
138
147
|
- `honua_describe_layer` — source schema, protocol, and advertised capabilities
|
|
139
|
-
- `honua_query_features`
|
|
148
|
+
- `honua_query_features` — includes geometry by default; `resultOffset` and `outSrid` are aliases for `offset` and `outSr`. Pass one spelling of each field. Count-only and geometry-precision inputs are refused; use `honua_count_features` for counts.
|
|
140
149
|
- `honua_count_features`
|
|
141
150
|
- `honua_get_extent`
|
|
142
151
|
- `honua_statistics`
|
|
143
152
|
- `honua_explain_capability_gap`
|
|
144
|
-
- `honua_get_style`, `honua_apply_style_preset` — structured-unavailable on a plain FeatureServer
|
|
153
|
+
- `honua_get_style`, `honua_apply_style_preset` — structured-unavailable on a plain FeatureServer. Preset resolution is read-only and returns a stylesheet for client-side application; persisted `serviceId`/`layerId` binding arguments are refused.
|
|
145
154
|
- `honua_docs_search` — answer a Honua documentation question from the local versioned docs corpus (`llms-full.txt`), with a source-file and release-version citation per hit. Each citation is pinned to the exact commit the corpus bytes come from, so it keeps naming the text that was searched after `trunk` moves; when that commit cannot be established (an installed package with no checkout, or a locally regenerated corpus) the citation reports `sourceRevision: null` and links to `trunk` rather than claiming a pin it cannot back. Offline: reads committed files, never the network, and reports structured-unavailable when the corpus is not present in the installation. Point `HONUA_DOCS_CORPUS_PATH` at a corpus file (any name) or at a directory holding `llms-full.txt` to supply the corpus explicitly; `HONUA_SOURCE_REVISION` declares the commit when git is unavailable.
|
|
146
155
|
|
|
147
156
|
### Protocol-neutral tool contract
|
|
@@ -346,6 +355,7 @@ cd honua-sdk-js
|
|
|
346
355
|
npm ci
|
|
347
356
|
```
|
|
348
357
|
|
|
358
|
+
<!-- doc-run: checkout -->
|
|
349
359
|
```bash
|
|
350
360
|
npm run build # the SDK that mcp/ links to; takes a few minutes
|
|
351
361
|
cd mcp
|
|
@@ -354,6 +364,7 @@ npm ci
|
|
|
354
364
|
|
|
355
365
|
From `mcp/`:
|
|
356
366
|
|
|
367
|
+
<!-- doc-run: checkout=mcp -->
|
|
357
368
|
```bash
|
|
358
369
|
# Run the certifier against the offline fixture backend and write artifacts:
|
|
359
370
|
npm run certify
|
|
@@ -384,6 +395,7 @@ skip-with-reason. Fixture-backed and deterministic — no network — as long as
|
|
|
384
395
|
instead (the scheduled live variant). Unset it if you exported it for `honua-mcp`
|
|
385
396
|
above:
|
|
386
397
|
|
|
398
|
+
<!-- doc-run: checkout=mcp -->
|
|
387
399
|
```bash
|
|
388
400
|
unset HONUA_BASE_URL
|
|
389
401
|
npm run certify:standalone # platform-free cert (plain FeatureServer fixture)
|
|
@@ -406,6 +418,7 @@ in `config/live-conformance-endpoints.v1.json`). Nothing Esri exists there: the
|
|
|
406
418
|
GeoServices entry points all reject, so a tool that still secretly required
|
|
407
419
|
`serviceId`/`layerId` fails loudly instead of passing by accident.
|
|
408
420
|
|
|
421
|
+
<!-- doc-run: checkout=mcp -->
|
|
409
422
|
```bash
|
|
410
423
|
npm run certify:standalone-ogc # non-GeoServices cert (OGC API Features fixture)
|
|
411
424
|
npm run test:certification:standalone-ogc # CI gate variant
|
|
@@ -521,6 +534,7 @@ honua-io/honua-server#1956.)
|
|
|
521
534
|
dependencies); keys come from the environment and are never hardcoded. Set
|
|
522
535
|
`HONUA_MCP_REMOTE_URL` to drive a live remote `/mcp` instead of the fixture.
|
|
523
536
|
|
|
537
|
+
<!-- doc-run: checkout=mcp -->
|
|
524
538
|
```bash
|
|
525
539
|
npm run eval # run the eval (live models join if their keys are set)
|
|
526
540
|
npm run eval:offline # force the deterministic control + fixture surface
|
|
@@ -546,6 +560,7 @@ guards. Because the fixture replays real recorded data, a wrong number or a
|
|
|
546
560
|
hallucinated place name fails. The grading taxonomy is documented in
|
|
547
561
|
[`evals/README.md`](evals/README.md).
|
|
548
562
|
|
|
563
|
+
<!-- doc-run: checkout=mcp -->
|
|
549
564
|
```bash
|
|
550
565
|
npm run eval:standalone # deterministic control over the census fixture (offline)
|
|
551
566
|
```
|
|
@@ -564,6 +579,7 @@ an extent that came from the declared collection extent must say so, and a CQL2
|
|
|
564
579
|
spatial predicate the endpoint does not publish must come back as a structured
|
|
565
580
|
capability refusal — never as an empty result set.
|
|
566
581
|
|
|
582
|
+
<!-- doc-run: checkout=mcp -->
|
|
567
583
|
```bash
|
|
568
584
|
npm run eval:ogc # deterministic control over the OGC API Features fixture (offline)
|
|
569
585
|
```
|
|
@@ -5,16 +5,22 @@ machine-readable schemas from the open **geospatial-mcp** standard.
|
|
|
5
5
|
|
|
6
6
|
- **Source repo:** https://github.com/honua-io/geospatial-mcp
|
|
7
7
|
- **Source path:** `spec/schemas/`
|
|
8
|
-
- **Source commit:** `
|
|
9
|
-
(`
|
|
10
|
-
- **Schema index date:** `2026-
|
|
8
|
+
- **Source commit:** `4fac81c2f93a110921bb62638bc5ab599ef5e626`
|
|
9
|
+
(`Declare release component versions for the nightly resolver (#92)`, trunk HEAD on 2026-10-08)
|
|
10
|
+
- **Schema index date:** `2026-08-12`
|
|
11
11
|
- **Dialect:** JSON Schema draft 2020-12
|
|
12
12
|
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
13
|
+
> Re-vendored at trunk HEAD for the 2026.1 rc.3 certification fix (unit K2).
|
|
14
|
+
> The earlier hold at `eb53989` (pre geospatial-mcp#58) is over: honua-server
|
|
15
|
+
> now serves the #58 platform-ops tools (`honua_ops_health`, `honua_ops_findings`,
|
|
16
|
+
> `honua_alert_events`, `honua_operate_events`, `honua_platform_release_status`,
|
|
17
|
+
> `honua_deploy_operations`, `honua_propose_rollback`).
|
|
18
|
+
>
|
|
19
|
+
> Known upstream lag: this index still maps `propose_operation` to the retired
|
|
20
|
+
> `honua_propose_operation` as `implemented`. Marking it `known-gap` is
|
|
21
|
+
> geospatial-mcp unit K4, which had not landed at this commit. Until it does, the
|
|
22
|
+
> certifier records `propose_operation` as a standard-tool known gap (reference
|
|
23
|
+
> tool not advertised), which is recorded, not failed. Re-pin once K4 merges.
|
|
18
24
|
|
|
19
25
|
## Why vendored
|
|
20
26
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://geospatial-mcp.honua.io/spec/schemas/common/controls.schema.json",
|
|
4
|
+
"title": "Composition controls",
|
|
5
|
+
"description": "Standard-owned `controls` collection for composition documents (MapPackage/AppPackage). A control is an input affordance the user operates; it emits the `change` event of the ADR-0030 interaction vocabulary and is the target a `control:{id}` reference resolves against. The kind vocabulary is closed — extending it requires a standard ADR — and deliberately excludes a feature-editing draw control (ADR-0028). Controls declare presentation and data binding only; behaviour is wired declaratively through the `interactions` block (common/interactions.schema.json). Controls never appear in `layout`: grid placement is restricted to `map` and `widget:{id}` by that schema's layoutRef. See docs/adr/0031-composition-controls.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"controlKind": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": [
|
|
11
|
+
"navigation",
|
|
12
|
+
"scale",
|
|
13
|
+
"fullscreen",
|
|
14
|
+
"geolocate",
|
|
15
|
+
"search",
|
|
16
|
+
"measure",
|
|
17
|
+
"timeSlider",
|
|
18
|
+
"filterSelect",
|
|
19
|
+
"filterSlider",
|
|
20
|
+
"filterDateRange",
|
|
21
|
+
"bookmarks",
|
|
22
|
+
"opacity",
|
|
23
|
+
"attribution",
|
|
24
|
+
"basemapSwitcher"
|
|
25
|
+
],
|
|
26
|
+
"description": "Closed control vocabulary. Map affordances: navigation (zoom/pan/compass), scale, fullscreen, geolocate, attribution, basemapSwitcher, bookmarks, measure, search. Data-binding affordances that emit `change`: timeSlider, filterSelect, filterSlider, filterDateRange, opacity. There is deliberately no feature-editing draw kind — autonomous agent mutation of source records stays excluded (ADR-0028); an annotation-only draw control may be admitted later by its own ADR. Extending this set requires a standard ADR (ADR-0031)."
|
|
27
|
+
},
|
|
28
|
+
"control": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"required": [
|
|
32
|
+
"id",
|
|
33
|
+
"kind"
|
|
34
|
+
],
|
|
35
|
+
"properties": {
|
|
36
|
+
"id": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 1,
|
|
39
|
+
"maxLength": 200,
|
|
40
|
+
"description": "Stable identifier, unique within the document's controls block. `control:{id}` references in the **interactions** block resolve against this value; add_control replaces an existing control with the same id. Controls are deliberately NOT layout grammar — ADR-0031 treats them as chrome the host positions, and `layoutItem.ref` is constrained to `map` and `widget:{id}` so a control can never claim a grid cell."
|
|
41
|
+
},
|
|
42
|
+
"kind": {
|
|
43
|
+
"$ref": "#/$defs/controlKind",
|
|
44
|
+
"description": "What the control is. The kind fixes the affordance's semantics, not its skin — hosts render each kind in their own idiom."
|
|
45
|
+
},
|
|
46
|
+
"title": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"maxLength": 200,
|
|
49
|
+
"description": "Human-readable label shown by the host. Optional; hosts fall back to a per-kind default label."
|
|
50
|
+
},
|
|
51
|
+
"sourceId": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"minLength": 1,
|
|
54
|
+
"description": "Identifier of the layer or datasource the control reads its domain from (e.g. the layer whose field values populate a filterSelect, or the layer a timeSlider ranges over). Data-binding kinds normally set it; presentation-only kinds (navigation, scale, fullscreen, attribution) omit it. Resolution of the identifier within the document is a validation-gate responsibility, as for layer references."
|
|
55
|
+
},
|
|
56
|
+
"config": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "Per-kind configuration (e.g. the field a filterSelect filters on, a slider's bounds and step, a bookmark list). Host-interpreted and intentionally open, matching the widget entry's `config`; it carries no expression language and grants no capability the composition surface does not already have."
|
|
59
|
+
},
|
|
60
|
+
"explorationContextId": {
|
|
61
|
+
"$ref": "exploration.schema.json#/$defs/componentBinding/properties/explorationContextId",
|
|
62
|
+
"description": "Exploration context this control joins (ADR-0032). A data-binding control bound to a context contributes its state to every component in that context, which is how a filter dropdown narrows peer widgets without an interactions edge per target."
|
|
63
|
+
},
|
|
64
|
+
"role": {
|
|
65
|
+
"$ref": "exploration.schema.json#/$defs/componentBinding/properties/role",
|
|
66
|
+
"description": "Role this control plays in its exploration context (ADR-0032); data-binding kinds normally bind as `filter`."
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"description": "One control entry. The shape mirrors the composition widget entry ({ id, kind, title?, sourceId?, config? }) so agents author both collections the same way."
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"properties": {
|
|
73
|
+
"controls": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": {
|
|
76
|
+
"$ref": "#/$defs/control"
|
|
77
|
+
},
|
|
78
|
+
"description": "Input affordances declared by this composition document. Optional and additive: a document without controls is valid. Ids must be unique within the collection (validation-gate check)."
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://geospatial-mcp.honua.io/spec/schemas/common/exploration.schema.json",
|
|
4
|
+
"title": "Composition exploration contexts",
|
|
5
|
+
"description": "Standard-owned `explorationContexts` collection for composition documents (MapPackage/AppPackage). An exploration context is a named unit of shared interaction state — filters, spatial filter, extent, selection, sort, page, visible fields, grouping, aggregation — that components bind to. Components that name the same context share that state, so cross-filtering is context membership rather than an event→action edge; this is the primary interaction model, and the `interactions` block (common/interactions.schema.json) is retained for one-shot gestures. Propagation between bound components is governed by a closed preset vocabulary keyed on the component's role. Contexts are data, never code: there is no expression language and no $event.* substitution here, because a context's state is not produced by an event. Reference resolution (context ids, source ids) is a validation-gate responsibility. See docs/adr/0032-shared-exploration-contexts.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"presetName": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": [
|
|
11
|
+
"globalLinked",
|
|
12
|
+
"mapDriven",
|
|
13
|
+
"gridDriven",
|
|
14
|
+
"chartDriven",
|
|
15
|
+
"decoupled"
|
|
16
|
+
],
|
|
17
|
+
"description": "Closed linked-view preset vocabulary, naming which bound components propagate state outward. globalLinked: every bound component contributes and receives every shared slice. mapDriven: only the map's extent and selection propagate outward, though filter components still contribute filter state. gridDriven: only the grid's selection, sort, and page propagate outward. chartDriven: only the chart's grouping, aggregation, and selection propagate outward. decoupled: nothing propagates and each component is independent. Extending this set requires a standard ADR (ADR-0032)."
|
|
18
|
+
},
|
|
19
|
+
"viewRole": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": [
|
|
22
|
+
"map",
|
|
23
|
+
"grid",
|
|
24
|
+
"chart",
|
|
25
|
+
"filter",
|
|
26
|
+
"detail",
|
|
27
|
+
"form",
|
|
28
|
+
"custom"
|
|
29
|
+
],
|
|
30
|
+
"description": "Coarse role a bound component plays in its context. The preset's propagation policy is keyed on this value, not on the component's kind, so two chart widgets of different kinds behave identically when both are bound as `chart`. `form` is retained for compatibility with older detail/filter integrations; new documents should prefer the more precise `filter` and `detail` roles."
|
|
31
|
+
},
|
|
32
|
+
"explorationStateSnapshot": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": true,
|
|
35
|
+
"required": [
|
|
36
|
+
"version"
|
|
37
|
+
],
|
|
38
|
+
"properties": {
|
|
39
|
+
"version": {
|
|
40
|
+
"type": "integer",
|
|
41
|
+
"const": 1,
|
|
42
|
+
"description": "Snapshot format version. Pinned to 1: this ADR defines only the initial format and gives implementations no semantics for any later value, so admitting `version: 2` would produce a schema-valid document no runtime could interpret portably. Later versions join through explicit versioned branches in a follow-on ADR."
|
|
43
|
+
},
|
|
44
|
+
"filters": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": true,
|
|
47
|
+
"description": "Shared filter clauses keyed by clause id."
|
|
48
|
+
},
|
|
49
|
+
"spatialFilter": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": true,
|
|
52
|
+
"description": "Shared spatial filter (geometry plus relationship)."
|
|
53
|
+
},
|
|
54
|
+
"extent": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": true,
|
|
57
|
+
"description": "Shared viewport extent."
|
|
58
|
+
},
|
|
59
|
+
"selection": {
|
|
60
|
+
"type": "array",
|
|
61
|
+
"items": {
|
|
62
|
+
"type": [
|
|
63
|
+
"string",
|
|
64
|
+
"number",
|
|
65
|
+
"object"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"description": "Shared feature selection. Raw ids are permitted for single-source documents; multi-source documents should use source-qualified targets."
|
|
69
|
+
},
|
|
70
|
+
"sort": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"additionalProperties": true
|
|
75
|
+
},
|
|
76
|
+
"description": "Shared sort order."
|
|
77
|
+
},
|
|
78
|
+
"page": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"additionalProperties": true,
|
|
81
|
+
"description": "Shared pagination offset and limit."
|
|
82
|
+
},
|
|
83
|
+
"visibleFields": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
"description": "Shared field visibility for tabular and form components. An empty array means all fields."
|
|
89
|
+
},
|
|
90
|
+
"grouping": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"description": "Shared group-by keys for chart and pivot components."
|
|
96
|
+
},
|
|
97
|
+
"aggregation": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"additionalProperties": true,
|
|
100
|
+
"description": "Shared aggregation specification."
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"description": "Optional versioned snapshot of a context's shared state at document load. Static JSON only — the same data-never-code posture ADR-0030 established for interaction arguments, minus the $event.* substitution, which has no meaning outside an event payload."
|
|
104
|
+
},
|
|
105
|
+
"explorationContext": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"required": [
|
|
109
|
+
"id"
|
|
110
|
+
],
|
|
111
|
+
"properties": {
|
|
112
|
+
"id": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"minLength": 1,
|
|
115
|
+
"maxLength": 200,
|
|
116
|
+
"description": "Stable identifier, unique within the document's explorationContexts block. A component's explorationContextId resolves against this value; create_exploration_context replaces an existing context with the same id."
|
|
117
|
+
},
|
|
118
|
+
"sourceIds": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"minLength": 1
|
|
123
|
+
},
|
|
124
|
+
"description": "Identifiers of the sources whose records this context's shared state addresses. Each must resolve within the document; resolution is a validation-gate responsibility, as for layer references."
|
|
125
|
+
},
|
|
126
|
+
"preset": {
|
|
127
|
+
"$ref": "#/$defs/presetName",
|
|
128
|
+
"default": "globalLinked",
|
|
129
|
+
"description": "Propagation policy for components bound to this context. Defaults to globalLinked, which is the behaviour an author means by 'link these together'."
|
|
130
|
+
},
|
|
131
|
+
"initialState": {
|
|
132
|
+
"$ref": "#/$defs/explorationStateSnapshot",
|
|
133
|
+
"description": "Optional starting shared state. Omit it and the context starts empty."
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"description": "One exploration context. Components bind to it by id; components sharing an id share its state."
|
|
137
|
+
},
|
|
138
|
+
"componentBinding": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"additionalProperties": true,
|
|
141
|
+
"properties": {
|
|
142
|
+
"explorationContextId": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"minLength": 1,
|
|
145
|
+
"maxLength": 200,
|
|
146
|
+
"description": "Identifier of the exploration context this component binds to. It must resolve against the document's explorationContexts block (validation-gate check). A component binds to at most one context; omitting the property leaves the component unbound and independent."
|
|
147
|
+
},
|
|
148
|
+
"role": {
|
|
149
|
+
"$ref": "#/$defs/viewRole",
|
|
150
|
+
"description": "The role this component plays in its context, which the preset's propagation policy is keyed on. Meaningful only alongside explorationContextId."
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"description": "The two optional properties a bindable component (the map, or an entry in layers, widgets, or controls) carries to join an exploration context. Cross-filtering is identity of explorationContextId: there is no edge to author, no ordering between bindings, and no fan-out to cap."
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"properties": {
|
|
157
|
+
"explorationContexts": {
|
|
158
|
+
"type": "array",
|
|
159
|
+
"items": {
|
|
160
|
+
"$ref": "#/$defs/explorationContext"
|
|
161
|
+
},
|
|
162
|
+
"description": "Units of shared interaction state declared by this composition document. Optional and additive: a document without exploration contexts is valid, and one whose components are all unbound behaves exactly as it did before this block existed. Ids must be unique within the collection, and a declared context with no bound components is valid but should warn (validation-gate checks)."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://geospatial-mcp.honua.io/spec/schemas/common/interactions.schema.json",
|
|
4
|
+
"title": "Composition interactions and layout",
|
|
5
|
+
"description": "Standard-owned declarative blocks for composition documents (MapPackage/AppPackage): `interactions` wires component events to bounded action verbs; `layout` places widgets on a grid. Bindings are data, never code: the only dynamic element is $event.* path substitution in string argument values. Actions never emit events (only user gestures do), so binding graphs cannot cycle by construction. Reference resolution and the per-(ref,event) fan-out cap (recommended 8) are validation-gate responsibilities. See docs/adr/0030-declarative-interactions-and-layout.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"componentRef": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^(map|layer:.+|widget:.+|control:.+)$",
|
|
11
|
+
"description": "Reference to a component declared in the same composition document: `map`, `layer:{id}`, `widget:{id}`, or `control:{id}`. Must resolve within the document (validation-gate check, not a schema check)."
|
|
12
|
+
},
|
|
13
|
+
"eventName": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": [
|
|
16
|
+
"featureSelect",
|
|
17
|
+
"featureHover",
|
|
18
|
+
"selection",
|
|
19
|
+
"change",
|
|
20
|
+
"viewportChange"
|
|
21
|
+
],
|
|
22
|
+
"description": "Closed event set: featureSelect/featureHover (layer sources), selection (widget sources), change (control sources), viewportChange (map source). Extending the set requires a standard ADR (ADR-0030)."
|
|
23
|
+
},
|
|
24
|
+
"actionVerb": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": [
|
|
27
|
+
"setFilter",
|
|
28
|
+
"setViewport",
|
|
29
|
+
"selectFeature",
|
|
30
|
+
"runWidgetQuery",
|
|
31
|
+
"setVisibility"
|
|
32
|
+
],
|
|
33
|
+
"description": "Closed verb set drawn from the existing composition vocabulary. Verbs mutate presentation/exploration state only, never source records (ADR-0028 unaffected). Extending the set requires a standard ADR (ADR-0030)."
|
|
34
|
+
},
|
|
35
|
+
"interaction": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"required": [
|
|
39
|
+
"id",
|
|
40
|
+
"on",
|
|
41
|
+
"do"
|
|
42
|
+
],
|
|
43
|
+
"properties": {
|
|
44
|
+
"id": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"minLength": 1,
|
|
47
|
+
"maxLength": 200,
|
|
48
|
+
"description": "Stable identifier, unique within the document's interactions block. bind_interaction replaces an existing interaction with the same id."
|
|
49
|
+
},
|
|
50
|
+
"on": {
|
|
51
|
+
"$ref": "#/$defs/interactionEvent"
|
|
52
|
+
},
|
|
53
|
+
"do": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"required": [
|
|
57
|
+
"ref",
|
|
58
|
+
"verb"
|
|
59
|
+
],
|
|
60
|
+
"properties": {
|
|
61
|
+
"ref": {
|
|
62
|
+
"$ref": "#/$defs/componentRef"
|
|
63
|
+
},
|
|
64
|
+
"verb": {
|
|
65
|
+
"$ref": "#/$defs/actionVerb"
|
|
66
|
+
},
|
|
67
|
+
"args": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"description": "Static JSON arguments for the verb. A string value beginning with `$event.` is replaced at dispatch time by the value at that path in the event payload (e.g. $event.featureId, $event.value, $event.bbox). No expression language: no arithmetic, no conditionals, no function calls."
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"description": "The action. Verb-driven state changes MUST NOT re-enter the interaction dispatcher (actions never emit events)."
|
|
73
|
+
},
|
|
74
|
+
"disabled": {
|
|
75
|
+
"type": "boolean",
|
|
76
|
+
"default": false,
|
|
77
|
+
"description": "Authored-but-inactive binding; retained in the document, skipped at dispatch."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"layoutItem": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": [
|
|
85
|
+
"ref",
|
|
86
|
+
"x",
|
|
87
|
+
"y",
|
|
88
|
+
"w",
|
|
89
|
+
"h"
|
|
90
|
+
],
|
|
91
|
+
"properties": {
|
|
92
|
+
"ref": {
|
|
93
|
+
"$ref": "#/$defs/layoutRef"
|
|
94
|
+
},
|
|
95
|
+
"x": {
|
|
96
|
+
"type": "integer",
|
|
97
|
+
"minimum": 0,
|
|
98
|
+
"description": "Grid column of the item's left edge (0-based)."
|
|
99
|
+
},
|
|
100
|
+
"y": {
|
|
101
|
+
"type": "integer",
|
|
102
|
+
"minimum": 0,
|
|
103
|
+
"description": "Grid row of the item's top edge (0-based)."
|
|
104
|
+
},
|
|
105
|
+
"w": {
|
|
106
|
+
"type": "integer",
|
|
107
|
+
"minimum": 1,
|
|
108
|
+
"description": "Width in grid columns."
|
|
109
|
+
},
|
|
110
|
+
"h": {
|
|
111
|
+
"type": "integer",
|
|
112
|
+
"minimum": 1,
|
|
113
|
+
"description": "Height in grid rows."
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"description": "Grid placement for one widget (or the map). The ref must resolve within the document — same validation gate as interactions."
|
|
117
|
+
},
|
|
118
|
+
"layoutRef": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"pattern": "^(map|widget:.+)$",
|
|
121
|
+
"description": "Reference to a component that can be placed on the layout grid: `map` or `widget:{id}`. Layers and controls are deliberately excluded — a layer is drawn inside the map and a control is an affordance the host positions, so neither has grid placement. This is narrower than `componentRef` on purpose; reusing that definition would let `layer:parcels` validate into a grid cell whose rendering is undefined."
|
|
122
|
+
},
|
|
123
|
+
"interactionEvent": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"required": [
|
|
127
|
+
"ref",
|
|
128
|
+
"event"
|
|
129
|
+
],
|
|
130
|
+
"properties": {
|
|
131
|
+
"ref": {
|
|
132
|
+
"$ref": "#/$defs/componentRef"
|
|
133
|
+
},
|
|
134
|
+
"event": {
|
|
135
|
+
"$ref": "#/$defs/eventName"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"allOf": [
|
|
139
|
+
{
|
|
140
|
+
"if": {
|
|
141
|
+
"properties": {
|
|
142
|
+
"ref": {
|
|
143
|
+
"const": "map"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"required": [
|
|
147
|
+
"ref"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"then": {
|
|
151
|
+
"properties": {
|
|
152
|
+
"event": {
|
|
153
|
+
"const": "viewportChange"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"description": "The map emits only viewportChange."
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"if": {
|
|
161
|
+
"properties": {
|
|
162
|
+
"ref": {
|
|
163
|
+
"pattern": "^layer:"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": [
|
|
167
|
+
"ref"
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
"then": {
|
|
171
|
+
"properties": {
|
|
172
|
+
"event": {
|
|
173
|
+
"enum": [
|
|
174
|
+
"featureSelect",
|
|
175
|
+
"featureHover"
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"description": "Layers emit only featureSelect and featureHover."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"if": {
|
|
184
|
+
"properties": {
|
|
185
|
+
"ref": {
|
|
186
|
+
"pattern": "^widget:"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"required": [
|
|
190
|
+
"ref"
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
"then": {
|
|
194
|
+
"properties": {
|
|
195
|
+
"event": {
|
|
196
|
+
"const": "selection"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
"description": "Widgets emit only selection."
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"if": {
|
|
204
|
+
"properties": {
|
|
205
|
+
"ref": {
|
|
206
|
+
"pattern": "^control:"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"required": [
|
|
210
|
+
"ref"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
"then": {
|
|
214
|
+
"properties": {
|
|
215
|
+
"event": {
|
|
216
|
+
"const": "change"
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"description": "Controls emit only change."
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
"description": "The event-source half of an interaction. The allOf branches encode the normative source mapping so a schema-valid binding cannot name an event its component kind never emits — previously `{ \"ref\": \"map\", \"event\": \"featureSelect\" }` validated despite having no defined dispatch semantics."
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"properties": {
|
|
227
|
+
"interactions": {
|
|
228
|
+
"type": "array",
|
|
229
|
+
"items": {
|
|
230
|
+
"$ref": "#/$defs/interaction"
|
|
231
|
+
},
|
|
232
|
+
"description": "Declarative event→action bindings between components of this composition document."
|
|
233
|
+
},
|
|
234
|
+
"layout": {
|
|
235
|
+
"type": "object",
|
|
236
|
+
"additionalProperties": false,
|
|
237
|
+
"properties": {
|
|
238
|
+
"grid": {
|
|
239
|
+
"type": "object",
|
|
240
|
+
"additionalProperties": false,
|
|
241
|
+
"properties": {
|
|
242
|
+
"columns": {
|
|
243
|
+
"type": "integer",
|
|
244
|
+
"minimum": 1,
|
|
245
|
+
"maximum": 24,
|
|
246
|
+
"default": 12,
|
|
247
|
+
"description": "Number of grid columns; 12 is the default."
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
"description": "Grid geometry. Rows grow as needed; only columns are declared."
|
|
251
|
+
},
|
|
252
|
+
"items": {
|
|
253
|
+
"type": "array",
|
|
254
|
+
"items": {
|
|
255
|
+
"$ref": "#/$defs/layoutItem"
|
|
256
|
+
},
|
|
257
|
+
"description": "Widget placements. A composition with widgets and no layout is valid; hosts choose a default flow."
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"description": "Presentation-only grid placement for the document's widgets."
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|