@honua/mcp-server 0.1.14 → 0.1.16

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 (81) hide show
  1. package/README.md +12 -3
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +14 -8
  3. package/certification/geospatial-mcp-schemas/common/controls.schema.json +81 -0
  4. package/certification/geospatial-mcp-schemas/common/exploration.schema.json +165 -0
  5. package/certification/geospatial-mcp-schemas/common/interactions.schema.json +263 -0
  6. package/certification/geospatial-mcp-schemas/index.json +155 -4
  7. package/certification/geospatial-mcp-schemas/resources/app-package.schema.json +17 -1
  8. package/certification/geospatial-mcp-schemas/resources/map-package.schema.json +17 -1
  9. package/certification/geospatial-mcp-schemas/resources/ops-findings.schema.json +61 -0
  10. package/certification/geospatial-mcp-schemas/resources/ops-health.schema.json +138 -0
  11. package/certification/geospatial-mcp-schemas/tools/add_control.schema.json +107 -0
  12. package/certification/geospatial-mcp-schemas/tools/alert_events.result.schema.json +49 -0
  13. package/certification/geospatial-mcp-schemas/tools/alert_events.schema.json +49 -0
  14. package/certification/geospatial-mcp-schemas/tools/bind_component_context.schema.json +60 -0
  15. package/certification/geospatial-mcp-schemas/tools/bind_interaction.schema.json +97 -0
  16. package/certification/geospatial-mcp-schemas/tools/create_exploration_context.schema.json +35 -0
  17. package/certification/geospatial-mcp-schemas/tools/deploy_operations.result.schema.json +109 -0
  18. package/certification/geospatial-mcp-schemas/tools/deploy_operations.schema.json +49 -0
  19. package/certification/geospatial-mcp-schemas/tools/operate_events.result.schema.json +67 -0
  20. package/certification/geospatial-mcp-schemas/tools/operate_events.schema.json +46 -0
  21. package/certification/geospatial-mcp-schemas/tools/ops_findings.schema.json +25 -0
  22. package/certification/geospatial-mcp-schemas/tools/ops_health.schema.json +10 -0
  23. package/certification/geospatial-mcp-schemas/tools/platform_release_status.result.schema.json +53 -0
  24. package/certification/geospatial-mcp-schemas/tools/platform_release_status.schema.json +10 -0
  25. package/certification/geospatial-mcp-schemas/tools/propose_operation.schema.json +7 -2
  26. package/certification/geospatial-mcp-schemas/tools/propose_rollback.schema.json +29 -0
  27. package/certification/geospatial-mcp-schemas/tools/remove_control.schema.json +114 -0
  28. package/certification/geospatial-mcp-schemas/tools/remove_exploration_context.schema.json +42 -0
  29. package/certification/geospatial-mcp-schemas/tools/remove_interaction.schema.json +99 -0
  30. package/certification/setup-parity.md +6 -1
  31. package/dist/src/certification/certifier.d.ts +17 -2
  32. package/dist/src/certification/certifier.d.ts.map +1 -1
  33. package/dist/src/certification/certifier.js +69 -11
  34. package/dist/src/certification/cli.js +4 -1
  35. package/dist/src/certification/lifecycle.d.ts +2 -0
  36. package/dist/src/certification/lifecycle.d.ts.map +1 -1
  37. package/dist/src/certification/lifecycle.js +3 -0
  38. package/dist/src/certification/operator-catalog.d.ts.map +1 -1
  39. package/dist/src/certification/operator-catalog.js +0 -12
  40. package/dist/src/certification/report.d.ts.map +1 -1
  41. package/dist/src/certification/report.js +2 -1
  42. package/dist/src/certification/roster.d.ts +99 -0
  43. package/dist/src/certification/roster.d.ts.map +1 -0
  44. package/dist/src/certification/roster.js +233 -0
  45. package/dist/src/certification/round-trip-target.d.ts +45 -0
  46. package/dist/src/certification/round-trip-target.d.ts.map +1 -0
  47. package/dist/src/certification/round-trip-target.js +122 -0
  48. package/dist/src/certification/run.d.ts.map +1 -1
  49. package/dist/src/certification/run.js +7 -0
  50. package/dist/src/certification/setup-parity.js +2 -2
  51. package/dist/src/eval/northstar-corpus.d.ts.map +1 -1
  52. package/dist/src/eval/northstar-corpus.js +12 -10
  53. package/dist/src/eval/operator-corpus.d.ts.map +1 -1
  54. package/dist/src/eval/operator-corpus.js +11 -6
  55. package/dist/src/eval/proposal-tools.d.ts +14 -0
  56. package/dist/src/eval/proposal-tools.d.ts.map +1 -0
  57. package/dist/src/eval/proposal-tools.js +20 -0
  58. package/dist/src/index.d.ts.map +1 -1
  59. package/dist/src/index.js +12 -3
  60. package/dist/src/neutral/query.js +1 -1
  61. package/dist/src/release/zero-to-map-cli.d.ts.map +1 -1
  62. package/dist/src/release/zero-to-map-cli.js +8 -2
  63. package/dist/src/release/zero-to-map-provision.d.ts +6 -0
  64. package/dist/src/release/zero-to-map-provision.d.ts.map +1 -1
  65. package/dist/src/release/zero-to-map-provision.js +6 -0
  66. package/dist/src/tools/apply-style-preset.d.ts +1 -1
  67. package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
  68. package/dist/src/tools/apply-style-preset.js +4 -2
  69. package/dist/src/tools/control-plane-command.d.ts +1 -1
  70. package/dist/src/tools/control-plane-command.d.ts.map +1 -1
  71. package/dist/src/tools/control-plane-command.js +3 -2
  72. package/dist/src/tools/docs-search.d.ts +2 -2
  73. package/dist/src/tools/docs-search.d.ts.map +1 -1
  74. package/dist/src/tools/docs-search.js +15 -4
  75. package/dist/src/tools/query-features.d.ts +10 -1
  76. package/dist/src/tools/query-features.d.ts.map +1 -1
  77. package/dist/src/tools/query-features.js +16 -4
  78. package/package.json +2 -2
  79. package/release/zero-to-map/configs/claude-code.mcp.json +1 -1
  80. package/release/zero-to-map/configs/claude-desktop.json +1 -1
  81. package/release/zero-to-map/configs/cursor.json +1 -1
@@ -2,14 +2,19 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/propose_operation.schema.json",
4
4
  "title": "propose_operation inputSchema",
5
- "description": "Argument shape for the control-plane proposal tool (advertised name honua_propose_operation). Proposes an in-scope mutating control-plane operation for human approval (never autonomously executed), consistent with the ADR-0028 no-direct-AI-mutation boundary in taxonomy.md §Non-Goals. x-honua-reference-shape: tracks the reference implementation.",
5
+ "description": "Argument shape for the control-plane proposal tool (advertised name honua_propose_operation). Proposes an in-scope mutating control-plane operation for human approval on a deterministic server; the proposal is NEVER autonomously executed — execution stays approval-gated behind the human console inbox — consistent with the ADR-0028 posture (deterministic server, approval-gated mutation, no autonomous AI data edits) in taxonomy.md §Non-Goals. x-honua-reference-shape: tracks the reference implementation.",
6
6
  "x-honua-reference-shape": true,
7
+ "x-honua-supported-kinds": {
8
+ "description": "Capability discovery: the kind enum is the full, wire-compatible OperationClass vocabulary, but not every member is routable by a given server at runtime. Clients learn which kinds are actually routable from the reference self-describing capability surface (list_capabilities) rather than assuming the full enum. In the reference implementation, Seed is currently NOT routable through the operation gateway; AdminConfigChange, Deploy, and MetadataRelease are. Advertising a non-routable kind yields a NotSupported outcome; the enum is retained unchanged so the wire contract stays stable as kinds become routable.",
9
+ "routable": ["AdminConfigChange", "Deploy", "MetadataRelease"],
10
+ "notRoutable": ["Seed"]
11
+ },
7
12
  "type": "object",
8
13
  "required": ["kind"],
9
14
  "properties": {
10
15
  "kind": {
11
16
  "type": "string",
12
- "description": "In-scope mutating control-plane operation class to propose.",
17
+ "description": "In-scope mutating control-plane operation class to propose. Wire-compatible OperationClass enum; the routable subset is discoverable at runtime (see x-honua-supported-kinds). Seed is currently not routable in the reference.",
13
18
  "enum": ["AdminConfigChange", "Deploy", "MetadataRelease", "Seed"]
14
19
  },
15
20
  "reason": {
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/propose_rollback.schema.json",
4
+ "title": "propose_rollback inputSchema",
5
+ "description": "Argument shape for the platform-rollback proposal tool (advertised name honua_propose_rollback). Proposes rolling a deploy target back to its prior revision by requesting a NEW FORWARD Deploy operation to that prior revision — it is NOT an in-flight-operation abort/undo and does not reverse an operation already running; it converges the target forward onto the previous known-good revision. Like propose_operation, this proposes an in-scope mutating control-plane operation for human approval and is NEVER autonomously executed: a deterministic server records the proposal and returns a proposal id / honua://proposals/{id}; execution stays approval-gated behind the human console inbox. Consistent with the honua-server ADR-0028 posture reconciled by the standard's ADR-0028 (deterministic server, approval-gated mutation, no autonomous AI data edits). Uses platform_release / server_upgrade vocabulary; distinct from the hosted-content honua://deployments/{deployment_id} resource. x-honua-reference-shape: this shape tracks the reference implementation.",
6
+ "x-honua-reference-shape": true,
7
+ "type": "object",
8
+ "required": ["targetId"],
9
+ "properties": {
10
+ "targetId": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Deploy target to roll back (from deploy_operations[].target.targetId or ops findings/health subject.targetId)."
14
+ },
15
+ "toRevision": {
16
+ "type": "string",
17
+ "description": "Optional prior revision to roll forward to. When omitted, the server selects the target's most recent prior succeeded Deploy revision (a rolled-back latest deploy is demoted; the prior succeeded deploy is chosen)."
18
+ },
19
+ "reason": {
20
+ "type": "string",
21
+ "description": "Operator-facing reason recorded on the proposal."
22
+ },
23
+ "idempotencyKey": {
24
+ "type": "string",
25
+ "description": "Stable idempotency key for the underlying forward Deploy operation."
26
+ }
27
+ },
28
+ "additionalProperties": false
29
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/remove_control.schema.json",
4
+ "title": "remove_control inputSchema",
5
+ "description": "Argument shape for remove_control (composition profile, ADR-0031). Removes one control, by id, from a composition document's `controls` collection. Target addressing matches add_control: a composition document (mapPackageId or appPackageId) at the standard level, with the reference implementation's draft-lifecycle spelling (draftId + generation) admitted as x-honua-reference-shape. Removing an unknown controlId is an error, not a no-op. Removing a control leaves any interaction bound to `control:{id}` unresolvable, so an implementation MUST either reject the removal or remove those bindings with it — silently keeping a dangling binding is not conformant. See docs/adr/0031-composition-controls.md. Exactly one target must be supplied: mapPackageId, appPackageId, or the x-honua-reference-shape draftId (which requires generation, and generation may not appear without it). The oneOf enforces that; without it a call with no destination, several destinations, a bare draftId, or a stray generation all validated against a contract no server can route.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "controlId"
10
+ ],
11
+ "properties": {
12
+ "mapPackageId": {
13
+ "type": "string",
14
+ "minLength": 1,
15
+ "description": "Target MapPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
16
+ },
17
+ "appPackageId": {
18
+ "type": "string",
19
+ "minLength": 1,
20
+ "description": "Target AppPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
21
+ },
22
+ "draftId": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "description": "x-honua-reference-shape: reference-implementation draft-lifecycle target. Requires generation."
26
+ },
27
+ "generation": {
28
+ "type": "integer",
29
+ "minimum": 0,
30
+ "description": "x-honua-reference-shape: optimistic-concurrency generation of the draft being mutated; the call fails on generation conflict."
31
+ },
32
+ "controlId": {
33
+ "type": "string",
34
+ "minLength": 1,
35
+ "maxLength": 200,
36
+ "description": "Id of the control to remove."
37
+ },
38
+ "cascadeInteractions": {
39
+ "type": "boolean",
40
+ "default": false,
41
+ "description": "When true, interactions whose on.ref or do.ref is `control:{controlId}` are removed with the control. When false (default), the call fails if any interaction still references it, so a document never silently retains an unresolvable binding."
42
+ }
43
+ },
44
+ "oneOf": [
45
+ {
46
+ "required": [
47
+ "mapPackageId"
48
+ ],
49
+ "not": {
50
+ "anyOf": [
51
+ {
52
+ "required": [
53
+ "appPackageId"
54
+ ]
55
+ },
56
+ {
57
+ "required": [
58
+ "draftId"
59
+ ]
60
+ },
61
+ {
62
+ "required": [
63
+ "generation"
64
+ ]
65
+ }
66
+ ]
67
+ }
68
+ },
69
+ {
70
+ "required": [
71
+ "appPackageId"
72
+ ],
73
+ "not": {
74
+ "anyOf": [
75
+ {
76
+ "required": [
77
+ "mapPackageId"
78
+ ]
79
+ },
80
+ {
81
+ "required": [
82
+ "draftId"
83
+ ]
84
+ },
85
+ {
86
+ "required": [
87
+ "generation"
88
+ ]
89
+ }
90
+ ]
91
+ }
92
+ },
93
+ {
94
+ "required": [
95
+ "draftId",
96
+ "generation"
97
+ ],
98
+ "not": {
99
+ "anyOf": [
100
+ {
101
+ "required": [
102
+ "mapPackageId"
103
+ ]
104
+ },
105
+ {
106
+ "required": [
107
+ "appPackageId"
108
+ ]
109
+ }
110
+ ]
111
+ }
112
+ }
113
+ ]
114
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/remove_exploration_context.schema.json",
4
+ "title": "remove_exploration_context inputSchema",
5
+ "description": "Argument shape for remove_exploration_context (composition profile, ADR-0032). Removes one exploration context, by id, from a composition document's `explorationContexts` collection. Target addressing matches create_exploration_context: a composition document (mapPackageId or appPackageId) at the standard level, with the reference implementation's draft-lifecycle spelling (draftId + generation) admitted as x-honua-reference-shape. Removing an unknown explorationContextId is an error, not a no-op. Removing a context leaves every component that names it bound to nothing, so an implementation MUST either reject the removal or clear those bindings with it — silently orphaning a binding is not conformant. See docs/adr/0032-shared-exploration-contexts.md.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": ["explorationContextId"],
9
+ "properties": {
10
+ "mapPackageId": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Target MapPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
14
+ },
15
+ "appPackageId": {
16
+ "type": "string",
17
+ "minLength": 1,
18
+ "description": "Target AppPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
19
+ },
20
+ "draftId": {
21
+ "type": "string",
22
+ "minLength": 1,
23
+ "description": "x-honua-reference-shape: reference-implementation draft-lifecycle target. Requires generation."
24
+ },
25
+ "generation": {
26
+ "type": "integer",
27
+ "minimum": 0,
28
+ "description": "x-honua-reference-shape: optimistic-concurrency generation of the draft being mutated; the call fails on generation conflict."
29
+ },
30
+ "explorationContextId": {
31
+ "type": "string",
32
+ "minLength": 1,
33
+ "maxLength": 200,
34
+ "description": "Id of the exploration context to remove."
35
+ },
36
+ "cascadeBindings": {
37
+ "type": "boolean",
38
+ "default": false,
39
+ "description": "When true, every component naming this context has its explorationContextId and role cleared, leaving the components in place and independent. When false (default), the call fails while any component is still bound, so a document never silently retains an unresolvable binding. This mirrors remove_control's cascadeInteractions."
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,99 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/remove_interaction.schema.json",
4
+ "title": "remove_interaction inputSchema",
5
+ "description": "Argument shape for remove_interaction (composition profile, ADR-0030). Removes one declarative event→action binding, by id, from a composition document. Target addressing matches bind_interaction: a composition document (mapPackageId or appPackageId) at the standard level, with the reference implementation's draft-lifecycle spelling (draftId + generation) admitted as x-honua-reference-shape. Removing an unknown interactionId is an error, not a no-op. See docs/adr/0030-declarative-interactions-and-layout.md. Exactly one target must be supplied: mapPackageId, appPackageId, or the x-honua-reference-shape draftId (which requires generation). The oneOf below enforces that; without it a call with no target, with both package ids, or with draftId and no generation validated against a contract no server can route.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "interactionId"
10
+ ],
11
+ "properties": {
12
+ "mapPackageId": {
13
+ "type": "string",
14
+ "minLength": 1,
15
+ "description": "Target MapPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
16
+ },
17
+ "appPackageId": {
18
+ "type": "string",
19
+ "minLength": 1,
20
+ "description": "Target AppPackage composition document. Exactly one of mapPackageId / appPackageId / draftId identifies the target."
21
+ },
22
+ "draftId": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "description": "x-honua-reference-shape: reference-implementation draft-lifecycle target. Requires generation."
26
+ },
27
+ "generation": {
28
+ "type": "integer",
29
+ "minimum": 0,
30
+ "description": "x-honua-reference-shape: optimistic-concurrency generation of the draft being mutated; the call fails on generation conflict."
31
+ },
32
+ "interactionId": {
33
+ "type": "string",
34
+ "minLength": 1,
35
+ "maxLength": 200,
36
+ "description": "Id of the interaction to remove."
37
+ }
38
+ },
39
+ "oneOf": [
40
+ {
41
+ "required": [
42
+ "mapPackageId"
43
+ ],
44
+ "not": {
45
+ "anyOf": [
46
+ {
47
+ "required": [
48
+ "appPackageId"
49
+ ]
50
+ },
51
+ {
52
+ "required": [
53
+ "draftId"
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ },
59
+ {
60
+ "required": [
61
+ "appPackageId"
62
+ ],
63
+ "not": {
64
+ "anyOf": [
65
+ {
66
+ "required": [
67
+ "mapPackageId"
68
+ ]
69
+ },
70
+ {
71
+ "required": [
72
+ "draftId"
73
+ ]
74
+ }
75
+ ]
76
+ }
77
+ },
78
+ {
79
+ "required": [
80
+ "draftId",
81
+ "generation"
82
+ ],
83
+ "not": {
84
+ "anyOf": [
85
+ {
86
+ "required": [
87
+ "mapPackageId"
88
+ ]
89
+ },
90
+ {
91
+ "required": [
92
+ "appPackageId"
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ ]
99
+ }
@@ -44,7 +44,12 @@ The placeholders above illustrate the shape and are deliberately invalid as
44
44
  qualification input. Discover the view from the server's capabilities; do not
45
45
  invent profile switches or assemble a replacement tool roster in the client.
46
46
  Retain complete descriptions, input/output schemas, annotations and `_meta`.
47
- The server's opaque digests are compared to the reviewed pin. The receipt also
47
+ The server's opaque digests are compared exactly to the reviewed pin. The three
48
+ server digest fields use `sha256:` followed by 64 lowercase hexadecimal
49
+ characters. Do not strip the prefix: older local validation accepted bare
50
+ digests that could never match the server's canonical metadata. The independent
51
+ manifest-file SHA-256 argument remains 64 lowercase hexadecimal characters
52
+ without a prefix. The receipt also
48
53
  computes independent normalized descriptor hashes for HTTP and stdio; these
49
54
  use a different serialization from the server and have distinct field names.
50
55
 
@@ -1,6 +1,7 @@
1
1
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { type AuthMode, type SuiteProvenance } from "../provenance.js";
3
3
  import { AUTH_ERROR_CODES } from "./contracts.js";
4
+ import { type RosterResolution } from "./roster.js";
4
5
  import { type CertTargetMode } from "./target.js";
5
6
  /** Per-tool certification record (machine-readable). */
6
7
  export interface ToolCertification {
@@ -38,9 +39,14 @@ export interface KnownGap {
38
39
  }
39
40
  /** A cross-cutting contract check against the certified surface. */
40
41
  export interface ContractCheck {
41
- contract: "output-schema" | "error-shape" | "auth-unauthenticated" | "list-pagination" | "query-pagination" | "mutating-round-trip" | "mutating-permission-denied" | "async-job-lifecycle";
42
+ contract: "output-schema" | "error-shape" | "auth-unauthenticated" | "list-pagination" | "query-pagination" | "mutating-round-trip" | "mutating-permission-denied" | "async-job-lifecycle" | "roster-parity" | "round-trip-target";
42
43
  target: string;
43
- status: "passed" | "failed" | "skipped";
44
+ /**
45
+ * `blocked` means the check could not run for a reason outside the surface
46
+ * (e.g. the server roster is not published at the pinned sha). A blocked
47
+ * contract is never a pass: it keeps the summary from passing.
48
+ */
49
+ status: "passed" | "failed" | "skipped" | "blocked";
44
50
  detail: string;
45
51
  }
46
52
  export interface CertificationReport {
@@ -78,6 +84,7 @@ export interface CertificationReport {
78
84
  contractsPassed: number;
79
85
  contractsFailed: number;
80
86
  contractsSkipped: number;
87
+ contractsBlocked: number;
81
88
  knownGaps: number;
82
89
  failures: number;
83
90
  };
@@ -107,6 +114,14 @@ export interface CertifyOptions {
107
114
  authMode?: AuthMode;
108
115
  /** Environment used to resolve round-trip identifiers. */
109
116
  env?: NodeJS.ProcessEnv;
117
+ /**
118
+ * honua-server tool roster for the `roster-parity` contract. Omitted for
119
+ * targets that are not the product `/mcp` (the in-process fixture, the
120
+ * platform-free surface), where the contract is skipped with that reason.
121
+ */
122
+ roster?: RosterResolution;
123
+ /** Whether the certified topology has a durable control plane (Redis). Default true. */
124
+ durableControlPlane?: boolean;
110
125
  }
111
126
  /** Determine whether a discovered tool is safe to round-trip (read-only). */
112
127
  export declare function isReadOnlyTool(tool: {
@@ -1 +1 @@
1
- {"version":3,"file":"certifier.d.ts","sourceRoot":"","sources":["../../../src/certification/certifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"certifier.d.ts","sourceRoot":"","sources":["../../../src/certification/certifier.ts"],"names":[],"mappings":"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"}
@@ -4,9 +4,11 @@ import { buildInvalidArgsInputs, buildRoundTripInputs, resolveRoundTripEnv } fro
4
4
  import { checkConformance, checkWellFormed, validateAgainstSchema } from "./json-schema.js";
5
5
  import { runDeepContracts } from "./lifecycle.js";
6
6
  import { OGC_COLLECTION_IDS } from "./ogc-data.js";
7
+ import { checkRosterParity, describeRosterParity } from "./roster.js";
8
+ import { DISCOVER_SERVICE_ID, discoverRoundTripTarget, wantsDiscovery, withoutServiceBoundInputs, } from "./round-trip-target.js";
7
9
  import { PLATFORM_FREE_DEGRADATIONS, buildReferenceToolLookup, loadSchemaFile, loadSchemaIndex, } from "./schema-index.js";
8
10
  import { isStandaloneTargetMode } from "./target.js";
9
- const STANDARD_SOURCE = "geospatial-mcp@54cbd49 (spec/schemas)";
11
+ const STANDARD_SOURCE = "geospatial-mcp@4fac81c (spec/schemas)";
10
12
  /** Determine whether a discovered tool is safe to round-trip (read-only). */
11
13
  export function isReadOnlyTool(tool) {
12
14
  if (tool.annotations && typeof tool.annotations.readOnlyHint === "boolean") {
@@ -36,8 +38,7 @@ function withTargetRoundTripDefaults(env, targetMode) {
36
38
  */
37
39
  export async function certify(options) {
38
40
  const { client } = options;
39
- const roundTripEnv = withTargetRoundTripDefaults(resolveRoundTripEnv(options.env), options.targetMode);
40
- const roundTripInputs = buildRoundTripInputs(roundTripEnv);
41
+ let roundTripEnv = withTargetRoundTripDefaults(resolveRoundTripEnv(options.env), options.targetMode);
41
42
  const index = loadSchemaIndex();
42
43
  const referenceLookup = buildReferenceToolLookup(index);
43
44
  const serverInfo = client.getServerVersion() ?? { name: "unknown", version: "0.0.0" };
@@ -48,10 +49,27 @@ export async function certify(options) {
48
49
  // reference-shape tools (e.g. apply_style_preset) intentionally degrade their
49
50
  // platform-bound required properties. Conformance is evaluated accordingly.
50
51
  const platformFree = isStandaloneTargetMode(options.targetMode);
52
+ const advertisedNames = new Set(advertisedTools.map((t) => t.name));
53
+ // `HONUA_MCP_SERVICE_ID=discover`: bind the round trips to the first layer the
54
+ // candidate actually publishes instead of a seeded id it may not have.
55
+ let targetResolution;
56
+ let env = options.env;
57
+ if (wantsDiscovery(roundTripEnv)) {
58
+ targetResolution = await discoverRoundTripTarget(client, advertisedNames);
59
+ if (targetResolution.status === "passed") {
60
+ roundTripEnv = { ...roundTripEnv, serviceId: targetResolution.serviceId, layerId: targetResolution.layerId };
61
+ env = {
62
+ ...(options.env ?? process.env),
63
+ HONUA_MCP_SERVICE_ID: targetResolution.serviceId,
64
+ HONUA_MCP_LAYER_ID: String(targetResolution.layerId),
65
+ };
66
+ }
67
+ }
68
+ const roundTripInputs = targetResolution?.status === "blocked"
69
+ ? withoutServiceBoundInputs(buildRoundTripInputs(roundTripEnv))
70
+ : buildRoundTripInputs(roundTripEnv);
51
71
  const tools = [];
52
- const advertisedNames = new Set();
53
72
  for (const tool of advertisedTools) {
54
- advertisedNames.add(tool.name);
55
73
  tools.push(await certifyTool(client, tool, referenceLookup, roundTripInputs, platformFree));
56
74
  }
57
75
  const resources = advertisedResources.map((r) => ({
@@ -64,7 +82,7 @@ export async function certify(options) {
64
82
  contracts.push(paginationCheck("resources", resourcePages));
65
83
  collectOutputSchemaContracts(tools, contracts);
66
84
  contracts.push(await runErrorShapeContract(client, advertisedTools));
67
- await runAuthContracts(options, advertisedTools, advertisedResources, contracts);
85
+ await runAuthContracts(options, advertisedTools, advertisedResources, contracts, roundTripInputs);
68
86
  // Deep contracts: mutating round-trip, unauthenticated-write refusal, async job
69
87
  // lifecycle, and query pagination. Safe by construction — mutations only run
70
88
  // against the disposable offline mock or an explicit scratch target.
@@ -73,9 +91,19 @@ export async function certify(options) {
73
91
  advertisedToolNames: advertisedNames,
74
92
  isDisposableBackend: options.backend === "mock-upstream",
75
93
  connectUnauthenticated: options.connectUnauthenticated,
76
- env: options.env,
94
+ env,
95
+ ...(targetResolution?.status === "blocked" ? { targetUnavailable: targetResolution.detail } : {}),
77
96
  });
78
97
  contracts.push(...deep);
98
+ if (targetResolution) {
99
+ contracts.push({
100
+ contract: "round-trip-target",
101
+ target: `HONUA_MCP_SERVICE_ID=${DISCOVER_SERVICE_ID}`,
102
+ status: targetResolution.status,
103
+ detail: targetResolution.detail,
104
+ });
105
+ }
106
+ contracts.push(rosterParityContract(options, advertisedNames));
79
107
  const knownGaps = collectKnownGaps(index, advertisedNames, referenceLookup);
80
108
  const gitSha = resolveSuiteGitSha(options.env);
81
109
  const provenance = {
@@ -288,7 +316,7 @@ async function runErrorShapeContract(client, advertised) {
288
316
  };
289
317
  }
290
318
  }
291
- async function runAuthContracts(options, advertised, resources, contracts) {
319
+ async function runAuthContracts(options, advertised, resources, contracts, roundTripInputs) {
292
320
  if (!options.connectUnauthenticated) {
293
321
  contracts.push({
294
322
  contract: "auth-unauthenticated",
@@ -310,7 +338,6 @@ async function runAuthContracts(options, advertised, resources, contracts) {
310
338
  // tools/call without credentials.
311
339
  const toolTarget = advertised.find((t) => isReadOnlyTool(t)) ?? advertised[0];
312
340
  if (toolTarget) {
313
- const roundTripInputs = buildRoundTripInputs(resolveRoundTripEnv(options.env));
314
341
  const args = roundTripInputs[toolTarget.name] ?? {};
315
342
  contracts.push(await authCheckToolsCall(unauth, toolTarget.name, args));
316
343
  }
@@ -373,6 +400,34 @@ function contractFromOutcome(contract, target, outcome) {
373
400
  detail: outcome.ok ? "contract satisfied" : outcome.errors.join("; "),
374
401
  };
375
402
  }
403
+ function rosterParityContract(options, advertisedNames) {
404
+ const roster = options.roster;
405
+ if (!roster) {
406
+ return {
407
+ contract: "roster-parity",
408
+ target: "tools/list",
409
+ status: "skipped",
410
+ detail: "roster parity runs only against a live honua-server /mcp; this target is not the product surface",
411
+ };
412
+ }
413
+ if (roster.status === "blocked") {
414
+ return {
415
+ contract: "roster-parity",
416
+ target: "tools/list",
417
+ status: "blocked",
418
+ detail: `${roster.reason} [${roster.source}]`,
419
+ };
420
+ }
421
+ const result = checkRosterParity(advertisedNames, roster.roster, {
422
+ durableControlPlane: options.durableControlPlane ?? true,
423
+ });
424
+ return {
425
+ contract: "roster-parity",
426
+ target: "tools/list",
427
+ status: result.ok ? "passed" : "failed",
428
+ detail: `${describeRosterParity(result, advertisedNames.size)} [${roster.source}]`,
429
+ };
430
+ }
376
431
  function collectKnownGaps(index, advertisedNames, referenceLookup) {
377
432
  const gaps = [];
378
433
  // Advertised names that resolve to a standard entry through the SDK-local
@@ -415,13 +470,15 @@ function assembleReport(args) {
415
470
  const toolsConformant = conformanceChecked.filter((t) => t.conformant === true).length;
416
471
  const toolsRoundTripped = tools.filter((t) => t.roundTrip === "passed").length;
417
472
  const toolsOutputValidated = tools.filter((t) => t.structuredOutputValidated === true).length;
418
- const contractsChecked = contracts.filter((c) => c.status !== "skipped").length;
473
+ const contractsChecked = contracts.filter((c) => c.status === "passed" || c.status === "failed").length;
419
474
  const contractsPassed = contracts.filter((c) => c.status === "passed").length;
420
475
  const contractsFailed = contracts.filter((c) => c.status === "failed").length;
421
476
  const contractsSkipped = contracts.filter((c) => c.status === "skipped").length;
477
+ const contractsBlocked = contracts.filter((c) => c.status === "blocked").length;
422
478
  const toolFailures = tools.reduce((acc, t) => acc + t.errors.length, 0);
423
479
  const failures = toolFailures + contractsFailed;
424
- const pass = failures === 0;
480
+ // A blocked contract is evidence that could not be gathered, never a pass.
481
+ const pass = failures === 0 && contractsBlocked === 0;
425
482
  return {
426
483
  schemaVersion: 2,
427
484
  generatedAt: new Date().toISOString(),
@@ -449,6 +506,7 @@ function assembleReport(args) {
449
506
  contractsPassed,
450
507
  contractsFailed,
451
508
  contractsSkipped,
509
+ contractsBlocked,
452
510
  knownGaps: knownGaps.length,
453
511
  failures,
454
512
  },
@@ -47,11 +47,14 @@ async function main() {
47
47
  const status = s.pass ? "PASS" : "FAIL";
48
48
  process.stdout.write(`MCP certification ${status} [${report.protocol.targetMode}]: ${s.toolsConformant}/${s.toolsConformanceChecked} conformant, ` +
49
49
  `${s.toolsOutputValidated} output-validated, ${s.contractsPassed}/${s.contractsChecked} contracts, ` +
50
- `${s.contractsSkipped} skipped, ${s.knownGaps} known gaps, ${s.failures} failures.\n`);
50
+ `${s.contractsSkipped} skipped, ${s.contractsBlocked} blocked, ${s.knownGaps} known gaps, ${s.failures} failures.\n`);
51
51
  const skipped = report.contracts.filter((c) => c.status === "skipped");
52
52
  for (const c of skipped) {
53
53
  process.stdout.write(` SKIP [${c.contract} / ${c.target}]: ${c.detail}\n`);
54
54
  }
55
+ for (const c of report.contracts.filter((contract) => contract.status === "blocked")) {
56
+ process.stdout.write(` BLOCKED [${c.contract} / ${c.target}]: ${c.detail}\n`);
57
+ }
55
58
  process.stdout.write(`Artifacts: ${paths.json}\n ${paths.markdown}\n`);
56
59
  if (!artifactOnly && !report.summary.pass) {
57
60
  process.exitCode = 1;
@@ -8,6 +8,8 @@ export interface DeepContractOptions {
8
8
  /** Open a fresh unauthenticated client (for the permission-denied case). */
9
9
  connectUnauthenticated?: (() => Promise<Client>) | undefined;
10
10
  env?: NodeJS.ProcessEnv;
11
+ /** Set when no published layer could be discovered; query pagination is skipped with this reason. */
12
+ targetUnavailable?: string;
11
13
  }
12
14
  /** Run all deep contracts, returning one ContractCheck per contract (never throws). */
13
15
  export declare function runDeepContracts(options: DeepContractOptions): Promise<ContractCheck[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/certification/lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAoCpD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,iFAAiF;IACjF,mBAAmB,EAAE,OAAO,CAAC;IAC7B,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAcD,uFAAuF;AACvF,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAW7F"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/certification/lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAoCpD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,iFAAiF;IACjF,mBAAmB,EAAE,OAAO,CAAC;IAC7B,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,qGAAqG;IACrG,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAcD,uFAAuF;AACvF,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAW7F"}
@@ -283,6 +283,9 @@ async function runQueryPagination(options, target) {
283
283
  if (!options.advertisedToolNames.has(QUERY_TOOL)) {
284
284
  return skip(contract, QUERY_TOOL, `${QUERY_TOOL} not advertised by this surface`);
285
285
  }
286
+ if (options.targetUnavailable) {
287
+ return skip(contract, QUERY_TOOL, `no published layer to page: ${options.targetUnavailable}`);
288
+ }
286
289
  const { client } = options;
287
290
  try {
288
291
  const pages = [];
@@ -1 +1 @@
1
- {"version":3,"file":"operator-catalog.d.ts","sourceRoot":"","sources":["../../../src/certification/operator-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAUnE,OAAO,EAAE,KAAK,UAAU,EAA6D,MAAM,mBAAmB,CAAC;AAE/G;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gFAAgF;AAChF,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,WAAW,GAAG,kBAAkB,CAAC;IACtG,+FAA+F;IAC/F,KAAK,EAAE;QACL,IAAI,EACA,kBAAkB,GAClB,qBAAqB,GACrB,gBAAgB,GAChB,gBAAgB,GAChB,iBAAiB,GACjB,SAAS,GACT,WAAW,GACX,qBAAqB,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACtE,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,YAAY,EAAE,UAAU,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AA0BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CA6UnD;AAOD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,EAAE,CA2B3D;AAED,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAoCpC,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuHD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAsGxF"}
1
+ {"version":3,"file":"operator-catalog.d.ts","sourceRoot":"","sources":["../../../src/certification/operator-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAUnE,OAAO,EAAE,KAAK,UAAU,EAA6D,MAAM,mBAAmB,CAAC;AAE/G;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gFAAgF;AAChF,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,WAAW,GAAG,kBAAkB,CAAC;IACtG,+FAA+F;IAC/F,KAAK,EAAE;QACL,IAAI,EACA,kBAAkB,GAClB,qBAAqB,GACrB,gBAAgB,GAChB,gBAAgB,GAChB,iBAAiB,GACjB,SAAS,GACT,WAAW,GACX,qBAAqB,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACtE,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,YAAY,EAAE,UAAU,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AA0BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAiUnD;AAOD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,EAAE,CA2B3D;AAED,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAoCpC,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuHD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAsGxF"}
@@ -238,18 +238,6 @@ export function buildOperatorTools() {
238
238
  readOnly: false,
239
239
  sampleOutput: { jobId: "job-001", status: "Cancelling" },
240
240
  },
241
- {
242
- name: "honua_propose_operation",
243
- title: "Propose operation",
244
- description: "Propose an in-scope control-plane operation for human approval.",
245
- inputSchema: vendoredInputSchema("honua_propose_operation"),
246
- outputSchema: OBJECT_SCHEMA({ proposalId: { type: "string" }, status: { type: "string" } }, [
247
- "proposalId",
248
- "status",
249
- ]),
250
- readOnly: false,
251
- sampleOutput: { proposalId: "prop-001", status: "PendingApproval" },
252
- },
253
241
  {
254
242
  name: "honua_publish_service",
255
243
  title: "Publish service",
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/certification/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CA2HlE"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/certification/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CA4HlE"}