@honua/mcp-server 0.1.2-beta.0 → 0.1.8-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +232 -9
- package/dist/src/certification/admin-parity.d.ts +96 -0
- package/dist/src/certification/admin-parity.d.ts.map +1 -0
- package/dist/src/certification/admin-parity.js +235 -0
- package/dist/src/certification/census-fixture-client.d.ts.map +1 -1
- package/dist/src/certification/census-fixture-client.js +5 -0
- package/dist/src/certification/certifier.d.ts +1 -1
- package/dist/src/certification/certifier.d.ts.map +1 -1
- package/dist/src/certification/certifier.js +28 -20
- package/dist/src/certification/cli.js +1 -1
- package/dist/src/certification/cql2.d.ts +24 -0
- package/dist/src/certification/cql2.d.ts.map +1 -0
- package/dist/src/certification/cql2.js +354 -0
- package/dist/src/certification/fixtures.d.ts +8 -0
- package/dist/src/certification/fixtures.d.ts.map +1 -1
- package/dist/src/certification/fixtures.js +26 -10
- package/dist/src/certification/ogc-data.d.ts +30 -0
- package/dist/src/certification/ogc-data.d.ts.map +1 -0
- package/dist/src/certification/ogc-data.js +726 -0
- package/dist/src/certification/ogc-fixture-client.d.ts +4 -0
- package/dist/src/certification/ogc-fixture-client.d.ts.map +1 -0
- package/dist/src/certification/ogc-fixture-client.js +190 -0
- package/dist/src/certification/target.d.ts +10 -1
- package/dist/src/certification/target.d.ts.map +1 -1
- package/dist/src/certification/target.js +63 -28
- package/dist/src/credential-endpoint.d.ts +3 -0
- package/dist/src/credential-endpoint.d.ts.map +1 -0
- package/dist/src/credential-endpoint.js +24 -0
- package/dist/src/eval/cli.js +6 -3
- package/dist/src/eval/corpus.d.ts +7 -0
- package/dist/src/eval/corpus.d.ts.map +1 -1
- package/dist/src/eval/corpus.js +12 -0
- package/dist/src/eval/ogc-corpus.d.ts +3 -0
- package/dist/src/eval/ogc-corpus.d.ts.map +1 -0
- package/dist/src/eval/ogc-corpus.js +437 -0
- package/dist/src/eval/runner.d.ts +6 -0
- package/dist/src/eval/runner.d.ts.map +1 -1
- package/dist/src/eval/runner.js +21 -7
- package/dist/src/helpers.d.ts +8 -13
- package/dist/src/helpers.d.ts.map +1 -1
- package/dist/src/helpers.js +7 -35
- package/dist/src/index.d.ts +36 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +55 -18
- package/dist/src/neutral/errors.d.ts +73 -0
- package/dist/src/neutral/errors.d.ts.map +1 -0
- package/dist/src/neutral/errors.js +112 -0
- package/dist/src/neutral/filter.d.ts +108 -0
- package/dist/src/neutral/filter.d.ts.map +1 -0
- package/dist/src/neutral/filter.js +271 -0
- package/dist/src/neutral/query.d.ts +101 -0
- package/dist/src/neutral/query.d.ts.map +1 -0
- package/dist/src/neutral/query.js +180 -0
- package/dist/src/neutral/result.d.ts +33 -0
- package/dist/src/neutral/result.d.ts.map +1 -0
- package/dist/src/neutral/result.js +38 -0
- package/dist/src/neutral/source-ref.d.ts +126 -0
- package/dist/src/neutral/source-ref.d.ts.map +1 -0
- package/dist/src/neutral/source-ref.js +267 -0
- package/dist/src/nl-map-control.d.ts.map +1 -1
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +29 -14
- package/dist/src/release/zero-to-map-checkpoint.d.ts +34 -0
- package/dist/src/release/zero-to-map-checkpoint.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-checkpoint.js +369 -0
- package/dist/src/release/zero-to-map-cli.d.ts +51 -0
- package/dist/src/release/zero-to-map-cli.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-cli.js +880 -0
- package/dist/src/release/zero-to-map-provision.d.ts +37 -0
- package/dist/src/release/zero-to-map-provision.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-provision.js +177 -0
- package/dist/src/release/zero-to-map.d.ts +296 -0
- package/dist/src/release/zero-to-map.d.ts.map +1 -0
- package/dist/src/release/zero-to-map.js +1069 -0
- package/dist/src/tools/admin-install-local.d.ts +30 -0
- package/dist/src/tools/admin-install-local.d.ts.map +1 -0
- package/dist/src/tools/admin-install-local.js +22 -0
- package/dist/src/tools/control-plane-command.d.ts +99 -0
- package/dist/src/tools/control-plane-command.d.ts.map +1 -0
- package/dist/src/tools/control-plane-command.js +180 -0
- package/dist/src/tools/count-features.d.ts +85 -11
- package/dist/src/tools/count-features.d.ts.map +1 -1
- package/dist/src/tools/count-features.js +49 -31
- package/dist/src/tools/describe-layer.d.ts +30 -6
- package/dist/src/tools/describe-layer.d.ts.map +1 -1
- package/dist/src/tools/describe-layer.js +99 -8
- package/dist/src/tools/docs-search.d.ts +100 -0
- package/dist/src/tools/docs-search.d.ts.map +1 -0
- package/dist/src/tools/docs-search.js +381 -0
- package/dist/src/tools/get-extent.d.ts +69 -11
- package/dist/src/tools/get-extent.d.ts.map +1 -1
- package/dist/src/tools/get-extent.js +28 -46
- package/dist/src/tools/list-services.d.ts +7 -0
- package/dist/src/tools/list-services.d.ts.map +1 -1
- package/dist/src/tools/list-services.js +8 -1
- package/dist/src/tools/list-sources.d.ts +44 -0
- package/dist/src/tools/list-sources.d.ts.map +1 -0
- package/dist/src/tools/list-sources.js +138 -0
- package/dist/src/tools/query-features.d.ts +99 -19
- package/dist/src/tools/query-features.d.ts.map +1 -1
- package/dist/src/tools/query-features.js +38 -45
- package/dist/src/tools/statistics.d.ts +80 -12
- package/dist/src/tools/statistics.d.ts.map +1 -1
- package/dist/src/tools/statistics.js +56 -23
- package/package.json +37 -8
- package/release/zero-to-map/README.md +174 -0
- package/release/zero-to-map/configs/claude-code.mcp.json +11 -0
- package/release/zero-to-map/configs/claude-desktop.json +11 -0
- package/release/zero-to-map/configs/cursor.json +11 -0
- package/release/zero-to-map/contracts/aws-ecs-provision-binding.schema.json +59 -0
- package/release/zero-to-map/contracts/checkpoint.schema.json +152 -0
- package/release/zero-to-map/contracts/console-receipt.schema.json +150 -0
- package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +73 -0
- package/release/zero-to-map/fixtures/parcels.geojson +33 -0
- package/release/zero-to-map/fixtures/zoning.geojson +24 -0
- package/release/zero-to-map/journey.v1.json +1248 -0
|
@@ -1,51 +1,44 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { jsonText } from "../helpers.js";
|
|
3
|
+
import { withCapabilityHonesty } from "../neutral/errors.js";
|
|
4
|
+
import { queryFilterFields, queryShapeFields, toQuery } from "../neutral/query.js";
|
|
5
|
+
import { projectDegraded, projectFeatures } from "../neutral/result.js";
|
|
6
|
+
import { resolveSource, sourceRefFields } from "../neutral/source-ref.js";
|
|
7
|
+
/**
|
|
8
|
+
* `honua_query_features` — protocol-neutral feature read (#1005).
|
|
9
|
+
*
|
|
10
|
+
* Addresses a source by its neutral `<protocol>:<address>` reference, filters
|
|
11
|
+
* with the typed semantic filter / GeoJSON geometry / canonical temporal
|
|
12
|
+
* predicate, and executes through the SDK's canonical `Source.query()`, so the
|
|
13
|
+
* same call means the same thing against GeoServices, OGC API Features, STAC,
|
|
14
|
+
* WFS, and OData. The deprecated `serviceId` + `layerId` pair is still accepted.
|
|
15
|
+
*/
|
|
3
16
|
export const schema = z.object({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
outFields: z.array(z.string()).optional().describe("Fields to return; defaults to all"),
|
|
8
|
-
geometry: z.record(z.unknown()).optional().describe("Esri JSON geometry for spatial filter"),
|
|
9
|
-
geometryType: z
|
|
10
|
-
.enum(GEOMETRY_TYPES)
|
|
11
|
-
.optional()
|
|
12
|
-
.describe("Esri geometry type. If omitted, inferred from geometry when possible."),
|
|
13
|
-
spatialRel: z
|
|
14
|
-
.enum(["intersects", "contains", "within"])
|
|
15
|
-
.optional()
|
|
16
|
-
.describe("Spatial relationship (default: intersects)"),
|
|
17
|
-
orderBy: z.string().optional().describe('Sort order, e.g. "name ASC"'),
|
|
18
|
-
limit: z.number().int().positive().optional().describe("Max features to return (default 100, max 2000)"),
|
|
19
|
-
offset: z.number().int().nonnegative().optional().describe("Number of features to skip (for pagination)"),
|
|
20
|
-
returnGeometry: z.boolean().optional().default(false).describe("Include geometry in results (default: false)"),
|
|
17
|
+
...sourceRefFields,
|
|
18
|
+
...queryFilterFields,
|
|
19
|
+
...queryShapeFields,
|
|
21
20
|
});
|
|
22
21
|
export async function execute(client, input) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
...(input.returnGeometry && f.geometry ? { geometry: f.geometry } : {}),
|
|
45
|
-
})),
|
|
46
|
-
exceededTransferLimit: response.exceededTransferLimit ?? false,
|
|
47
|
-
objectIdFieldName: response.objectIdFieldName ?? null,
|
|
48
|
-
geometryType: response.geometryType ?? null,
|
|
49
|
-
spatialReference: response.spatialReference ?? null,
|
|
22
|
+
return withCapabilityHonesty(async () => {
|
|
23
|
+
const resolved = resolveSource(client, input);
|
|
24
|
+
const query = toQuery(input, { protocol: resolved.descriptor.protocol });
|
|
25
|
+
const result = await resolved.source.query(query);
|
|
26
|
+
// Legacy addressing keeps legacy output: a client that still sends
|
|
27
|
+
// serviceId/layerId also still expects Esri-JSON geometry. Neutral
|
|
28
|
+
// addressing gets the neutral encoding.
|
|
29
|
+
const geometryFormat = input.geometryFormat ?? (resolved.legacyAddressing ? "esri-json" : "geojson");
|
|
30
|
+
const returnGeometry = input.returnGeometry ?? false;
|
|
31
|
+
const degraded = projectDegraded(result.degraded);
|
|
32
|
+
return jsonText({
|
|
33
|
+
source: resolved.ref.ref,
|
|
34
|
+
protocol: resolved.descriptor.protocol,
|
|
35
|
+
returnedCount: result.features.length,
|
|
36
|
+
features: projectFeatures(result, { returnGeometry, geometryFormat }),
|
|
37
|
+
geometryFormat: returnGeometry ? geometryFormat : null,
|
|
38
|
+
exceededTransferLimit: result.exceededTransferLimit,
|
|
39
|
+
totalCount: result.totalCount ?? null,
|
|
40
|
+
fields: result.fields ?? null,
|
|
41
|
+
...(degraded ? { degraded } : {}),
|
|
42
|
+
});
|
|
50
43
|
});
|
|
51
44
|
}
|
|
@@ -1,26 +1,87 @@
|
|
|
1
1
|
import type { HonuaClient } from "@honua/sdk-js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* `honua_statistics` — protocol-neutral aggregation (#1005).
|
|
5
|
+
*
|
|
6
|
+
* Lowers onto the canonical `AggregationSpec`, which GeoServices serves with
|
|
7
|
+
* `outStatistics` and which OGC API Features (no server-side aggregation)
|
|
8
|
+
* serves client-side over the returned page with an explicit `degraded` reason.
|
|
9
|
+
* The reason is reported in the result rather than dropped, so a page-scoped
|
|
10
|
+
* aggregate is never mistaken for an authoritative one.
|
|
11
|
+
*/
|
|
3
12
|
export declare const schema: z.ZodObject<{
|
|
4
|
-
|
|
5
|
-
layerId: z.ZodNumber;
|
|
6
|
-
statisticType: z.ZodEnum<["count", "sum", "avg", "min", "max", "stddev"]>;
|
|
13
|
+
statisticType: z.ZodEnum<["count", "sum", "avg", "min", "max", "stddev", "var"]>;
|
|
7
14
|
onField: z.ZodString;
|
|
8
|
-
groupBy: z.ZodOptional<z.ZodString
|
|
15
|
+
groupBy: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodString]>>;
|
|
16
|
+
filter: z.ZodOptional<z.ZodType<import("../neutral/filter.js").FilterNode, z.ZodTypeDef, import("../neutral/filter.js").FilterNode>>;
|
|
9
17
|
where: z.ZodOptional<z.ZodString>;
|
|
18
|
+
bbox: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
|
|
19
|
+
bboxSrid: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
geometry: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
21
|
+
geometryType: z.ZodOptional<z.ZodEnum<["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"]>>;
|
|
22
|
+
spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within", "crosses", "touches", "overlaps", "bbox-intersects"]>>;
|
|
23
|
+
temporal: z.ZodOptional<z.ZodObject<{
|
|
24
|
+
instant: z.ZodOptional<z.ZodString>;
|
|
25
|
+
start: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
26
|
+
end: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
27
|
+
field: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
field?: string | undefined;
|
|
30
|
+
instant?: string | undefined;
|
|
31
|
+
start?: string | null | undefined;
|
|
32
|
+
end?: string | null | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
field?: string | undefined;
|
|
35
|
+
instant?: string | undefined;
|
|
36
|
+
start?: string | null | undefined;
|
|
37
|
+
end?: string | null | undefined;
|
|
38
|
+
}>>;
|
|
39
|
+
source: z.ZodOptional<z.ZodString>;
|
|
40
|
+
layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
|
|
41
|
+
serviceId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
layerId: z.ZodOptional<z.ZodNumber>;
|
|
10
43
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
|
|
12
|
-
layerId: number;
|
|
13
|
-
statisticType: "count" | "sum" | "avg" | "min" | "max" | "stddev";
|
|
44
|
+
statisticType: "count" | "sum" | "avg" | "min" | "max" | "stddev" | "var";
|
|
14
45
|
onField: string;
|
|
46
|
+
filter?: import("../neutral/filter.js").FilterNode | undefined;
|
|
47
|
+
serviceId?: string | undefined;
|
|
48
|
+
geometry?: Record<string, unknown> | undefined;
|
|
49
|
+
bbox?: number[] | undefined;
|
|
50
|
+
geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
|
|
51
|
+
temporal?: {
|
|
52
|
+
field?: string | undefined;
|
|
53
|
+
instant?: string | undefined;
|
|
54
|
+
start?: string | null | undefined;
|
|
55
|
+
end?: string | null | undefined;
|
|
56
|
+
} | undefined;
|
|
57
|
+
layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
|
|
58
|
+
layerId?: number | undefined;
|
|
59
|
+
source?: string | undefined;
|
|
15
60
|
where?: string | undefined;
|
|
16
|
-
|
|
61
|
+
bboxSrid?: number | undefined;
|
|
62
|
+
spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
|
|
63
|
+
groupBy?: string | string[] | undefined;
|
|
17
64
|
}, {
|
|
18
|
-
|
|
19
|
-
layerId: number;
|
|
20
|
-
statisticType: "count" | "sum" | "avg" | "min" | "max" | "stddev";
|
|
65
|
+
statisticType: "count" | "sum" | "avg" | "min" | "max" | "stddev" | "var";
|
|
21
66
|
onField: string;
|
|
67
|
+
filter?: import("../neutral/filter.js").FilterNode | undefined;
|
|
68
|
+
serviceId?: string | undefined;
|
|
69
|
+
geometry?: Record<string, unknown> | undefined;
|
|
70
|
+
bbox?: number[] | undefined;
|
|
71
|
+
geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
|
|
72
|
+
temporal?: {
|
|
73
|
+
field?: string | undefined;
|
|
74
|
+
instant?: string | undefined;
|
|
75
|
+
start?: string | null | undefined;
|
|
76
|
+
end?: string | null | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
|
|
79
|
+
layerId?: number | undefined;
|
|
80
|
+
source?: string | undefined;
|
|
22
81
|
where?: string | undefined;
|
|
23
|
-
|
|
82
|
+
bboxSrid?: number | undefined;
|
|
83
|
+
spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
|
|
84
|
+
groupBy?: string | string[] | undefined;
|
|
24
85
|
}>;
|
|
25
86
|
export type Input = z.infer<typeof schema>;
|
|
26
87
|
export declare function execute(client: HonuaClient, input: Input): Promise<{
|
|
@@ -28,5 +89,12 @@ export declare function execute(client: HonuaClient, input: Input): Promise<{
|
|
|
28
89
|
type: "text";
|
|
29
90
|
text: string;
|
|
30
91
|
}>;
|
|
92
|
+
} | {
|
|
93
|
+
structuredContent: Record<string, unknown>;
|
|
94
|
+
isError: true;
|
|
95
|
+
content: Array<{
|
|
96
|
+
type: "text";
|
|
97
|
+
text: string;
|
|
98
|
+
}>;
|
|
31
99
|
}>;
|
|
32
100
|
//# sourceMappingURL=statistics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../../src/tools/statistics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../../src/tools/statistics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAQ3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;;GAgC9D"}
|
|
@@ -1,31 +1,64 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { jsonText } from "../helpers.js";
|
|
3
|
+
import { withCapabilityHonesty } from "../neutral/errors.js";
|
|
4
|
+
import { queryFilterFields, toQuery } from "../neutral/query.js";
|
|
5
|
+
import { projectDegraded } from "../neutral/result.js";
|
|
6
|
+
import { isGeoServicesProtocol, resolveSource, sourceRefFields } from "../neutral/source-ref.js";
|
|
7
|
+
/**
|
|
8
|
+
* `honua_statistics` — protocol-neutral aggregation (#1005).
|
|
9
|
+
*
|
|
10
|
+
* Lowers onto the canonical `AggregationSpec`, which GeoServices serves with
|
|
11
|
+
* `outStatistics` and which OGC API Features (no server-side aggregation)
|
|
12
|
+
* serves client-side over the returned page with an explicit `degraded` reason.
|
|
13
|
+
* The reason is reported in the result rather than dropped, so a page-scoped
|
|
14
|
+
* aggregate is never mistaken for an authoritative one.
|
|
15
|
+
*/
|
|
3
16
|
export const schema = z.object({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
statisticType: z
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
...sourceRefFields,
|
|
18
|
+
...queryFilterFields,
|
|
19
|
+
statisticType: z
|
|
20
|
+
.enum(["count", "sum", "avg", "min", "max", "stddev", "var"])
|
|
21
|
+
.describe("The aggregate function to compute."),
|
|
22
|
+
onField: z.string().describe("The field to compute the statistic on."),
|
|
23
|
+
groupBy: z
|
|
24
|
+
.union([z.array(z.string()), z.string()])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Field(s) to group results by."),
|
|
10
27
|
});
|
|
28
|
+
function normalizeGroupBy(groupBy) {
|
|
29
|
+
if (groupBy === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
const fields = (Array.isArray(groupBy) ? groupBy : groupBy.split(",")).map((f) => f.trim()).filter(Boolean);
|
|
32
|
+
return fields.length > 0 ? fields : undefined;
|
|
33
|
+
}
|
|
11
34
|
export async function execute(client, input) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
return withCapabilityHonesty(async () => {
|
|
36
|
+
const resolved = resolveSource(client, input);
|
|
37
|
+
const base = toQuery(input, { protocol: resolved.descriptor.protocol, paginate: false });
|
|
38
|
+
// GeoServices rejects an `outStatistics` query that carries no `where`;
|
|
39
|
+
// every other protocol treats "no filter" as "all records". Supplying the
|
|
40
|
+
// tautology keeps the neutral "omit the filter ⇒ aggregate everything"
|
|
41
|
+
// contract true on Esri endpoints.
|
|
42
|
+
if (isGeoServicesProtocol(resolved.descriptor.protocol) && base.filter === undefined && base.where === undefined) {
|
|
43
|
+
base.where = "1=1";
|
|
44
|
+
}
|
|
45
|
+
const groupBy = normalizeGroupBy(input.groupBy);
|
|
46
|
+
const alias = `${input.statisticType}_${input.onField}`;
|
|
47
|
+
const result = await resolved.source.queryAggregate({
|
|
48
|
+
...base,
|
|
49
|
+
returnGeometry: false,
|
|
50
|
+
aggregation: {
|
|
51
|
+
...(groupBy ? { groupBy } : {}),
|
|
52
|
+
metrics: [{ fn: input.statisticType, field: input.onField, alias }],
|
|
22
53
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
54
|
+
});
|
|
55
|
+
const degraded = projectDegraded(result.degraded);
|
|
56
|
+
return jsonText({
|
|
57
|
+
source: resolved.ref.ref,
|
|
58
|
+
protocol: resolved.descriptor.protocol,
|
|
59
|
+
statistic: alias,
|
|
60
|
+
statistics: (result.aggregateRows ?? []).map((attributes) => ({ attributes })),
|
|
61
|
+
...(degraded ? { degraded } : {}),
|
|
62
|
+
});
|
|
30
63
|
});
|
|
31
64
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honua/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "0.1.8-beta.0",
|
|
4
|
+
"description": "Platform-free MCP server for geospatial services: expose discovery, query, and analysis over any public ArcGIS/Esri FeatureServer or OGC API endpoint to any MCP client, with no platform lock-in",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Honua",
|
|
7
|
+
"homepage": "https://honua-io.github.io/honua-sdk-js/",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/honua-io/honua-sdk-js/issues"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"gis",
|
|
13
|
+
"geospatial",
|
|
14
|
+
"maplibre",
|
|
15
|
+
"typescript",
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"ai-agent",
|
|
19
|
+
"arcgis",
|
|
20
|
+
"ogc-api",
|
|
21
|
+
"feature-service"
|
|
22
|
+
],
|
|
5
23
|
"type": "module",
|
|
6
24
|
"bin": {
|
|
7
25
|
"honua-mcp": "dist/src/index.js",
|
|
8
|
-
"honua-mcp-proxy": "dist/src/proxy.js"
|
|
26
|
+
"honua-mcp-proxy": "dist/src/proxy.js",
|
|
27
|
+
"honua-zero-to-map-release": "dist/src/release/zero-to-map-cli.js"
|
|
9
28
|
},
|
|
10
29
|
"exports": {
|
|
11
30
|
".": {
|
|
@@ -23,7 +42,7 @@
|
|
|
23
42
|
"./package.json": "./package.json"
|
|
24
43
|
},
|
|
25
44
|
"scripts": {
|
|
26
|
-
"clean": "
|
|
45
|
+
"clean": "node scripts/clean-output.mjs",
|
|
27
46
|
"build": "npm run clean --silent && tsc -p tsconfig.json",
|
|
28
47
|
"prepack": "npm run build --silent",
|
|
29
48
|
"typecheck": "tsc --noEmit",
|
|
@@ -32,17 +51,23 @@
|
|
|
32
51
|
"test:coverage": "vitest run --coverage",
|
|
33
52
|
"certify": "npm run build --silent && node dist/src/certification/cli.js",
|
|
34
53
|
"certify:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
|
|
54
|
+
"certify:standalone-ogc": "npm run build --silent && node dist/src/certification/cli.js --target standalone-ogc",
|
|
35
55
|
"test:certification:nl-map-control": "vitest run test/certification/nl-map-control.test.ts",
|
|
36
56
|
"test:certification": "npm run build --silent && vitest run test/certification && node dist/src/certification/cli.js",
|
|
57
|
+
"test:certification:admin": "vitest run test/proxy.test.ts test/certification/admin-parity.test.ts",
|
|
58
|
+
"certify:admin-parity": "npm run build --silent && node dist/src/certification/admin-parity.js",
|
|
37
59
|
"test:certification:artifact": "npm run build --silent && node dist/src/certification/cli.js --artifact-only",
|
|
38
60
|
"test:certification:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
|
|
61
|
+
"test:certification:standalone-ogc": "npm run build --silent && node dist/src/certification/cli.js --target standalone-ogc",
|
|
39
62
|
"eval": "npm run build --silent && node dist/src/eval/cli.js",
|
|
40
63
|
"eval:offline": "npm run build --silent && node dist/src/eval/cli.js --offline",
|
|
41
64
|
"eval:standalone": "npm run build --silent && node dist/src/eval/cli.js --corpus standalone --offline",
|
|
65
|
+
"eval:ogc": "npm run build --silent && node dist/src/eval/cli.js --corpus ogc --offline",
|
|
42
66
|
"eval:live": "npm run build --silent && node dist/src/eval/cli.js --corpus operator --artifact-only",
|
|
43
67
|
"leaderboard": "node scripts/render-leaderboard.mjs",
|
|
44
68
|
"test:eval": "npm run build --silent && vitest run test/eval && node dist/src/eval/cli.js --offline",
|
|
45
69
|
"test:eval:artifact": "npm run build --silent && node dist/src/eval/cli.js --artifact-only",
|
|
70
|
+
"release:zero-to-map": "npm run build --silent && node dist/src/release/zero-to-map-cli.js",
|
|
46
71
|
"lint": "biome lint src/ test/",
|
|
47
72
|
"lint:fix": "biome lint --write src/ test/",
|
|
48
73
|
"format": "biome format src/ test/",
|
|
@@ -57,10 +82,12 @@
|
|
|
57
82
|
"zod": "^3.24.0"
|
|
58
83
|
},
|
|
59
84
|
"overrides": {
|
|
60
|
-
"hono": "^4.12.
|
|
85
|
+
"hono": "^4.12.27",
|
|
86
|
+
"@hono/node-server": "^2.0.5",
|
|
87
|
+
"fast-uri": "^3.1.3"
|
|
61
88
|
},
|
|
62
89
|
"peerDependencies": {
|
|
63
|
-
"@honua/sdk-js": "^0.1.
|
|
90
|
+
"@honua/sdk-js": "^0.1.8-beta.0"
|
|
64
91
|
},
|
|
65
92
|
"devDependencies": {
|
|
66
93
|
"@biomejs/biome": "1.9.4",
|
|
@@ -72,11 +99,13 @@
|
|
|
72
99
|
},
|
|
73
100
|
"files": [
|
|
74
101
|
"dist/src",
|
|
102
|
+
"release/zero-to-map",
|
|
75
103
|
"certification/geospatial-mcp-schemas",
|
|
76
|
-
"README.md"
|
|
104
|
+
"README.md",
|
|
105
|
+
"LICENSE"
|
|
77
106
|
],
|
|
78
107
|
"engines": {
|
|
79
|
-
"node": ">=20.
|
|
108
|
+
"node": ">=20.19.0"
|
|
80
109
|
},
|
|
81
110
|
"repository": {
|
|
82
111
|
"type": "git",
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Zero-to-map 2026.1 release journey
|
|
2
|
+
|
|
3
|
+
This bundle is the executable contract for `honua-release#123` D9.3: install
|
|
4
|
+
Honua, configure and publish deterministic data with AI-accessible admin tools,
|
|
5
|
+
run Buffer through the AI-facing Esri MCP tool, the SDK's Esri GPServer
|
|
6
|
+
compatibility path, and the MCP-native analysis verb, compose distinct map,
|
|
7
|
+
app, and dashboard packages in Studio, save each as an immutable version, read
|
|
8
|
+
that exact version, reopen it as a new draft, record three publication intents,
|
|
9
|
+
and save three new intent-bearing versions. It then stops at the human Console
|
|
10
|
+
gate before verifying the three approved public URLs. Both MCP
|
|
11
|
+
jobs are polled through `honua://jobs/{id}` and joined to their
|
|
12
|
+
`honua://jobs/{id}/results` packages; a queued submission alone cannot pass.
|
|
13
|
+
|
|
14
|
+
The driver reuses `honua admin install` and `honua-mcp-proxy`. It does not ship
|
|
15
|
+
a second installer, admin client, or MCP transport. The installer is the
|
|
16
|
+
canonical credential-issuance boundary: material is handed directly to its
|
|
17
|
+
private environment/MCP configuration files, while its JSON receipt contains
|
|
18
|
+
only the credential ID, requested/effective grants, active status, and a digest
|
|
19
|
+
of the file reference.
|
|
20
|
+
|
|
21
|
+
## Inspect the contract without changing anything
|
|
22
|
+
|
|
23
|
+
From the `mcp` package directory:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm run release:zero-to-map -- --output zero-to-map-contract-receipt.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Contract mode never invokes Docker, CLI, MCP, GPServer, Console, or HTTP. Its
|
|
30
|
+
receipt is intentionally `blocked`, with the first live action blocked and all
|
|
31
|
+
later actions skipped. Static issue references remain under `dependencyRefs`;
|
|
32
|
+
they are never reported as runtime blockers on a passed live receipt.
|
|
33
|
+
|
|
34
|
+
## Configure an MCP client
|
|
35
|
+
|
|
36
|
+
The `configs` directory contains equivalent examples for Claude Desktop,
|
|
37
|
+
Claude Code, and Cursor. Each launches the existing `honua-mcp-proxy` and points
|
|
38
|
+
it at the remote MCP endpoint. Set `HONUA_MCP_AUTH_TOKEN` separately when
|
|
39
|
+
the deployment requires bearer authentication; do not put credentials in the
|
|
40
|
+
journey plan or a release receipt.
|
|
41
|
+
Credential-bearing proxy and live-journey endpoints require HTTPS, except for
|
|
42
|
+
exact loopback HTTP used by the bounded local-Docker profile. User information,
|
|
43
|
+
query parameters, fragments, cross-origin redirects, and any catalog that is not
|
|
44
|
+
exactly the enabled-profile roster fail closed before the first mutation.
|
|
45
|
+
|
|
46
|
+
## Run a live candidate
|
|
47
|
+
|
|
48
|
+
Prerequisites:
|
|
49
|
+
|
|
50
|
+
- Node.js 20.19 or newer and Docker with Compose
|
|
51
|
+
- the 2026.1 `honua` CLI work from `honua-sdk-js#1370-#1373`
|
|
52
|
+
- a server build exposing the complete default 432-tool roster (47 static plus
|
|
53
|
+
385 `honua_admin_*` tools), with all eleven secret/session Admin operations
|
|
54
|
+
absent, **and** the `analysis` and `esri-gp` server profiles enabled so the
|
|
55
|
+
three `honua_esri_gp_*` tools and `honua_buffer_features` are advertised
|
|
56
|
+
(441 tools in total), plus the Studio lifecycle tools including
|
|
57
|
+
`honua_studio_save_version`, `honua_studio_get_version`, and
|
|
58
|
+
`honua_studio_reopen_version`
|
|
59
|
+
- the `geoprocessing/Buffer` GPServer task seeded by the deployment
|
|
60
|
+
- these fixtures available to the server at an HTTP(S) base URL
|
|
61
|
+
- a writable checkpoint path; the first pass emits the exact resolved Console
|
|
62
|
+
receipt request after all pre-approval work completes
|
|
63
|
+
|
|
64
|
+
Serve the checked-in fixtures from a URL reachable by the Honua container, for
|
|
65
|
+
example with an existing static-file server. Then run:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npm run release:zero-to-map -- \
|
|
69
|
+
--execute --yes \
|
|
70
|
+
--target local-docker \
|
|
71
|
+
--mcp-url http://localhost:8080/mcp \
|
|
72
|
+
--var fixtureBaseUrl=http://host.docker.internal:4173 \
|
|
73
|
+
--var dbSecretReference=env:HONUA_ZERO_TO_MAP_DB_CONNECTION \
|
|
74
|
+
--var dbSecretType=environment \
|
|
75
|
+
--var candidateId=manifest-sha256:<platform-manifest-sha256> \
|
|
76
|
+
--var releaseId=2026.1 \
|
|
77
|
+
--checkpoint ./zero-to-map.checkpoint.json \
|
|
78
|
+
--output ./zero-to-map-live-receipt.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The first pass exits `2` with `external-receipt-missing`. It has already run
|
|
82
|
+
stages 1-5 exactly once. Give the checkpoint's resolved
|
|
83
|
+
`consoleReceiptRequest` to the Console producer, carry
|
|
84
|
+
`checkpoint.integrity.digest` outside that file, and resume with the same
|
|
85
|
+
command plus:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
--checkpoint-digest <checkpoint.integrity.digest> \
|
|
89
|
+
--console-receipt ./console-approval.json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The resume atomically claims the checkpoint before any adapter work. A second
|
|
93
|
+
or concurrent claimant fails closed. The checkpoint is bound to the exact plan
|
|
94
|
+
bytes, SDK source SHA (`HONUA_SOURCE_REVISION`), target, MCP endpoint,
|
|
95
|
+
candidate/release, and (for AWS) provisioning receipt; it contains only
|
|
96
|
+
allowlisted captures/evidence, including the non-secret deterministic
|
|
97
|
+
`serviceName` identity and access-credential ID/grants/reference digest required
|
|
98
|
+
by external producers, and never the database password, admin key, token, or
|
|
99
|
+
authorization material. A successful resume
|
|
100
|
+
marks it consumed and binds the final receipt hash.
|
|
101
|
+
|
|
102
|
+
For a deployment already provisioned by the DevOps ECS producer, use
|
|
103
|
+
`--target aws-ecs --provision-receipt <pre-teardown-binding.json>`. The binding
|
|
104
|
+
must match `contracts/aws-ecs-provision-binding.schema.json`, the candidate and
|
|
105
|
+
release variables, and `--mcp-url=<binding.endpoint>/mcp`. Stage 1 then records
|
|
106
|
+
the real image/digest, Terraform, readiness, and a secret-free access receipt
|
|
107
|
+
whose reference digest is verified against the producer's Secrets Manager ARN;
|
|
108
|
+
it does not invoke or pretend to invoke the Docker installer.
|
|
109
|
+
|
|
110
|
+
The catalog is checked in full before the first MCP call. Missing tools or
|
|
111
|
+
input-schema drift block the run without sending that call.
|
|
112
|
+
|
|
113
|
+
The preflight verifies each enabled server profile independently and *derives*
|
|
114
|
+
the expected total from them; it never compares the catalog against a single
|
|
115
|
+
hardcoded number:
|
|
116
|
+
|
|
117
|
+
| Profile | Members | Verified by |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| `base` | 47 static + 385 `honua_admin_*` = 432 | the generated Admin contract, plus the 11 audited secret/session exclusions proven absent |
|
|
120
|
+
| `analysis` | 6 | `honua_buffer_features` by name; the rest by declared size (the server has not published an `analysis` roster - honua-server#3363) |
|
|
121
|
+
| `esri-gp` | 3 | exactly `honua_esri_gp_list_tasks`, `honua_esri_gp_describe_task`, `honua_esri_gp_execute_task` from `contracts/esri-gp-mcp.v1.json` |
|
|
122
|
+
|
|
123
|
+
The enabled candidate therefore advertises 432 + 6 + 3 = 441 tools. A missing,
|
|
124
|
+
unexpected, duplicate, excluded, truncated, or schema-drifted member fails with
|
|
125
|
+
its own labelled finding naming the journey stage and action that needs the
|
|
126
|
+
tool; a short catalog is reported as a pagination fault rather than a roster
|
|
127
|
+
decision. The pagination safety ceiling in `listAllTools` is deliberately
|
|
128
|
+
separate from every roster number, so reading a larger profile-enabled catalog
|
|
129
|
+
is never mistaken for a hostile server. Default-profile certification
|
|
130
|
+
(`certifyAdminCatalogParity`) continues to require exactly 432.
|
|
131
|
+
|
|
132
|
+
The connection is
|
|
133
|
+
created with `secretReference` + `secretType`; raw database credentials never
|
|
134
|
+
enter MCP arguments, checkpoints, or receipts. One-time-secret and session-bound
|
|
135
|
+
operations (including API-key issuance) are deliberately absent from MCP and
|
|
136
|
+
must be handled by the install/control-plane credential boundary. The journey
|
|
137
|
+
then exercises only secret-safe API-key list and effective-permissions tools to
|
|
138
|
+
verify the provisioned identity and its exact grants. Admin operations
|
|
139
|
+
must return a completed PublishedOperation; queued or approval-required handles
|
|
140
|
+
produce explicit blocked receipts. The driver reads connection/layer IDs only
|
|
141
|
+
from `structuredContent.details.response`, the server's PublishedOperation
|
|
142
|
+
endpoint-response seam.
|
|
143
|
+
|
|
144
|
+
The Studio version tools are also preflighted before the first server mutation.
|
|
145
|
+
Map, app, and dashboard each exercise layer, style, view, widget, and control
|
|
146
|
+
mutations before validation; family-specific shortcuts or embedded lookalikes
|
|
147
|
+
cannot satisfy the release roster.
|
|
148
|
+
The journey does not treat `honua_studio_get_draft` as reopen evidence: every
|
|
149
|
+
family must capture its `itemId`, immutable `versionId`, `contentHash`, and the
|
|
150
|
+
new draft whose `baseVersionId` points back to that exact version. After the
|
|
151
|
+
proposal increments that reopened draft's generation, the journey saves a new
|
|
152
|
+
immutable publication version. Console must create and publish the request for
|
|
153
|
+
that new version, never the earlier pre-intent version.
|
|
154
|
+
|
|
155
|
+
The Console receipt is accepted only when it is `passed` and binds this
|
|
156
|
+
journey/release contract to the exact connection, service, layers, three GP job
|
|
157
|
+
identities, result packages/artifacts, map/app/dashboard draft and immutable
|
|
158
|
+
version identities, all three reopened drafts and intent-bearing publication
|
|
159
|
+
versions, all three Console request/publication/status/public-URL identities,
|
|
160
|
+
execution operations, audit correlations, routes, candidate, and release. Each
|
|
161
|
+
audit operation must equal its family's execution operation, each proposal ID
|
|
162
|
+
must equal its publication request ID, and the app public URL must equal the
|
|
163
|
+
top-level share URL. The Studio proposal actions reject any pre-approval
|
|
164
|
+
publication ID or public URL and must report both `recorded=true` and
|
|
165
|
+
`humanConfirmationRequired=true`. Agent execution never claims the human
|
|
166
|
+
publication step. Stage 7 independently requires identity-preserving HTTPS
|
|
167
|
+
HTTP 200 responses for map, app, and dashboard URLs.
|
|
168
|
+
|
|
169
|
+
## Evidence status
|
|
170
|
+
|
|
171
|
+
The fixtures, plan, contracts, and simulated end-to-end tests are deterministic.
|
|
172
|
+
They are not a live release recording. Until all deployment dependencies are
|
|
173
|
+
available and an operator supplies a matching Console receipt, the honest
|
|
174
|
+
candidate result is `blocked`/`skipped`, not passed.
|