@honua/mcp-server 0.1.1-beta.0 → 0.1.4-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 +202 -7
- 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 +45 -2
- 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 +14 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -15
- 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/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/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 +31 -7
|
@@ -1,41 +1,114 @@
|
|
|
1
1
|
import type { HonuaClient } from "@honua/sdk-js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* `honua_query_features` — protocol-neutral feature read (#1005).
|
|
5
|
+
*
|
|
6
|
+
* Addresses a source by its neutral `<protocol>:<address>` reference, filters
|
|
7
|
+
* with the typed semantic filter / GeoJSON geometry / canonical temporal
|
|
8
|
+
* predicate, and executes through the SDK's canonical `Source.query()`, so the
|
|
9
|
+
* same call means the same thing against GeoServices, OGC API Features, STAC,
|
|
10
|
+
* WFS, and OData. The deprecated `serviceId` + `layerId` pair is still accepted.
|
|
11
|
+
*/
|
|
3
12
|
export declare const schema: z.ZodObject<{
|
|
4
|
-
serviceId: z.ZodString;
|
|
5
|
-
layerId: z.ZodNumber;
|
|
6
|
-
where: z.ZodOptional<z.ZodString>;
|
|
7
13
|
outFields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
orderBy: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodObject<{
|
|
15
|
+
field: z.ZodString;
|
|
16
|
+
direction: z.ZodOptional<z.ZodEnum<["asc", "desc"]>>;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
field: string;
|
|
19
|
+
direction?: "asc" | "desc" | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
field: string;
|
|
22
|
+
direction?: "asc" | "desc" | undefined;
|
|
23
|
+
}>, "many">, z.ZodString]>>;
|
|
12
24
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
13
25
|
offset: z.ZodOptional<z.ZodNumber>;
|
|
14
26
|
returnGeometry: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
27
|
+
geometryFormat: z.ZodOptional<z.ZodEnum<["geojson", "esri-json"]>>;
|
|
28
|
+
outSr: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
|
|
29
|
+
filter: z.ZodOptional<z.ZodType<import("../neutral/filter.js").FilterNode, z.ZodTypeDef, import("../neutral/filter.js").FilterNode>>;
|
|
30
|
+
where: z.ZodOptional<z.ZodString>;
|
|
31
|
+
bbox: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
|
|
32
|
+
bboxSrid: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
geometry: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
34
|
+
geometryType: z.ZodOptional<z.ZodEnum<["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"]>>;
|
|
35
|
+
spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within", "crosses", "touches", "overlaps", "bbox-intersects"]>>;
|
|
36
|
+
temporal: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
instant: z.ZodOptional<z.ZodString>;
|
|
38
|
+
start: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
end: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
field: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
field?: string | undefined;
|
|
43
|
+
instant?: string | undefined;
|
|
44
|
+
start?: string | null | undefined;
|
|
45
|
+
end?: string | null | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
field?: string | undefined;
|
|
48
|
+
instant?: string | undefined;
|
|
49
|
+
start?: string | null | undefined;
|
|
50
|
+
end?: string | null | undefined;
|
|
51
|
+
}>>;
|
|
52
|
+
source: z.ZodOptional<z.ZodString>;
|
|
53
|
+
layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
|
|
54
|
+
serviceId: z.ZodOptional<z.ZodString>;
|
|
55
|
+
layerId: z.ZodOptional<z.ZodNumber>;
|
|
15
56
|
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
serviceId: string;
|
|
17
|
-
layerId: number;
|
|
18
57
|
returnGeometry: boolean;
|
|
19
|
-
|
|
58
|
+
filter?: import("../neutral/filter.js").FilterNode | undefined;
|
|
59
|
+
serviceId?: string | undefined;
|
|
20
60
|
geometry?: Record<string, unknown> | undefined;
|
|
61
|
+
bbox?: number[] | undefined;
|
|
21
62
|
geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
63
|
+
temporal?: {
|
|
64
|
+
field?: string | undefined;
|
|
65
|
+
instant?: string | undefined;
|
|
66
|
+
start?: string | null | undefined;
|
|
67
|
+
end?: string | null | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
|
|
70
|
+
layerId?: number | undefined;
|
|
71
|
+
source?: string | undefined;
|
|
72
|
+
orderBy?: string | {
|
|
73
|
+
field: string;
|
|
74
|
+
direction?: "asc" | "desc" | undefined;
|
|
75
|
+
}[] | undefined;
|
|
76
|
+
where?: string | undefined;
|
|
77
|
+
bboxSrid?: number | undefined;
|
|
78
|
+
spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
|
|
25
79
|
limit?: number | undefined;
|
|
80
|
+
outFields?: string[] | undefined;
|
|
26
81
|
offset?: number | undefined;
|
|
82
|
+
geometryFormat?: "geojson" | "esri-json" | undefined;
|
|
83
|
+
outSr?: string | number | undefined;
|
|
27
84
|
}, {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
where?: string | undefined;
|
|
85
|
+
filter?: import("../neutral/filter.js").FilterNode | undefined;
|
|
86
|
+
serviceId?: string | undefined;
|
|
31
87
|
geometry?: Record<string, unknown> | undefined;
|
|
88
|
+
bbox?: number[] | undefined;
|
|
32
89
|
geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
90
|
+
temporal?: {
|
|
91
|
+
field?: string | undefined;
|
|
92
|
+
instant?: string | undefined;
|
|
93
|
+
start?: string | null | undefined;
|
|
94
|
+
end?: string | null | undefined;
|
|
95
|
+
} | undefined;
|
|
96
|
+
layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
|
|
97
|
+
layerId?: number | undefined;
|
|
98
|
+
source?: string | undefined;
|
|
99
|
+
orderBy?: string | {
|
|
100
|
+
field: string;
|
|
101
|
+
direction?: "asc" | "desc" | undefined;
|
|
102
|
+
}[] | undefined;
|
|
103
|
+
where?: string | undefined;
|
|
104
|
+
bboxSrid?: number | undefined;
|
|
105
|
+
spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
|
|
36
106
|
limit?: number | undefined;
|
|
107
|
+
outFields?: string[] | undefined;
|
|
37
108
|
offset?: number | undefined;
|
|
38
109
|
returnGeometry?: boolean | undefined;
|
|
110
|
+
geometryFormat?: "geojson" | "esri-json" | undefined;
|
|
111
|
+
outSr?: string | number | undefined;
|
|
39
112
|
}>;
|
|
40
113
|
export type Input = z.infer<typeof schema>;
|
|
41
114
|
export declare function execute(client: HonuaClient, input: Input): Promise<{
|
|
@@ -43,5 +116,12 @@ export declare function execute(client: HonuaClient, input: Input): Promise<{
|
|
|
43
116
|
type: "text";
|
|
44
117
|
text: string;
|
|
45
118
|
}>;
|
|
119
|
+
} | {
|
|
120
|
+
structuredContent: Record<string, unknown>;
|
|
121
|
+
isError: true;
|
|
122
|
+
content: Array<{
|
|
123
|
+
type: "text";
|
|
124
|
+
text: string;
|
|
125
|
+
}>;
|
|
46
126
|
}>;
|
|
47
127
|
//# sourceMappingURL=query-features.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-features.d.ts","sourceRoot":"","sources":["../../../src/tools/query-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"query-features.d.ts","sourceRoot":"","sources":["../../../src/tools/query-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;;GAyB9D"}
|
|
@@ -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,7 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honua/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "0.1.4-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",
|
|
@@ -23,7 +41,7 @@
|
|
|
23
41
|
"./package.json": "./package.json"
|
|
24
42
|
},
|
|
25
43
|
"scripts": {
|
|
26
|
-
"clean": "
|
|
44
|
+
"clean": "node scripts/clean-output.mjs",
|
|
27
45
|
"build": "npm run clean --silent && tsc -p tsconfig.json",
|
|
28
46
|
"prepack": "npm run build --silent",
|
|
29
47
|
"typecheck": "tsc --noEmit",
|
|
@@ -32,13 +50,16 @@
|
|
|
32
50
|
"test:coverage": "vitest run --coverage",
|
|
33
51
|
"certify": "npm run build --silent && node dist/src/certification/cli.js",
|
|
34
52
|
"certify:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
|
|
53
|
+
"certify:standalone-ogc": "npm run build --silent && node dist/src/certification/cli.js --target standalone-ogc",
|
|
35
54
|
"test:certification:nl-map-control": "vitest run test/certification/nl-map-control.test.ts",
|
|
36
55
|
"test:certification": "npm run build --silent && vitest run test/certification && node dist/src/certification/cli.js",
|
|
37
56
|
"test:certification:artifact": "npm run build --silent && node dist/src/certification/cli.js --artifact-only",
|
|
38
57
|
"test:certification:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
|
|
58
|
+
"test:certification:standalone-ogc": "npm run build --silent && node dist/src/certification/cli.js --target standalone-ogc",
|
|
39
59
|
"eval": "npm run build --silent && node dist/src/eval/cli.js",
|
|
40
60
|
"eval:offline": "npm run build --silent && node dist/src/eval/cli.js --offline",
|
|
41
61
|
"eval:standalone": "npm run build --silent && node dist/src/eval/cli.js --corpus standalone --offline",
|
|
62
|
+
"eval:ogc": "npm run build --silent && node dist/src/eval/cli.js --corpus ogc --offline",
|
|
42
63
|
"eval:live": "npm run build --silent && node dist/src/eval/cli.js --corpus operator --artifact-only",
|
|
43
64
|
"leaderboard": "node scripts/render-leaderboard.mjs",
|
|
44
65
|
"test:eval": "npm run build --silent && vitest run test/eval && node dist/src/eval/cli.js --offline",
|
|
@@ -57,10 +78,12 @@
|
|
|
57
78
|
"zod": "^3.24.0"
|
|
58
79
|
},
|
|
59
80
|
"overrides": {
|
|
60
|
-
"hono": "^4.12.
|
|
81
|
+
"hono": "^4.12.27",
|
|
82
|
+
"@hono/node-server": "^2.0.5",
|
|
83
|
+
"fast-uri": "^3.1.3"
|
|
61
84
|
},
|
|
62
85
|
"peerDependencies": {
|
|
63
|
-
"@honua/sdk-js": "^0.1.
|
|
86
|
+
"@honua/sdk-js": "^0.1.4-beta.0"
|
|
64
87
|
},
|
|
65
88
|
"devDependencies": {
|
|
66
89
|
"@biomejs/biome": "1.9.4",
|
|
@@ -73,10 +96,11 @@
|
|
|
73
96
|
"files": [
|
|
74
97
|
"dist/src",
|
|
75
98
|
"certification/geospatial-mcp-schemas",
|
|
76
|
-
"README.md"
|
|
99
|
+
"README.md",
|
|
100
|
+
"LICENSE"
|
|
77
101
|
],
|
|
78
102
|
"engines": {
|
|
79
|
-
"node": ">=20.
|
|
103
|
+
"node": ">=20.19.0"
|
|
80
104
|
},
|
|
81
105
|
"repository": {
|
|
82
106
|
"type": "git",
|