@honua/mcp-server 0.0.20-alpha.0 → 0.1.0-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 (92) hide show
  1. package/README.md +92 -15
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +15 -7
  3. package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +6 -0
  4. package/certification/geospatial-mcp-schemas/index.json +88 -9
  5. package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +13 -9
  6. package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
  7. package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
  8. package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
  9. package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
  10. package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +13 -3
  11. package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
  12. package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
  13. package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
  14. package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +20 -0
  15. package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +5 -0
  16. package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
  17. package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
  18. package/dist/src/capability.d.ts +48 -0
  19. package/dist/src/capability.d.ts.map +1 -0
  20. package/dist/src/capability.js +44 -0
  21. package/dist/src/certification/census-data.d.ts +33 -0
  22. package/dist/src/certification/census-data.d.ts.map +1 -0
  23. package/dist/src/certification/census-data.js +506 -0
  24. package/dist/src/certification/census-fixture-client.d.ts +7 -0
  25. package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
  26. package/dist/src/certification/census-fixture-client.js +226 -0
  27. package/dist/src/certification/certifier.d.ts +7 -1
  28. package/dist/src/certification/certifier.d.ts.map +1 -1
  29. package/dist/src/certification/certifier.js +52 -7
  30. package/dist/src/certification/cli.js +19 -4
  31. package/dist/src/certification/contracts.d.ts +41 -0
  32. package/dist/src/certification/contracts.d.ts.map +1 -1
  33. package/dist/src/certification/contracts.js +71 -0
  34. package/dist/src/certification/fixtures.d.ts +13 -0
  35. package/dist/src/certification/fixtures.d.ts.map +1 -1
  36. package/dist/src/certification/fixtures.js +28 -0
  37. package/dist/src/certification/json-schema.d.ts +23 -1
  38. package/dist/src/certification/json-schema.d.ts.map +1 -1
  39. package/dist/src/certification/json-schema.js +10 -2
  40. package/dist/src/certification/lifecycle.d.ts +14 -0
  41. package/dist/src/certification/lifecycle.d.ts.map +1 -0
  42. package/dist/src/certification/lifecycle.js +326 -0
  43. package/dist/src/certification/operator-catalog.d.ts +8 -0
  44. package/dist/src/certification/operator-catalog.d.ts.map +1 -1
  45. package/dist/src/certification/operator-catalog.js +189 -5
  46. package/dist/src/certification/report.d.ts.map +1 -1
  47. package/dist/src/certification/report.js +13 -0
  48. package/dist/src/certification/run.d.ts.map +1 -1
  49. package/dist/src/certification/run.js +1 -0
  50. package/dist/src/certification/schema-index.d.ts +25 -0
  51. package/dist/src/certification/schema-index.d.ts.map +1 -1
  52. package/dist/src/certification/schema-index.js +27 -0
  53. package/dist/src/certification/target.d.ts +9 -1
  54. package/dist/src/certification/target.d.ts.map +1 -1
  55. package/dist/src/certification/target.js +54 -2
  56. package/dist/src/eval/cli.js +4 -2
  57. package/dist/src/eval/corpus.d.ts +7 -0
  58. package/dist/src/eval/corpus.d.ts.map +1 -1
  59. package/dist/src/eval/corpus.js +16 -0
  60. package/dist/src/eval/drivers/deterministic.d.ts.map +1 -1
  61. package/dist/src/eval/drivers/deterministic.js +13 -0
  62. package/dist/src/eval/grade.d.ts.map +1 -1
  63. package/dist/src/eval/grade.js +20 -1
  64. package/dist/src/eval/report.d.ts +5 -0
  65. package/dist/src/eval/report.d.ts.map +1 -1
  66. package/dist/src/eval/report.js +3 -0
  67. package/dist/src/eval/runner.d.ts +6 -0
  68. package/dist/src/eval/runner.d.ts.map +1 -1
  69. package/dist/src/eval/runner.js +21 -7
  70. package/dist/src/eval/standalone-corpus.d.ts +3 -0
  71. package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
  72. package/dist/src/eval/standalone-corpus.js +699 -0
  73. package/dist/src/eval/types.d.ts +25 -0
  74. package/dist/src/eval/types.d.ts.map +1 -1
  75. package/dist/src/index.d.ts +29 -5
  76. package/dist/src/index.d.ts.map +1 -1
  77. package/dist/src/index.js +41 -21
  78. package/dist/src/provenance.d.ts +44 -0
  79. package/dist/src/provenance.d.ts.map +1 -0
  80. package/dist/src/provenance.js +37 -0
  81. package/dist/src/resources/styles.d.ts.map +1 -1
  82. package/dist/src/resources/styles.js +36 -3
  83. package/dist/src/styles.d.ts +2 -0
  84. package/dist/src/styles.d.ts.map +1 -1
  85. package/dist/src/styles.js +29 -3
  86. package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
  87. package/dist/src/tools/apply-style-preset.js +14 -2
  88. package/dist/src/tools/get-style.d.ts.map +1 -1
  89. package/dist/src/tools/get-style.js +24 -13
  90. package/dist/src/tools/list-services.d.ts.map +1 -1
  91. package/dist/src/tools/list-services.js +16 -1
  92. package/package.json +11 -2
@@ -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;CAC9B;AAED,+CAA+C;AAC/C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;CAC3D;AAED,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,qDAAqD;IACrD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC;IACtE,gEAAgE;IAChE,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACpF"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/eval/types.ts"],"names":[],"mappings":"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"}
@@ -13,11 +13,35 @@ export declare const SERVER_VERSION: string;
13
13
  export declare function resolveRuntimeOptions(env: NodeJS.ProcessEnv): RuntimeOptions;
14
14
  export declare function createClientFromEnv(env?: NodeJS.ProcessEnv): HonuaClient;
15
15
  /**
16
- * @deprecated Static, hand-maintained 9-tool MCP server. This is NO LONGER the
17
- * product surface and is NOT on the certification trust path — the honua-server
18
- * `/mcp` catalog is the source of truth, mirrored to stdio by `honua-mcp-proxy`
19
- * (see {@link runProxy}). `createServer` is retained ONLY as a labeled offline
20
- * test fixture (proxy parity harness). Do not ship it as the operator surface.
16
+ * The PLATFORM-FREE standalone MCP surface (issue #369).
17
+ *
18
+ * This is the direct-SDK operator surface: nine read-only geospatial tools wired
19
+ * straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
20
+ * over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
21
+ * Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
22
+ * FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
23
+ * no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
24
+ * and what the platform-free certification lane + eval corpus exercise.
25
+ *
26
+ * Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
27
+ * a `/rest/services` catalog) degrade gracefully on a plain target: they return a
28
+ * structured "not available on this target" result rather than crashing or
29
+ * returning misleading empty data (see `capability.ts`).
30
+ *
31
+ * The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
32
+ * mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
33
+ * mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
34
+ * deployment. Standalone is the front door; the proxy is the upgrade path.
21
35
  */
22
36
  export declare function createServer(client: HonuaClient): McpServer;
37
+ /**
38
+ * Run the platform-free standalone server end-to-end over stdio.
39
+ *
40
+ * Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
41
+ * transport/auth) and exposes {@link createServer} — the nine read-only tools —
42
+ * to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
43
+ * `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
44
+ * required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
45
+ */
46
+ export declare function runStandalone(env?: NodeJS.ProcessEnv): Promise<void>;
23
47
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAgBtF,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;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;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,aAyF/C"}
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;AAgBtF,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;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,aAyF/C;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF"}
package/dist/src/index.js CHANGED
@@ -117,11 +117,25 @@ export function createClientFromEnv(env = process.env) {
117
117
  });
118
118
  }
119
119
  /**
120
- * @deprecated Static, hand-maintained 9-tool MCP server. This is NO LONGER the
121
- * product surface and is NOT on the certification trust path — the honua-server
122
- * `/mcp` catalog is the source of truth, mirrored to stdio by `honua-mcp-proxy`
123
- * (see {@link runProxy}). `createServer` is retained ONLY as a labeled offline
124
- * test fixture (proxy parity harness). Do not ship it as the operator surface.
120
+ * The PLATFORM-FREE standalone MCP surface (issue #369).
121
+ *
122
+ * This is the direct-SDK operator surface: nine read-only geospatial tools wired
123
+ * straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
124
+ * over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
125
+ * Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
126
+ * FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
127
+ * no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
128
+ * and what the platform-free certification lane + eval corpus exercise.
129
+ *
130
+ * Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
131
+ * a `/rest/services` catalog) degrade gracefully on a plain target: they return a
132
+ * structured "not available on this target" result rather than crashing or
133
+ * returning misleading empty data (see `capability.ts`).
134
+ *
135
+ * The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
136
+ * mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
137
+ * mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
138
+ * deployment. Standalone is the front door; the proxy is the upgrade path.
125
139
  */
126
140
  export function createServer(client) {
127
141
  const server = new McpServer({
@@ -129,15 +143,15 @@ export function createServer(client) {
129
143
  version: SERVER_VERSION,
130
144
  });
131
145
  // ── 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)));
146
+ 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)));
133
147
  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
148
  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
149
  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
150
  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
151
  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 Honua 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)));
139
- server.tool("honua_get_style", "Resolve a style from the 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.", getStyle.schema.shape, async (args) => getStyle.execute(client, getStyle.schema.parse(args)));
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)));
152
+ 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)));
153
+ 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)));
154
+ 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
155
  // ── Resources ──────────────────────────────────────────────────
142
156
  server.resource("services-catalog", servicesResource.uri, async (uri) => servicesResource.read(client));
143
157
  server.resource("layer-schema", new ResourceTemplate(layerSchemaResource.uriTemplate, { list: undefined }), async (uri, params) => layerSchemaResource.read(client, params.encodedServiceId, params.layerId));
@@ -145,22 +159,28 @@ export function createServer(client) {
145
159
  server.resource("style", new ResourceTemplate(stylesResource.uriTemplate, { list: undefined }), async (uri, params) => stylesResource.read(client, params.styleId));
146
160
  return server;
147
161
  }
148
- /* v8 ignore start -- deprecated bin entry: delegates to the dynamic proxy at
149
- runtime; the proxy's logic is unit-tested in proxy.test.ts. */
162
+ /* v8 ignore start -- live-process entry: wires the stdio transport to a real
163
+ SDK client against HONUA_BASE_URL; exercised by running the bin, not unit tests.
164
+ The unit-testable logic (option resolution, client construction, server wiring)
165
+ lives in the exported functions above and is covered there. */
150
166
  /**
151
- * Deprecated `honua-mcp` bin entry.
167
+ * Run the platform-free standalone server end-to-end over stdio.
152
168
  *
153
- * The static 9-tool server has been retired from the product/trust path. This
154
- * bin now prints a deprecation notice and launches the dynamic proxy
155
- * (`honua-mcp-proxy`), which mirrors the honua-server `/mcp` catalog. It
156
- * therefore requires `HONUA_MCP_REMOTE_URL` (the remote `/mcp` endpoint) instead
157
- * of the old `HONUA_BASE_URL`.
169
+ * Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
170
+ * transport/auth) and exposes {@link createServer} — the nine read-only tools —
171
+ * to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
172
+ * `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
173
+ * required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
158
174
  */
175
+ export async function runStandalone(env = process.env) {
176
+ const client = createClientFromEnv(env);
177
+ const server = createServer(client);
178
+ const transport = new StdioServerTransport();
179
+ await server.connect(transport);
180
+ }
181
+ /** `honua-mcp` bin entry — the platform-free standalone server. */
159
182
  async function main() {
160
- process.stderr.write("DEPRECATION: `honua-mcp` is deprecated and now proxies the honua-server /mcp catalog. " +
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();
183
+ await runStandalone();
164
184
  }
165
185
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
166
186
  main().catch((err) => {
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Self-proving provenance for certification & eval artifacts (A5 provability).
3
+ *
4
+ * A published report is only evidence if a reader can verify WHAT was tested and
5
+ * WITH WHICH code. Every artifact carries a `provenance` block naming the target
6
+ * surface, the negotiated MCP protocol version, the advertised tool count, the
7
+ * auth mode, the git SHA of the certification/eval suite, and a timestamp. That
8
+ * turns "we pass" into "here is the exact, reproducible run that passed".
9
+ */
10
+ /** How the eval/cert authenticated to the surface (proves the run was authed). */
11
+ export type AuthMode = "bearer" | "api-key" | "anonymous" | "none" | "unknown";
12
+ /** The reproducibility fingerprint attached to every artifact. */
13
+ export interface SuiteProvenance {
14
+ /** Git SHA of the certification/eval suite that produced this artifact. */
15
+ suiteGitSha: string;
16
+ /** Where the SHA came from (CI env, local git, or unresolved). */
17
+ suiteGitShaSource: "env" | "git" | "unknown";
18
+ /** Human-readable target surface / URL under test. */
19
+ targetUrl: string;
20
+ /** MCP protocol version negotiated with the surface, when observable. */
21
+ protocolVersion: string | null;
22
+ /** Number of tools the surface advertised. */
23
+ toolCount: number;
24
+ /** How the run authenticated to the surface. */
25
+ authMode: AuthMode;
26
+ }
27
+ /**
28
+ * Resolve the git SHA of the running suite. Prefers the CI-provided `GITHUB_SHA`
29
+ * (or an explicit `HONUA_SUITE_GIT_SHA` override), then falls back to
30
+ * `git rev-parse HEAD`, then to `"unknown"` so an artifact is always self-labeled
31
+ * even outside a git checkout.
32
+ */
33
+ export declare function resolveSuiteGitSha(env?: NodeJS.ProcessEnv): {
34
+ sha: string;
35
+ source: SuiteProvenance["suiteGitShaSource"];
36
+ };
37
+ /**
38
+ * Read the negotiated MCP protocol version off a connected client, if its
39
+ * transport exposes one (the streamable-HTTP client transport does after
40
+ * `initialize`). Returns `null` for transports that do not surface it
41
+ * (stdio / in-memory), so the field is honest rather than fabricated.
42
+ */
43
+ export declare function readNegotiatedProtocolVersion(client: unknown): string | null;
44
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/provenance.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/E,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,iBAAiB,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;IAC7C,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC,mBAAmB,CAAC,CAAC;CAC9C,CAiBA;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI5E"}
@@ -0,0 +1,37 @@
1
+ import { execFileSync } from "node:child_process";
2
+ /**
3
+ * Resolve the git SHA of the running suite. Prefers the CI-provided `GITHUB_SHA`
4
+ * (or an explicit `HONUA_SUITE_GIT_SHA` override), then falls back to
5
+ * `git rev-parse HEAD`, then to `"unknown"` so an artifact is always self-labeled
6
+ * even outside a git checkout.
7
+ */
8
+ export function resolveSuiteGitSha(env = process.env) {
9
+ const fromEnv = (env.HONUA_SUITE_GIT_SHA ?? env.GITHUB_SHA ?? "").trim();
10
+ if (fromEnv.length > 0) {
11
+ return { sha: fromEnv, source: "env" };
12
+ }
13
+ try {
14
+ const sha = execFileSync("git", ["rev-parse", "HEAD"], {
15
+ encoding: "utf8",
16
+ stdio: ["ignore", "pipe", "ignore"],
17
+ }).trim();
18
+ if (sha.length > 0) {
19
+ return { sha, source: "git" };
20
+ }
21
+ }
22
+ catch {
23
+ // not a git checkout / git unavailable
24
+ }
25
+ return { sha: "unknown", source: "unknown" };
26
+ }
27
+ /**
28
+ * Read the negotiated MCP protocol version off a connected client, if its
29
+ * transport exposes one (the streamable-HTTP client transport does after
30
+ * `initialize`). Returns `null` for transports that do not surface it
31
+ * (stdio / in-memory), so the field is honest rather than fabricated.
32
+ */
33
+ export function readNegotiatedProtocolVersion(client) {
34
+ const transport = client.transport;
35
+ const version = transport?.protocolVersion;
36
+ return typeof version === "string" && version.length > 0 ? version : null;
37
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/resources/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,8DAA8D;AAC9D,eAAO,MAAM,GAAG,mBAAmB,CAAC;AAEpC,6EAA6E;AAC7E,eAAO,MAAM,WAAW,6BAA6B,CAAC;AAEtD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW;;;;;;GAiBpD;AAED;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM;;;;;;GAY9D"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/resources/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQjD,8DAA8D;AAC9D,eAAO,MAAM,GAAG,mBAAmB,CAAC;AAEpC,6EAA6E;AAC7E,eAAO,MAAM,WAAW,6BAA6B,CAAC;AAEtD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW;;;;;;GAyBpD;AAeD;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM;;;;;;GAoB9D"}
@@ -1,4 +1,7 @@
1
- import { listStyles, resolveStyleRef } from "../styles.js";
1
+ import { CapabilityUnavailableError, capabilityUnavailablePayload } from "../capability.js";
2
+ import { STYLE_SURFACE, listStyles, resolveStyleRef } from "../styles.js";
3
+ const STYLE_GUIDANCE = "This target is a plain FeatureServer/OGC endpoint with no OGC API - Styles surface. " +
4
+ "Build a MapLibre style client-side, or point honua-mcp at a Honua deployment for managed styles.";
2
5
  /** URI for the styles catalog resource (`honua://styles`). */
3
6
  export const uri = "honua://styles";
4
7
  /** URI template for a single style resource (`honua://styles/{styleId}`). */
@@ -8,7 +11,16 @@ export const uriTemplate = "honua://styles/{styleId}";
8
11
  * with its stable styleId, title, and canonical URI (`honua://styles/{id}`).
9
12
  */
10
13
  export async function readCatalog(client) {
11
- const list = await listStyles(client);
14
+ let list;
15
+ try {
16
+ list = await listStyles(client);
17
+ }
18
+ catch (err) {
19
+ if (err instanceof CapabilityUnavailableError) {
20
+ return unavailableContents(uri, err.reason);
21
+ }
22
+ throw err;
23
+ }
12
24
  const styles = list.styles.map((entry) => ({
13
25
  styleId: entry.id,
14
26
  title: entry.title ?? entry.id,
@@ -24,13 +36,34 @@ export async function readCatalog(client) {
24
36
  ],
25
37
  };
26
38
  }
39
+ /** Structured "not available on this target" resource body (platform-free mode). */
40
+ function unavailableContents(resourceUri, reason) {
41
+ return {
42
+ contents: [
43
+ {
44
+ uri: resourceUri,
45
+ mimeType: "application/json",
46
+ text: JSON.stringify(capabilityUnavailablePayload(STYLE_SURFACE, reason, STYLE_GUIDANCE), null, 2),
47
+ },
48
+ ],
49
+ };
50
+ }
27
51
  /**
28
52
  * Read a single style as a `StyleRef` projection (style_id / title /
29
53
  * description / style_version / encodings / legend_url), resolved against the
30
54
  * server's `/ogc/styles/{styleId}` surface.
31
55
  */
32
56
  export async function read(client, styleId) {
33
- const styleRef = await resolveStyleRef(client, styleId);
57
+ let styleRef;
58
+ try {
59
+ styleRef = await resolveStyleRef(client, styleId);
60
+ }
61
+ catch (err) {
62
+ if (err instanceof CapabilityUnavailableError) {
63
+ return unavailableContents(`honua://styles/${encodeURIComponent(styleId)}`, err.reason);
64
+ }
65
+ throw err;
66
+ }
34
67
  return {
35
68
  contents: [
36
69
  {
@@ -1,4 +1,6 @@
1
1
  import type { HonuaClient } from "@honua/sdk-js";
2
+ /** Human label for the Honua-only styling surface (capability degradation). */
3
+ export declare const STYLE_SURFACE = "OGC API - Styles";
2
4
  /**
3
5
  * Thin read-only client over the server's OGC API – Styles surface
4
6
  * (honua-server, ADR-0048):
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AAEH,qEAAqE;AACrE,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,sCAAsC,CAAC;AAE7E,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,4CAA4C,CAAC;AAE3E,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,4CAA4C,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;CAIlB,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,eAAe,CAAC;AAE3D,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,GAAG,MAAM,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAeD,mDAAmD;AACnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAoB3E;AAED,qEAAqE;AACrE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGtG;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElG;AASD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4B7F"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,+EAA+E;AAC/E,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AAEH,qEAAqE;AACrE,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,sCAAsC,CAAC;AAE7E,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,4CAA4C,CAAC;AAE3E,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,4CAA4C,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;CAIlB,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,eAAe,CAAC;AAE3D,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,GAAG,MAAM,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAqCD,mDAAmD;AACnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuB3E;AAED,qEAAqE;AACrE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGtG;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElG;AASD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4B7F"}
@@ -1,3 +1,6 @@
1
+ import { CapabilityUnavailableError } from "./capability.js";
2
+ /** Human label for the Honua-only styling surface (capability degradation). */
3
+ export const STYLE_SURFACE = "OGC API - Styles";
1
4
  /**
2
5
  * Thin read-only client over the server's OGC API – Styles surface
3
6
  * (honua-server, ADR-0048):
@@ -38,8 +41,28 @@ function isRecord(value) {
38
41
  return typeof value === "object" && value !== null && !Array.isArray(value);
39
42
  }
40
43
  async function getJson(client, path, accept) {
41
- const response = await client.pipelineFetch("GET", path, { headers: { Accept: accept } });
42
- return (await response.json());
44
+ let response;
45
+ try {
46
+ response = await client.pipelineFetch("GET", path, { headers: { Accept: accept } });
47
+ }
48
+ catch (err) {
49
+ // A plain FeatureServer / OGC-Features endpoint has no styling surface, so
50
+ // `GET /ogc/styles*` fails (404, network error, or the SDK's non-2xx throw).
51
+ // Degrade gracefully rather than surfacing a raw crash.
52
+ throw new CapabilityUnavailableError(STYLE_SURFACE, `request to ${path} failed: ${errorMessage(err)}`);
53
+ }
54
+ if (!response.ok) {
55
+ throw new CapabilityUnavailableError(STYLE_SURFACE, `${path} returned HTTP ${response.status}`);
56
+ }
57
+ try {
58
+ return (await response.json());
59
+ }
60
+ catch {
61
+ throw new CapabilityUnavailableError(STYLE_SURFACE, `${path} did not return JSON (target has no OGC API - Styles surface)`);
62
+ }
63
+ }
64
+ function errorMessage(err) {
65
+ return err instanceof Error ? err.message : String(err);
43
66
  }
44
67
  function stylePath(styleId) {
45
68
  return `${OGC_STYLES_PATH}/${encodeURIComponent(styleId)}`;
@@ -48,7 +71,10 @@ function stylePath(styleId) {
48
71
  export async function listStyles(client) {
49
72
  const body = await getJson(client, OGC_STYLES_PATH, "application/json");
50
73
  if (!isRecord(body) || !Array.isArray(body.styles)) {
51
- return { styles: [] };
74
+ // A well-formed styles document always carries a `styles` array (possibly
75
+ // empty). Anything else means the target is not an OGC API - Styles surface;
76
+ // report that honestly instead of returning a misleading empty catalog.
77
+ throw new CapabilityUnavailableError(STYLE_SURFACE, `${OGC_STYLES_PATH} response is not a styles document`);
52
78
  }
53
79
  const styles = [];
54
80
  for (const entry of body.styles) {
@@ -1 +1 @@
1
- {"version":3,"file":"apply-style-preset.d.ts","sourceRoot":"","sources":["../../../src/tools/apply-style-preset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,MAAM;;;;;;EAEjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GAa9D"}
1
+ {"version":3,"file":"apply-style-preset.d.ts","sourceRoot":"","sources":["../../../src/tools/apply-style-preset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,eAAO,MAAM,MAAM;;;;;;EAEjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GAqB9D"}
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { CapabilityUnavailableError, capabilityUnavailable } from "../capability.js";
2
3
  import { jsonText } from "../helpers.js";
3
- import { resolveStyleRef } from "../styles.js";
4
+ import { STYLE_SURFACE, resolveStyleRef } from "../styles.js";
5
+ const STYLE_GUIDANCE = "This target is a plain FeatureServer/OGC endpoint with no style presets. " +
6
+ "Build a MapLibre style client-side from the layer's fields, or use a Honua deployment for managed presets.";
4
7
  export const schema = z.object({
5
8
  styleId: z.string().describe("Stable style identifier of the preset to resolve."),
6
9
  });
@@ -18,7 +21,16 @@ export async function execute(client, input) {
18
21
  // `resolveStyleRef` already fetches the MapLibre stylesheet and inlines it on
19
22
  // the `mapbox-style` encoding, so reuse that body instead of issuing a second
20
23
  // identical GET /ogc/styles/{id}.
21
- const styleRef = await resolveStyleRef(client, input.styleId);
24
+ let styleRef;
25
+ try {
26
+ styleRef = await resolveStyleRef(client, input.styleId);
27
+ }
28
+ catch (err) {
29
+ if (err instanceof CapabilityUnavailableError) {
30
+ return capabilityUnavailable(STYLE_SURFACE, err.reason, STYLE_GUIDANCE);
31
+ }
32
+ throw err;
33
+ }
22
34
  const stylesheet = styleRef.encodings.find((e) => e.encoding === "mapbox-style")?.inlineBody ?? null;
23
35
  return jsonText({
24
36
  style: styleRef,
@@ -1 +1 @@
1
- {"version":3,"file":"get-style.d.ts","sourceRoot":"","sources":["../../../src/tools/get-style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,MAAM;;;;;;EAEjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GAe9D"}
1
+ {"version":3,"file":"get-style.d.ts","sourceRoot":"","sources":["../../../src/tools/get-style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,eAAO,MAAM,MAAM;;;;;;EAEjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GAsB9D"}
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { CapabilityUnavailableError, capabilityUnavailable } from "../capability.js";
2
3
  import { jsonText } from "../helpers.js";
3
- import { listStyles, resolveStyleRef } from "../styles.js";
4
+ import { STYLE_SURFACE, listStyles, resolveStyleRef } from "../styles.js";
5
+ const STYLE_GUIDANCE = "This target is a plain FeatureServer/OGC endpoint with no server-side style catalog. " +
6
+ "Query features and build a MapLibre style client-side, or point honua-mcp at a Honua deployment for managed styles.";
4
7
  export const schema = z.object({
5
8
  styleId: z.string().optional().describe("Stable style identifier. Omit to list the styles available on the server."),
6
9
  });
@@ -12,17 +15,25 @@ export const schema = z.object({
12
15
  * is available.
13
16
  */
14
17
  export async function execute(client, input) {
15
- if (!input.styleId) {
16
- const list = await listStyles(client);
17
- return jsonText({
18
- styles: list.styles.map((entry) => ({
19
- styleId: entry.id,
20
- title: entry.title ?? entry.id,
21
- uri: `honua://styles/${encodeURIComponent(entry.id)}`,
22
- })),
23
- default: list.default ?? null,
24
- });
18
+ try {
19
+ if (!input.styleId) {
20
+ const list = await listStyles(client);
21
+ return jsonText({
22
+ styles: list.styles.map((entry) => ({
23
+ styleId: entry.id,
24
+ title: entry.title ?? entry.id,
25
+ uri: `honua://styles/${encodeURIComponent(entry.id)}`,
26
+ })),
27
+ default: list.default ?? null,
28
+ });
29
+ }
30
+ const styleRef = await resolveStyleRef(client, input.styleId);
31
+ return jsonText(styleRef);
32
+ }
33
+ catch (err) {
34
+ if (err instanceof CapabilityUnavailableError) {
35
+ return capabilityUnavailable(STYLE_SURFACE, err.reason, STYLE_GUIDANCE);
36
+ }
37
+ throw err;
25
38
  }
26
- const styleRef = await resolveStyleRef(client, input.styleId);
27
- return jsonText(styleRef);
28
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list-services.d.ts","sourceRoot":"","sources":["../../../src/tools/list-services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,MAAM;;;;;;EAQjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAI3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GAgC9D"}
1
+ {"version":3,"file":"list-services.d.ts","sourceRoot":"","sources":["../../../src/tools/list-services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,MAAM;;;;;;EAQjB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAI3C,wBAAsB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK;;;;;GA+C9D"}
@@ -9,7 +9,22 @@ export const schema = z.object({
9
9
  });
10
10
  const METADATA_CONCURRENCY = 8;
11
11
  export async function execute(client, input) {
12
- const response = await client.listServices();
12
+ let response;
13
+ try {
14
+ response = await client.listServices();
15
+ }
16
+ catch (err) {
17
+ // Some targets expose no service catalog at `/rest/services` — e.g. a base
18
+ // URL pointed straight at a single FeatureServer, or an OGC-only endpoint.
19
+ // Degrade to a structured note instead of crashing: the agent can still
20
+ // describe/query a known service+layer directly.
21
+ return jsonText({
22
+ catalogAvailable: false,
23
+ services: [],
24
+ reason: `service catalog is not available on this target: ${err instanceof Error ? err.message : String(err)}`,
25
+ guidance: "No /rest/services directory here. Call honua_describe_layer / honua_query_features with a known serviceId and layerId, or point the base URL at an ArcGIS folder that lists services.",
26
+ });
27
+ }
13
28
  const services = (response.services ?? []).filter((s) => s.type === "FeatureServer");
14
29
  if (!input.includeDetails) {
15
30
  return jsonText(services.map((s) => ({ serviceId: s.name, type: s.type })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honua/mcp-server",
3
- "version": "0.0.20-alpha.0",
3
+ "version": "0.1.0-beta.0",
4
4
  "description": "MCP server for Honua geospatial feature services",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,11 +16,15 @@
16
16
  "test:unit": "vitest run",
17
17
  "test:coverage": "vitest run --coverage",
18
18
  "certify": "npm run build --silent && node dist/src/certification/cli.js",
19
+ "certify:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
19
20
  "test:certification": "npm run build --silent && vitest run test/certification && node dist/src/certification/cli.js",
20
21
  "test:certification:artifact": "npm run build --silent && node dist/src/certification/cli.js --artifact-only",
22
+ "test:certification:standalone": "npm run build --silent && node dist/src/certification/cli.js --target standalone",
21
23
  "eval": "npm run build --silent && node dist/src/eval/cli.js",
22
24
  "eval:offline": "npm run build --silent && node dist/src/eval/cli.js --offline",
25
+ "eval:standalone": "npm run build --silent && node dist/src/eval/cli.js --corpus standalone --offline",
23
26
  "eval:live": "npm run build --silent && node dist/src/eval/cli.js --corpus operator --artifact-only",
27
+ "leaderboard": "node scripts/render-leaderboard.mjs",
24
28
  "test:eval": "npm run build --silent && vitest run test/eval && node dist/src/eval/cli.js --offline",
25
29
  "test:eval:artifact": "npm run build --silent && node dist/src/eval/cli.js --artifact-only",
26
30
  "lint": "biome lint src/ test/",
@@ -40,7 +44,7 @@
40
44
  "hono": "^4.12.21"
41
45
  },
42
46
  "peerDependencies": {
43
- "@honua/sdk-js": "^0.0.20-alpha.0"
47
+ "@honua/sdk-js": "^0.1.0-beta.0"
44
48
  },
45
49
  "devDependencies": {
46
50
  "@biomejs/biome": "1.9.4",
@@ -57,5 +61,10 @@
57
61
  ],
58
62
  "engines": {
59
63
  "node": ">=20.0.0"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/honua-io/honua-sdk-js.git",
68
+ "directory": "mcp"
60
69
  }
61
70
  }