@honua/mcp-server 0.0.20-alpha.0 → 0.1.1-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 +141 -15
- package/certification/geospatial-mcp-schemas/PROVENANCE.md +15 -7
- package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +6 -0
- package/certification/geospatial-mcp-schemas/index.json +88 -9
- package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +13 -9
- package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
- package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
- package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +13 -3
- package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
- package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
- package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
- package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +20 -0
- package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +5 -0
- package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
- package/dist/src/agent-execution.d.ts +16 -0
- package/dist/src/agent-execution.d.ts.map +1 -0
- package/dist/src/agent-execution.js +46 -0
- package/dist/src/capability.d.ts +48 -0
- package/dist/src/capability.d.ts.map +1 -0
- package/dist/src/capability.js +44 -0
- package/dist/src/certification/census-data.d.ts +33 -0
- package/dist/src/certification/census-data.d.ts.map +1 -0
- package/dist/src/certification/census-data.js +506 -0
- package/dist/src/certification/census-fixture-client.d.ts +7 -0
- package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
- package/dist/src/certification/census-fixture-client.js +226 -0
- package/dist/src/certification/certifier.d.ts +7 -1
- package/dist/src/certification/certifier.d.ts.map +1 -1
- package/dist/src/certification/certifier.js +52 -7
- package/dist/src/certification/cli.js +19 -4
- package/dist/src/certification/contracts.d.ts +41 -0
- package/dist/src/certification/contracts.d.ts.map +1 -1
- package/dist/src/certification/contracts.js +71 -0
- package/dist/src/certification/fixture-client.d.ts.map +1 -1
- package/dist/src/certification/fixtures.d.ts +13 -0
- package/dist/src/certification/fixtures.d.ts.map +1 -1
- package/dist/src/certification/fixtures.js +28 -0
- package/dist/src/certification/json-schema.d.ts +23 -1
- package/dist/src/certification/json-schema.d.ts.map +1 -1
- package/dist/src/certification/json-schema.js +10 -2
- package/dist/src/certification/lifecycle.d.ts +14 -0
- package/dist/src/certification/lifecycle.d.ts.map +1 -0
- package/dist/src/certification/lifecycle.js +326 -0
- package/dist/src/certification/operator-catalog.d.ts +8 -0
- package/dist/src/certification/operator-catalog.d.ts.map +1 -1
- package/dist/src/certification/operator-catalog.js +189 -5
- package/dist/src/certification/report.d.ts.map +1 -1
- package/dist/src/certification/report.js +13 -0
- package/dist/src/certification/run.d.ts.map +1 -1
- package/dist/src/certification/run.js +1 -0
- package/dist/src/certification/schema-index.d.ts +25 -0
- package/dist/src/certification/schema-index.d.ts.map +1 -1
- package/dist/src/certification/schema-index.js +27 -0
- package/dist/src/certification/target.d.ts +9 -1
- package/dist/src/certification/target.d.ts.map +1 -1
- package/dist/src/certification/target.js +54 -2
- package/dist/src/eval/cli.js +4 -2
- package/dist/src/eval/corpus.d.ts +7 -0
- package/dist/src/eval/corpus.d.ts.map +1 -1
- package/dist/src/eval/corpus.js +16 -0
- package/dist/src/eval/drivers/deterministic.d.ts.map +1 -1
- package/dist/src/eval/drivers/deterministic.js +13 -0
- package/dist/src/eval/grade.d.ts.map +1 -1
- package/dist/src/eval/grade.js +20 -1
- package/dist/src/eval/report.d.ts +5 -0
- package/dist/src/eval/report.d.ts.map +1 -1
- package/dist/src/eval/report.js +3 -0
- package/dist/src/eval/runner.d.ts +6 -0
- package/dist/src/eval/runner.d.ts.map +1 -1
- package/dist/src/eval/runner.js +21 -7
- package/dist/src/eval/standalone-corpus.d.ts +3 -0
- package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
- package/dist/src/eval/standalone-corpus.js +699 -0
- package/dist/src/eval/types.d.ts +25 -0
- package/dist/src/eval/types.d.ts.map +1 -1
- package/dist/src/helpers.d.ts +1 -1
- package/dist/src/helpers.d.ts.map +1 -1
- package/dist/src/index.d.ts +37 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +47 -22
- package/dist/src/nl-map-control.d.ts +70 -0
- package/dist/src/nl-map-control.d.ts.map +1 -0
- package/dist/src/nl-map-control.js +470 -0
- package/dist/src/provenance.d.ts +44 -0
- package/dist/src/provenance.d.ts.map +1 -0
- package/dist/src/provenance.js +37 -0
- package/dist/src/resources/styles.d.ts.map +1 -1
- package/dist/src/resources/styles.js +36 -3
- package/dist/src/stable-json.d.ts +14 -0
- package/dist/src/stable-json.d.ts.map +1 -0
- package/dist/src/stable-json.js +126 -0
- package/dist/src/styles.d.ts +2 -0
- package/dist/src/styles.d.ts.map +1 -1
- package/dist/src/styles.js +29 -3
- package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
- package/dist/src/tools/apply-style-preset.js +14 -2
- package/dist/src/tools/explain-capability-gap.d.ts +3 -3
- package/dist/src/tools/explain-capability-gap.d.ts.map +1 -1
- package/dist/src/tools/explain-capability-gap.js +1 -1
- package/dist/src/tools/get-style.d.ts.map +1 -1
- package/dist/src/tools/get-style.js +24 -13
- package/dist/src/tools/list-services.d.ts.map +1 -1
- package/dist/src/tools/list-services.js +16 -1
- package/package.json +27 -2
package/dist/src/eval/types.d.ts
CHANGED
|
@@ -53,6 +53,22 @@ export interface SuccessCriteria {
|
|
|
53
53
|
forbiddenTools?: string[];
|
|
54
54
|
/** Case-insensitive substrings the final answer must contain. */
|
|
55
55
|
answerMustInclude?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* SEMANTIC assertions (issue #369) — grade the MEANING of the answer, not just
|
|
58
|
+
* the tool trajectory. Ride on the deterministic control against grounded
|
|
59
|
+
* fixtures (known feature counts, known geographic facts), so a wrong number or
|
|
60
|
+
* a hallucinated place name fails the scenario.
|
|
61
|
+
*/
|
|
62
|
+
/** Case-insensitive substrings the final answer must NOT contain (anti-hallucination / wrong-answer guard). */
|
|
63
|
+
answerMustNotInclude?: string[];
|
|
64
|
+
/** Regular-expression sources the final answer must ALL match (case-insensitive) — exact numeric / value checks. */
|
|
65
|
+
answerMustMatch?: string[];
|
|
66
|
+
/**
|
|
67
|
+
* The prompt is deliberately ambiguous or unsupported: a correct client asks a
|
|
68
|
+
* clarifying question (or refuses) instead of guessing. When true, a `clarified`
|
|
69
|
+
* transcript grades as `pass` and a completed transcript grades as `fail`.
|
|
70
|
+
*/
|
|
71
|
+
expectClarification?: boolean;
|
|
56
72
|
}
|
|
57
73
|
/** One GIS workflow in the held-out corpus. */
|
|
58
74
|
export interface Scenario {
|
|
@@ -70,6 +86,15 @@ export interface Scenario {
|
|
|
70
86
|
tool: string;
|
|
71
87
|
args: Record<string, unknown>;
|
|
72
88
|
}[];
|
|
89
|
+
/**
|
|
90
|
+
* Ambiguity/refusal scenarios: the deterministic driver asks this clarifying
|
|
91
|
+
* question INSTEAD of running the script (an empty script is typical). Live LLM
|
|
92
|
+
* drivers decide for themselves whether to clarify from the prompt alone. Pair
|
|
93
|
+
* with `criteria.expectClarification: true`.
|
|
94
|
+
*/
|
|
95
|
+
clarify?: {
|
|
96
|
+
question: string;
|
|
97
|
+
};
|
|
73
98
|
}
|
|
74
99
|
/** Per-scenario grade for one model. */
|
|
75
100
|
export type GradeOutcome = "pass" | "fail" | "clarified" | "error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/eval/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAChF;AAED,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,sBAAsB,EAAE,OAAO,CAAC;IAChC,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sEAAsE;IACtE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/eval/types.ts"],"names":[],"mappings":"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"}
|
package/dist/src/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EsriGeometryType, EsriSpatialRel } from "@honua/sdk-js";
|
|
1
|
+
import type { EsriGeometryType, EsriSpatialRel } from "@honua/sdk-js/honua";
|
|
2
2
|
export declare const GEOMETRY_TYPES: readonly ["esriGeometryPoint", "esriGeometryPolyline", "esriGeometryPolygon", "esriGeometryEnvelope", "esriGeometryMultipoint"];
|
|
3
3
|
export declare function mapSpatialRel(rel: string | undefined): EsriSpatialRel | undefined;
|
|
4
4
|
export declare function clampLimit(limit: number | undefined): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAM5E,eAAO,MAAM,cAAc,iIAMjB,CAAC;AAQX,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAKjF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7C,YAAY,EAAE,gBAAgB,GAAG,SAAS,GACzC,gBAAgB,GAAG,SAAS,CA8B9B;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAE5F;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAWvE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAqBd"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
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
|
+
import { type NlMapControlMcpHost } from "./nl-map-control.js";
|
|
6
|
+
export { createNlMapControlMcpHost, registerNlMapControlMcpTools, } from "./nl-map-control.js";
|
|
7
|
+
export type { CreateNlMapControlMcpHostOptions, McpNlMapExecutionResponse, McpNlMapPlanResponse, McpNlMapReceipt, NlMapControlMcpHost, NlMapControlMcpRequestContext, } from "./nl-map-control.js";
|
|
5
8
|
export interface RuntimeOptions {
|
|
6
9
|
baseUrl: string;
|
|
7
10
|
apiKey: string | undefined;
|
|
@@ -9,15 +12,43 @@ export interface RuntimeOptions {
|
|
|
9
12
|
timeoutMs: number;
|
|
10
13
|
retryMaxRetries: number;
|
|
11
14
|
}
|
|
15
|
+
export interface CreateServerOptions {
|
|
16
|
+
/** Optional BYO-LLM/map-runtime host for the experimental NL map-plan tools. */
|
|
17
|
+
readonly nlMapControl?: NlMapControlMcpHost;
|
|
18
|
+
}
|
|
12
19
|
export declare const SERVER_VERSION: string;
|
|
13
20
|
export declare function resolveRuntimeOptions(env: NodeJS.ProcessEnv): RuntimeOptions;
|
|
14
21
|
export declare function createClientFromEnv(env?: NodeJS.ProcessEnv): HonuaClient;
|
|
15
22
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
23
|
+
* The PLATFORM-FREE standalone MCP surface (issue #369).
|
|
24
|
+
*
|
|
25
|
+
* This is the direct-SDK operator surface: nine read-only geospatial tools wired
|
|
26
|
+
* straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
|
|
27
|
+
* over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
|
|
28
|
+
* Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
|
|
29
|
+
* FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
|
|
30
|
+
* no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
|
|
31
|
+
* and what the platform-free certification lane + eval corpus exercise.
|
|
32
|
+
*
|
|
33
|
+
* Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
|
|
34
|
+
* a `/rest/services` catalog) degrade gracefully on a plain target: they return a
|
|
35
|
+
* structured "not available on this target" result rather than crashing or
|
|
36
|
+
* returning misleading empty data (see `capability.ts`).
|
|
37
|
+
*
|
|
38
|
+
* The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
|
|
39
|
+
* mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
|
|
40
|
+
* mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
|
|
41
|
+
* deployment. Standalone is the front door; the proxy is the upgrade path.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createServer(client: HonuaClient, options?: CreateServerOptions): McpServer;
|
|
44
|
+
/**
|
|
45
|
+
* Run the platform-free standalone server end-to-end over stdio.
|
|
46
|
+
*
|
|
47
|
+
* Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
|
|
48
|
+
* transport/auth) and exposes {@link createServer} — the nine read-only tools —
|
|
49
|
+
* to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
|
|
50
|
+
* `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
|
|
51
|
+
* required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
|
|
21
52
|
*/
|
|
22
|
-
export declare function
|
|
53
|
+
export declare function runStandalone(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
23
54
|
//# sourceMappingURL=index.d.ts.map
|
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;
|
|
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;AAc7F,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;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,mBAAwB,aA6FlF;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF"}
|
package/dist/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { HonuaClient } from "@honua/sdk-js";
|
|
5
5
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { registerNlMapControlMcpTools } from "./nl-map-control.js";
|
|
7
8
|
import * as layerSchemaResource from "./resources/layer-schema.js";
|
|
8
9
|
import * as servicesResource from "./resources/services.js";
|
|
9
10
|
import * as stylesResource from "./resources/styles.js";
|
|
@@ -16,6 +17,7 @@ import * as getStyle from "./tools/get-style.js";
|
|
|
16
17
|
import * as listServices from "./tools/list-services.js";
|
|
17
18
|
import * as queryFeatures from "./tools/query-features.js";
|
|
18
19
|
import * as statistics from "./tools/statistics.js";
|
|
20
|
+
export { createNlMapControlMcpHost, registerNlMapControlMcpTools, } from "./nl-map-control.js";
|
|
19
21
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
20
22
|
const DEFAULT_MAX_RETRIES = 2;
|
|
21
23
|
const DEFAULT_SERVER_VERSION = "0.0.3-alpha.0";
|
|
@@ -117,27 +119,44 @@ export function createClientFromEnv(env = process.env) {
|
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
122
|
+
* The PLATFORM-FREE standalone MCP surface (issue #369).
|
|
123
|
+
*
|
|
124
|
+
* This is the direct-SDK operator surface: nine read-only geospatial tools wired
|
|
125
|
+
* straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
|
|
126
|
+
* over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
|
|
127
|
+
* Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
|
|
128
|
+
* FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
|
|
129
|
+
* no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
|
|
130
|
+
* and what the platform-free certification lane + eval corpus exercise.
|
|
131
|
+
*
|
|
132
|
+
* Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
|
|
133
|
+
* a `/rest/services` catalog) degrade gracefully on a plain target: they return a
|
|
134
|
+
* structured "not available on this target" result rather than crashing or
|
|
135
|
+
* returning misleading empty data (see `capability.ts`).
|
|
136
|
+
*
|
|
137
|
+
* The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
|
|
138
|
+
* mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
|
|
139
|
+
* mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
|
|
140
|
+
* deployment. Standalone is the front door; the proxy is the upgrade path.
|
|
125
141
|
*/
|
|
126
|
-
export function createServer(client) {
|
|
142
|
+
export function createServer(client, options = {}) {
|
|
127
143
|
const server = new McpServer({
|
|
128
144
|
name: "honua",
|
|
129
145
|
version: SERVER_VERSION,
|
|
130
146
|
});
|
|
131
147
|
// ── Tools ──────────────────────────────────────────────────────
|
|
132
|
-
server.tool("honua_list_services", "Discover all available feature services. Set includeDetails=true for descriptions, layer counts, and spatial references.", listServices.schema.shape, async (args) => listServices.execute(client, listServices.schema.parse(args)));
|
|
148
|
+
server.tool("honua_list_services", "Discover all available feature services on any FeatureServer folder. Set includeDetails=true for descriptions, layer counts, and spatial references. Degrades gracefully when the target has no service catalog.", listServices.schema.shape, async (args) => listServices.execute(client, listServices.schema.parse(args)));
|
|
149
|
+
if (options.nlMapControl) {
|
|
150
|
+
registerNlMapControlMcpTools(server, options.nlMapControl);
|
|
151
|
+
}
|
|
133
152
|
server.tool("honua_describe_layer", "Get full schema for a layer — fields, geometry type, extent, relationships.", describeLayer.schema.shape, async (args) => describeLayer.execute(client, describeLayer.schema.parse(args)));
|
|
134
153
|
server.tool("honua_query_features", "Query features with attribute filters, spatial filters, field selection, and pagination. returnGeometry defaults to false to save tokens.", queryFeatures.schema.shape, async (args) => queryFeatures.execute(client, queryFeatures.schema.parse(args)));
|
|
135
154
|
server.tool("honua_count_features", "Count features matching a filter without returning data. Use before querying to check cardinality.", countFeatures.schema.shape, async (args) => countFeatures.execute(client, countFeatures.schema.parse(args)));
|
|
136
155
|
server.tool("honua_get_extent", "Get the spatial bounding box of features matching a filter.", getExtent.schema.shape, async (args) => getExtent.execute(client, getExtent.schema.parse(args)));
|
|
137
156
|
server.tool("honua_statistics", "Compute aggregate statistics (count, sum, avg, min, max, stddev) on a field, optionally grouped.", statistics.schema.shape, async (args) => statistics.execute(client, statistics.schema.parse(args)));
|
|
138
|
-
server.tool("honua_explain_capability_gap", "Explain whether a
|
|
139
|
-
server.tool("honua_get_style", "Resolve a style from
|
|
140
|
-
server.tool("honua_apply_style_preset", "Resolve a style preset and its MapLibre stylesheet for client-side application. Read-only: returns the stylesheet to apply locally; it does not mutate server state.", applyStylePreset.schema.shape, async (args) => applyStylePreset.execute(client, applyStylePreset.schema.parse(args)));
|
|
157
|
+
server.tool("honua_explain_capability_gap", "Explain whether a source/protocol supports a capability and suggest a safe fallback when it does not.", explainCapabilityGap.schema.shape, async (args) => explainCapabilityGap.execute(client, explainCapabilityGap.schema.parse(args)));
|
|
158
|
+
server.tool("honua_get_style", "Resolve a style from a server's OGC API – Styles surface. Pass styleId for the canonical StyleRef (style_id, title, encodings incl. inlined MapLibre); omit it to list available styles. On a plain FeatureServer with no styling surface, reports a structured 'not available on this target' result.", getStyle.schema.shape, async (args) => getStyle.execute(client, getStyle.schema.parse(args)));
|
|
159
|
+
server.tool("honua_apply_style_preset", "Resolve a style preset and its MapLibre stylesheet for client-side application. Read-only: returns the stylesheet to apply locally; it does not mutate server state. Degrades gracefully on a target with no styling surface.", applyStylePreset.schema.shape, async (args) => applyStylePreset.execute(client, applyStylePreset.schema.parse(args)));
|
|
141
160
|
// ── Resources ──────────────────────────────────────────────────
|
|
142
161
|
server.resource("services-catalog", servicesResource.uri, async (uri) => servicesResource.read(client));
|
|
143
162
|
server.resource("layer-schema", new ResourceTemplate(layerSchemaResource.uriTemplate, { list: undefined }), async (uri, params) => layerSchemaResource.read(client, params.encodedServiceId, params.layerId));
|
|
@@ -145,22 +164,28 @@ export function createServer(client) {
|
|
|
145
164
|
server.resource("style", new ResourceTemplate(stylesResource.uriTemplate, { list: undefined }), async (uri, params) => stylesResource.read(client, params.styleId));
|
|
146
165
|
return server;
|
|
147
166
|
}
|
|
148
|
-
/* v8 ignore start --
|
|
149
|
-
|
|
167
|
+
/* v8 ignore start -- live-process entry: wires the stdio transport to a real
|
|
168
|
+
SDK client against HONUA_BASE_URL; exercised by running the bin, not unit tests.
|
|
169
|
+
The unit-testable logic (option resolution, client construction, server wiring)
|
|
170
|
+
lives in the exported functions above and is covered there. */
|
|
150
171
|
/**
|
|
151
|
-
*
|
|
172
|
+
* Run the platform-free standalone server end-to-end over stdio.
|
|
152
173
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* (
|
|
156
|
-
*
|
|
157
|
-
*
|
|
174
|
+
* Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
|
|
175
|
+
* transport/auth) and exposes {@link createServer} — the nine read-only tools —
|
|
176
|
+
* to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
|
|
177
|
+
* `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
|
|
178
|
+
* required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
|
|
158
179
|
*/
|
|
180
|
+
export async function runStandalone(env = process.env) {
|
|
181
|
+
const client = createClientFromEnv(env);
|
|
182
|
+
const server = createServer(client);
|
|
183
|
+
const transport = new StdioServerTransport();
|
|
184
|
+
await server.connect(transport);
|
|
185
|
+
}
|
|
186
|
+
/** `honua-mcp` bin entry — the platform-free standalone server. */
|
|
159
187
|
async function main() {
|
|
160
|
-
|
|
161
|
-
"Use `honua-mcp-proxy` directly. Set HONUA_MCP_REMOTE_URL to the remote /mcp endpoint.\n");
|
|
162
|
-
const { runProxy } = await import("./proxy.js");
|
|
163
|
-
await runProxy();
|
|
188
|
+
await runStandalone();
|
|
164
189
|
}
|
|
165
190
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
166
191
|
main().catch((err) => {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type AgentApprovalUseConsumer } from "@honua/sdk-js/agent-safety";
|
|
2
|
+
import { type CreateNlMapControlOptions, NL_MAP_CONTROL_VERSION, type NlMapControl, type NlMapPlan, type NlMapPlanExecution } from "@honua/sdk-js/nl-map-control";
|
|
3
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
declare const MCP_NL_MAP_RECEIPT_KIND: "honua.mcp-nl-map-receipt";
|
|
5
|
+
export interface NlMapControlMcpRequestContext {
|
|
6
|
+
readonly signal?: AbortSignal;
|
|
7
|
+
/** Scopes authenticated by the MCP transport. Caller arguments never populate this field. */
|
|
8
|
+
readonly transportAuthorizationScopes?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
export interface CreateNlMapControlMcpHostOptions {
|
|
11
|
+
/** The identical controller configuration used by the in-app NL surface. */
|
|
12
|
+
readonly control: CreateNlMapControlOptions;
|
|
13
|
+
/** Atomic, host-authenticated approval-use store. Required for every approved MCP execution. */
|
|
14
|
+
readonly approvalUseConsumer: AgentApprovalUseConsumer;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve trusted scopes for transports without MCP `authInfo` (for example an
|
|
17
|
+
* embedded stdio host). The input contains transport-authenticated scopes only.
|
|
18
|
+
*/
|
|
19
|
+
readonly resolveAuthorizationScopes?: (transportScopes: readonly string[]) => readonly string[] | Promise<readonly string[]>;
|
|
20
|
+
}
|
|
21
|
+
export interface McpNlMapPlanResponse {
|
|
22
|
+
readonly plan: NlMapPlan;
|
|
23
|
+
readonly approvalRequired: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface McpNlMapReceipt {
|
|
26
|
+
readonly kind: typeof MCP_NL_MAP_RECEIPT_KIND;
|
|
27
|
+
readonly version: typeof NL_MAP_CONTROL_VERSION;
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly planId: string;
|
|
30
|
+
readonly planFingerprint: `sha256:${string}`;
|
|
31
|
+
readonly mode: NlMapPlanExecution["mode"];
|
|
32
|
+
readonly outcome: NlMapPlanExecution["outcome"];
|
|
33
|
+
readonly approvalDigest?: `sha256:${string}`;
|
|
34
|
+
readonly startedAt: string;
|
|
35
|
+
readonly completedAt: string;
|
|
36
|
+
readonly steps: NlMapPlanExecution["receipt"]["steps"];
|
|
37
|
+
/** Digest of the SDK receipt retained by the host. Its raw instruction is never returned over MCP. */
|
|
38
|
+
readonly sdkReceiptDigest: `sha256:${string}`;
|
|
39
|
+
readonly receiptDigest: `sha256:${string}`;
|
|
40
|
+
}
|
|
41
|
+
export interface McpNlMapExecutionResponse {
|
|
42
|
+
readonly planId: string;
|
|
43
|
+
readonly planFingerprint: `sha256:${string}`;
|
|
44
|
+
readonly mode: NlMapPlanExecution["mode"];
|
|
45
|
+
readonly outcome: NlMapPlanExecution["outcome"];
|
|
46
|
+
readonly receipt: McpNlMapReceipt;
|
|
47
|
+
}
|
|
48
|
+
export interface NlMapControlMcpHost {
|
|
49
|
+
readonly control: NlMapControl;
|
|
50
|
+
propose(input: {
|
|
51
|
+
readonly instruction: string;
|
|
52
|
+
}, context?: NlMapControlMcpRequestContext): Promise<McpNlMapPlanResponse>;
|
|
53
|
+
execute(input: {
|
|
54
|
+
readonly plan: unknown;
|
|
55
|
+
readonly approval?: unknown;
|
|
56
|
+
}, context?: NlMapControlMcpRequestContext): Promise<McpNlMapExecutionResponse>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Bind the experimental NL map controller to the MCP trust boundary.
|
|
60
|
+
*
|
|
61
|
+
* The host pre-authorizes and atomically consumes every approved step before
|
|
62
|
+
* delegating to `createNlMapControl`. This preserves the SDK's exact plan and
|
|
63
|
+
* signature checks while preventing approval replay through a second transport
|
|
64
|
+
* call. No caller-provided credential or cursor is included in MCP output.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createNlMapControlMcpHost(options: CreateNlMapControlMcpHostOptions): NlMapControlMcpHost;
|
|
67
|
+
/** Register `proposeMapPlan` and `executeMapPlan` on a real MCP server. */
|
|
68
|
+
export declare function registerNlMapControlMcpTools(server: McpServer, host: NlMapControlMcpHost): void;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=nl-map-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nl-map-control.d.ts","sourceRoot":"","sources":["../../src/nl-map-control.ts"],"names":[],"mappings":"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"}
|