@honua/mcp-server 0.0.10-alpha.0 → 0.0.20-alpha.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 (147) hide show
  1. package/README.md +167 -0
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +33 -0
  3. package/certification/geospatial-mcp-schemas/README.md +90 -0
  4. package/certification/geospatial-mcp-schemas/common/geoprocessing-error.schema.json +45 -0
  5. package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +73 -0
  6. package/certification/geospatial-mcp-schemas/index.json +311 -0
  7. package/certification/geospatial-mcp-schemas/resources/app-package.schema.json +20 -0
  8. package/certification/geospatial-mcp-schemas/resources/app-template.schema.json +15 -0
  9. package/certification/geospatial-mcp-schemas/resources/artifact.schema.json +69 -0
  10. package/certification/geospatial-mcp-schemas/resources/deployment.schema.json +27 -0
  11. package/certification/geospatial-mcp-schemas/resources/map-package.schema.json +21 -0
  12. package/certification/geospatial-mcp-schemas/resources/map-template.schema.json +15 -0
  13. package/certification/geospatial-mcp-schemas/resources/provenance.schema.json +50 -0
  14. package/certification/geospatial-mcp-schemas/resources/published-service.schema.json +21 -0
  15. package/certification/geospatial-mcp-schemas/resources/result-package.schema.json +64 -0
  16. package/certification/geospatial-mcp-schemas/resources/style.schema.json +12 -0
  17. package/certification/geospatial-mcp-schemas/resources/theme.schema.json +12 -0
  18. package/certification/geospatial-mcp-schemas/resources/workspace.schema.json +63 -0
  19. package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +23 -0
  20. package/certification/geospatial-mcp-schemas/tools/cancel_job.schema.json +15 -0
  21. package/certification/geospatial-mcp-schemas/tools/clarify_intent.schema.json +69 -0
  22. package/certification/geospatial-mcp-schemas/tools/compose_mixed_protocol_map.schema.json +21 -0
  23. package/certification/geospatial-mcp-schemas/tools/create_app_package.schema.json +33 -0
  24. package/certification/geospatial-mcp-schemas/tools/create_map_package.schema.json +41 -0
  25. package/certification/geospatial-mcp-schemas/tools/execute_plan.schema.json +64 -0
  26. package/certification/geospatial-mcp-schemas/tools/geocode_address.schema.json +36 -0
  27. package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +30 -0
  28. package/certification/geospatial-mcp-schemas/tools/ground_candidates.schema.json +66 -0
  29. package/certification/geospatial-mcp-schemas/tools/list_capabilities.schema.json +21 -0
  30. package/certification/geospatial-mcp-schemas/tools/list_layers.schema.json +14 -0
  31. package/certification/geospatial-mcp-schemas/tools/plan_analysis.schema.json +19 -0
  32. package/certification/geospatial-mcp-schemas/tools/preview_app_package.schema.json +15 -0
  33. package/certification/geospatial-mcp-schemas/tools/preview_map_package.schema.json +17 -0
  34. package/certification/geospatial-mcp-schemas/tools/preview_package.schema.json +40 -0
  35. package/certification/geospatial-mcp-schemas/tools/propose_operation.schema.json +28 -0
  36. package/certification/geospatial-mcp-schemas/tools/publish_result.schema.json +29 -0
  37. package/certification/geospatial-mcp-schemas/tools/publish_service.schema.json +59 -0
  38. package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +54 -0
  39. package/certification/geospatial-mcp-schemas/tools/refine_map_package.schema.json +22 -0
  40. package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +55 -0
  41. package/certification/geospatial-mcp-schemas/tools/resolve_entity.schema.json +30 -0
  42. package/certification/geospatial-mcp-schemas/tools/solve_route.schema.json +39 -0
  43. package/certification/geospatial-mcp-schemas/tools/validate_package.schema.json +35 -0
  44. package/certification/geospatial-mcp-schemas/tools/validate_plan.schema.json +61 -0
  45. package/dist/src/certification/certifier.d.ts +119 -0
  46. package/dist/src/certification/certifier.d.ts.map +1 -0
  47. package/dist/src/certification/certifier.js +410 -0
  48. package/dist/src/certification/cli.d.ts +3 -0
  49. package/dist/src/certification/cli.d.ts.map +1 -0
  50. package/dist/src/certification/cli.js +48 -0
  51. package/dist/src/certification/contracts.d.ts +62 -0
  52. package/dist/src/certification/contracts.d.ts.map +1 -0
  53. package/dist/src/certification/contracts.js +174 -0
  54. package/dist/src/certification/fixture-client.d.ts +4 -0
  55. package/dist/src/certification/fixture-client.d.ts.map +1 -0
  56. package/dist/src/certification/fixture-client.js +113 -0
  57. package/dist/src/certification/fixtures.d.ts +32 -0
  58. package/dist/src/certification/fixtures.d.ts.map +1 -0
  59. package/dist/src/certification/fixtures.js +64 -0
  60. package/dist/src/certification/json-schema.d.ts +49 -0
  61. package/dist/src/certification/json-schema.d.ts.map +1 -0
  62. package/dist/src/certification/json-schema.js +164 -0
  63. package/dist/src/certification/mock-upstream.d.ts +29 -0
  64. package/dist/src/certification/mock-upstream.d.ts.map +1 -0
  65. package/dist/src/certification/mock-upstream.js +96 -0
  66. package/dist/src/certification/operator-catalog.d.ts +82 -0
  67. package/dist/src/certification/operator-catalog.d.ts.map +1 -0
  68. package/dist/src/certification/operator-catalog.js +466 -0
  69. package/dist/src/certification/report.d.ts +4 -0
  70. package/dist/src/certification/report.d.ts.map +1 -0
  71. package/dist/src/certification/report.js +90 -0
  72. package/dist/src/certification/run.d.ts +28 -0
  73. package/dist/src/certification/run.d.ts.map +1 -0
  74. package/dist/src/certification/run.js +31 -0
  75. package/dist/src/certification/schema-index.d.ts +69 -0
  76. package/dist/src/certification/schema-index.d.ts.map +1 -0
  77. package/dist/src/certification/schema-index.js +82 -0
  78. package/dist/src/certification/target.d.ts +40 -0
  79. package/dist/src/certification/target.d.ts.map +1 -0
  80. package/dist/src/certification/target.js +167 -0
  81. package/dist/src/eval/cli.d.ts +3 -0
  82. package/dist/src/eval/cli.d.ts.map +1 -0
  83. package/dist/src/eval/cli.js +116 -0
  84. package/dist/src/eval/corpus.d.ts +27 -0
  85. package/dist/src/eval/corpus.d.ts.map +1 -0
  86. package/dist/src/eval/corpus.js +134 -0
  87. package/dist/src/eval/drivers/anthropic.d.ts +14 -0
  88. package/dist/src/eval/drivers/anthropic.d.ts.map +1 -0
  89. package/dist/src/eval/drivers/anthropic.js +120 -0
  90. package/dist/src/eval/drivers/bedrock.d.ts +54 -0
  91. package/dist/src/eval/drivers/bedrock.d.ts.map +1 -0
  92. package/dist/src/eval/drivers/bedrock.js +178 -0
  93. package/dist/src/eval/drivers/deterministic.d.ts +17 -0
  94. package/dist/src/eval/drivers/deterministic.d.ts.map +1 -0
  95. package/dist/src/eval/drivers/deterministic.js +51 -0
  96. package/dist/src/eval/drivers/index.d.ts +34 -0
  97. package/dist/src/eval/drivers/index.d.ts.map +1 -0
  98. package/dist/src/eval/drivers/index.js +74 -0
  99. package/dist/src/eval/drivers/openai.d.ts +14 -0
  100. package/dist/src/eval/drivers/openai.d.ts.map +1 -0
  101. package/dist/src/eval/drivers/openai.js +136 -0
  102. package/dist/src/eval/grade.d.ts +14 -0
  103. package/dist/src/eval/grade.d.ts.map +1 -0
  104. package/dist/src/eval/grade.js +56 -0
  105. package/dist/src/eval/northstar-corpus.d.ts +3 -0
  106. package/dist/src/eval/northstar-corpus.d.ts.map +1 -0
  107. package/dist/src/eval/northstar-corpus.js +194 -0
  108. package/dist/src/eval/operator-corpus.d.ts +3 -0
  109. package/dist/src/eval/operator-corpus.d.ts.map +1 -0
  110. package/dist/src/eval/operator-corpus.js +193 -0
  111. package/dist/src/eval/report.d.ts +104 -0
  112. package/dist/src/eval/report.d.ts.map +1 -0
  113. package/dist/src/eval/report.js +130 -0
  114. package/dist/src/eval/runner.d.ts +22 -0
  115. package/dist/src/eval/runner.d.ts.map +1 -0
  116. package/dist/src/eval/runner.js +116 -0
  117. package/dist/src/eval/types.d.ts +93 -0
  118. package/dist/src/eval/types.d.ts.map +1 -0
  119. package/dist/src/eval/types.js +10 -0
  120. package/dist/src/helpers.d.ts +11 -0
  121. package/dist/src/helpers.d.ts.map +1 -1
  122. package/dist/src/helpers.js +22 -0
  123. package/dist/src/index.d.ts +7 -0
  124. package/dist/src/index.d.ts.map +1 -1
  125. package/dist/src/index.js +30 -4
  126. package/dist/src/proxy.d.ts +46 -0
  127. package/dist/src/proxy.d.ts.map +1 -0
  128. package/dist/src/proxy.js +124 -0
  129. package/dist/src/resources/styles.d.ts +29 -0
  130. package/dist/src/resources/styles.d.ts.map +1 -0
  131. package/dist/src/resources/styles.js +43 -0
  132. package/dist/src/styles.d.ts +110 -0
  133. package/dist/src/styles.d.ts.map +1 -0
  134. package/dist/src/styles.js +118 -0
  135. package/dist/src/tools/apply-style-preset.d.ts +27 -0
  136. package/dist/src/tools/apply-style-preset.d.ts.map +1 -0
  137. package/dist/src/tools/apply-style-preset.js +29 -0
  138. package/dist/src/tools/count-features.d.ts.map +1 -1
  139. package/dist/src/tools/count-features.js +5 -4
  140. package/dist/src/tools/get-extent.d.ts.map +1 -1
  141. package/dist/src/tools/get-extent.js +6 -4
  142. package/dist/src/tools/get-style.d.ts +24 -0
  143. package/dist/src/tools/get-style.d.ts.map +1 -0
  144. package/dist/src/tools/get-style.js +28 -0
  145. package/dist/src/tools/query-features.d.ts.map +1 -1
  146. package/dist/src/tools/query-features.js +4 -1
  147. package/package.json +19 -4
@@ -0,0 +1,90 @@
1
+ /** Render a human-readable Markdown summary of a certification report. */
2
+ export function renderMarkdown(report) {
3
+ const s = report.summary;
4
+ const status = s.pass ? "✅ PASS" : "❌ FAIL";
5
+ const lines = [];
6
+ lines.push(`# MCP Certification — ${report.server.name} v${report.server.version}`);
7
+ lines.push("");
8
+ lines.push(`**Result:** ${status}`);
9
+ lines.push("");
10
+ lines.push(`- Generated: \`${report.generatedAt}\``);
11
+ lines.push(`- Certified surface: ${report.protocol.surface}`);
12
+ lines.push(`- Target mode: \`${report.protocol.targetMode}\``);
13
+ lines.push(`- MCP transport: \`${report.protocol.mcpTransport}\``);
14
+ lines.push(`- Backend: \`${report.protocol.backend}\`${report.protocol.honuaTransport ? ` (Honua transport: \`${report.protocol.honuaTransport}\`)` : ""}`);
15
+ lines.push(`- Standard: \`${report.standard.source}\` (index ${report.standard.indexDate}, ${report.standard.dialect})`);
16
+ lines.push("");
17
+ lines.push("## Summary");
18
+ lines.push("");
19
+ lines.push("| Metric | Value |");
20
+ lines.push("| --- | ---: |");
21
+ lines.push(`| Tools discovered | ${s.toolsDiscovered} |`);
22
+ lines.push(`| Tools with valid inputSchema | ${s.toolsSchemaValid} / ${s.toolsDiscovered} |`);
23
+ lines.push(`| Tools conformance-checked | ${s.toolsConformanceChecked} |`);
24
+ lines.push(`| Tools conformant | ${s.toolsConformant} / ${s.toolsConformanceChecked} |`);
25
+ lines.push(`| Tools round-tripped | ${s.toolsRoundTripped} |`);
26
+ lines.push(`| Tools with output-schema validated | ${s.toolsOutputValidated} |`);
27
+ lines.push(`| Resources discovered | ${s.resourcesDiscovered} |`);
28
+ lines.push(`| Prompts discovered | ${s.promptsDiscovered} |`);
29
+ lines.push(`| Contracts checked | ${s.contractsPassed} / ${s.contractsChecked} passed |`);
30
+ lines.push(`| Known gaps | ${s.knownGaps} |`);
31
+ lines.push(`| Failures | ${s.failures} |`);
32
+ lines.push("");
33
+ lines.push("## Tools");
34
+ lines.push("");
35
+ lines.push("| Tool | Schema | Standard | Conformant | Read-only | Round-trip | Output schema |");
36
+ lines.push("| --- | :---: | --- | :---: | :---: | :---: | :---: |");
37
+ for (const t of report.tools) {
38
+ const conformant = t.conformant === null ? "n/a" : t.conformant ? "yes" : "**NO**";
39
+ const outputCol = t.structuredOutputValidated === true
40
+ ? "validated"
41
+ : t.structuredOutputValidated === false
42
+ ? "**INVALID**"
43
+ : t.hasOutputSchema
44
+ ? "advertised"
45
+ : "no";
46
+ lines.push(`| \`${t.name}\` | ${t.schemaValid ? "valid" : "**invalid**"} | ${t.standardName ?? "—"} | ${conformant} | ${t.readOnly ? "yes" : "no"} | ${t.roundTrip} | ${outputCol} |`);
47
+ }
48
+ lines.push("");
49
+ lines.push("## Contracts");
50
+ lines.push("");
51
+ lines.push("| Contract | Target | Status | Detail |");
52
+ lines.push("| --- | --- | :---: | --- |");
53
+ for (const c of report.contracts) {
54
+ const icon = c.status === "passed" ? "✅" : c.status === "failed" ? "❌" : "➖";
55
+ lines.push(`| \`${c.contract}\` | \`${c.target}\` | ${icon} ${c.status} | ${c.detail} |`);
56
+ }
57
+ lines.push("");
58
+ const toolErrors = report.tools.filter((t) => t.errors.length > 0);
59
+ if (toolErrors.length > 0) {
60
+ lines.push("### Tool failures");
61
+ lines.push("");
62
+ for (const t of toolErrors) {
63
+ lines.push(`- \`${t.name}\``);
64
+ for (const e of t.errors) {
65
+ lines.push(` - ${e}`);
66
+ }
67
+ }
68
+ lines.push("");
69
+ }
70
+ if (report.resources.length > 0) {
71
+ lines.push("## Resources");
72
+ lines.push("");
73
+ for (const r of report.resources) {
74
+ lines.push(`- \`${r.name}\` — \`${r.uri}\``);
75
+ }
76
+ lines.push("");
77
+ }
78
+ if (report.knownGaps.length > 0) {
79
+ lines.push("## Known gaps");
80
+ lines.push("");
81
+ lines.push("These are standard capabilities not yet advertised as discrete tools, or advertised tools outside the standard. They are recorded, not failed.");
82
+ lines.push("");
83
+ for (const g of report.knownGaps) {
84
+ const family = g.family ? ` _(${g.family})_` : "";
85
+ lines.push(`- **${g.kind}** \`${g.name}\`${family} — ${g.detail}`);
86
+ }
87
+ lines.push("");
88
+ }
89
+ return `${lines.join("\n")}\n`;
90
+ }
@@ -0,0 +1,28 @@
1
+ import { type CertificationReport } from "./certifier.js";
2
+ import { type CertificationTarget } from "./target.js";
3
+ /**
4
+ * Programmatic certification runner.
5
+ *
6
+ * The certifier connects to one of three targets (selected by
7
+ * `HONUA_MCP_CERT_TARGET`) and certifies the honua `/mcp` OPERATOR surface — the
8
+ * real ~20-tool catalog with output schemas, structured errors, cursor
9
+ * pagination, and an auth boundary — never the retired 9-tool static server:
10
+ *
11
+ * - `offline` (default): an in-process streamable-HTTP mock of `/mcp`.
12
+ * Deterministic, zero network, gates every PR.
13
+ * - `remote` : a live `/mcp` (HONUA_MCP_REMOTE_URL + auth). workflow_dispatch.
14
+ * - `stdio-proxy` : the `honua-mcp-proxy` binary against an upstream, over stdio.
15
+ */
16
+ export interface RunOptions {
17
+ env?: NodeJS.ProcessEnv;
18
+ }
19
+ export declare function runCertification(options?: RunOptions): Promise<CertificationReport>;
20
+ /** Certify an already-opened target, closing it when done. */
21
+ export declare function certifyTarget(target: CertificationTarget, env?: NodeJS.ProcessEnv): Promise<CertificationReport>;
22
+ export interface ArtifactPaths {
23
+ json: string;
24
+ markdown: string;
25
+ }
26
+ /** Write the JSON + Markdown certification artifacts to disk. */
27
+ export declare function writeArtifacts(report: CertificationReport, paths: ArtifactPaths): void;
28
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/certification/run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,aAAa,CAAC;AAEhF;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAI7F;AAED,8DAA8D;AAC9D,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAc9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAGtF"}
@@ -0,0 +1,31 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { certify } from "./certifier.js";
3
+ import { renderMarkdown } from "./report.js";
4
+ import { openCertificationTarget } from "./target.js";
5
+ export async function runCertification(options = {}) {
6
+ const env = options.env ?? process.env;
7
+ const target = await openCertificationTarget(env);
8
+ return certifyTarget(target, env);
9
+ }
10
+ /** Certify an already-opened target, closing it when done. */
11
+ export async function certifyTarget(target, env = process.env) {
12
+ try {
13
+ return await certify({
14
+ client: target.client,
15
+ targetMode: target.mode,
16
+ backend: target.backend,
17
+ surface: target.serverLabel,
18
+ honuaTransport: target.honuaTransport,
19
+ connectUnauthenticated: target.supportsUnauthenticatedPass ? () => target.connectUnauthenticated() : undefined,
20
+ env,
21
+ });
22
+ }
23
+ finally {
24
+ await target.close();
25
+ }
26
+ }
27
+ /** Write the JSON + Markdown certification artifacts to disk. */
28
+ export function writeArtifacts(report, paths) {
29
+ writeFileSync(paths.json, `${JSON.stringify(report, null, 2)}\n`, "utf8");
30
+ writeFileSync(paths.markdown, renderMarkdown(report), "utf8");
31
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Loader for the vendored geospatial-mcp JSON Schemas and their index.
3
+ *
4
+ * The schemas are vendored under `certification/geospatial-mcp-schemas/` (see
5
+ * that directory's `PROVENANCE.md`). Loading is filesystem-only and fully
6
+ * deterministic — no network access and no model/API calls.
7
+ */
8
+ export interface StandardToolEntry {
9
+ /** Bare taxonomy.md tool name (e.g. `query_features`). */
10
+ standardName: string;
11
+ /** Name the reference implementation advertises, or null when unimplemented. */
12
+ referenceToolName: string | null;
13
+ family: string;
14
+ /** Relative path of the schema file, e.g. `tools/query_features.schema.json`. */
15
+ schema: string;
16
+ implementationStatus: "implemented" | "known-gap";
17
+ notes?: string;
18
+ }
19
+ export interface StandardResourceEntry {
20
+ family: string;
21
+ uriForm: string;
22
+ schema: string;
23
+ shapeFidelity: string;
24
+ implementationStatus?: "implemented" | "known-gap";
25
+ notes?: string;
26
+ }
27
+ export interface SchemaIndex {
28
+ title: string;
29
+ date: string;
30
+ dialect: string;
31
+ tools: StandardToolEntry[];
32
+ resources: StandardResourceEntry[];
33
+ common: {
34
+ name: string;
35
+ schema: string;
36
+ role: string;
37
+ }[];
38
+ conformance?: {
39
+ name: string;
40
+ schema: string;
41
+ role: string;
42
+ }[];
43
+ }
44
+ /**
45
+ * Reference-implementation aliasing decision (SDK-local, NOT part of the
46
+ * vendored standard): the honua-server /mcp surface advertises
47
+ * `honua_dry_run_plan` alongside `honua_validate_plan`, both driven by the same
48
+ * partial-plan input schema (see the `validate_plan` entry's notes in the
49
+ * vendored index). The vendored `index.json` is a byte-exact copy of the
50
+ * upstream standard and must never be hand-edited to encode this, so the alias
51
+ * lives here in code instead.
52
+ */
53
+ export declare const REFERENCE_TOOL_ALIASES: Readonly<Record<string, string>>;
54
+ export type JsonSchema = Record<string, unknown>;
55
+ export declare const SCHEMA_ROOT: URL;
56
+ /** Load and cache the machine-readable schema index. */
57
+ export declare function loadSchemaIndex(): SchemaIndex;
58
+ /** Load and cache a single vendored schema by its index-relative path. */
59
+ export declare function loadSchemaFile(relativePath: string): JsonSchema;
60
+ /** Resolve the absolute on-disk path of the vendored schema root (for diagnostics). */
61
+ export declare function schemaRootPath(): string;
62
+ /**
63
+ * Build a lookup from the name a tool advertises (`referenceToolName`) to its
64
+ * standard schema entry. Only entries that name a reference tool are included.
65
+ * SDK-local reference aliases (see {@link REFERENCE_TOOL_ALIASES}) map extra
66
+ * advertised names onto the same standard entry.
67
+ */
68
+ export declare function buildReferenceToolLookup(index: SchemaIndex): Map<string, StandardToolEntry>;
69
+ //# sourceMappingURL=schema-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-index.d.ts","sourceRoot":"","sources":["../../../src/certification/schema-index.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,aAAa,GAAG,WAAW,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzD,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAwBjD,eAAO,MAAM,WAAW,KAAsB,CAAC;AAK/C,wDAAwD;AACxD,wBAAgB,eAAe,IAAI,WAAW,CAM7C;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAS/D;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAc3F"}
@@ -0,0 +1,82 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ /**
4
+ * Reference-implementation aliasing decision (SDK-local, NOT part of the
5
+ * vendored standard): the honua-server /mcp surface advertises
6
+ * `honua_dry_run_plan` alongside `honua_validate_plan`, both driven by the same
7
+ * partial-plan input schema (see the `validate_plan` entry's notes in the
8
+ * vendored index). The vendored `index.json` is a byte-exact copy of the
9
+ * upstream standard and must never be hand-edited to encode this, so the alias
10
+ * lives here in code instead.
11
+ */
12
+ export const REFERENCE_TOOL_ALIASES = {
13
+ honua_dry_run_plan: "honua_validate_plan",
14
+ };
15
+ /**
16
+ * Resolved root of the vendored schema tree.
17
+ *
18
+ * The vendored schemas live at the package root under
19
+ * `certification/geospatial-mcp-schemas/`. This module runs from two locations:
20
+ * - compiled: `dist/src/certification/schema-index.js` (3 levels deep)
21
+ * - vitest: `src/certification/schema-index.ts` (2 levels deep)
22
+ * so we probe candidate depths and pick the one that exists on disk.
23
+ */
24
+ function resolveSchemaRoot() {
25
+ const candidates = ["../../../certification/geospatial-mcp-schemas/", "../../certification/geospatial-mcp-schemas/"];
26
+ for (const candidate of candidates) {
27
+ const url = new URL(candidate, import.meta.url);
28
+ if (existsSync(new URL("index.json", url))) {
29
+ return url;
30
+ }
31
+ }
32
+ // Fall back to the compiled-layout path; the loader will surface a clear
33
+ // ENOENT if the vendored schemas are genuinely missing.
34
+ return new URL(candidates[0], import.meta.url);
35
+ }
36
+ export const SCHEMA_ROOT = resolveSchemaRoot();
37
+ let cachedIndex;
38
+ const schemaCache = new Map();
39
+ /** Load and cache the machine-readable schema index. */
40
+ export function loadSchemaIndex() {
41
+ if (!cachedIndex) {
42
+ const indexPath = new URL("index.json", SCHEMA_ROOT);
43
+ cachedIndex = JSON.parse(readFileSync(indexPath, "utf8"));
44
+ }
45
+ return cachedIndex;
46
+ }
47
+ /** Load and cache a single vendored schema by its index-relative path. */
48
+ export function loadSchemaFile(relativePath) {
49
+ const cached = schemaCache.get(relativePath);
50
+ if (cached) {
51
+ return cached;
52
+ }
53
+ const path = new URL(relativePath, SCHEMA_ROOT);
54
+ const schema = JSON.parse(readFileSync(path, "utf8"));
55
+ schemaCache.set(relativePath, schema);
56
+ return schemaCache.get(relativePath);
57
+ }
58
+ /** Resolve the absolute on-disk path of the vendored schema root (for diagnostics). */
59
+ export function schemaRootPath() {
60
+ return fileURLToPath(SCHEMA_ROOT);
61
+ }
62
+ /**
63
+ * Build a lookup from the name a tool advertises (`referenceToolName`) to its
64
+ * standard schema entry. Only entries that name a reference tool are included.
65
+ * SDK-local reference aliases (see {@link REFERENCE_TOOL_ALIASES}) map extra
66
+ * advertised names onto the same standard entry.
67
+ */
68
+ export function buildReferenceToolLookup(index) {
69
+ const lookup = new Map();
70
+ for (const tool of index.tools) {
71
+ if (tool.referenceToolName) {
72
+ lookup.set(tool.referenceToolName, tool);
73
+ }
74
+ }
75
+ for (const [alias, canonical] of Object.entries(REFERENCE_TOOL_ALIASES)) {
76
+ const entry = lookup.get(canonical);
77
+ if (entry) {
78
+ lookup.set(alias, entry);
79
+ }
80
+ }
81
+ return lookup;
82
+ }
@@ -0,0 +1,40 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { type ProxyOptions } from "../proxy.js";
3
+ /**
4
+ * Certification target selection.
5
+ *
6
+ * The certifier drives one of three targets, all reached through the SAME MCP
7
+ * client path the proxy uses so what's certified is the real operator surface,
8
+ * never the retired static server:
9
+ *
10
+ * - `offline` (CI default): a real in-process streamable-HTTP mock of the
11
+ * honua `/mcp` operator catalog. Gates every PR without a
12
+ * live server.
13
+ * - `remote` : a live streamable-HTTP `/mcp` (HONUA_MCP_REMOTE_URL + auth).
14
+ * Reuses the proxy's upstream connection logic verbatim.
15
+ * - `stdio-proxy` : spawns the `honua-mcp-proxy` binary against an upstream and
16
+ * certifies through stdio, proving the proxy chain end-to-end.
17
+ */
18
+ export type CertTargetMode = "offline" | "remote" | "stdio-proxy";
19
+ export interface CertificationTarget {
20
+ mode: CertTargetMode;
21
+ backend: "mock-upstream" | "live";
22
+ /** Human-readable label for the certified surface (artifact provenance). */
23
+ serverLabel: string;
24
+ honuaTransport: string | null;
25
+ /** Authenticated, connected MCP client. */
26
+ client: Client;
27
+ /** Whether an unauthenticated pass is possible for the auth contract. */
28
+ supportsUnauthenticatedPass: boolean;
29
+ /** Open a fresh, UNauthenticated client to the same surface (auth contract). */
30
+ connectUnauthenticated(): Promise<Client>;
31
+ close(): Promise<void>;
32
+ }
33
+ export declare function resolveTargetMode(env?: NodeJS.ProcessEnv): CertTargetMode;
34
+ /** Resolve the remote `/mcp` endpoint + credentials from the environment. */
35
+ export declare function resolveRemoteProxyOptions(env: NodeJS.ProcessEnv): ProxyOptions;
36
+ /** Resolve the built `honua-mcp-proxy` entry usable from src (vitest) or dist. */
37
+ export declare function resolveProxyEntry(): string;
38
+ /** Open the configured certification target. */
39
+ export declare function openCertificationTarget(env?: NodeJS.ProcessEnv): Promise<CertificationTarget>;
40
+ //# sourceMappingURL=target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,aAAa,CAAC;AAGjE;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,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,CAMtF;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAc9E;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED,gDAAgD;AAChD,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAShH"}
@@ -0,0 +1,167 @@
1
+ import { existsSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
+ import { connectUpstream } from "../proxy.js";
6
+ import { startMockUpstream } from "./mock-upstream.js";
7
+ export function resolveTargetMode(env = process.env) {
8
+ const raw = (env.HONUA_MCP_CERT_TARGET ?? "offline").trim().toLowerCase();
9
+ if (raw === "offline" || raw === "remote" || raw === "stdio-proxy") {
10
+ return raw;
11
+ }
12
+ throw new Error(`HONUA_MCP_CERT_TARGET must be "offline", "remote", or "stdio-proxy", received "${raw}"`);
13
+ }
14
+ /** Resolve the remote `/mcp` endpoint + credentials from the environment. */
15
+ export function resolveRemoteProxyOptions(env) {
16
+ const remoteUrl = env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL;
17
+ if (!remoteUrl) {
18
+ throw new Error("HONUA_MCP_REMOTE_URL is required for remote / stdio-proxy certification against a live /mcp.");
19
+ }
20
+ const parsed = new URL(remoteUrl);
21
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
22
+ throw new Error(`HONUA_MCP_REMOTE_URL must use http or https: ${remoteUrl}`);
23
+ }
24
+ return {
25
+ remoteUrl: parsed.toString(),
26
+ authToken: env.HONUA_MCP_AUTH_TOKEN,
27
+ apiKey: env.HONUA_API_KEY,
28
+ };
29
+ }
30
+ /** Resolve the built `honua-mcp-proxy` entry usable from src (vitest) or dist. */
31
+ export function resolveProxyEntry() {
32
+ const candidates = ["../proxy.js", "../../dist/src/proxy.js"];
33
+ for (const candidate of candidates) {
34
+ const url = new URL(candidate, import.meta.url);
35
+ if (url.protocol === "file:" && existsSync(url)) {
36
+ const path = fileURLToPath(url);
37
+ if (path.endsWith(".js")) {
38
+ return path;
39
+ }
40
+ }
41
+ }
42
+ throw new Error("Could not locate the built honua-mcp-proxy entry (dist/src/proxy.js). Run `npm run build` before stdio-proxy certification.");
43
+ }
44
+ /** Open the configured certification target. */
45
+ export async function openCertificationTarget(env = process.env) {
46
+ const mode = resolveTargetMode(env);
47
+ if (mode === "remote") {
48
+ return openRemoteTarget(env);
49
+ }
50
+ if (mode === "stdio-proxy") {
51
+ return openStdioProxyTarget(env);
52
+ }
53
+ return openOfflineTarget(env);
54
+ }
55
+ async function openOfflineTarget(env) {
56
+ const mock = await startMockUpstream();
57
+ const authedClients = [];
58
+ const client = await connectUpstream({ remoteUrl: mock.url, authToken: mock.authToken });
59
+ authedClients.push(client);
60
+ return {
61
+ mode: "offline",
62
+ backend: "mock-upstream",
63
+ serverLabel: "honua /mcp operator catalog (in-process streamable-HTTP mock)",
64
+ honuaTransport: env.HONUA_TRANSPORT ?? null,
65
+ client,
66
+ supportsUnauthenticatedPass: true,
67
+ async connectUnauthenticated() {
68
+ const unauth = await connectUpstream({ remoteUrl: mock.url });
69
+ authedClients.push(unauth);
70
+ return unauth;
71
+ },
72
+ async close() {
73
+ for (const c of authedClients) {
74
+ await c.close().catch(() => { });
75
+ }
76
+ await mock.close();
77
+ },
78
+ };
79
+ }
80
+ async function openRemoteTarget(env) {
81
+ const options = resolveRemoteProxyOptions(env);
82
+ const clients = [];
83
+ const client = await connectUpstream(options);
84
+ clients.push(client);
85
+ return {
86
+ mode: "remote",
87
+ backend: "live",
88
+ serverLabel: `live honua /mcp (${options.remoteUrl})`,
89
+ honuaTransport: env.HONUA_TRANSPORT ?? null,
90
+ client,
91
+ supportsUnauthenticatedPass: true,
92
+ async connectUnauthenticated() {
93
+ const unauth = await connectUpstream({ remoteUrl: options.remoteUrl });
94
+ clients.push(unauth);
95
+ return unauth;
96
+ },
97
+ async close() {
98
+ for (const c of clients) {
99
+ await c.close().catch(() => { });
100
+ }
101
+ },
102
+ };
103
+ }
104
+ async function openStdioProxyTarget(env) {
105
+ const proxyEntry = resolveProxyEntry();
106
+ const clients = [];
107
+ // Upstream: a live /mcp when configured, otherwise the in-process mock.
108
+ let mock;
109
+ let upstreamUrl;
110
+ let upstreamToken;
111
+ let backend;
112
+ if (env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL) {
113
+ const options = resolveRemoteProxyOptions(env);
114
+ upstreamUrl = options.remoteUrl;
115
+ upstreamToken = options.authToken;
116
+ backend = "live";
117
+ }
118
+ else {
119
+ mock = await startMockUpstream();
120
+ upstreamUrl = mock.url;
121
+ upstreamToken = mock.authToken;
122
+ backend = "mock-upstream";
123
+ }
124
+ const spawnProxyClient = async (withAuth) => {
125
+ const childEnv = {
126
+ ...process.env,
127
+ HONUA_MCP_REMOTE_URL: upstreamUrl,
128
+ };
129
+ if (withAuth && upstreamToken) {
130
+ childEnv.HONUA_MCP_AUTH_TOKEN = upstreamToken;
131
+ }
132
+ else {
133
+ delete childEnv.HONUA_MCP_AUTH_TOKEN;
134
+ delete childEnv.HONUA_API_KEY;
135
+ }
136
+ const transport = new StdioClientTransport({
137
+ command: process.execPath,
138
+ args: [proxyEntry],
139
+ env: childEnv,
140
+ stderr: "inherit",
141
+ });
142
+ const c = new Client({ name: "honua-mcp-certifier", version: "1.0.0" });
143
+ await c.connect(transport);
144
+ clients.push(c);
145
+ return c;
146
+ };
147
+ const client = await spawnProxyClient(true);
148
+ return {
149
+ mode: "stdio-proxy",
150
+ backend,
151
+ serverLabel: `honua-mcp-proxy → ${backend === "live" ? upstreamUrl : "in-process mock /mcp"}`,
152
+ honuaTransport: env.HONUA_TRANSPORT ?? null,
153
+ client,
154
+ supportsUnauthenticatedPass: true,
155
+ async connectUnauthenticated() {
156
+ return spawnProxyClient(false);
157
+ },
158
+ async close() {
159
+ for (const c of clients) {
160
+ await c.close().catch(() => { });
161
+ }
162
+ if (mock) {
163
+ await mock.close();
164
+ }
165
+ },
166
+ };
167
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/eval/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { resolveCorpus } from "./corpus.js";
6
+ import { parseDriverName, selectDrivers } from "./drivers/index.js";
7
+ import { renderMarkdown } from "./report.js";
8
+ import { runEval } from "./runner.js";
9
+ /**
10
+ * Cross-model MCP workflow eval CLI (honua-server #1956).
11
+ *
12
+ * Modes:
13
+ * (default) Run the eval, write artifacts, exit non-zero if the
14
+ * deterministic control did not pass every scenario.
15
+ * --artifact-only Run the eval, write artifacts, ALWAYS exit 0 (evidence
16
+ * upload step; runs with always() in CI).
17
+ * --offline Force the in-memory fixture surface + deterministic driver
18
+ * only, even if API keys / HONUA_MCP_REMOTE_URL are set.
19
+ * --driver <name> Run the deterministic control plus the named live driver(s)
20
+ * (repeatable or comma-separated): anthropic | openai | bedrock.
21
+ * The named driver is run even if its key/opt-in is absent so an
22
+ * explicit request yields an honest driverError, not a silent
23
+ * skip. Without --driver, drivers are auto-resolved from env.
24
+ * --corpus <name> Select the corpus explicitly: analyst | operator | northstar |
25
+ * all. This overrides HONUA_EVAL_CORPUS and is how the `eval:live`
26
+ * script targets the operator surface without relying on shell env
27
+ * (cross-platform). `northstar` targets the three P1-gate
28
+ * workflows. Defaults to the env-driven selection.
29
+ *
30
+ * Corpus selection is env-driven (see `resolveCorpus`): default is the analyst
31
+ * corpus (in-process SDK fixture surface, the CI control); `HONUA_EVAL_CORPUS=operator`
32
+ * targets the honua-server operator MCP surface for live runs; `all` runs both.
33
+ *
34
+ * Offline and deterministic by default: zero model/API calls. Live cross-model
35
+ * runs activate when ANTHROPIC_API_KEY / OPENAI_API_KEY are set (Anthropic/OpenAI
36
+ * APIs) or HONUA_EVAL_BEDROCK is set (Claude via Amazon Bedrock, AWS credential
37
+ * chain), and live remote MCP runs when HONUA_MCP_REMOTE_URL is set. Keys are
38
+ * never hardcoded — they come from the environment.
39
+ */
40
+ const CORPUS_NAMES = ["analyst", "operator", "northstar", "all"];
41
+ function parseArgs(argv) {
42
+ let artifactOnly = false;
43
+ let offline = false;
44
+ let outDir = fileURLToPath(new URL("../../../", import.meta.url));
45
+ const drivers = [];
46
+ let corpus;
47
+ for (let i = 0; i < argv.length; i++) {
48
+ const arg = argv[i];
49
+ if (arg === "--artifact-only") {
50
+ artifactOnly = true;
51
+ }
52
+ else if (arg === "--offline") {
53
+ offline = true;
54
+ }
55
+ else if (arg === "--out-dir") {
56
+ const value = argv[++i];
57
+ if (!value) {
58
+ throw new Error("--out-dir requires a directory argument");
59
+ }
60
+ outDir = resolve(value);
61
+ }
62
+ else if (arg === "--corpus") {
63
+ const value = argv[++i];
64
+ if (!value || !CORPUS_NAMES.includes(value)) {
65
+ throw new Error(`--corpus requires one of: ${CORPUS_NAMES.join(", ")}`);
66
+ }
67
+ corpus = value;
68
+ }
69
+ else if (arg === "--driver") {
70
+ const value = argv[++i];
71
+ if (!value) {
72
+ throw new Error("--driver requires a driver name (anthropic | openai | bedrock)");
73
+ }
74
+ for (const name of value.split(",")) {
75
+ const trimmed = name.trim();
76
+ if (trimmed.length > 0) {
77
+ drivers.push(parseDriverName(trimmed));
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return { artifactOnly, offline, outDir, drivers, ...(corpus ? { corpus } : {}) };
83
+ }
84
+ /** Map the `--corpus` alias to the corresponding HONUA_EVAL_CORPUS selection. */
85
+ function corpusForName(name) {
86
+ const selector = name === "analyst" ? "" : name; // analyst is the env default
87
+ return resolveCorpus({ ...process.env, HONUA_EVAL_CORPUS: selector });
88
+ }
89
+ async function main() {
90
+ const { artifactOnly, offline, outDir, drivers, corpus } = parseArgs(process.argv.slice(2));
91
+ const report = await runEval({
92
+ forceOffline: offline,
93
+ ...(drivers.length > 0 ? { drivers: selectDrivers(drivers) } : {}),
94
+ ...(corpus ? { corpus: corpusForName(corpus) } : {}),
95
+ });
96
+ const jsonPath = resolve(outDir, "mcp-eval-results.json");
97
+ const mdPath = resolve(outDir, "mcp-eval-results.md");
98
+ writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
99
+ writeFileSync(mdPath, renderMarkdown(report), "utf8");
100
+ const s = report.summary;
101
+ const status = s.pass ? "PASS" : "FAIL";
102
+ process.stdout.write(`MCP cross-model eval ${status}: ${s.scenarios} scenarios, ${s.modelsEvaluated} models ` +
103
+ `(${s.liveModelsEvaluated} live), surface=${report.surface.backend}.\n`);
104
+ for (const m of report.models) {
105
+ process.stdout.write(` ${m.id}: success ${(m.successRate * 100).toFixed(0)}%, clarify ${(m.clarificationRate * 100).toFixed(0)}%, ` +
106
+ `edit ${(m.editRate * 100).toFixed(0)}% (${m.pass}/${m.scenarios} passed)\n`);
107
+ }
108
+ process.stdout.write(`Artifacts: ${jsonPath}\n ${mdPath}\n`);
109
+ if (!artifactOnly && !s.pass) {
110
+ process.exitCode = 1;
111
+ }
112
+ }
113
+ main().catch((err) => {
114
+ process.stderr.write(`Eval error: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
115
+ process.exit(1);
116
+ });