@honua/mcp-server 0.1.4-beta.0 → 0.1.9-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 (50) hide show
  1. package/README.md +30 -2
  2. package/dist/src/certification/admin-parity.d.ts +96 -0
  3. package/dist/src/certification/admin-parity.d.ts.map +1 -0
  4. package/dist/src/certification/admin-parity.js +235 -0
  5. package/dist/src/certification/target.d.ts.map +1 -1
  6. package/dist/src/certification/target.js +18 -26
  7. package/dist/src/credential-endpoint.d.ts +3 -0
  8. package/dist/src/credential-endpoint.d.ts.map +1 -0
  9. package/dist/src/credential-endpoint.js +24 -0
  10. package/dist/src/entrypoint.d.ts +20 -0
  11. package/dist/src/entrypoint.d.ts.map +1 -0
  12. package/dist/src/entrypoint.js +38 -0
  13. package/dist/src/index.d.ts +22 -1
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +33 -5
  16. package/dist/src/proxy.d.ts.map +1 -1
  17. package/dist/src/proxy.js +31 -16
  18. package/dist/src/release/zero-to-map-checkpoint.d.ts +34 -0
  19. package/dist/src/release/zero-to-map-checkpoint.d.ts.map +1 -0
  20. package/dist/src/release/zero-to-map-checkpoint.js +369 -0
  21. package/dist/src/release/zero-to-map-cli.d.ts +51 -0
  22. package/dist/src/release/zero-to-map-cli.d.ts.map +1 -0
  23. package/dist/src/release/zero-to-map-cli.js +881 -0
  24. package/dist/src/release/zero-to-map-provision.d.ts +37 -0
  25. package/dist/src/release/zero-to-map-provision.d.ts.map +1 -0
  26. package/dist/src/release/zero-to-map-provision.js +177 -0
  27. package/dist/src/release/zero-to-map.d.ts +296 -0
  28. package/dist/src/release/zero-to-map.d.ts.map +1 -0
  29. package/dist/src/release/zero-to-map.js +1069 -0
  30. package/dist/src/tools/admin-install-local.d.ts +30 -0
  31. package/dist/src/tools/admin-install-local.d.ts.map +1 -0
  32. package/dist/src/tools/admin-install-local.js +22 -0
  33. package/dist/src/tools/control-plane-command.d.ts +99 -0
  34. package/dist/src/tools/control-plane-command.d.ts.map +1 -0
  35. package/dist/src/tools/control-plane-command.js +180 -0
  36. package/dist/src/tools/docs-search.d.ts +100 -0
  37. package/dist/src/tools/docs-search.d.ts.map +1 -0
  38. package/dist/src/tools/docs-search.js +381 -0
  39. package/package.json +8 -3
  40. package/release/zero-to-map/README.md +174 -0
  41. package/release/zero-to-map/configs/claude-code.mcp.json +11 -0
  42. package/release/zero-to-map/configs/claude-desktop.json +11 -0
  43. package/release/zero-to-map/configs/cursor.json +11 -0
  44. package/release/zero-to-map/contracts/aws-ecs-provision-binding.schema.json +59 -0
  45. package/release/zero-to-map/contracts/checkpoint.schema.json +152 -0
  46. package/release/zero-to-map/contracts/console-receipt.schema.json +150 -0
  47. package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +73 -0
  48. package/release/zero-to-map/fixtures/parcels.geojson +33 -0
  49. package/release/zero-to-map/fixtures/zoning.geojson +24 -0
  50. package/release/zero-to-map/journey.v1.json +1248 -0
package/README.md CHANGED
@@ -96,11 +96,27 @@ return a structured result
96
96
  instead of crashing, hanging, or returning misleading empty data. This is the
97
97
  same skip-with-reason honesty the certification suite uses.
98
98
 
99
+ ### Bootstrap-only local installation
100
+
101
+ Local installation is deliberately absent from the ordinary `honua-mcp`
102
+ catalog. During an explicitly approved laptop bootstrap, start a separate,
103
+ single-purpose process that exposes only `honua_admin_install_local`:
104
+
105
+ ```bash
106
+ HONUA_MCP_BOOTSTRAP=1 npx -y -p @honua/mcp-server honua-mcp
107
+ ```
108
+
109
+ The tool requires `confirm: true`. Stop and remove this bootstrap server from
110
+ the MCP client configuration after installation; the normal platform-free
111
+ server remains read-only and cannot invoke the installer.
112
+
99
113
  ## Environment Variables
100
114
 
101
- - `HONUA_BASE_URL` (required): absolute base URL — a public ArcGIS folder
115
+ - `HONUA_BASE_URL` (required in normal mode): absolute base URL — a public ArcGIS folder
102
116
  (`https://services.arcgis.com/<org>/arcgis`) or a Honua deployment
103
117
  (`https://honua.example.com`).
118
+ - `HONUA_MCP_BOOTSTRAP` (bootstrap only): set to `1` or `true` to start the
119
+ isolated local-install catalog instead of the ordinary read-only server.
104
120
  - `HONUA_TRANSPORT` (optional): `grpc-web` (default, Honua deployments) or `rest`.
105
121
  Use `rest` for plain public ArcGIS/OGC endpoints.
106
122
  - `HONUA_API_KEY` (optional): API key when your deployment requires it. Public
@@ -123,6 +139,7 @@ All read-only:
123
139
  - `honua_statistics`
124
140
  - `honua_explain_capability_gap`
125
141
  - `honua_get_style`, `honua_apply_style_preset` — structured-unavailable on a plain FeatureServer
142
+ - `honua_docs_search` — answer a Honua documentation question from the local versioned docs corpus (`llms-full.txt`), with a source-file and release-version citation per hit. Each citation is pinned to the exact commit the corpus bytes come from, so it keeps naming the text that was searched after `trunk` moves; when that commit cannot be established (an installed package with no checkout, or a locally regenerated corpus) the citation reports `sourceRevision: null` and links to `trunk` rather than claiming a pin it cannot back. Offline: reads committed files, never the network, and reports structured-unavailable when the corpus is not present in the installation. Point `HONUA_DOCS_CORPUS_PATH` at a corpus file (any name) or at a directory holding `llms-full.txt` to supply the corpus explicitly; `HONUA_SOURCE_REVISION` declares the commit when git is unavailable.
126
143
 
127
144
  ### Protocol-neutral tool contract
128
145
 
@@ -387,7 +404,15 @@ Environment variables:
387
404
  - `HONUA_MCP_REMOTE_URL` (required; alias `HONUA_MCP_URL`): the remote honua
388
405
  `/mcp` endpoint to proxy.
389
406
  - `HONUA_MCP_AUTH_TOKEN` (optional): sent as `Authorization: Bearer <token>`.
390
- - `HONUA_API_KEY` (optional): sent as `x-api-key`.
407
+ - `HONUA_ADMIN_KEY` or `HONUA_API_KEY` (optional): sent as `x-api-key`.
408
+
409
+ Configure at most one authentication scheme and one API-key source. The proxy
410
+ rejects bearer plus API-key authentication and rejects simultaneous
411
+ `HONUA_ADMIN_KEY` / `HONUA_API_KEY` values instead of choosing by precedence.
412
+ Credentialed endpoints require HTTPS except for exact `localhost`, `127.0.0.1`,
413
+ or `[::1]` HTTP development endpoints. Userinfo, query parameters, fragments,
414
+ non-HTTP(S) URLs are rejected before connect, and redirect targets are never
415
+ followed or sent credentials.
391
416
 
392
417
  A parity test (`test/proxy.test.ts`) asserts the tool/resource/template catalog
393
418
  the downstream client sees is byte-identical to the upstream surface, that
@@ -496,6 +521,9 @@ Shared env for every live run:
496
521
  - `HONUA_API_KEY`: sent as `x-api-key` (⇒ auth mode `api-key`) when the deployment uses key auth instead.
497
522
  - `HONUA_EVAL_REQUIRE_AUTH=1` (recommended): fail fast if neither credential is present.
498
523
 
524
+ Set exactly one of `HONUA_MCP_AUTH_TOKEN` or `HONUA_API_KEY` for authenticated
525
+ live runs; ambiguous authentication fails closed.
526
+
499
527
  ```bash
500
528
  # Anthropic Claude (default claude-opus-4-8; override with HONUA_EVAL_ANTHROPIC_MODEL):
501
529
  HONUA_MCP_REMOTE_URL="https://demo.honua.io/mcp" \
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
3
+ export interface AdminParityReceipt {
4
+ readonly schemaVersion: "honua.admin-mcp-parity.v1";
5
+ readonly family: "honua_admin_*";
6
+ readonly expectedTools: number;
7
+ readonly expectedStaticTools: number;
8
+ readonly expectedTotalTools: number;
9
+ readonly directTools: number;
10
+ readonly proxiedTools: number;
11
+ readonly directStaticTools: number;
12
+ readonly proxiedStaticTools: number;
13
+ readonly directTotalTools: number;
14
+ readonly proxiedTotalTools: number;
15
+ readonly coverageSha256: string;
16
+ readonly exclusionRosterSha256: string;
17
+ readonly contractServerSha: string | null;
18
+ readonly pass: boolean;
19
+ readonly differences: readonly string[];
20
+ }
21
+ export interface AdminParityExpectation {
22
+ readonly publishedToolNames: readonly string[];
23
+ readonly excludedToolNames: readonly string[];
24
+ readonly expectedStaticTools: number;
25
+ readonly expectedTotalTools: number;
26
+ }
27
+ export declare function certifyAdminCatalogParity(directCatalog: readonly Tool[], proxiedCatalog: readonly Tool[], expectation?: AdminParityExpectation): AdminParityReceipt;
28
+ export declare function normalizeAdminTools(tools: readonly Tool[]): {
29
+ name: string;
30
+ title: string | undefined;
31
+ description: string | undefined;
32
+ inputSchema: {
33
+ [x: string]: unknown;
34
+ type: "object";
35
+ properties?: {
36
+ [x: string]: object;
37
+ } | undefined;
38
+ required?: string[] | undefined;
39
+ };
40
+ outputSchema: {
41
+ [x: string]: unknown;
42
+ type: "object";
43
+ properties?: {
44
+ [x: string]: object;
45
+ } | undefined;
46
+ required?: string[] | undefined;
47
+ } | undefined;
48
+ annotations: {
49
+ title?: string | undefined;
50
+ readOnlyHint?: boolean | undefined;
51
+ destructiveHint?: boolean | undefined;
52
+ idempotentHint?: boolean | undefined;
53
+ openWorldHint?: boolean | undefined;
54
+ } | undefined;
55
+ }[];
56
+ /**
57
+ * Hostile-server safety ceiling on the number of `tools/list` pages a single
58
+ * catalog read may consume.
59
+ *
60
+ * This is deliberately NOT the expected roster size. A roster assertion says
61
+ * "this catalog is wrong"; this ceiling says "this server never stopped
62
+ * paging". Conflating the two produced a single unactionable diagnostic and,
63
+ * worse, made an opt-in profile catalog (base + analysis + Esri GP) impossible
64
+ * to read at all because the *transport* refused any catalog larger than the
65
+ * default roster.
66
+ */
67
+ export declare const MCP_TOOLS_LIST_MAX_PAGES = 1024;
68
+ /**
69
+ * Hostile-server safety ceiling on the number of tools a single catalog read
70
+ * may accumulate. Independent of {@link MCP_DEFAULT_TOTAL_TOOL_COUNT} so that
71
+ * enabling additive server profiles is a roster question, not a transport
72
+ * failure.
73
+ */
74
+ export declare const MCP_TOOLS_LIST_MAX_TOOLS = 8192;
75
+ /** Maximum accepted `tools/list` continuation cursor length, in characters. */
76
+ export declare const MCP_TOOLS_LIST_MAX_CURSOR_LENGTH = 1024;
77
+ /**
78
+ * Read the complete MCP tool catalog. The server deliberately pages large
79
+ * catalogs, so a single tools/list response is not certification evidence for
80
+ * the 432-tool default roster (47 static plus 385 Admin MCP tools).
81
+ *
82
+ * Roster size is *not* enforced here. This function is responsible only for
83
+ * retrieving a complete, non-hostile catalog; deciding whether that catalog is
84
+ * the 432-tool default roster or a profile-enabled superset belongs to the
85
+ * certification and preflight callers.
86
+ */
87
+ export declare function listAllTools(client: {
88
+ listTools(request?: {
89
+ cursor?: string;
90
+ }): Promise<{
91
+ tools: Tool[];
92
+ nextCursor?: string;
93
+ }>;
94
+ }): Promise<Tool[]>;
95
+ export declare function runAdminParityCertification(env?: NodeJS.ProcessEnv, outputPath?: string): Promise<AdminParityReceipt>;
96
+ //# sourceMappingURL=admin-parity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-parity.d.ts","sourceRoot":"","sources":["../../../src/certification/admin-parity.ts"],"names":[],"mappings":";AAeA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAI/D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,aAAa,EAAE,2BAA2B,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AASD,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,SAAS,IAAI,EAAE,EAC9B,cAAc,EAAE,SAAS,IAAI,EAAE,EAC/B,WAAW,GAAE,sBAA4C,GACxD,kBAAkB,CAqFpB;AAgCD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;IAYzD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,+EAA+E;AAC/E,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AAEtD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IACzC,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3F,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAgClB;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,UAAU,SAA0B,GACnC,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+ import { writeFile } from "node:fs/promises";
3
+ import { ADMIN_MCP_CONTRACT_SERVER_SHA, ADMIN_MCP_COVERAGE_SHA256, ADMIN_MCP_EXCLUDED_OPERATIONS, ADMIN_MCP_EXCLUSION_ROSTER_SHA256, ADMIN_MCP_PUBLISHED_TOOL_NAMES, ADMIN_PUBLISHED_OPERATION_COUNT, MCP_DEFAULT_STATIC_TOOL_COUNT, MCP_DEFAULT_TOTAL_TOOL_COUNT, } from "@honua/sdk-js/control-plane";
4
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
6
+ import { isMainEntrypoint } from "../entrypoint.js";
7
+ import { connectUpstream, createProxyServer, resolveProxyOptions } from "../proxy.js";
8
+ const DEFAULT_EXPECTATION = {
9
+ publishedToolNames: ADMIN_MCP_PUBLISHED_TOOL_NAMES,
10
+ excludedToolNames: ADMIN_MCP_EXCLUDED_OPERATIONS.map((operation) => operation.toolName),
11
+ expectedStaticTools: MCP_DEFAULT_STATIC_TOOL_COUNT,
12
+ expectedTotalTools: MCP_DEFAULT_TOTAL_TOOL_COUNT,
13
+ };
14
+ export function certifyAdminCatalogParity(directCatalog, proxiedCatalog, expectation = DEFAULT_EXPECTATION) {
15
+ const direct = normalizeAdminTools(directCatalog);
16
+ const proxied = normalizeAdminTools(proxiedCatalog);
17
+ const expectedTools = expectation.publishedToolNames.length;
18
+ const directStaticTools = directCatalog.length - direct.length;
19
+ const proxiedStaticTools = proxiedCatalog.length - proxied.length;
20
+ const differences = [];
21
+ if (expectedTools !== ADMIN_PUBLISHED_OPERATION_COUNT) {
22
+ differences.push(`certification expectation contains ${expectedTools} admin tools; generated contract requires ${ADMIN_PUBLISHED_OPERATION_COUNT}`);
23
+ }
24
+ if (direct.length !== expectedTools) {
25
+ differences.push(`direct catalog exposes ${direct.length} admin tools; expected ${expectedTools}`);
26
+ }
27
+ if (proxied.length !== expectedTools) {
28
+ differences.push(`proxied catalog exposes ${proxied.length} admin tools; expected ${expectedTools}`);
29
+ }
30
+ if (directStaticTools !== expectation.expectedStaticTools) {
31
+ differences.push(`direct catalog exposes ${directStaticTools} static tools; expected ${expectation.expectedStaticTools}`);
32
+ }
33
+ if (proxiedStaticTools !== expectation.expectedStaticTools) {
34
+ differences.push(`proxied catalog exposes ${proxiedStaticTools} static tools; expected ${expectation.expectedStaticTools}`);
35
+ }
36
+ if (directCatalog.length !== expectation.expectedTotalTools) {
37
+ differences.push(`direct catalog exposes ${directCatalog.length} total tools; expected ${expectation.expectedTotalTools}`);
38
+ }
39
+ if (proxiedCatalog.length !== expectation.expectedTotalTools) {
40
+ differences.push(`proxied catalog exposes ${proxiedCatalog.length} total tools; expected ${expectation.expectedTotalTools}`);
41
+ }
42
+ if (expectation.expectedStaticTools + expectedTools !== expectation.expectedTotalTools) {
43
+ differences.push(`certification equation failed: ${expectation.expectedStaticTools} static + ${expectedTools} admin ` +
44
+ `!= ${expectation.expectedTotalTools} total tools`);
45
+ }
46
+ for (const duplicate of findDuplicateNames(direct)) {
47
+ differences.push(`direct catalog repeats admin tool ${duplicate}`);
48
+ }
49
+ for (const duplicate of findDuplicateNames(proxied)) {
50
+ differences.push(`proxied catalog repeats admin tool ${duplicate}`);
51
+ }
52
+ for (const duplicate of findDuplicateNames(directCatalog)) {
53
+ if (!duplicate.startsWith("honua_admin_"))
54
+ differences.push(`direct catalog repeats static tool ${duplicate}`);
55
+ }
56
+ for (const duplicate of findDuplicateNames(proxiedCatalog)) {
57
+ if (!duplicate.startsWith("honua_admin_"))
58
+ differences.push(`proxied catalog repeats static tool ${duplicate}`);
59
+ }
60
+ compareExpectedRoster("direct", direct, expectation, differences);
61
+ compareExpectedRoster("proxied", proxied, expectation, differences);
62
+ const directByName = new Map(direct.map((tool) => [tool.name, tool]));
63
+ const proxiedByName = new Map(proxied.map((tool) => [tool.name, tool]));
64
+ for (const name of [...new Set([...directByName.keys(), ...proxiedByName.keys()])].sort()) {
65
+ const directTool = directByName.get(name);
66
+ const proxiedTool = proxiedByName.get(name);
67
+ if (!directTool)
68
+ differences.push(`${name} is missing from the direct HTTP catalog`);
69
+ else if (!proxiedTool)
70
+ differences.push(`${name} is missing from the proxied catalog`);
71
+ else if (stableJson(directTool) !== stableJson(proxiedTool))
72
+ differences.push(`${name} descriptor differs`);
73
+ }
74
+ return {
75
+ schemaVersion: "honua.admin-mcp-parity.v1",
76
+ family: "honua_admin_*",
77
+ expectedTools,
78
+ expectedStaticTools: expectation.expectedStaticTools,
79
+ expectedTotalTools: expectation.expectedTotalTools,
80
+ directTools: direct.length,
81
+ proxiedTools: proxied.length,
82
+ directStaticTools,
83
+ proxiedStaticTools,
84
+ directTotalTools: directCatalog.length,
85
+ proxiedTotalTools: proxiedCatalog.length,
86
+ coverageSha256: ADMIN_MCP_COVERAGE_SHA256,
87
+ exclusionRosterSha256: ADMIN_MCP_EXCLUSION_ROSTER_SHA256,
88
+ contractServerSha: ADMIN_MCP_CONTRACT_SERVER_SHA,
89
+ pass: differences.length === 0,
90
+ differences,
91
+ };
92
+ }
93
+ function compareExpectedRoster(transport, tools, expectation, differences) {
94
+ const actual = new Set(tools.map((tool) => tool.name));
95
+ const expected = new Set(expectation.publishedToolNames);
96
+ for (const name of [...expected].sort()) {
97
+ if (!actual.has(name))
98
+ differences.push(`${name} is missing from the ${transport} catalog contract`);
99
+ }
100
+ for (const name of [...actual].sort()) {
101
+ if (!expected.has(name))
102
+ differences.push(`${name} is not classified as published in the ${transport} catalog contract`);
103
+ }
104
+ for (const name of [...expectation.excludedToolNames].sort()) {
105
+ if (actual.has(name))
106
+ differences.push(`${transport} catalog advertises explicitly excluded admin tool ${name}`);
107
+ }
108
+ }
109
+ function findDuplicateNames(tools) {
110
+ const seen = new Set();
111
+ const duplicates = new Set();
112
+ for (const tool of tools) {
113
+ if (seen.has(tool.name))
114
+ duplicates.add(tool.name);
115
+ else
116
+ seen.add(tool.name);
117
+ }
118
+ return [...duplicates].sort();
119
+ }
120
+ export function normalizeAdminTools(tools) {
121
+ return tools
122
+ .filter((tool) => tool.name.startsWith("honua_admin_"))
123
+ .map((tool) => ({
124
+ name: tool.name,
125
+ title: tool.title,
126
+ description: tool.description,
127
+ inputSchema: tool.inputSchema,
128
+ outputSchema: tool.outputSchema,
129
+ annotations: tool.annotations,
130
+ }))
131
+ .sort((left, right) => left.name.localeCompare(right.name));
132
+ }
133
+ /**
134
+ * Hostile-server safety ceiling on the number of `tools/list` pages a single
135
+ * catalog read may consume.
136
+ *
137
+ * This is deliberately NOT the expected roster size. A roster assertion says
138
+ * "this catalog is wrong"; this ceiling says "this server never stopped
139
+ * paging". Conflating the two produced a single unactionable diagnostic and,
140
+ * worse, made an opt-in profile catalog (base + analysis + Esri GP) impossible
141
+ * to read at all because the *transport* refused any catalog larger than the
142
+ * default roster.
143
+ */
144
+ export const MCP_TOOLS_LIST_MAX_PAGES = 1_024;
145
+ /**
146
+ * Hostile-server safety ceiling on the number of tools a single catalog read
147
+ * may accumulate. Independent of {@link MCP_DEFAULT_TOTAL_TOOL_COUNT} so that
148
+ * enabling additive server profiles is a roster question, not a transport
149
+ * failure.
150
+ */
151
+ export const MCP_TOOLS_LIST_MAX_TOOLS = 8_192;
152
+ /** Maximum accepted `tools/list` continuation cursor length, in characters. */
153
+ export const MCP_TOOLS_LIST_MAX_CURSOR_LENGTH = 1_024;
154
+ /**
155
+ * Read the complete MCP tool catalog. The server deliberately pages large
156
+ * catalogs, so a single tools/list response is not certification evidence for
157
+ * the 432-tool default roster (47 static plus 385 Admin MCP tools).
158
+ *
159
+ * Roster size is *not* enforced here. This function is responsible only for
160
+ * retrieving a complete, non-hostile catalog; deciding whether that catalog is
161
+ * the 432-tool default roster or a profile-enabled superset belongs to the
162
+ * certification and preflight callers.
163
+ */
164
+ export async function listAllTools(client) {
165
+ const tools = [];
166
+ let cursor;
167
+ const seenCursors = new Set();
168
+ let pageCount = 0;
169
+ do {
170
+ pageCount += 1;
171
+ if (pageCount > MCP_TOOLS_LIST_MAX_PAGES) {
172
+ throw new Error(`MCP tools/list exceeded the ${MCP_TOOLS_LIST_MAX_PAGES}-page pagination safety ceiling after ${tools.length} tools; the server did not terminate its cursor chain. This is a pagination fault, not a roster assertion.`);
173
+ }
174
+ const page = await client.listTools(cursor ? { cursor } : undefined);
175
+ if (tools.length + page.tools.length > MCP_TOOLS_LIST_MAX_TOOLS) {
176
+ throw new Error(`MCP tools/list exceeded the ${MCP_TOOLS_LIST_MAX_TOOLS}-tool pagination safety ceiling on page ${pageCount}; the server returned an implausible catalog. This is a pagination fault, not a roster assertion.`);
177
+ }
178
+ tools.push(...page.tools);
179
+ cursor = page.nextCursor;
180
+ if (cursor !== undefined && (cursor.length === 0 || cursor.length > MCP_TOOLS_LIST_MAX_CURSOR_LENGTH)) {
181
+ throw new Error("MCP tools/list returned an invalid bounded cursor");
182
+ }
183
+ if (cursor && seenCursors.has(cursor)) {
184
+ throw new Error(`MCP tools/list repeated cursor ${cursor}`);
185
+ }
186
+ if (cursor && page.tools.length === 0) {
187
+ throw new Error("MCP tools/list returned an empty page with a continuation cursor");
188
+ }
189
+ if (cursor)
190
+ seenCursors.add(cursor);
191
+ } while (cursor);
192
+ return tools;
193
+ }
194
+ export async function runAdminParityCertification(env = process.env, outputPath = "admin-mcp-parity.json") {
195
+ if (ADMIN_MCP_CONTRACT_SERVER_SHA === null) {
196
+ throw new Error("Admin MCP live certification is blocked until config/admin-client.v1.json pins the final server head.");
197
+ }
198
+ const upstream = await connectUpstream(resolveProxyOptions(env));
199
+ const proxy = createProxyServer(upstream);
200
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
201
+ const proxied = new Client({ name: "honua-admin-parity", version: "1.0.0" });
202
+ try {
203
+ await proxy.connect(serverTransport);
204
+ await proxied.connect(clientTransport);
205
+ const directTools = await listAllTools(upstream);
206
+ const proxiedTools = await listAllTools(proxied);
207
+ const receipt = certifyAdminCatalogParity(directTools, proxiedTools);
208
+ await writeFile(outputPath, `${JSON.stringify(receipt, null, 2)}\n`, "utf8");
209
+ if (!receipt.pass)
210
+ throw new Error(`Admin MCP parity failed: ${receipt.differences.join("; ")}`);
211
+ return receipt;
212
+ }
213
+ finally {
214
+ await proxied.close().catch(() => { });
215
+ await proxy.close().catch(() => { });
216
+ await upstream.close().catch(() => { });
217
+ }
218
+ }
219
+ function stableJson(value) {
220
+ if (Array.isArray(value))
221
+ return `[${value.map(stableJson).join(",")}]`;
222
+ if (value && typeof value === "object") {
223
+ return `{${Object.entries(value)
224
+ .sort(([left], [right]) => left.localeCompare(right))
225
+ .map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`)
226
+ .join(",")}}`;
227
+ }
228
+ return JSON.stringify(value) ?? "undefined";
229
+ }
230
+ if (isMainEntrypoint(import.meta.url)) {
231
+ runAdminParityCertification(process.env, process.argv[2]).catch((error) => {
232
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
233
+ process.exitCode = 1;
234
+ });
235
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,aAAa,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEpG,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAEpE;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,2BAA2B,EAAE,OAAO,CAAC;IACrC,gFAAgF;IAChF,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CActF;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAc9E;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED,gDAAgD;AAChD,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAehH"}
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,YAAY,EAAwC,MAAM,aAAa,CAAC;AAMtF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEpG,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAEpE;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,2BAA2B,EAAE,OAAO,CAAC;IACrC,gFAAgF;IAChF,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CActF;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAK9E;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED,gDAAgD;AAChD,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAehH"}
@@ -4,7 +4,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
4
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
5
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
6
6
  import { createClientFromEnv, createServer } from "../index.js";
7
- import { connectUpstream } from "../proxy.js";
7
+ import { connectUpstream, resolveProxyOptions } from "../proxy.js";
8
8
  import { createStandaloneFixtureClient } from "./census-fixture-client.js";
9
9
  import { startMockUpstream } from "./mock-upstream.js";
10
10
  import { OGC_ENDPOINT } from "./ogc-data.js";
@@ -26,19 +26,10 @@ export function resolveTargetMode(env = process.env) {
26
26
  }
27
27
  /** Resolve the remote `/mcp` endpoint + credentials from the environment. */
28
28
  export function resolveRemoteProxyOptions(env) {
29
- const remoteUrl = env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL;
30
- if (!remoteUrl) {
29
+ if (!(env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL)) {
31
30
  throw new Error("HONUA_MCP_REMOTE_URL is required for remote / stdio-proxy certification against a live /mcp.");
32
31
  }
33
- const parsed = new URL(remoteUrl);
34
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
35
- throw new Error(`HONUA_MCP_REMOTE_URL must use http or https: ${remoteUrl}`);
36
- }
37
- return {
38
- remoteUrl: parsed.toString(),
39
- authToken: env.HONUA_MCP_AUTH_TOKEN,
40
- apiKey: env.HONUA_API_KEY,
41
- };
32
+ return resolveProxyOptions(env);
42
33
  }
43
34
  /** Resolve the built `honua-mcp-proxy` entry usable from src (vitest) or dist. */
44
35
  export function resolveProxyEntry() {
@@ -200,32 +191,33 @@ async function openStdioProxyTarget(env) {
200
191
  const clients = [];
201
192
  // Upstream: a live /mcp when configured, otherwise the in-process mock.
202
193
  let mock;
203
- let upstreamUrl;
204
- let upstreamToken;
194
+ let upstreamOptions;
205
195
  let backend;
206
196
  if (env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL) {
207
- const options = resolveRemoteProxyOptions(env);
208
- upstreamUrl = options.remoteUrl;
209
- upstreamToken = options.authToken;
197
+ upstreamOptions = resolveRemoteProxyOptions(env);
210
198
  backend = "live";
211
199
  }
212
200
  else {
213
201
  mock = await startMockUpstream();
214
- upstreamUrl = mock.url;
215
- upstreamToken = mock.authToken;
202
+ upstreamOptions = { remoteUrl: mock.url, authToken: mock.authToken };
216
203
  backend = "mock-upstream";
217
204
  }
218
205
  const spawnProxyClient = async (withAuth) => {
219
206
  const childEnv = {
220
207
  ...process.env,
221
- HONUA_MCP_REMOTE_URL: upstreamUrl,
208
+ HONUA_MCP_REMOTE_URL: upstreamOptions.remoteUrl,
222
209
  };
223
- if (withAuth && upstreamToken) {
224
- childEnv.HONUA_MCP_AUTH_TOKEN = upstreamToken;
210
+ delete childEnv.HONUA_MCP_AUTH_TOKEN;
211
+ delete childEnv.HONUA_ADMIN_KEY;
212
+ delete childEnv.HONUA_API_KEY;
213
+ if (withAuth && upstreamOptions.authToken) {
214
+ childEnv.HONUA_MCP_AUTH_TOKEN = upstreamOptions.authToken;
215
+ }
216
+ else if (withAuth && upstreamOptions.apiKey) {
217
+ childEnv.HONUA_API_KEY = upstreamOptions.apiKey;
225
218
  }
226
219
  else {
227
- delete childEnv.HONUA_MCP_AUTH_TOKEN;
228
- delete childEnv.HONUA_API_KEY;
220
+ // Keep the certification's anonymous negative control credential-free.
229
221
  }
230
222
  const transport = new StdioClientTransport({
231
223
  command: process.execPath,
@@ -242,8 +234,8 @@ async function openStdioProxyTarget(env) {
242
234
  return {
243
235
  mode: "stdio-proxy",
244
236
  backend,
245
- serverLabel: `honua-mcp-proxy → ${backend === "live" ? upstreamUrl : "in-process mock /mcp"}`,
246
- authMode: upstreamToken ? "bearer" : env.HONUA_API_KEY ? "api-key" : "anonymous",
237
+ serverLabel: `honua-mcp-proxy → ${backend === "live" ? upstreamOptions.remoteUrl : "in-process mock /mcp"}`,
238
+ authMode: upstreamOptions.authToken ? "bearer" : upstreamOptions.apiKey ? "api-key" : "anonymous",
247
239
  honuaTransport: env.HONUA_TRANSPORT ?? null,
248
240
  client,
249
241
  supportsUnauthenticatedPass: true,
@@ -0,0 +1,3 @@
1
+ /** Validate an HTTP endpoint before any transport is created. */
2
+ export declare function requireSecureCredentialEndpoint(value: string, label: string, credentialed?: boolean): URL;
3
+ //# sourceMappingURL=credential-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-endpoint.d.ts","sourceRoot":"","sources":["../../src/credential-endpoint.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,UAAO,GAAG,GAAG,CAiBtG"}
@@ -0,0 +1,24 @@
1
+ /** Validate an HTTP endpoint before any transport is created. */
2
+ export function requireSecureCredentialEndpoint(value, label, credentialed = true) {
3
+ let url;
4
+ try {
5
+ url = new URL(value);
6
+ }
7
+ catch {
8
+ throw new Error(`${label} must be a valid absolute URL`);
9
+ }
10
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
11
+ throw new Error(`${label} must use http or https`);
12
+ }
13
+ if (url.username || url.password || url.search || url.hash) {
14
+ throw new Error(`${label} must not include embedded credentials, query parameters, or a fragment`);
15
+ }
16
+ if (credentialed && url.protocol === "http:" && !isLoopbackHost(url.hostname)) {
17
+ throw new Error(`${label} requires HTTPS except for exact loopback HTTP development endpoints`);
18
+ }
19
+ return url;
20
+ }
21
+ function isLoopbackHost(hostname) {
22
+ const normalized = hostname.toLowerCase();
23
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
24
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * True when the module identified by `moduleUrl` is the process entrypoint.
3
+ *
4
+ * The obvious spelling of this check — `process.argv[1] === fileURLToPath(import.meta.url)`
5
+ * — is correct only when the module file is invoked by its own path. npm installs a
6
+ * package's `bin` entries as symlinks under `node_modules/.bin`, so a user (or an agent)
7
+ * running the documented command gets `process.argv[1]` pointing at the shim, never at the
8
+ * module file. The strings differ, the guard does not match, and the binary exits 0 having
9
+ * done nothing at all (#1528).
10
+ *
11
+ * That is the worst available failure shape: no output, no diagnostic, no non-zero status —
12
+ * nothing for a caller to react to. It is how the honua-release#123 terminal driver recorded
13
+ * the pinned `honua-mcp-proxy` stage as blocked.
14
+ *
15
+ * Resolving both sides through `realpath` makes the shim and the module compare equal. The
16
+ * guard is kept rather than dropped because these modules are also imported by tests, which
17
+ * must not start a server as a side effect of importing.
18
+ */
19
+ export declare function isMainEntrypoint(moduleUrl: string): boolean;
20
+ //# sourceMappingURL=entrypoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../../src/entrypoint.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAkB3D"}
@@ -0,0 +1,38 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ /**
4
+ * True when the module identified by `moduleUrl` is the process entrypoint.
5
+ *
6
+ * The obvious spelling of this check — `process.argv[1] === fileURLToPath(import.meta.url)`
7
+ * — is correct only when the module file is invoked by its own path. npm installs a
8
+ * package's `bin` entries as symlinks under `node_modules/.bin`, so a user (or an agent)
9
+ * running the documented command gets `process.argv[1]` pointing at the shim, never at the
10
+ * module file. The strings differ, the guard does not match, and the binary exits 0 having
11
+ * done nothing at all (#1528).
12
+ *
13
+ * That is the worst available failure shape: no output, no diagnostic, no non-zero status —
14
+ * nothing for a caller to react to. It is how the honua-release#123 terminal driver recorded
15
+ * the pinned `honua-mcp-proxy` stage as blocked.
16
+ *
17
+ * Resolving both sides through `realpath` makes the shim and the module compare equal. The
18
+ * guard is kept rather than dropped because these modules are also imported by tests, which
19
+ * must not start a server as a side effect of importing.
20
+ */
21
+ export function isMainEntrypoint(moduleUrl) {
22
+ const invoked = process.argv[1];
23
+ if (!invoked) {
24
+ return false;
25
+ }
26
+ const modulePath = fileURLToPath(moduleUrl);
27
+ if (invoked === modulePath) {
28
+ return true;
29
+ }
30
+ // realpathSync throws for a path that does not exist, which is not an error here:
31
+ // it just means this module is not what was invoked.
32
+ try {
33
+ return realpathSync(invoked) === realpathSync(modulePath);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
@@ -3,7 +3,10 @@ import { HonuaClient } from "@honua/sdk-js";
3
3
  import type { HonuaTransport } from "@honua/sdk-js";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { type NlMapControlMcpHost } from "./nl-map-control.js";
6
+ import type { ControlPlaneCommandMcpHost } from "./tools/control-plane-command.js";
6
7
  export { createNlMapControlMcpHost, registerNlMapControlMcpTools, } from "./nl-map-control.js";
8
+ export { CONTROL_PLANE_COMMAND_TOOL_PREFIX, controlPlaneCommandToolName, controlPlaneCommandToolSchema, registerControlPlaneCommandTools, } from "./tools/control-plane-command.js";
9
+ export type { ControlPlaneCommandMcpHost } from "./tools/control-plane-command.js";
7
10
  export type { CreateNlMapControlMcpHostOptions, McpNlMapExecutionResponse, McpNlMapPlanResponse, McpNlMapReceipt, NlMapControlMcpHost, NlMapControlMcpRequestContext, } from "./nl-map-control.js";
8
11
  export interface RuntimeOptions {
9
12
  baseUrl: string;
@@ -15,6 +18,13 @@ export interface RuntimeOptions {
15
18
  export interface CreateServerOptions {
16
19
  /** Optional BYO-LLM/map-runtime host for the experimental NL map-plan tools. */
17
20
  readonly nlMapControl?: NlMapControlMcpHost;
21
+ /**
22
+ * Optional control-plane command runtime. When supplied, the server also
23
+ * publishes one generated thin adapter per shared command (see
24
+ * {@link registerControlPlaneCommandTools}). Omitted by default so the
25
+ * standalone catalog stays platform-free and read-only.
26
+ */
27
+ readonly controlPlaneCommands?: ControlPlaneCommandMcpHost;
18
28
  }
19
29
  export declare const SERVER_VERSION: string;
20
30
  export declare function resolveRuntimeOptions(env: NodeJS.ProcessEnv): RuntimeOptions;
@@ -46,13 +56,24 @@ export declare function createClientFromEnv(env?: NodeJS.ProcessEnv): HonuaClien
46
56
  * mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
47
57
  * mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
48
58
  * deployment. Standalone is the front door; the proxy is the upgrade path.
59
+ * Local installation is intentionally absent from this catalog and is exposed
60
+ * only by {@link createBootstrapServer}.
61
+ *
62
+ * Control-plane MUTATION is likewise absent by default, and when a host does
63
+ * enable it ({@link CreateServerOptions.controlPlaneCommands}) MCP does not get
64
+ * a workflow of its own: the tools are generated from the shared command
65
+ * catalog in `@honua/sdk-js/control-plane`, so an MCP invocation and the
66
+ * equivalent `honua` CLI, Studio, or direct-JS invocation share one
67
+ * implementation and one receipt `auditKey`.
49
68
  */
50
69
  export declare function createServer(client: HonuaClient, options?: CreateServerOptions): McpServer;
70
+ /** Bootstrap-only MCP surface for a laptop that does not have Honua yet. */
71
+ export declare function createBootstrapServer(): McpServer;
51
72
  /**
52
73
  * Run the platform-free standalone server end-to-end over stdio.
53
74
  *
54
75
  * Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
55
- * transport/auth) and exposes {@link createServer} — the nine read-only tools —
76
+ * transport/auth) and exposes {@link createServer} — the ten read-only tools —
56
77
  * to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
57
78
  * `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
58
79
  * required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAGtF,OAAO,EAAE,KAAK,mBAAmB,EAAgC,MAAM,qBAAqB,CAAC;AAe7F,OAAO,EACL,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gCAAgC,EAChC,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CAC7C;AAMD,eAAO,MAAM,cAAc,QAAyB,CAAC;AA2DrD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,cAAc,CAgD5E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,WAAW,CASrF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,mBAAwB,aAsGlF;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAItF,OAAO,EAAE,KAAK,mBAAmB,EAAgC,MAAM,qBAAqB,CAAC;AAM7F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAanF,OAAO,EACL,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,2BAA2B,EAC3B,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,YAAY,EACV,gCAAgC,EAChC,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;CAC5D;AAMD,eAAO,MAAM,cAAc,QAAyB,CAAC;AA2DrD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,cAAc,CAgD5E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,WAAW,CASrF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,mBAAwB,aAoHlF;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI,SAAS,CAIjD;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF"}