@honua/mcp-server 0.1.4-beta.0 → 0.1.8-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -2
- package/dist/src/certification/admin-parity.d.ts +96 -0
- package/dist/src/certification/admin-parity.d.ts.map +1 -0
- package/dist/src/certification/admin-parity.js +235 -0
- package/dist/src/certification/target.d.ts.map +1 -1
- package/dist/src/certification/target.js +18 -26
- package/dist/src/credential-endpoint.d.ts +3 -0
- package/dist/src/credential-endpoint.d.ts.map +1 -0
- package/dist/src/credential-endpoint.js +24 -0
- package/dist/src/index.d.ts +22 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +31 -3
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +29 -14
- package/dist/src/release/zero-to-map-checkpoint.d.ts +34 -0
- package/dist/src/release/zero-to-map-checkpoint.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-checkpoint.js +369 -0
- package/dist/src/release/zero-to-map-cli.d.ts +51 -0
- package/dist/src/release/zero-to-map-cli.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-cli.js +880 -0
- package/dist/src/release/zero-to-map-provision.d.ts +37 -0
- package/dist/src/release/zero-to-map-provision.d.ts.map +1 -0
- package/dist/src/release/zero-to-map-provision.js +177 -0
- package/dist/src/release/zero-to-map.d.ts +296 -0
- package/dist/src/release/zero-to-map.d.ts.map +1 -0
- package/dist/src/release/zero-to-map.js +1069 -0
- package/dist/src/tools/admin-install-local.d.ts +30 -0
- package/dist/src/tools/admin-install-local.d.ts.map +1 -0
- package/dist/src/tools/admin-install-local.js +22 -0
- package/dist/src/tools/control-plane-command.d.ts +99 -0
- package/dist/src/tools/control-plane-command.d.ts.map +1 -0
- package/dist/src/tools/control-plane-command.js +180 -0
- package/dist/src/tools/docs-search.d.ts +100 -0
- package/dist/src/tools/docs-search.d.ts.map +1 -0
- package/dist/src/tools/docs-search.js +381 -0
- package/package.json +8 -3
- package/release/zero-to-map/README.md +174 -0
- package/release/zero-to-map/configs/claude-code.mcp.json +11 -0
- package/release/zero-to-map/configs/claude-desktop.json +11 -0
- package/release/zero-to-map/configs/cursor.json +11 -0
- package/release/zero-to-map/contracts/aws-ecs-provision-binding.schema.json +59 -0
- package/release/zero-to-map/contracts/checkpoint.schema.json +152 -0
- package/release/zero-to-map/contracts/console-receipt.schema.json +150 -0
- package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +73 -0
- package/release/zero-to-map/fixtures/parcels.geojson +33 -0
- package/release/zero-to-map/fixtures/zoning.geojson +24 -0
- package/release/zero-to-map/journey.v1.json +1248 -0
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":";AAgBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG/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 { fileURLToPath } from "node:url";
|
|
4
|
+
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";
|
|
5
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
+
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.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 (process.argv[1] === fileURLToPath(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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
204
|
-
let upstreamToken;
|
|
194
|
+
let upstreamOptions;
|
|
205
195
|
let backend;
|
|
206
196
|
if (env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL) {
|
|
207
|
-
|
|
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
|
-
|
|
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:
|
|
208
|
+
HONUA_MCP_REMOTE_URL: upstreamOptions.remoteUrl,
|
|
222
209
|
};
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
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" ?
|
|
246
|
-
authMode:
|
|
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 @@
|
|
|
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
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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
|
|
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.
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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;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"}
|