@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.
Files changed (117) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +232 -9
  3. package/dist/src/certification/admin-parity.d.ts +96 -0
  4. package/dist/src/certification/admin-parity.d.ts.map +1 -0
  5. package/dist/src/certification/admin-parity.js +235 -0
  6. package/dist/src/certification/census-fixture-client.d.ts.map +1 -1
  7. package/dist/src/certification/census-fixture-client.js +5 -0
  8. package/dist/src/certification/certifier.d.ts +1 -1
  9. package/dist/src/certification/certifier.d.ts.map +1 -1
  10. package/dist/src/certification/certifier.js +28 -20
  11. package/dist/src/certification/cli.js +1 -1
  12. package/dist/src/certification/cql2.d.ts +24 -0
  13. package/dist/src/certification/cql2.d.ts.map +1 -0
  14. package/dist/src/certification/cql2.js +354 -0
  15. package/dist/src/certification/fixtures.d.ts +8 -0
  16. package/dist/src/certification/fixtures.d.ts.map +1 -1
  17. package/dist/src/certification/fixtures.js +26 -10
  18. package/dist/src/certification/ogc-data.d.ts +30 -0
  19. package/dist/src/certification/ogc-data.d.ts.map +1 -0
  20. package/dist/src/certification/ogc-data.js +726 -0
  21. package/dist/src/certification/ogc-fixture-client.d.ts +4 -0
  22. package/dist/src/certification/ogc-fixture-client.d.ts.map +1 -0
  23. package/dist/src/certification/ogc-fixture-client.js +190 -0
  24. package/dist/src/certification/target.d.ts +10 -1
  25. package/dist/src/certification/target.d.ts.map +1 -1
  26. package/dist/src/certification/target.js +63 -28
  27. package/dist/src/credential-endpoint.d.ts +3 -0
  28. package/dist/src/credential-endpoint.d.ts.map +1 -0
  29. package/dist/src/credential-endpoint.js +24 -0
  30. package/dist/src/eval/cli.js +6 -3
  31. package/dist/src/eval/corpus.d.ts +7 -0
  32. package/dist/src/eval/corpus.d.ts.map +1 -1
  33. package/dist/src/eval/corpus.js +12 -0
  34. package/dist/src/eval/ogc-corpus.d.ts +3 -0
  35. package/dist/src/eval/ogc-corpus.d.ts.map +1 -0
  36. package/dist/src/eval/ogc-corpus.js +437 -0
  37. package/dist/src/eval/runner.d.ts +6 -0
  38. package/dist/src/eval/runner.d.ts.map +1 -1
  39. package/dist/src/eval/runner.js +21 -7
  40. package/dist/src/helpers.d.ts +8 -13
  41. package/dist/src/helpers.d.ts.map +1 -1
  42. package/dist/src/helpers.js +7 -35
  43. package/dist/src/index.d.ts +36 -8
  44. package/dist/src/index.d.ts.map +1 -1
  45. package/dist/src/index.js +55 -18
  46. package/dist/src/neutral/errors.d.ts +73 -0
  47. package/dist/src/neutral/errors.d.ts.map +1 -0
  48. package/dist/src/neutral/errors.js +112 -0
  49. package/dist/src/neutral/filter.d.ts +108 -0
  50. package/dist/src/neutral/filter.d.ts.map +1 -0
  51. package/dist/src/neutral/filter.js +271 -0
  52. package/dist/src/neutral/query.d.ts +101 -0
  53. package/dist/src/neutral/query.d.ts.map +1 -0
  54. package/dist/src/neutral/query.js +180 -0
  55. package/dist/src/neutral/result.d.ts +33 -0
  56. package/dist/src/neutral/result.d.ts.map +1 -0
  57. package/dist/src/neutral/result.js +38 -0
  58. package/dist/src/neutral/source-ref.d.ts +126 -0
  59. package/dist/src/neutral/source-ref.d.ts.map +1 -0
  60. package/dist/src/neutral/source-ref.js +267 -0
  61. package/dist/src/nl-map-control.d.ts.map +1 -1
  62. package/dist/src/proxy.d.ts.map +1 -1
  63. package/dist/src/proxy.js +29 -14
  64. package/dist/src/release/zero-to-map-checkpoint.d.ts +34 -0
  65. package/dist/src/release/zero-to-map-checkpoint.d.ts.map +1 -0
  66. package/dist/src/release/zero-to-map-checkpoint.js +369 -0
  67. package/dist/src/release/zero-to-map-cli.d.ts +51 -0
  68. package/dist/src/release/zero-to-map-cli.d.ts.map +1 -0
  69. package/dist/src/release/zero-to-map-cli.js +880 -0
  70. package/dist/src/release/zero-to-map-provision.d.ts +37 -0
  71. package/dist/src/release/zero-to-map-provision.d.ts.map +1 -0
  72. package/dist/src/release/zero-to-map-provision.js +177 -0
  73. package/dist/src/release/zero-to-map.d.ts +296 -0
  74. package/dist/src/release/zero-to-map.d.ts.map +1 -0
  75. package/dist/src/release/zero-to-map.js +1069 -0
  76. package/dist/src/tools/admin-install-local.d.ts +30 -0
  77. package/dist/src/tools/admin-install-local.d.ts.map +1 -0
  78. package/dist/src/tools/admin-install-local.js +22 -0
  79. package/dist/src/tools/control-plane-command.d.ts +99 -0
  80. package/dist/src/tools/control-plane-command.d.ts.map +1 -0
  81. package/dist/src/tools/control-plane-command.js +180 -0
  82. package/dist/src/tools/count-features.d.ts +85 -11
  83. package/dist/src/tools/count-features.d.ts.map +1 -1
  84. package/dist/src/tools/count-features.js +49 -31
  85. package/dist/src/tools/describe-layer.d.ts +30 -6
  86. package/dist/src/tools/describe-layer.d.ts.map +1 -1
  87. package/dist/src/tools/describe-layer.js +99 -8
  88. package/dist/src/tools/docs-search.d.ts +100 -0
  89. package/dist/src/tools/docs-search.d.ts.map +1 -0
  90. package/dist/src/tools/docs-search.js +381 -0
  91. package/dist/src/tools/get-extent.d.ts +69 -11
  92. package/dist/src/tools/get-extent.d.ts.map +1 -1
  93. package/dist/src/tools/get-extent.js +28 -46
  94. package/dist/src/tools/list-services.d.ts +7 -0
  95. package/dist/src/tools/list-services.d.ts.map +1 -1
  96. package/dist/src/tools/list-services.js +8 -1
  97. package/dist/src/tools/list-sources.d.ts +44 -0
  98. package/dist/src/tools/list-sources.d.ts.map +1 -0
  99. package/dist/src/tools/list-sources.js +138 -0
  100. package/dist/src/tools/query-features.d.ts +99 -19
  101. package/dist/src/tools/query-features.d.ts.map +1 -1
  102. package/dist/src/tools/query-features.js +38 -45
  103. package/dist/src/tools/statistics.d.ts +80 -12
  104. package/dist/src/tools/statistics.d.ts.map +1 -1
  105. package/dist/src/tools/statistics.js +56 -23
  106. package/package.json +37 -8
  107. package/release/zero-to-map/README.md +174 -0
  108. package/release/zero-to-map/configs/claude-code.mcp.json +11 -0
  109. package/release/zero-to-map/configs/claude-desktop.json +11 -0
  110. package/release/zero-to-map/configs/cursor.json +11 -0
  111. package/release/zero-to-map/contracts/aws-ecs-provision-binding.schema.json +59 -0
  112. package/release/zero-to-map/contracts/checkpoint.schema.json +152 -0
  113. package/release/zero-to-map/contracts/console-receipt.schema.json +150 -0
  114. package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +73 -0
  115. package/release/zero-to-map/fixtures/parcels.geojson +33 -0
  116. package/release/zero-to-map/fixtures/zoning.geojson +24 -0
  117. package/release/zero-to-map/journey.v1.json +1248 -0
@@ -1,26 +1,77 @@
1
1
  import type { HonuaClient } from "@honua/sdk-js";
2
2
  import { z } from "zod";
3
+ /**
4
+ * `honua_get_extent` — protocol-neutral bounding box (#1005).
5
+ *
6
+ * Runs the canonical `Source.queryExtent()`. Protocols with no server-side
7
+ * extent operation (OGC API Features, STAC, OData) answer from the collection's
8
+ * declared extent under the degraded capability policy, and say so; protocols
9
+ * that cannot answer at all refuse with a structured capability error.
10
+ */
3
11
  export declare const schema: z.ZodObject<{
4
- serviceId: z.ZodString;
5
- layerId: z.ZodNumber;
12
+ filter: z.ZodOptional<z.ZodType<import("../neutral/filter.js").FilterNode, z.ZodTypeDef, import("../neutral/filter.js").FilterNode>>;
6
13
  where: z.ZodOptional<z.ZodString>;
14
+ bbox: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
15
+ bboxSrid: z.ZodOptional<z.ZodNumber>;
7
16
  geometry: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
17
  geometryType: z.ZodOptional<z.ZodEnum<["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"]>>;
9
- spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within"]>>;
18
+ spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within", "crosses", "touches", "overlaps", "bbox-intersects"]>>;
19
+ temporal: z.ZodOptional<z.ZodObject<{
20
+ instant: z.ZodOptional<z.ZodString>;
21
+ start: z.ZodOptional<z.ZodNullable<z.ZodString>>;
22
+ end: z.ZodOptional<z.ZodNullable<z.ZodString>>;
23
+ field: z.ZodOptional<z.ZodString>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ field?: string | undefined;
26
+ instant?: string | undefined;
27
+ start?: string | null | undefined;
28
+ end?: string | null | undefined;
29
+ }, {
30
+ field?: string | undefined;
31
+ instant?: string | undefined;
32
+ start?: string | null | undefined;
33
+ end?: string | null | undefined;
34
+ }>>;
35
+ source: z.ZodOptional<z.ZodString>;
36
+ layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
37
+ serviceId: z.ZodOptional<z.ZodString>;
38
+ layerId: z.ZodOptional<z.ZodNumber>;
10
39
  }, "strip", z.ZodTypeAny, {
11
- serviceId: string;
12
- layerId: number;
13
- where?: string | undefined;
40
+ filter?: import("../neutral/filter.js").FilterNode | undefined;
41
+ serviceId?: string | undefined;
14
42
  geometry?: Record<string, unknown> | undefined;
43
+ bbox?: number[] | undefined;
15
44
  geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
16
- spatialRel?: "intersects" | "contains" | "within" | undefined;
17
- }, {
18
- serviceId: string;
19
- layerId: number;
45
+ temporal?: {
46
+ field?: string | undefined;
47
+ instant?: string | undefined;
48
+ start?: string | null | undefined;
49
+ end?: string | null | undefined;
50
+ } | undefined;
51
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
52
+ layerId?: number | undefined;
53
+ source?: string | undefined;
20
54
  where?: string | undefined;
55
+ bboxSrid?: number | undefined;
56
+ spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
57
+ }, {
58
+ filter?: import("../neutral/filter.js").FilterNode | undefined;
59
+ serviceId?: string | undefined;
21
60
  geometry?: Record<string, unknown> | undefined;
61
+ bbox?: number[] | undefined;
22
62
  geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
23
- spatialRel?: "intersects" | "contains" | "within" | undefined;
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
+ where?: string | undefined;
73
+ bboxSrid?: number | undefined;
74
+ spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
24
75
  }>;
25
76
  export type Input = z.infer<typeof schema>;
26
77
  export declare function execute(client: HonuaClient, input: Input): Promise<{
@@ -28,5 +79,12 @@ export declare function execute(client: HonuaClient, input: Input): Promise<{
28
79
  type: "text";
29
80
  text: string;
30
81
  }>;
82
+ } | {
83
+ structuredContent: Record<string, unknown>;
84
+ isError: true;
85
+ content: Array<{
86
+ type: "text";
87
+ text: string;
88
+ }>;
31
89
  }>;
32
90
  //# sourceMappingURL=get-extent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-extent.d.ts","sourceRoot":"","sources":["../../../src/tools/get-extent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;EAajB,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;;;;;GAoC9D"}
1
+ {"version":3,"file":"get-extent.d.ts","sourceRoot":"","sources":["../../../src/tools/get-extent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjB,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;;;;;;;;;;;;GAgB9D"}
@@ -1,51 +1,33 @@
1
1
  import { z } from "zod";
2
- import { GEOMETRY_TYPES, coerceCount, jsonText, mapSpatialRel, resolveGeometryType } from "../helpers.js";
2
+ import { jsonText } from "../helpers.js";
3
+ import { withCapabilityHonesty } from "../neutral/errors.js";
4
+ import { queryFilterFields, toQuery } from "../neutral/query.js";
5
+ import { resolveSource, sourceRefFields } from "../neutral/source-ref.js";
6
+ /**
7
+ * `honua_get_extent` — protocol-neutral bounding box (#1005).
8
+ *
9
+ * Runs the canonical `Source.queryExtent()`. Protocols with no server-side
10
+ * extent operation (OGC API Features, STAC, OData) answer from the collection's
11
+ * declared extent under the degraded capability policy, and say so; protocols
12
+ * that cannot answer at all refuse with a structured capability error.
13
+ */
3
14
  export const schema = z.object({
4
- serviceId: z.string().describe("The feature service ID"),
5
- layerId: z.number().int().nonnegative().describe("The layer ID within the service"),
6
- where: z.string().optional().describe("SQL WHERE clause"),
7
- geometry: z.record(z.unknown()).optional().describe("Esri JSON geometry for spatial filter"),
8
- geometryType: z
9
- .enum(GEOMETRY_TYPES)
10
- .optional()
11
- .describe("Esri geometry type. If omitted, inferred from geometry when possible."),
12
- spatialRel: z
13
- .enum(["intersects", "contains", "within"])
14
- .optional()
15
- .describe("Spatial relationship (default: intersects)"),
15
+ ...sourceRefFields,
16
+ ...queryFilterFields,
16
17
  });
17
18
  export async function execute(client, input) {
18
- const response = (await client.queryFeatures({
19
- serviceId: input.serviceId,
20
- layerId: input.layerId,
21
- where: input.where,
22
- geometry: input.geometry,
23
- geometryType: resolveGeometryType(input.geometry, input.geometryType),
24
- spatialRel: mapSpatialRel(input.spatialRel),
25
- returnGeometry: false,
26
- extraParams: { returnExtentOnly: true },
27
- }));
28
- // Accept large counts returned as strings by the grpc-web transport (chosen
29
- // to preserve precision beyond Number.MAX_SAFE_INTEGER) as well as numbers.
30
- const count = coerceCount(response.count);
31
- const hasExtent = Object.prototype.hasOwnProperty.call(response, "extent");
32
- if (!hasExtent) {
33
- // Some backends can return count-only payloads for empty extent queries.
34
- if (count !== undefined) {
35
- return jsonText({ extent: null, count });
36
- }
37
- throw new Error("Extent query did not return an extent payload.");
38
- }
39
- let extent;
40
- if (response.extent === null) {
41
- extent = null;
42
- }
43
- else if (typeof response.extent === "object" && !Array.isArray(response.extent)) {
44
- // A JSON array is `typeof "object"` but is not a valid extent envelope.
45
- extent = response.extent;
46
- }
47
- else {
48
- throw new Error("Extent query returned an invalid extent payload.");
49
- }
50
- return jsonText({ extent, count });
19
+ return withCapabilityHonesty(async () => {
20
+ const resolved = resolveSource(client, input);
21
+ const query = toQuery(input, { protocol: resolved.descriptor.protocol, paginate: false });
22
+ const { extent, count } = await resolved.source.queryExtent(query);
23
+ return jsonText({
24
+ source: resolved.ref.ref,
25
+ protocol: resolved.descriptor.protocol,
26
+ extent: extent ?? null,
27
+ count: count ?? null,
28
+ // An extent that did not come from a server-side extent operation is an
29
+ // honest, but weaker, answer; name the capability so the caller can tell.
30
+ extentCapability: resolved.source.capabilities.has("queryExtent") ? "server" : "declared-extent",
31
+ });
32
+ });
51
33
  }
@@ -1,5 +1,12 @@
1
1
  import type { HonuaClient } from "@honua/sdk-js";
2
2
  import { z } from "zod";
3
+ /**
4
+ * `honua_list_services` — the GeoServices-specific service catalog.
5
+ *
6
+ * Retained for existing clients; `honua_list_sources` is the protocol-neutral
7
+ * replacement (#1005), because "what services are under /rest/services?" is a
8
+ * question only an Esri endpoint can answer.
9
+ */
3
10
  export declare const schema: z.ZodObject<{
4
11
  includeDetails: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
5
12
  }, "strip", z.ZodTypeAny, {
@@ -1 +1 @@
1
- {"version":3,"file":"list-services.d.ts","sourceRoot":"","sources":["../../../src/tools/list-services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,MAAM;;;;;;EAQjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAI3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GA+C9D"}
1
+ {"version":3,"file":"list-services.d.ts","sourceRoot":"","sources":["../../../src/tools/list-services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;GAMG;AACH,eAAO,MAAM,MAAM;;;;;;EAQjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAI3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GA+C9D"}
@@ -1,5 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { jsonText, mapWithConcurrency, metadataErrorText } from "../helpers.js";
3
+ /**
4
+ * `honua_list_services` — the GeoServices-specific service catalog.
5
+ *
6
+ * Retained for existing clients; `honua_list_sources` is the protocol-neutral
7
+ * replacement (#1005), because "what services are under /rest/services?" is a
8
+ * question only an Esri endpoint can answer.
9
+ */
3
10
  export const schema = z.object({
4
11
  includeDetails: z
5
12
  .boolean()
@@ -22,7 +29,7 @@ export async function execute(client, input) {
22
29
  catalogAvailable: false,
23
30
  services: [],
24
31
  reason: `service catalog is not available on this target: ${err instanceof Error ? err.message : String(err)}`,
25
- guidance: "No /rest/services directory here. Call honua_describe_layer / honua_query_features with a known serviceId and layerId, or point the base URL at an ArcGIS folder that lists services.",
32
+ guidance: "No /rest/services directory here — this tool only sees a GeoServices catalog. Call honua_list_sources, which probes every protocol family this endpoint may publish (OGC API Features included) and returns protocol-neutral source references.",
26
33
  });
27
34
  }
28
35
  const services = (response.services ?? []).filter((s) => s.type === "FeatureServer");
@@ -0,0 +1,44 @@
1
+ import type { HonuaClient } from "@honua/sdk-js";
2
+ import { z } from "zod";
3
+ /**
4
+ * `honua_list_sources` — protocol-neutral discovery (#1005).
5
+ *
6
+ * `honua_list_services` answers a GeoServices-only question ("what is in
7
+ * `/rest/services`?"). This tool answers the neutral one — "what sources can I
8
+ * query here, and how do I address them?" — and emits the exact
9
+ * `<protocol>:<address>` references every other tool accepts, so an agent never
10
+ * has to construct one.
11
+ *
12
+ * Each protocol family is probed independently and degrades on its own: an
13
+ * endpoint that serves OGC collections but has no `/rest/services` directory
14
+ * reports the GeoServices family as unavailable-with-reason and still returns
15
+ * its OGC sources. A family that is absent is never reported as "no sources".
16
+ */
17
+ export declare const schema: z.ZodObject<{
18
+ protocol: z.ZodDefault<z.ZodOptional<z.ZodEnum<["auto", "geoservices", "ogc-features", "ogc-records"]>>>;
19
+ layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
20
+ maxServices: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ protocol: "ogc-features" | "ogc-records" | "geoservices" | "auto";
23
+ maxServices: number;
24
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
25
+ }, {
26
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
27
+ protocol?: "ogc-features" | "ogc-records" | "geoservices" | "auto" | undefined;
28
+ maxServices?: number | undefined;
29
+ }>;
30
+ export type Input = z.infer<typeof schema>;
31
+ export declare function execute(client: HonuaClient, input: Input): Promise<{
32
+ content: Array<{
33
+ type: "text";
34
+ text: string;
35
+ }>;
36
+ } | {
37
+ structuredContent: Record<string, unknown>;
38
+ isError: true;
39
+ content: Array<{
40
+ type: "text";
41
+ text: string;
42
+ }>;
43
+ }>;
44
+ //# sourceMappingURL=list-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-sources.d.ts","sourceRoot":"","sources":["../../../src/tools/list-sources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;EAoBjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AA6F3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;;GAkC9D"}
@@ -0,0 +1,138 @@
1
+ import { PROTOCOL_DEFAULT_CAPABILITIES } from "@honua/sdk-js/contract";
2
+ import { z } from "zod";
3
+ import { jsonText, mapWithConcurrency } from "../helpers.js";
4
+ import { withCapabilityHonesty } from "../neutral/errors.js";
5
+ import { SOURCE_LAYOUTS, ogcLayoutFor } from "../neutral/source-ref.js";
6
+ /**
7
+ * `honua_list_sources` — protocol-neutral discovery (#1005).
8
+ *
9
+ * `honua_list_services` answers a GeoServices-only question ("what is in
10
+ * `/rest/services`?"). This tool answers the neutral one — "what sources can I
11
+ * query here, and how do I address them?" — and emits the exact
12
+ * `<protocol>:<address>` references every other tool accepts, so an agent never
13
+ * has to construct one.
14
+ *
15
+ * Each protocol family is probed independently and degrades on its own: an
16
+ * endpoint that serves OGC collections but has no `/rest/services` directory
17
+ * reports the GeoServices family as unavailable-with-reason and still returns
18
+ * its OGC sources. A family that is absent is never reported as "no sources".
19
+ */
20
+ export const schema = z.object({
21
+ protocol: z
22
+ .enum(["auto", "geoservices", "ogc-features", "ogc-records"])
23
+ .optional()
24
+ .default("auto")
25
+ .describe("Restrict discovery to one protocol family. `auto` (default) probes every family this server can list."),
26
+ layout: z
27
+ .enum(SOURCE_LAYOUTS)
28
+ .optional()
29
+ .describe('Endpoint layout for the OGC families: "honua-facade" (default) or "ogc-api"/"auto" for a third-party OGC API root.'),
30
+ maxServices: z
31
+ .number()
32
+ .int()
33
+ .positive()
34
+ .max(200)
35
+ .optional()
36
+ .default(25)
37
+ .describe("Maximum GeoServices services expanded into per-layer sources (each costs one metadata request)."),
38
+ });
39
+ const METADATA_CONCURRENCY = 8;
40
+ function capabilitiesFor(protocol) {
41
+ return [...PROTOCOL_DEFAULT_CAPABILITIES[protocol]].sort();
42
+ }
43
+ async function discoverGeoServices(client, maxServices) {
44
+ let response;
45
+ try {
46
+ response = await client.listServices();
47
+ }
48
+ catch (err) {
49
+ return {
50
+ available: false,
51
+ reason: `no GeoServices catalog on this target: ${err instanceof Error ? err.message : String(err)}`,
52
+ sources: [],
53
+ };
54
+ }
55
+ const services = (response.services ?? []).filter((s) => s.type === "FeatureServer" || s.type === "MapServer");
56
+ const expanded = services.slice(0, maxServices);
57
+ const perService = await mapWithConcurrency(expanded, METADATA_CONCURRENCY, async (service) => {
58
+ const protocol = service.type === "MapServer" ? "geoservices-map-service" : "geoservices-feature-service";
59
+ try {
60
+ const meta = await client.getFeatureServiceMetadata(service.name);
61
+ const layers = [...(meta.layers ?? []), ...(meta.tables ?? [])];
62
+ return layers.map((layer) => ({
63
+ source: `${protocol}:${service.name}/${layer.id}`,
64
+ protocol,
65
+ title: layer.name ?? null,
66
+ capabilities: capabilitiesFor(protocol),
67
+ }));
68
+ }
69
+ catch {
70
+ // The service exists but its metadata is unreadable; still emit the
71
+ // conventional layer-0 reference rather than dropping the service.
72
+ return [
73
+ {
74
+ source: `${protocol}:${service.name}/0`,
75
+ protocol,
76
+ title: service.name,
77
+ capabilities: capabilitiesFor(protocol),
78
+ },
79
+ ];
80
+ }
81
+ });
82
+ return { available: true, sources: perService.flat() };
83
+ }
84
+ async function discoverOgc(client, family, layout) {
85
+ try {
86
+ const resolved = await ogcLayoutFor(client, layout);
87
+ const request = resolved ? { layout: resolved } : {};
88
+ const response = family === "ogc-records"
89
+ ? await client.listOgcRecordCollections(request)
90
+ : await client.listOgcCollections(request);
91
+ return {
92
+ available: true,
93
+ sources: (response.collections ?? []).map((collection) => ({
94
+ source: `${family}:${collection.id}`,
95
+ protocol: family,
96
+ title: collection.title ?? collection.id,
97
+ capabilities: capabilitiesFor(family),
98
+ })),
99
+ };
100
+ }
101
+ catch (err) {
102
+ return {
103
+ available: false,
104
+ reason: `no ${family} collections on this target: ${err instanceof Error ? err.message : String(err)}`,
105
+ sources: [],
106
+ };
107
+ }
108
+ }
109
+ export async function execute(client, input) {
110
+ return withCapabilityHonesty(async () => {
111
+ const families = {};
112
+ if (input.protocol === "auto" || input.protocol === "geoservices") {
113
+ families.geoservices = await discoverGeoServices(client, input.maxServices);
114
+ }
115
+ if (input.protocol === "auto" || input.protocol === "ogc-features") {
116
+ families["ogc-features"] = await discoverOgc(client, "ogc-features", input.layout);
117
+ }
118
+ if (input.protocol === "ogc-records") {
119
+ families["ogc-records"] = await discoverOgc(client, "ogc-records", input.layout);
120
+ }
121
+ const sources = Object.values(families).flatMap((family) => family.sources);
122
+ return jsonText({
123
+ sources,
124
+ sourceCount: sources.length,
125
+ families: Object.fromEntries(Object.entries(families).map(([name, outcome]) => [
126
+ name,
127
+ {
128
+ available: outcome.available,
129
+ ...(outcome.reason ? { reason: outcome.reason } : {}),
130
+ count: outcome.sources.length,
131
+ },
132
+ ])),
133
+ guidance: sources.length > 0
134
+ ? "Pass a `source` value verbatim to honua_describe_layer / honua_query_features / honua_count_features / honua_get_extent / honua_statistics."
135
+ : 'No source catalog is published here. Address a known source directly, e.g. source="ogc-features:<collectionId>", "stac:<collectionId>", "wfs:<typeName>", "odata:<entitySet>", or "geoservices-feature-service:<serviceId>/<layerId>".',
136
+ });
137
+ });
138
+ }
@@ -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
- geometry: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
9
- geometryType: z.ZodOptional<z.ZodEnum<["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"]>>;
10
- spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within"]>>;
11
- orderBy: z.ZodOptional<z.ZodString>;
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
- where?: string | undefined;
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
- spatialRel?: "intersects" | "contains" | "within" | undefined;
23
- outFields?: string[] | undefined;
24
- orderBy?: string | undefined;
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
- serviceId: string;
29
- layerId: number;
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
- spatialRel?: "intersects" | "contains" | "within" | undefined;
34
- outFields?: string[] | undefined;
35
- orderBy?: string | undefined;
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;AAGxB,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBjB,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;;;;;GA+B9D"}
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"}