@honua/mcp-server 0.0.10-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.
- package/README.md +259 -15
- package/certification/geospatial-mcp-schemas/PROVENANCE.md +41 -0
- package/certification/geospatial-mcp-schemas/README.md +90 -0
- package/certification/geospatial-mcp-schemas/common/geoprocessing-error.schema.json +45 -0
- package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +79 -0
- package/certification/geospatial-mcp-schemas/index.json +390 -0
- package/certification/geospatial-mcp-schemas/resources/app-package.schema.json +20 -0
- package/certification/geospatial-mcp-schemas/resources/app-template.schema.json +15 -0
- package/certification/geospatial-mcp-schemas/resources/artifact.schema.json +69 -0
- package/certification/geospatial-mcp-schemas/resources/deployment.schema.json +27 -0
- package/certification/geospatial-mcp-schemas/resources/map-package.schema.json +21 -0
- package/certification/geospatial-mcp-schemas/resources/map-template.schema.json +15 -0
- package/certification/geospatial-mcp-schemas/resources/provenance.schema.json +50 -0
- package/certification/geospatial-mcp-schemas/resources/published-service.schema.json +21 -0
- package/certification/geospatial-mcp-schemas/resources/result-package.schema.json +64 -0
- package/certification/geospatial-mcp-schemas/resources/style.schema.json +12 -0
- package/certification/geospatial-mcp-schemas/resources/theme.schema.json +12 -0
- package/certification/geospatial-mcp-schemas/resources/workspace.schema.json +63 -0
- package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +27 -0
- package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/cancel_job.schema.json +15 -0
- package/certification/geospatial-mcp-schemas/tools/clarify_intent.schema.json +69 -0
- package/certification/geospatial-mcp-schemas/tools/compose_mixed_protocol_map.schema.json +21 -0
- package/certification/geospatial-mcp-schemas/tools/create_app_package.schema.json +33 -0
- package/certification/geospatial-mcp-schemas/tools/create_map_package.schema.json +41 -0
- package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
- package/certification/geospatial-mcp-schemas/tools/execute_plan.schema.json +64 -0
- package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/geocode_address.schema.json +36 -0
- package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
- package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +40 -0
- package/certification/geospatial-mcp-schemas/tools/ground_candidates.schema.json +66 -0
- package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
- package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
- package/certification/geospatial-mcp-schemas/tools/list_capabilities.schema.json +21 -0
- package/certification/geospatial-mcp-schemas/tools/list_layers.schema.json +14 -0
- package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
- package/certification/geospatial-mcp-schemas/tools/plan_analysis.schema.json +19 -0
- package/certification/geospatial-mcp-schemas/tools/preview_app_package.schema.json +15 -0
- package/certification/geospatial-mcp-schemas/tools/preview_map_package.schema.json +17 -0
- package/certification/geospatial-mcp-schemas/tools/preview_package.schema.json +40 -0
- package/certification/geospatial-mcp-schemas/tools/propose_operation.schema.json +28 -0
- package/certification/geospatial-mcp-schemas/tools/publish_result.schema.json +29 -0
- package/certification/geospatial-mcp-schemas/tools/publish_service.schema.json +59 -0
- package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +74 -0
- package/certification/geospatial-mcp-schemas/tools/refine_map_package.schema.json +22 -0
- package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/resolve_entity.schema.json +30 -0
- package/certification/geospatial-mcp-schemas/tools/solve_route.schema.json +39 -0
- package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
- package/certification/geospatial-mcp-schemas/tools/validate_package.schema.json +35 -0
- package/certification/geospatial-mcp-schemas/tools/validate_plan.schema.json +61 -0
- package/dist/src/capability.d.ts +48 -0
- package/dist/src/capability.d.ts.map +1 -0
- package/dist/src/capability.js +44 -0
- package/dist/src/certification/census-data.d.ts +33 -0
- package/dist/src/certification/census-data.d.ts.map +1 -0
- package/dist/src/certification/census-data.js +506 -0
- package/dist/src/certification/census-fixture-client.d.ts +7 -0
- package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
- package/dist/src/certification/census-fixture-client.js +226 -0
- package/dist/src/certification/certifier.d.ts +125 -0
- package/dist/src/certification/certifier.d.ts.map +1 -0
- package/dist/src/certification/certifier.js +455 -0
- package/dist/src/certification/cli.d.ts +3 -0
- package/dist/src/certification/cli.d.ts.map +1 -0
- package/dist/src/certification/cli.js +63 -0
- package/dist/src/certification/contracts.d.ts +103 -0
- package/dist/src/certification/contracts.d.ts.map +1 -0
- package/dist/src/certification/contracts.js +245 -0
- package/dist/src/certification/fixture-client.d.ts +4 -0
- package/dist/src/certification/fixture-client.d.ts.map +1 -0
- package/dist/src/certification/fixture-client.js +113 -0
- package/dist/src/certification/fixtures.d.ts +45 -0
- package/dist/src/certification/fixtures.d.ts.map +1 -0
- package/dist/src/certification/fixtures.js +92 -0
- package/dist/src/certification/json-schema.d.ts +71 -0
- package/dist/src/certification/json-schema.d.ts.map +1 -0
- package/dist/src/certification/json-schema.js +172 -0
- package/dist/src/certification/lifecycle.d.ts +14 -0
- package/dist/src/certification/lifecycle.d.ts.map +1 -0
- package/dist/src/certification/lifecycle.js +326 -0
- package/dist/src/certification/mock-upstream.d.ts +29 -0
- package/dist/src/certification/mock-upstream.d.ts.map +1 -0
- package/dist/src/certification/mock-upstream.js +96 -0
- package/dist/src/certification/operator-catalog.d.ts +90 -0
- package/dist/src/certification/operator-catalog.d.ts.map +1 -0
- package/dist/src/certification/operator-catalog.js +650 -0
- package/dist/src/certification/report.d.ts +4 -0
- package/dist/src/certification/report.d.ts.map +1 -0
- package/dist/src/certification/report.js +103 -0
- package/dist/src/certification/run.d.ts +28 -0
- package/dist/src/certification/run.d.ts.map +1 -0
- package/dist/src/certification/run.js +32 -0
- package/dist/src/certification/schema-index.d.ts +94 -0
- package/dist/src/certification/schema-index.d.ts.map +1 -0
- package/dist/src/certification/schema-index.js +109 -0
- package/dist/src/certification/target.d.ts +48 -0
- package/dist/src/certification/target.d.ts.map +1 -0
- package/dist/src/certification/target.js +219 -0
- package/dist/src/eval/cli.d.ts +3 -0
- package/dist/src/eval/cli.d.ts.map +1 -0
- package/dist/src/eval/cli.js +118 -0
- package/dist/src/eval/corpus.d.ts +34 -0
- package/dist/src/eval/corpus.d.ts.map +1 -0
- package/dist/src/eval/corpus.js +150 -0
- package/dist/src/eval/drivers/anthropic.d.ts +14 -0
- package/dist/src/eval/drivers/anthropic.d.ts.map +1 -0
- package/dist/src/eval/drivers/anthropic.js +120 -0
- package/dist/src/eval/drivers/bedrock.d.ts +54 -0
- package/dist/src/eval/drivers/bedrock.d.ts.map +1 -0
- package/dist/src/eval/drivers/bedrock.js +178 -0
- package/dist/src/eval/drivers/deterministic.d.ts +17 -0
- package/dist/src/eval/drivers/deterministic.d.ts.map +1 -0
- package/dist/src/eval/drivers/deterministic.js +64 -0
- package/dist/src/eval/drivers/index.d.ts +34 -0
- package/dist/src/eval/drivers/index.d.ts.map +1 -0
- package/dist/src/eval/drivers/index.js +74 -0
- package/dist/src/eval/drivers/openai.d.ts +14 -0
- package/dist/src/eval/drivers/openai.d.ts.map +1 -0
- package/dist/src/eval/drivers/openai.js +136 -0
- package/dist/src/eval/grade.d.ts +14 -0
- package/dist/src/eval/grade.d.ts.map +1 -0
- package/dist/src/eval/grade.js +75 -0
- package/dist/src/eval/northstar-corpus.d.ts +3 -0
- package/dist/src/eval/northstar-corpus.d.ts.map +1 -0
- package/dist/src/eval/northstar-corpus.js +194 -0
- package/dist/src/eval/operator-corpus.d.ts +3 -0
- package/dist/src/eval/operator-corpus.d.ts.map +1 -0
- package/dist/src/eval/operator-corpus.js +193 -0
- package/dist/src/eval/report.d.ts +109 -0
- package/dist/src/eval/report.d.ts.map +1 -0
- package/dist/src/eval/report.js +133 -0
- package/dist/src/eval/runner.d.ts +28 -0
- package/dist/src/eval/runner.d.ts.map +1 -0
- package/dist/src/eval/runner.js +130 -0
- package/dist/src/eval/standalone-corpus.d.ts +3 -0
- package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
- package/dist/src/eval/standalone-corpus.js +699 -0
- package/dist/src/eval/types.d.ts +118 -0
- package/dist/src/eval/types.d.ts.map +1 -0
- package/dist/src/eval/types.js +10 -0
- package/dist/src/helpers.d.ts +11 -0
- package/dist/src/helpers.d.ts.map +1 -1
- package/dist/src/helpers.js +22 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +50 -4
- package/dist/src/provenance.d.ts +44 -0
- package/dist/src/provenance.d.ts.map +1 -0
- package/dist/src/provenance.js +37 -0
- package/dist/src/proxy.d.ts +46 -0
- package/dist/src/proxy.d.ts.map +1 -0
- package/dist/src/proxy.js +124 -0
- package/dist/src/resources/styles.d.ts +29 -0
- package/dist/src/resources/styles.d.ts.map +1 -0
- package/dist/src/resources/styles.js +76 -0
- package/dist/src/styles.d.ts +112 -0
- package/dist/src/styles.d.ts.map +1 -0
- package/dist/src/styles.js +144 -0
- package/dist/src/tools/apply-style-preset.d.ts +27 -0
- package/dist/src/tools/apply-style-preset.d.ts.map +1 -0
- package/dist/src/tools/apply-style-preset.js +41 -0
- package/dist/src/tools/count-features.d.ts.map +1 -1
- package/dist/src/tools/count-features.js +5 -4
- package/dist/src/tools/get-extent.d.ts.map +1 -1
- package/dist/src/tools/get-extent.js +6 -4
- package/dist/src/tools/get-style.d.ts +24 -0
- package/dist/src/tools/get-style.d.ts.map +1 -0
- package/dist/src/tools/get-style.js +39 -0
- package/dist/src/tools/list-services.d.ts.map +1 -1
- package/dist/src/tools/list-services.js +16 -1
- package/dist/src/tools/query-features.d.ts.map +1 -1
- package/dist/src/tools/query-features.js +4 -1
- package/package.json +28 -4
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { validateAgainstSchema } from "./json-schema.js";
|
|
3
|
+
import { loadSchemaFile } from "./schema-index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Pure, deterministic contract-check analyzers for MCP certification.
|
|
6
|
+
*
|
|
7
|
+
* These operate on values already obtained from the certified surface (tool
|
|
8
|
+
* results, thrown errors, paginated list responses) so they are unit-testable
|
|
9
|
+
* without any transport. The certifier does the I/O; these judge the shape.
|
|
10
|
+
*
|
|
11
|
+
* Contracts enforced (§P0.2):
|
|
12
|
+
* - output-schema : a tool's structuredContent validates against its outputSchema.
|
|
13
|
+
* - error-shape : invalid arguments yield an `isError` tool result carrying a
|
|
14
|
+
* structured, actionable code (validated against the vendored
|
|
15
|
+
* geoprocessing-error schema), never a protocol error.
|
|
16
|
+
* - auth : unauthenticated tools/call and resources/read surface a
|
|
17
|
+
* structured `unauthenticated` / `permission_denied` signal.
|
|
18
|
+
* - pagination : list `nextCursor` is honored and terminates.
|
|
19
|
+
*/
|
|
20
|
+
/** Machine codes that satisfy the unauthenticated / permission-denied contract. */
|
|
21
|
+
export const AUTH_ERROR_CODES = ["unauthenticated", "permission_denied"];
|
|
22
|
+
/** geoprocessing-error `kind` values that denote an authorization failure. */
|
|
23
|
+
export const AUTH_ERROR_KINDS = ["AuthorizationDenied", "Unauthenticated", "PermissionDenied"];
|
|
24
|
+
let cachedErrorSchema;
|
|
25
|
+
function geoprocessingErrorSchema() {
|
|
26
|
+
if (!cachedErrorSchema) {
|
|
27
|
+
cachedErrorSchema = loadSchemaFile("common/geoprocessing-error.schema.json");
|
|
28
|
+
}
|
|
29
|
+
return cachedErrorSchema;
|
|
30
|
+
}
|
|
31
|
+
function isRecord(value) {
|
|
32
|
+
return typeof value === "object" && value !== null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalize a structured error envelope out of any of the places a surface may
|
|
36
|
+
* carry it: a tool result's `structuredContent`, an `McpError`'s `data`, or a
|
|
37
|
+
* bare geoprocessing-error object. Returns undefined when no structure is found.
|
|
38
|
+
*/
|
|
39
|
+
export function extractStructuredError(source) {
|
|
40
|
+
if (source instanceof McpError) {
|
|
41
|
+
const fromData = extractStructuredError(source.data);
|
|
42
|
+
if (fromData) {
|
|
43
|
+
return fromData;
|
|
44
|
+
}
|
|
45
|
+
// A raw code may live on the error's message-less data; nothing structured.
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!isRecord(source)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
// Tool result → dig into structuredContent first.
|
|
52
|
+
if (isRecord(source.structuredContent)) {
|
|
53
|
+
const nested = extractStructuredError(source.structuredContent);
|
|
54
|
+
if (nested) {
|
|
55
|
+
return nested;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const code = typeof source.code === "string" ? source.code : undefined;
|
|
59
|
+
// Envelope with a nested geoprocessing-error under `error`.
|
|
60
|
+
if (isRecord(source.error)) {
|
|
61
|
+
return { code, error: source.error };
|
|
62
|
+
}
|
|
63
|
+
// The object may itself be a geoprocessing-error (has `kind` + `message`).
|
|
64
|
+
if (typeof source.kind === "string" && typeof source.message === "string") {
|
|
65
|
+
return { code, error: source };
|
|
66
|
+
}
|
|
67
|
+
if (code) {
|
|
68
|
+
return { code };
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
/** Validate a value against the vendored geoprocessing-error schema. */
|
|
73
|
+
export function validateGeoprocessingError(error) {
|
|
74
|
+
const result = validateAgainstSchema(geoprocessingErrorSchema(), error);
|
|
75
|
+
return { ok: result.valid, errors: result.errors };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* error-shape contract: an invalid-argument tool call must return an `isError`
|
|
79
|
+
* result whose structuredContent carries an actionable code and a
|
|
80
|
+
* geoprocessing-error-shaped `ValidationFailed` envelope.
|
|
81
|
+
*/
|
|
82
|
+
export function checkInvalidArgsContract(result) {
|
|
83
|
+
const errors = [];
|
|
84
|
+
if (result.isError !== true) {
|
|
85
|
+
errors.push("expected isError=true for an invalid-argument call (got a protocol error or a success)");
|
|
86
|
+
}
|
|
87
|
+
const envelope = extractStructuredError(result);
|
|
88
|
+
if (!envelope) {
|
|
89
|
+
errors.push("no structured error envelope found on the tool result");
|
|
90
|
+
return { ok: false, errors };
|
|
91
|
+
}
|
|
92
|
+
if (!envelope.code || envelope.code.trim().length === 0) {
|
|
93
|
+
errors.push("structured error is missing a machine-actionable `code`");
|
|
94
|
+
}
|
|
95
|
+
if (envelope.error === undefined) {
|
|
96
|
+
errors.push("structured error is missing a geoprocessing-error envelope");
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const validation = validateGeoprocessingError(envelope.error);
|
|
100
|
+
if (!validation.ok) {
|
|
101
|
+
errors.push(...validation.errors.map((e) => `geoprocessing-error: ${e}`));
|
|
102
|
+
}
|
|
103
|
+
const kind = isRecord(envelope.error) ? envelope.error.kind : undefined;
|
|
104
|
+
if (kind !== "ValidationFailed") {
|
|
105
|
+
errors.push(`expected geoprocessing-error kind "ValidationFailed", got "${String(kind)}"`);
|
|
106
|
+
}
|
|
107
|
+
const violations = isRecord(envelope.error) ? envelope.error.violations : undefined;
|
|
108
|
+
if (!Array.isArray(violations) || violations.length === 0) {
|
|
109
|
+
errors.push("expected at least one structured validation `violation`");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { ok: errors.length === 0, errors };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* auth contract: an unauthenticated call must surface a structured
|
|
116
|
+
* `unauthenticated` / `permission_denied` signal — either as a code on the
|
|
117
|
+
* envelope or as an AuthorizationDenied geoprocessing-error kind.
|
|
118
|
+
*/
|
|
119
|
+
export function checkAuthContract(source) {
|
|
120
|
+
const envelope = extractStructuredError(source);
|
|
121
|
+
if (!envelope) {
|
|
122
|
+
return { ok: false, errors: ["no structured error found for the unauthenticated request"] };
|
|
123
|
+
}
|
|
124
|
+
const code = envelope.code;
|
|
125
|
+
const kind = isRecord(envelope.error) ? envelope.error.kind : undefined;
|
|
126
|
+
const codeOk = typeof code === "string" && AUTH_ERROR_CODES.includes(code);
|
|
127
|
+
const kindOk = typeof kind === "string" && AUTH_ERROR_KINDS.includes(kind);
|
|
128
|
+
if (codeOk || kindOk) {
|
|
129
|
+
return { ok: true, errors: [] };
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
errors: [
|
|
134
|
+
`expected a structured auth signal (code in [${AUTH_ERROR_CODES.join(", ")}] or kind in [${AUTH_ERROR_KINDS.join(", ")}]), got code="${String(code)}" kind="${String(kind)}"`,
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Documented job states, ordered by lifecycle rank (terminal states are highest). */
|
|
139
|
+
export const JOB_STATE_RANK = {
|
|
140
|
+
Submitted: 0,
|
|
141
|
+
Queued: 0,
|
|
142
|
+
Running: 1,
|
|
143
|
+
Succeeded: 2,
|
|
144
|
+
Failed: 2,
|
|
145
|
+
Cancelled: 2,
|
|
146
|
+
};
|
|
147
|
+
const TERMINAL_JOB_STATES = new Set(["Succeeded", "Failed", "Cancelled"]);
|
|
148
|
+
/**
|
|
149
|
+
* mutating-round-trip contract: an insert must be observable, an update must be
|
|
150
|
+
* reflected, and a delete must remove the feature — the full edit lifecycle
|
|
151
|
+
* verified against the surface's own read path.
|
|
152
|
+
*/
|
|
153
|
+
export function checkFeatureRoundTrip(obs) {
|
|
154
|
+
const errors = [];
|
|
155
|
+
if (obs.insertedObjectId === undefined) {
|
|
156
|
+
errors.push("insert did not return a new objectId");
|
|
157
|
+
}
|
|
158
|
+
if (!obs.presentAfterInsert) {
|
|
159
|
+
errors.push("inserted feature was not observable via a follow-up query");
|
|
160
|
+
}
|
|
161
|
+
if (obs.attributeAfterUpdate !== obs.expectedAttribute) {
|
|
162
|
+
errors.push(`updated attribute did not round-trip (expected ${JSON.stringify(obs.expectedAttribute)}, read ${JSON.stringify(obs.attributeAfterUpdate)})`);
|
|
163
|
+
}
|
|
164
|
+
if (!obs.absentAfterDelete) {
|
|
165
|
+
errors.push("feature was still observable after delete");
|
|
166
|
+
}
|
|
167
|
+
return { ok: errors.length === 0, errors };
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* async-job-lifecycle contract: an execute → poll(honua://jobs/{id}) → fetch-result
|
|
171
|
+
* flow must follow the documented state machine — statuses are all recognized,
|
|
172
|
+
* rank never regresses, progress is monotonic within [0,1], the job terminates
|
|
173
|
+
* (Succeeded here), and the result package is retrievable.
|
|
174
|
+
*/
|
|
175
|
+
export function checkJobLifecycle(obs) {
|
|
176
|
+
const errors = [];
|
|
177
|
+
const allStatuses = [obs.initialStatus, ...obs.polledStatuses, obs.finalStatus];
|
|
178
|
+
let lastRank = -1;
|
|
179
|
+
for (const status of allStatuses) {
|
|
180
|
+
const rank = JOB_STATE_RANK[status];
|
|
181
|
+
if (rank === undefined) {
|
|
182
|
+
errors.push(`unrecognized job state "${status}" (not in the documented state machine)`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (rank < lastRank) {
|
|
186
|
+
errors.push(`job state regressed from rank ${lastRank} to "${status}" (rank ${rank})`);
|
|
187
|
+
}
|
|
188
|
+
lastRank = rank;
|
|
189
|
+
}
|
|
190
|
+
let lastFraction = -1;
|
|
191
|
+
for (const fraction of obs.progressFractions) {
|
|
192
|
+
if (typeof fraction !== "number" || Number.isNaN(fraction) || fraction < 0 || fraction > 1) {
|
|
193
|
+
errors.push(`progress fraction ${String(fraction)} is out of the documented [0,1] range`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (fraction < lastFraction) {
|
|
197
|
+
errors.push(`progress fraction regressed from ${lastFraction} to ${fraction}`);
|
|
198
|
+
}
|
|
199
|
+
lastFraction = fraction;
|
|
200
|
+
}
|
|
201
|
+
if (!TERMINAL_JOB_STATES.has(obs.finalStatus)) {
|
|
202
|
+
errors.push(`job did not reach a terminal state (ended "${obs.finalStatus}")`);
|
|
203
|
+
}
|
|
204
|
+
if (obs.finalStatus === "Succeeded" && !obs.resultReady) {
|
|
205
|
+
errors.push("job succeeded but no ready result package could be fetched");
|
|
206
|
+
}
|
|
207
|
+
return { ok: errors.length === 0, errors };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* pagination contract: following `nextCursor` yields additional distinct items,
|
|
211
|
+
* never repeats a cursor (terminates), and the final page has no cursor.
|
|
212
|
+
*/
|
|
213
|
+
export function checkPaginationContract(pages) {
|
|
214
|
+
const errors = [];
|
|
215
|
+
if (pages.length === 0) {
|
|
216
|
+
return { ok: false, errors: ["no list pages captured"] };
|
|
217
|
+
}
|
|
218
|
+
const seenCursors = new Set();
|
|
219
|
+
const seenKeys = new Set();
|
|
220
|
+
for (let i = 0; i < pages.length; i++) {
|
|
221
|
+
const page = pages[i];
|
|
222
|
+
for (const key of page.keys) {
|
|
223
|
+
seenKeys.add(key);
|
|
224
|
+
}
|
|
225
|
+
const isLast = i === pages.length - 1;
|
|
226
|
+
if (!isLast) {
|
|
227
|
+
if (!page.nextCursor) {
|
|
228
|
+
errors.push(`page ${i} is not the last page but advertised no nextCursor`);
|
|
229
|
+
}
|
|
230
|
+
else if (seenCursors.has(page.nextCursor)) {
|
|
231
|
+
errors.push(`nextCursor "${page.nextCursor}" repeated — pagination does not terminate`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
seenCursors.add(page.nextCursor);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (page.nextCursor) {
|
|
238
|
+
errors.push(`final page still advertises nextCursor "${page.nextCursor}" — pagination did not terminate`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (pages.length > 1 && seenKeys.size <= pages[0].keys.length) {
|
|
242
|
+
errors.push("following nextCursor did not yield any additional distinct items");
|
|
243
|
+
}
|
|
244
|
+
return { ok: errors.length === 0, errors };
|
|
245
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-client.d.ts","sourceRoot":"","sources":["../../../src/certification/fixture-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,eAAe,CAAC;AAsDvB,mFAAmF;AACnF,wBAAgB,mBAAmB,IAAI,WAAW,CAkEjD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline fixture `HonuaClient` for deterministic certification.
|
|
3
|
+
*
|
|
4
|
+
* This is the standalone (non-vitest) counterpart of `test/test-helpers.ts`:
|
|
5
|
+
* it returns canned, schema-shaped responses for every method the MCP tools and
|
|
6
|
+
* resources call, so the certifier can run `tools/call` round-trips with no live
|
|
7
|
+
* backend, no network access, and no model/API calls. CI instead points the
|
|
8
|
+
* certifier at a live honua-server via `createClientFromEnv`.
|
|
9
|
+
*/
|
|
10
|
+
const STYLES_LIST = {
|
|
11
|
+
styles: [
|
|
12
|
+
{ id: "topographic", title: "Topographic", links: [] },
|
|
13
|
+
{ id: "imagery", title: "Imagery", links: [] },
|
|
14
|
+
],
|
|
15
|
+
default: "topographic",
|
|
16
|
+
};
|
|
17
|
+
const TOPOGRAPHIC_METADATA = {
|
|
18
|
+
id: "topographic",
|
|
19
|
+
title: "Topographic",
|
|
20
|
+
description: "Default topographic basemap style",
|
|
21
|
+
version: "3",
|
|
22
|
+
links: [{ rel: "preview", type: "image/png", href: "https://example.test/ogc/styles/topographic/preview" }],
|
|
23
|
+
};
|
|
24
|
+
const TOPOGRAPHIC_STYLESHEET = {
|
|
25
|
+
version: 8,
|
|
26
|
+
name: "Topographic",
|
|
27
|
+
layers: [{ id: "background", type: "background" }],
|
|
28
|
+
};
|
|
29
|
+
function jsonResponse(body, status = 200) {
|
|
30
|
+
return new Response(JSON.stringify(body), {
|
|
31
|
+
status,
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function fixtureFetch(_method, path) {
|
|
36
|
+
if (path === "/ogc/styles") {
|
|
37
|
+
return jsonResponse(STYLES_LIST);
|
|
38
|
+
}
|
|
39
|
+
if (path === "/ogc/styles/topographic/metadata") {
|
|
40
|
+
return jsonResponse(TOPOGRAPHIC_METADATA);
|
|
41
|
+
}
|
|
42
|
+
if (path === "/ogc/styles/topographic") {
|
|
43
|
+
return jsonResponse(TOPOGRAPHIC_STYLESHEET);
|
|
44
|
+
}
|
|
45
|
+
return jsonResponse({ error: "not found" }, 404);
|
|
46
|
+
}
|
|
47
|
+
/** Build an offline fixture client usable anywhere a `HonuaClient` is expected. */
|
|
48
|
+
export function createFixtureClient() {
|
|
49
|
+
const client = {
|
|
50
|
+
serverBaseUrl: "https://example.test",
|
|
51
|
+
async listServices() {
|
|
52
|
+
return {
|
|
53
|
+
services: [
|
|
54
|
+
{ name: "Parks", type: "FeatureServer" },
|
|
55
|
+
{ name: "Basemap", type: "MapServer" },
|
|
56
|
+
{ name: "Census", type: "FeatureServer" },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
async getFeatureServiceMetadata(_serviceId) {
|
|
61
|
+
return {
|
|
62
|
+
serviceDescription: "Test service",
|
|
63
|
+
layers: [
|
|
64
|
+
{ id: 0, name: "Layer 0" },
|
|
65
|
+
{ id: 1, name: "Layer 1" },
|
|
66
|
+
],
|
|
67
|
+
spatialReference: { wkid: 4326 },
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
async getLayerMetadata(_serviceId, _layerId) {
|
|
71
|
+
return {
|
|
72
|
+
id: 0,
|
|
73
|
+
name: "Test Layer",
|
|
74
|
+
description: "A test layer",
|
|
75
|
+
geometryType: "esriGeometryPoint",
|
|
76
|
+
fields: [
|
|
77
|
+
{ name: "OBJECTID", type: "esriFieldTypeOID" },
|
|
78
|
+
{ name: "NAME", type: "esriFieldTypeString", alias: "Feature Name" },
|
|
79
|
+
{ name: "VALUE", type: "esriFieldTypeDouble" },
|
|
80
|
+
],
|
|
81
|
+
extent: { xmin: -180, ymin: -90, xmax: 180, ymax: 90 },
|
|
82
|
+
spatialReference: { wkid: 4326 },
|
|
83
|
+
relationships: [{ id: 0, name: "rel_0", relatedTableId: 1 }],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
async queryFeatures(request) {
|
|
87
|
+
const extraParams = request?.extraParams ?? {};
|
|
88
|
+
// count-only and extent-only projections mirror the GeoServices query
|
|
89
|
+
// surface the tools exercise (returnCountOnly / returnExtentOnly).
|
|
90
|
+
if (extraParams.returnCountOnly === true) {
|
|
91
|
+
return { count: 2 };
|
|
92
|
+
}
|
|
93
|
+
if (extraParams.returnExtentOnly === true) {
|
|
94
|
+
return {
|
|
95
|
+
count: 2,
|
|
96
|
+
extent: { xmin: -180, ymin: -90, xmax: 180, ymax: 90, spatialReference: { wkid: 4326 } },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
objectIdFieldName: "OBJECTID",
|
|
101
|
+
geometryType: "esriGeometryPoint",
|
|
102
|
+
spatialReference: { wkid: 4326 },
|
|
103
|
+
features: [
|
|
104
|
+
{ attributes: { OBJECTID: 1, NAME: "Park A", VALUE: 100 } },
|
|
105
|
+
{ attributes: { OBJECTID: 2, NAME: "Park B", VALUE: 200 } },
|
|
106
|
+
],
|
|
107
|
+
exceededTransferLimit: false,
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
pipelineFetch: fixtureFetch,
|
|
111
|
+
};
|
|
112
|
+
return client;
|
|
113
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic round-trip fixture inputs for the honua `/mcp` operator surface.
|
|
3
|
+
*
|
|
4
|
+
* Certification calls `tools/call` on read-only operator tools to validate their
|
|
5
|
+
* structured output against the advertised outputSchema. Inputs are resolved
|
|
6
|
+
* from environment overrides so the same harness drives both the offline mock
|
|
7
|
+
* upstream and a live honua-server `/mcp` in remote mode, without ever calling a
|
|
8
|
+
* mutating tool (execute/cancel/publish/propose/create).
|
|
9
|
+
*/
|
|
10
|
+
export interface RoundTripEnv {
|
|
11
|
+
serviceId: string;
|
|
12
|
+
layerId: number;
|
|
13
|
+
address: string;
|
|
14
|
+
styleId: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** Resolve round-trip identifiers from the environment, with safe fallbacks. */
|
|
17
|
+
export declare function resolveRoundTripEnv(env?: NodeJS.ProcessEnv): RoundTripEnv;
|
|
18
|
+
/**
|
|
19
|
+
* Build the read-only round-trip argument map keyed by advertised tool name.
|
|
20
|
+
* Tools without a fixture entry are still discovered, schema/conformance/output
|
|
21
|
+
* checked, but are not round-tripped (no call is made). Mutating tools are never
|
|
22
|
+
* listed here.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildRoundTripInputs(env: RoundTripEnv): Record<string, Record<string, unknown>>;
|
|
25
|
+
/** Target layer for the mutating round-trip + pagination contracts. */
|
|
26
|
+
export interface EditTarget {
|
|
27
|
+
serviceId: string;
|
|
28
|
+
layerId: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the editable/pageable target for the deep contracts. Defaults to the
|
|
32
|
+
* offline mock's seeded `svc-parks` layer 0; override with
|
|
33
|
+
* `HONUA_MCP_EDIT_SERVICE_ID` / `HONUA_MCP_EDIT_LAYER_ID` (or the shared
|
|
34
|
+
* `HONUA_MCP_SERVICE_ID` / `HONUA_MCP_LAYER_ID`) to point a live mutating cert at
|
|
35
|
+
* a dedicated scratch service.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveEditTarget(env?: NodeJS.ProcessEnv): EditTarget;
|
|
38
|
+
/**
|
|
39
|
+
* A deliberately invalid argument set for the error-shape contract. Certification
|
|
40
|
+
* picks the first advertised tool that has an entry here and calls it with these
|
|
41
|
+
* args, expecting a structured `ValidationFailed` tool result (not a protocol
|
|
42
|
+
* error). `honua_query_features` requires `layerId`, so omitting it is invalid.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildInvalidArgsInputs(): Record<string, Record<string, unknown>>;
|
|
45
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/certification/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAQtF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsC/F;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAOlF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAMhF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic round-trip fixture inputs for the honua `/mcp` operator surface.
|
|
3
|
+
*
|
|
4
|
+
* Certification calls `tools/call` on read-only operator tools to validate their
|
|
5
|
+
* structured output against the advertised outputSchema. Inputs are resolved
|
|
6
|
+
* from environment overrides so the same harness drives both the offline mock
|
|
7
|
+
* upstream and a live honua-server `/mcp` in remote mode, without ever calling a
|
|
8
|
+
* mutating tool (execute/cancel/publish/propose/create).
|
|
9
|
+
*/
|
|
10
|
+
/** Resolve round-trip identifiers from the environment, with safe fallbacks. */
|
|
11
|
+
export function resolveRoundTripEnv(env = process.env) {
|
|
12
|
+
const serviceId = env.HONUA_MCP_SERVICE_ID ?? env.HONUA_SERVICE_ID ?? "svc-parks";
|
|
13
|
+
const layerRaw = env.HONUA_MCP_LAYER_ID ?? env.HONUA_LAYER_ID ?? "0";
|
|
14
|
+
const parsedLayer = Number.parseInt(layerRaw, 10);
|
|
15
|
+
const layerId = Number.isFinite(parsedLayer) && parsedLayer >= 0 ? parsedLayer : 0;
|
|
16
|
+
const address = env.HONUA_MCP_ADDRESS ?? "1600 Pennsylvania Ave NW, Washington DC";
|
|
17
|
+
const styleId = env.HONUA_MCP_STYLE_ID ?? env.HONUA_STYLE_ID;
|
|
18
|
+
return { serviceId, layerId, address, styleId };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the read-only round-trip argument map keyed by advertised tool name.
|
|
22
|
+
* Tools without a fixture entry are still discovered, schema/conformance/output
|
|
23
|
+
* checked, but are not round-tripped (no call is made). Mutating tools are never
|
|
24
|
+
* listed here.
|
|
25
|
+
*/
|
|
26
|
+
export function buildRoundTripInputs(env) {
|
|
27
|
+
const { serviceId, layerId, address } = env;
|
|
28
|
+
return {
|
|
29
|
+
// ── Platform-free standalone surface tools (issue #369). Round-tripped by the
|
|
30
|
+
// `standalone` cert target against a plain public FeatureServer fixture. The
|
|
31
|
+
// Honua-only style tools resolve to a structured "not available on this
|
|
32
|
+
// target" result (non-error) — certifying graceful degradation, not a crash.
|
|
33
|
+
honua_list_services: {},
|
|
34
|
+
honua_describe_layer: { serviceId, layerId },
|
|
35
|
+
honua_count_features: { serviceId, layerId },
|
|
36
|
+
honua_get_extent: { serviceId, layerId },
|
|
37
|
+
honua_statistics: { serviceId, layerId, statisticType: "count", onField: "OBJECTID" },
|
|
38
|
+
honua_explain_capability_gap: { capability: "query", protocol: "wmts" },
|
|
39
|
+
honua_get_style: {},
|
|
40
|
+
honua_apply_style_preset: { styleId: "topographic" },
|
|
41
|
+
// ── Honua-enhanced operator catalog tools (offline/remote/stdio-proxy targets).
|
|
42
|
+
honua_list_layers: {},
|
|
43
|
+
honua_list_capabilities: {},
|
|
44
|
+
honua_query_features: { serviceId, layerId, limit: 1 },
|
|
45
|
+
honua_render_map: { layers: [{ serviceId, layerId }], bbox: [-77.1, 38.8, -76.9, 39.0] },
|
|
46
|
+
honua_geocode_address: { address },
|
|
47
|
+
honua_solve_route: {
|
|
48
|
+
stops: [
|
|
49
|
+
{ lon: -77.03, lat: 38.9 },
|
|
50
|
+
{ lon: -76.61, lat: 39.29 },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
honua_plan_analysis: { intent: "Count parks within the district boundary" },
|
|
54
|
+
honua_ground_candidates: { goal: "Analyze park coverage" },
|
|
55
|
+
honua_clarify_intent: {
|
|
56
|
+
goal: "Analyze park coverage",
|
|
57
|
+
intentId: "int-001",
|
|
58
|
+
response: { answers: { q1: ["yes"] } },
|
|
59
|
+
},
|
|
60
|
+
honua_validate_plan: { plan: {} },
|
|
61
|
+
honua_dry_run_plan: { plan: { planId: "plan-001", steps: [{ stepId: "s1", kind: "QueryFeatures" }] } },
|
|
62
|
+
honua_resolve_entity: { text: "parks" },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the editable/pageable target for the deep contracts. Defaults to the
|
|
67
|
+
* offline mock's seeded `svc-parks` layer 0; override with
|
|
68
|
+
* `HONUA_MCP_EDIT_SERVICE_ID` / `HONUA_MCP_EDIT_LAYER_ID` (or the shared
|
|
69
|
+
* `HONUA_MCP_SERVICE_ID` / `HONUA_MCP_LAYER_ID`) to point a live mutating cert at
|
|
70
|
+
* a dedicated scratch service.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveEditTarget(env = process.env) {
|
|
73
|
+
const base = resolveRoundTripEnv(env);
|
|
74
|
+
const serviceId = env.HONUA_MCP_EDIT_SERVICE_ID ?? base.serviceId;
|
|
75
|
+
const layerRaw = env.HONUA_MCP_EDIT_LAYER_ID;
|
|
76
|
+
const parsedLayer = layerRaw !== undefined ? Number.parseInt(layerRaw, 10) : Number.NaN;
|
|
77
|
+
const layerId = Number.isFinite(parsedLayer) && parsedLayer >= 0 ? parsedLayer : base.layerId;
|
|
78
|
+
return { serviceId, layerId };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A deliberately invalid argument set for the error-shape contract. Certification
|
|
82
|
+
* picks the first advertised tool that has an entry here and calls it with these
|
|
83
|
+
* args, expecting a structured `ValidationFailed` tool result (not a protocol
|
|
84
|
+
* error). `honua_query_features` requires `layerId`, so omitting it is invalid.
|
|
85
|
+
*/
|
|
86
|
+
export function buildInvalidArgsInputs() {
|
|
87
|
+
return {
|
|
88
|
+
honua_query_features: { serviceId: "svc-parks" },
|
|
89
|
+
honua_geocode_address: { maxResults: 3 },
|
|
90
|
+
honua_solve_route: { travelProfile: "driving" },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ValidateFunction } from "ajv/dist/2020.js";
|
|
2
|
+
import type { JsonSchema } from "./schema-index.js";
|
|
3
|
+
export interface WellFormedResult {
|
|
4
|
+
wellFormed: boolean;
|
|
5
|
+
errors: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Check that a value is a structurally well-formed JSON Schema by asking Ajv to
|
|
9
|
+
* compile it. A schema that compiles is usable as a validator; a schema Ajv
|
|
10
|
+
* refuses to compile is malformed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkWellFormed(schema: unknown): WellFormedResult;
|
|
13
|
+
export interface ValidationResult {
|
|
14
|
+
valid: boolean;
|
|
15
|
+
errors: string[];
|
|
16
|
+
}
|
|
17
|
+
/** Compile a schema into a reusable validator, or throw a descriptive error. */
|
|
18
|
+
export declare function compileValidator(schema: JsonSchema): ValidateFunction;
|
|
19
|
+
/** Validate a value against a JSON Schema and collect human-readable errors. */
|
|
20
|
+
export declare function validateAgainstSchema(schema: JsonSchema, value: unknown): ValidationResult;
|
|
21
|
+
export interface ConformanceResult {
|
|
22
|
+
conformant: boolean;
|
|
23
|
+
/** Required standard properties the advertised schema honors. */
|
|
24
|
+
matchedRequired: string[];
|
|
25
|
+
/** Conformance violations (missing or type-incompatible required properties). */
|
|
26
|
+
violations: string[];
|
|
27
|
+
/** Non-fatal observations (e.g. advertised extra properties, type widenings). */
|
|
28
|
+
notes: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface ConformanceOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Required standard properties this surface is permitted to omit because it is
|
|
33
|
+
* a documented, platform-gated degradation of the tool (see the SDK-local
|
|
34
|
+
* `PLATFORM_FREE_DEGRADATIONS` registry in `schema-index.ts`).
|
|
35
|
+
*
|
|
36
|
+
* A property listed here is a platform *binding* — an identifier that only has
|
|
37
|
+
* meaning against a Honua publishing/authoring surface (e.g. `apply_style_preset`
|
|
38
|
+
* requires `serviceId`+`layerId` to bind a preset onto a *published* layer via
|
|
39
|
+
* the server OGC API – Styles authoring surface, ADR-0048). A platform-free
|
|
40
|
+
* target has no such surface, so it degrades the tool to a non-error, client-side
|
|
41
|
+
* projection keyed on the taxonomy essence alone (e.g. `styleId`) and legitimately
|
|
42
|
+
* omits the binding identifiers.
|
|
43
|
+
*
|
|
44
|
+
* When a required standard property is absent AND listed here, it is recorded as
|
|
45
|
+
* a degradation note rather than a conformance violation. Everything else stays
|
|
46
|
+
* strict: unlisted required properties still fail, and type incompatibilities on
|
|
47
|
+
* properties the surface DOES advertise remain violations — degradation relaxes
|
|
48
|
+
* platform binding, never correctness on what is accepted.
|
|
49
|
+
*/
|
|
50
|
+
shedRequired?: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Deterministically compare an advertised tool inputSchema against a standard
|
|
54
|
+
* geospatial-mcp tool schema.
|
|
55
|
+
*
|
|
56
|
+
* Conformance contract (intentionally permissive to avoid false negatives — we
|
|
57
|
+
* fail only on demonstrable non-conformance, never on the absence of a tool):
|
|
58
|
+
*
|
|
59
|
+
* 1. Every property the standard marks `required` MUST be present in the
|
|
60
|
+
* advertised schema's properties (a client that follows the standard will
|
|
61
|
+
* send it, so the tool must accept it).
|
|
62
|
+
* 2. For each such required property, when both schemas declare a `type`, the
|
|
63
|
+
* advertised type must be compatible (equal, or a superset, or `integer`
|
|
64
|
+
* accepted where the standard says `number`).
|
|
65
|
+
*
|
|
66
|
+
* Extra advertised properties and looser-but-compatible types are recorded as
|
|
67
|
+
* notes, not violations: a richer reference shape is allowed to extend the
|
|
68
|
+
* bare standard.
|
|
69
|
+
*/
|
|
70
|
+
export declare function checkConformance(advertised: JsonSchema, standard: JsonSchema, options?: ConformanceOptions): ConformanceResult;
|
|
71
|
+
//# sourceMappingURL=json-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../../src/certification/json-schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA2C,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAElG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAwBpD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAcjE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AASD,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAGrE;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAQ1F;AAmCD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iFAAiF;IACjF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iFAAiF;IACjF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,UAAU,EACpB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAkEnB"}
|