@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
@@ -0,0 +1,30 @@
1
+ import { type LocalInstallRuntime } from "@honua/sdk-js/local-install";
2
+ import { z } from "zod";
3
+ export declare const schema: z.ZodObject<{
4
+ directory: z.ZodString;
5
+ profile: z.ZodDefault<z.ZodEnum<["quickstart", "gp-dev"]>>;
6
+ httpPort: z.ZodDefault<z.ZodNumber>;
7
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
8
+ confirm: z.ZodLiteral<true>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ directory: string;
11
+ profile: "quickstart" | "gp-dev";
12
+ httpPort: number;
13
+ timeoutMs: number;
14
+ confirm: true;
15
+ }, {
16
+ directory: string;
17
+ confirm: true;
18
+ profile?: "quickstart" | "gp-dev" | undefined;
19
+ httpPort?: number | undefined;
20
+ timeoutMs?: number | undefined;
21
+ }>;
22
+ export type AdminInstallLocalInput = z.infer<typeof schema>;
23
+ export declare function execute(input: AdminInstallLocalInput, runtime?: LocalInstallRuntime): Promise<{
24
+ content: {
25
+ type: "text";
26
+ text: string;
27
+ }[];
28
+ structuredContent: Record<string, unknown>;
29
+ }>;
30
+ //# sourceMappingURL=admin-install-local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-install-local.d.ts","sourceRoot":"","sources":["../../../src/tools/admin-install-local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAqB,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;EAMjB,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE5D,wBAAsB,OAAO,CAAC,KAAK,EAAE,sBAAsB,EAAE,OAAO,GAAE,mBAAwB;;;;;;GAe7F"}
@@ -0,0 +1,22 @@
1
+ import { installHonuaLocal } from "@honua/sdk-js/local-install";
2
+ import { z } from "zod";
3
+ export const schema = z.object({
4
+ directory: z.string().min(1).describe("Directory that will own compose.yaml, .env, and MCP configuration."),
5
+ profile: z.enum(["quickstart", "gp-dev"]).default("quickstart"),
6
+ httpPort: z.number().int().min(1).max(65535).default(8080),
7
+ timeoutMs: z.number().int().min(1_000).max(600_000).default(180_000),
8
+ confirm: z.literal(true).describe("Explicit approval to create files and start Docker containers."),
9
+ });
10
+ export async function execute(input, runtime = {}) {
11
+ const result = await installHonuaLocal({
12
+ directory: input.directory,
13
+ profile: input.profile,
14
+ httpPort: input.httpPort,
15
+ timeoutMs: input.timeoutMs,
16
+ }, runtime);
17
+ const structuredContent = { ...result };
18
+ return {
19
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
20
+ structuredContent,
21
+ };
22
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The MCP transport adapter for the shared control-plane command layer.
3
+ *
4
+ * Every tool registered here is *generated* from `HONUA_COMMANDS` in
5
+ * `@honua/sdk-js/control-plane`: the name, title, description, and argument
6
+ * schema are projections of the command's own declaration, and the handler
7
+ * does exactly three things — build a `HonuaCommandInvocation`, call
8
+ * `HonuaCommandRuntime.execute`, and render the returned receipt. There is no
9
+ * MCP-side sequencing, no MCP-side validation beyond the projected schema, and
10
+ * no MCP-side authorization: an equivalent `honua map publish` invocation, an
11
+ * equivalent Studio invocation, and a direct `runtime.execute(...)` call all
12
+ * produce the same receipt and the same `auditKey`, differing only in the
13
+ * recorded `transport`.
14
+ *
15
+ * ## Why the projected object is `passthrough`
16
+ *
17
+ * A Zod object strips unknown keys by default. Stripping here would mean an
18
+ * agent that sent `approvedBy` got a *silent success* instead of the shared
19
+ * typed refusal, and MCP would then be the one transport where a sealed input
20
+ * schema is enforced by omission rather than by the command. So the projection
21
+ * lets unknown keys through to the runtime, where the command's closed schema
22
+ * (`additionalProperties: false`) rejects them with the same `validation`
23
+ * `HonuaCommandError` every other transport gets.
24
+ *
25
+ * ## What an agent may not assert
26
+ *
27
+ * The tool arguments carry no identity fields and no request headers. Acting
28
+ * identity and tenant come from the host that constructed the runtime — a
29
+ * model-supplied `actor` would be an authority claim dressed as an argument.
30
+ * `Idempotency-Key` and `If-Match` are likewise not header inputs: `ifMatch`
31
+ * and `idempotencyKey` travel as invocation fields so the value on the wire is
32
+ * the value the receipt records.
33
+ *
34
+ * @module
35
+ */
36
+ import { type HonuaAnyCommand, type HonuaCommandIdentity, type HonuaCommandRuntime } from "@honua/sdk-js/control-plane";
37
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
38
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
39
+ import { z } from "zod";
40
+ /** Prefix every generated command tool carries. */
41
+ export declare const CONTROL_PLANE_COMMAND_TOOL_PREFIX = "honua_command_";
42
+ /**
43
+ * Host wiring for the command tools.
44
+ *
45
+ * The host owns the credential (through the runtime's client) and the acting
46
+ * identity echo. Neither is reachable from tool arguments.
47
+ */
48
+ export interface ControlPlaneCommandMcpHost {
49
+ /** Runtime built from the caller's own `HonuaClient`; never a shared admin key. */
50
+ readonly runtime: HonuaCommandRuntime;
51
+ /** Acting identity echoed onto every receipt. Host-supplied, never model-supplied. */
52
+ readonly identity?: HonuaCommandIdentity;
53
+ }
54
+ /** `map-package.publish` → `honua_command_map_package_publish`. */
55
+ export declare function controlPlaneCommandToolName(commandId: string): string;
56
+ /**
57
+ * Project a command's JSON-schema input into the tool's argument schema.
58
+ *
59
+ * `input` is the command's own declared shape; the three siblings are the
60
+ * invocation fields a caller is allowed to set. The outer object is `strict`
61
+ * so an unknown *invocation* field is an MCP-level mistake, while the inner
62
+ * object is `passthrough` so an unknown *input* field reaches the command's
63
+ * sealed schema and is refused there.
64
+ */
65
+ export declare function controlPlaneCommandToolSchema(command: HonuaAnyCommand): z.ZodObject<{
66
+ input: z.ZodTypeAny;
67
+ dryRun: z.ZodOptional<z.ZodBoolean>;
68
+ idempotencyKey: z.ZodOptional<z.ZodString>;
69
+ ifMatch: z.ZodOptional<z.ZodString>;
70
+ }, "strict", z.ZodTypeAny, {
71
+ input?: any;
72
+ dryRun?: boolean | undefined;
73
+ idempotencyKey?: string | undefined;
74
+ ifMatch?: string | undefined;
75
+ }, {
76
+ input?: any;
77
+ dryRun?: boolean | undefined;
78
+ idempotencyKey?: string | undefined;
79
+ ifMatch?: string | undefined;
80
+ }>;
81
+ type ControlPlaneCommandToolArgs = z.infer<ReturnType<typeof controlPlaneCommandToolSchema>>;
82
+ /**
83
+ * Run one command for the MCP transport and render its receipt.
84
+ *
85
+ * A {@link HonuaCommandError} is returned as a tool error carrying the shared
86
+ * serialized taxonomy, not rethrown as an opaque MCP failure: an agent has to
87
+ * be able to tell a `validation` refusal from a `conflict` it should retry.
88
+ */
89
+ export declare function execute(host: ControlPlaneCommandMcpHost, command: HonuaAnyCommand, args: ControlPlaneCommandToolArgs): Promise<CallToolResult>;
90
+ /**
91
+ * Register one MCP tool per catalog command.
92
+ *
93
+ * Opt-in, exactly like the NL map-control tools: the platform-free standalone
94
+ * catalog stays read-only and Honua-server-free unless a host supplies a
95
+ * control-plane runtime.
96
+ */
97
+ export declare function registerControlPlaneCommandTools(server: McpServer, host: ControlPlaneCommandMcpHost): void;
98
+ export {};
99
+ //# sourceMappingURL=control-plane-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-plane-command.d.ts","sourceRoot":"","sources":["../../../src/tools/control-plane-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAEL,KAAK,eAAe,EAEpB,KAAK,oBAAoB,EAGzB,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,mDAAmD;AACnD,eAAO,MAAM,iCAAiC,mBAAmB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CAC1C;AAED,mEAAmE;AACnE,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,eAAe;;;;;;;;;;;;;;;GAgBrE;AAED,KAAK,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,6BAA6B,CAAC,CAAC,CAAC;AAE7F;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,0BAA0B,EAChC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,cAAc,CAAC,CAsBzB;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,0BAA0B,GAAG,IAAI,CAkB1G"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * The MCP transport adapter for the shared control-plane command layer.
3
+ *
4
+ * Every tool registered here is *generated* from `HONUA_COMMANDS` in
5
+ * `@honua/sdk-js/control-plane`: the name, title, description, and argument
6
+ * schema are projections of the command's own declaration, and the handler
7
+ * does exactly three things — build a `HonuaCommandInvocation`, call
8
+ * `HonuaCommandRuntime.execute`, and render the returned receipt. There is no
9
+ * MCP-side sequencing, no MCP-side validation beyond the projected schema, and
10
+ * no MCP-side authorization: an equivalent `honua map publish` invocation, an
11
+ * equivalent Studio invocation, and a direct `runtime.execute(...)` call all
12
+ * produce the same receipt and the same `auditKey`, differing only in the
13
+ * recorded `transport`.
14
+ *
15
+ * ## Why the projected object is `passthrough`
16
+ *
17
+ * A Zod object strips unknown keys by default. Stripping here would mean an
18
+ * agent that sent `approvedBy` got a *silent success* instead of the shared
19
+ * typed refusal, and MCP would then be the one transport where a sealed input
20
+ * schema is enforced by omission rather than by the command. So the projection
21
+ * lets unknown keys through to the runtime, where the command's closed schema
22
+ * (`additionalProperties: false`) rejects them with the same `validation`
23
+ * `HonuaCommandError` every other transport gets.
24
+ *
25
+ * ## What an agent may not assert
26
+ *
27
+ * The tool arguments carry no identity fields and no request headers. Acting
28
+ * identity and tenant come from the host that constructed the runtime — a
29
+ * model-supplied `actor` would be an authority claim dressed as an argument.
30
+ * `Idempotency-Key` and `If-Match` are likewise not header inputs: `ifMatch`
31
+ * and `idempotencyKey` travel as invocation fields so the value on the wire is
32
+ * the value the receipt records.
33
+ *
34
+ * @module
35
+ */
36
+ import { HONUA_COMMANDS, HonuaCommandError, } from "@honua/sdk-js/control-plane";
37
+ import { z } from "zod";
38
+ /** Prefix every generated command tool carries. */
39
+ export const CONTROL_PLANE_COMMAND_TOOL_PREFIX = "honua_command_";
40
+ /** `map-package.publish` → `honua_command_map_package_publish`. */
41
+ export function controlPlaneCommandToolName(commandId) {
42
+ return `${CONTROL_PLANE_COMMAND_TOOL_PREFIX}${commandId.replace(/[.-]/g, "_")}`;
43
+ }
44
+ /**
45
+ * Project a command's JSON-schema input into the tool's argument schema.
46
+ *
47
+ * `input` is the command's own declared shape; the three siblings are the
48
+ * invocation fields a caller is allowed to set. The outer object is `strict`
49
+ * so an unknown *invocation* field is an MCP-level mistake, while the inner
50
+ * object is `passthrough` so an unknown *input* field reaches the command's
51
+ * sealed schema and is refused there.
52
+ */
53
+ export function controlPlaneCommandToolSchema(command) {
54
+ return z
55
+ .object({
56
+ input: zodForSchema(command.inputSchema),
57
+ dryRun: z
58
+ .boolean()
59
+ .optional()
60
+ .describe("Preview the plan without contacting the server. The command never reaches `execute`."),
61
+ idempotencyKey: z
62
+ .string()
63
+ .min(1)
64
+ .optional()
65
+ .describe("Explicit Idempotency-Key. Derived deterministically from the input when omitted."),
66
+ ifMatch: z.string().min(1).optional().describe("Optimistic-concurrency validator, sent as If-Match."),
67
+ })
68
+ .strict();
69
+ }
70
+ /**
71
+ * Run one command for the MCP transport and render its receipt.
72
+ *
73
+ * A {@link HonuaCommandError} is returned as a tool error carrying the shared
74
+ * serialized taxonomy, not rethrown as an opaque MCP failure: an agent has to
75
+ * be able to tell a `validation` refusal from a `conflict` it should retry.
76
+ */
77
+ export async function execute(host, command, args) {
78
+ const invocation = {
79
+ transport: "mcp",
80
+ ...(host.identity ? { identity: host.identity } : {}),
81
+ ...(args.idempotencyKey ? { idempotencyKey: args.idempotencyKey } : {}),
82
+ ...(args.ifMatch ? { ifMatch: args.ifMatch } : {}),
83
+ ...(args.dryRun ? { dryRun: true } : {}),
84
+ };
85
+ try {
86
+ const receipt = await host.runtime.execute(command, args.input, invocation);
87
+ return { content: [{ type: "text", text: JSON.stringify(receipt, null, 2) }] };
88
+ }
89
+ catch (error) {
90
+ if (error instanceof HonuaCommandError) {
91
+ return {
92
+ isError: true,
93
+ content: [
94
+ { type: "text", text: JSON.stringify({ ...error.toJSON(), message: error.message }, null, 2) },
95
+ ],
96
+ };
97
+ }
98
+ throw error;
99
+ }
100
+ }
101
+ /**
102
+ * Register one MCP tool per catalog command.
103
+ *
104
+ * Opt-in, exactly like the NL map-control tools: the platform-free standalone
105
+ * catalog stays read-only and Honua-server-free unless a host supplies a
106
+ * control-plane runtime.
107
+ */
108
+ export function registerControlPlaneCommandTools(server, host) {
109
+ for (const command of Object.values(HONUA_COMMANDS)) {
110
+ server.registerTool(controlPlaneCommandToolName(command.id), {
111
+ title: command.title,
112
+ description: `${command.description} Runs the shared \`${command.id}\` control-plane command and returns its deterministic receipt; the receipt's auditKey matches the equivalent CLI, Studio, or direct-JS invocation.`,
113
+ inputSchema: controlPlaneCommandToolSchema(command),
114
+ annotations: {
115
+ readOnlyHint: command.mode === "read",
116
+ destructiveHint: false,
117
+ idempotentHint: true,
118
+ openWorldHint: true,
119
+ },
120
+ }, async (args) => execute(host, command, args));
121
+ }
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // JSON Schema → Zod projection
125
+ // ---------------------------------------------------------------------------
126
+ function zodForSchema(schema) {
127
+ const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
128
+ let base;
129
+ switch (type) {
130
+ case "string": {
131
+ let value = z.string();
132
+ if (schema.minLength !== undefined)
133
+ value = value.min(schema.minLength);
134
+ if (schema.maxLength !== undefined)
135
+ value = value.max(schema.maxLength);
136
+ base = value;
137
+ break;
138
+ }
139
+ case "integer":
140
+ case "number": {
141
+ let value = z.number();
142
+ if (type === "integer")
143
+ value = value.int();
144
+ if (schema.minimum !== undefined)
145
+ value = value.min(schema.minimum);
146
+ if (schema.maximum !== undefined)
147
+ value = value.max(schema.maximum);
148
+ base = value;
149
+ break;
150
+ }
151
+ case "boolean":
152
+ base = z.boolean();
153
+ break;
154
+ case "array":
155
+ base = z.array(schema.items ? zodForSchema(schema.items) : z.unknown());
156
+ break;
157
+ case "object":
158
+ base = zodForObject(schema);
159
+ break;
160
+ default:
161
+ base = z.unknown();
162
+ break;
163
+ }
164
+ return schema.description ? base.describe(schema.description) : base;
165
+ }
166
+ function zodForObject(schema) {
167
+ const properties = schema.properties;
168
+ // An object with no declared properties (`options` on `import.create`) is a
169
+ // free-form bag the command passes through verbatim.
170
+ if (!properties)
171
+ return z.record(z.unknown());
172
+ const required = new Set(schema.required ?? []);
173
+ const shape = {};
174
+ for (const [name, property] of Object.entries(properties)) {
175
+ const value = zodForSchema(property);
176
+ shape[name] = required.has(name) ? value : value.optional();
177
+ }
178
+ // Passthrough is deliberate — see this module's header.
179
+ return z.object(shape).passthrough();
180
+ }
@@ -1,32 +1,106 @@
1
1
  import type { HonuaClient } from "@honua/sdk-js";
2
+ import type { CapabilityAwareSource, Query } from "@honua/sdk-js/contract";
2
3
  import { z } from "zod";
4
+ import { projectDegraded } from "../neutral/result.js";
5
+ /**
6
+ * `honua_count_features` — protocol-neutral cardinality check (#1005).
7
+ *
8
+ * There is no single "count" verb across the protocols, so the count is taken
9
+ * from whichever canonical path the source's declared capabilities support:
10
+ *
11
+ * 1. `queryExtent` (GeoServices, WFS, gRPC) returns an exact server count
12
+ * alongside the envelope in one round trip.
13
+ * 2. otherwise a single-record `query` reports `totalCount`, which is where
14
+ * OGC API Features / STAC / OData carry `numberMatched`.
15
+ *
16
+ * When neither path yields a number the tool refuses with a structured
17
+ * capability error rather than reporting `0`.
18
+ */
3
19
  export declare const schema: z.ZodObject<{
4
- serviceId: z.ZodString;
5
- layerId: z.ZodNumber;
20
+ filter: z.ZodOptional<z.ZodType<import("../neutral/filter.js").FilterNode, z.ZodTypeDef, import("../neutral/filter.js").FilterNode>>;
6
21
  where: z.ZodOptional<z.ZodString>;
22
+ bbox: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
23
+ bboxSrid: z.ZodOptional<z.ZodNumber>;
7
24
  geometry: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
25
  geometryType: z.ZodOptional<z.ZodEnum<["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"]>>;
9
- spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within"]>>;
26
+ spatialRel: z.ZodOptional<z.ZodEnum<["intersects", "contains", "within", "crosses", "touches", "overlaps", "bbox-intersects"]>>;
27
+ temporal: z.ZodOptional<z.ZodObject<{
28
+ instant: z.ZodOptional<z.ZodString>;
29
+ start: z.ZodOptional<z.ZodNullable<z.ZodString>>;
30
+ end: z.ZodOptional<z.ZodNullable<z.ZodString>>;
31
+ field: z.ZodOptional<z.ZodString>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ field?: string | undefined;
34
+ instant?: string | undefined;
35
+ start?: string | null | undefined;
36
+ end?: string | null | undefined;
37
+ }, {
38
+ field?: string | undefined;
39
+ instant?: string | undefined;
40
+ start?: string | null | undefined;
41
+ end?: string | null | undefined;
42
+ }>>;
43
+ source: z.ZodOptional<z.ZodString>;
44
+ layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
45
+ serviceId: z.ZodOptional<z.ZodString>;
46
+ layerId: z.ZodOptional<z.ZodNumber>;
10
47
  }, "strip", z.ZodTypeAny, {
11
- serviceId: string;
12
- layerId: number;
13
- where?: string | undefined;
48
+ filter?: import("../neutral/filter.js").FilterNode | undefined;
49
+ serviceId?: string | undefined;
14
50
  geometry?: Record<string, unknown> | undefined;
51
+ bbox?: number[] | undefined;
15
52
  geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
16
- spatialRel?: "intersects" | "contains" | "within" | undefined;
17
- }, {
18
- serviceId: string;
19
- layerId: number;
53
+ temporal?: {
54
+ field?: string | undefined;
55
+ instant?: string | undefined;
56
+ start?: string | null | undefined;
57
+ end?: string | null | undefined;
58
+ } | undefined;
59
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
60
+ layerId?: number | undefined;
61
+ source?: string | undefined;
20
62
  where?: string | undefined;
63
+ bboxSrid?: number | undefined;
64
+ spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
65
+ }, {
66
+ filter?: import("../neutral/filter.js").FilterNode | undefined;
67
+ serviceId?: string | undefined;
21
68
  geometry?: Record<string, unknown> | undefined;
69
+ bbox?: number[] | undefined;
22
70
  geometryType?: "esriGeometryPoint" | "esriGeometryPolyline" | "esriGeometryPolygon" | "esriGeometryEnvelope" | "esriGeometryMultipoint" | undefined;
23
- spatialRel?: "intersects" | "contains" | "within" | undefined;
71
+ temporal?: {
72
+ field?: string | undefined;
73
+ instant?: string | undefined;
74
+ start?: string | null | undefined;
75
+ end?: string | null | undefined;
76
+ } | undefined;
77
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
78
+ layerId?: number | undefined;
79
+ source?: string | undefined;
80
+ where?: string | undefined;
81
+ bboxSrid?: number | undefined;
82
+ spatialRel?: "intersects" | "contains" | "within" | "crosses" | "touches" | "overlaps" | "bbox-intersects" | undefined;
24
83
  }>;
25
84
  export type Input = z.infer<typeof schema>;
85
+ interface CountOutcome {
86
+ count: number;
87
+ strategy: "queryExtent" | "totalCount";
88
+ degraded: ReturnType<typeof projectDegraded>;
89
+ }
90
+ /** Resolve a feature count through the canonical `Source` surface. */
91
+ export declare function countThroughSource(source: CapabilityAwareSource, query: Query, protocol: string, sourceId: string): Promise<CountOutcome>;
26
92
  export declare function execute(client: HonuaClient, input: Input): Promise<{
27
93
  content: Array<{
28
94
  type: "text";
29
95
  text: string;
30
96
  }>;
97
+ } | {
98
+ structuredContent: Record<string, unknown>;
99
+ isError: true;
100
+ content: Array<{
101
+ type: "text";
102
+ text: string;
103
+ }>;
31
104
  }>;
105
+ export {};
32
106
  //# sourceMappingURL=count-features.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"count-features.d.ts","sourceRoot":"","sources":["../../../src/tools/count-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;;;;;;;;;;;;;;;;;;;;;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;;;;;GAqB9D"}
1
+ {"version":3,"file":"count-features.d.ts","sourceRoot":"","sources":["../../../src/tools/count-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,GAAG,YAAY,CAAC;IACvC,QAAQ,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;CAC9C;AAED,sEAAsE;AACtE,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CAevB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;;GAmB9D"}
@@ -1,36 +1,54 @@
1
+ import { HonuaCapabilityNotSupportedError } from "@honua/sdk-js";
1
2
  import { z } from "zod";
2
- import { GEOMETRY_TYPES, coerceCount, jsonText, mapSpatialRel, resolveGeometryType } from "../helpers.js";
3
+ import { jsonText } from "../helpers.js";
4
+ import { withCapabilityHonesty } from "../neutral/errors.js";
5
+ import { queryFilterFields, toQuery } from "../neutral/query.js";
6
+ import { projectDegraded } from "../neutral/result.js";
7
+ import { resolveSource, sourceRefFields } from "../neutral/source-ref.js";
8
+ /**
9
+ * `honua_count_features` — protocol-neutral cardinality check (#1005).
10
+ *
11
+ * There is no single "count" verb across the protocols, so the count is taken
12
+ * from whichever canonical path the source's declared capabilities support:
13
+ *
14
+ * 1. `queryExtent` (GeoServices, WFS, gRPC) returns an exact server count
15
+ * alongside the envelope in one round trip.
16
+ * 2. otherwise a single-record `query` reports `totalCount`, which is where
17
+ * OGC API Features / STAC / OData carry `numberMatched`.
18
+ *
19
+ * When neither path yields a number the tool refuses with a structured
20
+ * capability error rather than reporting `0`.
21
+ */
3
22
  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, e.g. \"status = 'active'\""),
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)"),
23
+ ...sourceRefFields,
24
+ ...queryFilterFields,
16
25
  });
17
- 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
- outFields: "OBJECTID",
27
- extraParams: { returnCountOnly: true },
28
- }));
29
- // The default grpc-web transport returns very large counts as a string to
30
- // preserve precision beyond Number.MAX_SAFE_INTEGER; accept both forms.
31
- const count = coerceCount(response.count);
32
- if (count === undefined) {
33
- throw new Error("Count query did not return a numeric count.");
26
+ /** Resolve a feature count through the canonical `Source` surface. */
27
+ export async function countThroughSource(source, query, protocol, sourceId) {
28
+ if (source.capabilities.has("queryExtent")) {
29
+ const extent = await source.queryExtent(query);
30
+ if (typeof extent.count === "number" && Number.isFinite(extent.count)) {
31
+ return { count: extent.count, strategy: "queryExtent", degraded: undefined };
32
+ }
33
+ }
34
+ const probe = { ...query, returnGeometry: false, pagination: { ...query.pagination, limit: 1 } };
35
+ const result = await source.query(probe);
36
+ if (typeof result.totalCount === "number" && Number.isFinite(result.totalCount)) {
37
+ return { count: result.totalCount, strategy: "totalCount", degraded: projectDegraded(result.degraded) };
34
38
  }
35
- return jsonText({ count });
39
+ throw new HonuaCapabilityNotSupportedError("count", protocol, sourceId);
40
+ }
41
+ export async function execute(client, input) {
42
+ return withCapabilityHonesty(async () => {
43
+ const resolved = resolveSource(client, input);
44
+ const query = toQuery(input, { protocol: resolved.descriptor.protocol, paginate: false });
45
+ const outcome = await countThroughSource(resolved.source, query, resolved.descriptor.protocol, resolved.descriptor.id);
46
+ return jsonText({
47
+ source: resolved.ref.ref,
48
+ protocol: resolved.descriptor.protocol,
49
+ count: outcome.count,
50
+ countStrategy: outcome.strategy,
51
+ ...(outcome.degraded ? { degraded: outcome.degraded } : {}),
52
+ });
53
+ });
36
54
  }
@@ -1,14 +1,31 @@
1
1
  import type { HonuaClient } from "@honua/sdk-js";
2
2
  import { z } from "zod";
3
+ /**
4
+ * `honua_describe_layer` — protocol-neutral source description (#1005).
5
+ *
6
+ * The envelope is the same for every protocol: the neutral source reference,
7
+ * its protocol, the capabilities the runtime will actually honor, and a field
8
+ * schema. Where the schema comes from is protocol-specific (GeoServices layer
9
+ * metadata, an OGC collection + its queryables); the shape the agent reads is
10
+ * not. Protocols with no metadata document report `schemaAvailable: false`
11
+ * with a reason instead of an empty field list, which would read as "this
12
+ * source has no attributes".
13
+ */
3
14
  export declare const schema: z.ZodObject<{
4
- serviceId: z.ZodString;
5
- layerId: z.ZodNumber;
15
+ source: z.ZodOptional<z.ZodString>;
16
+ layout: z.ZodOptional<z.ZodEnum<["honua-facade", "ogc-api", "auto", "stac-api", "stac-static"]>>;
17
+ serviceId: z.ZodOptional<z.ZodString>;
18
+ layerId: z.ZodOptional<z.ZodNumber>;
6
19
  }, "strip", z.ZodTypeAny, {
7
- serviceId: string;
8
- layerId: number;
20
+ serviceId?: string | undefined;
21
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
22
+ layerId?: number | undefined;
23
+ source?: string | undefined;
9
24
  }, {
10
- serviceId: string;
11
- layerId: number;
25
+ serviceId?: string | undefined;
26
+ layout?: "honua-facade" | "ogc-api" | "auto" | "stac-api" | "stac-static" | undefined;
27
+ layerId?: number | undefined;
28
+ source?: string | undefined;
12
29
  }>;
13
30
  export type Input = z.infer<typeof schema>;
14
31
  export declare function execute(client: HonuaClient, input: Input): Promise<{
@@ -16,5 +33,12 @@ export declare function execute(client: HonuaClient, input: Input): Promise<{
16
33
  type: "text";
17
34
  text: string;
18
35
  }>;
36
+ } | {
37
+ structuredContent: Record<string, unknown>;
38
+ isError: true;
39
+ content: Array<{
40
+ type: "text";
41
+ text: string;
42
+ }>;
19
43
  }>;
20
44
  //# sourceMappingURL=describe-layer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"describe-layer.d.ts","sourceRoot":"","sources":["../../../src/tools/describe-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,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;;;;;GAa9D"}
1
+ {"version":3,"file":"describe-layer.d.ts","sourceRoot":"","sources":["../../../src/tools/describe-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;EAEjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AA+F3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;;GAoB9D"}