@honua/mcp-server 0.0.20-alpha.0 → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -15
- package/certification/geospatial-mcp-schemas/PROVENANCE.md +15 -7
- package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +6 -0
- package/certification/geospatial-mcp-schemas/index.json +88 -9
- package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +13 -9
- package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
- package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
- package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
- package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +13 -3
- package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
- package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
- package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
- package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +20 -0
- package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +5 -0
- package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
- package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
- package/dist/src/capability.d.ts +48 -0
- package/dist/src/capability.d.ts.map +1 -0
- package/dist/src/capability.js +44 -0
- package/dist/src/certification/census-data.d.ts +33 -0
- package/dist/src/certification/census-data.d.ts.map +1 -0
- package/dist/src/certification/census-data.js +506 -0
- package/dist/src/certification/census-fixture-client.d.ts +7 -0
- package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
- package/dist/src/certification/census-fixture-client.js +226 -0
- package/dist/src/certification/certifier.d.ts +7 -1
- package/dist/src/certification/certifier.d.ts.map +1 -1
- package/dist/src/certification/certifier.js +52 -7
- package/dist/src/certification/cli.js +19 -4
- package/dist/src/certification/contracts.d.ts +41 -0
- package/dist/src/certification/contracts.d.ts.map +1 -1
- package/dist/src/certification/contracts.js +71 -0
- package/dist/src/certification/fixtures.d.ts +13 -0
- package/dist/src/certification/fixtures.d.ts.map +1 -1
- package/dist/src/certification/fixtures.js +28 -0
- package/dist/src/certification/json-schema.d.ts +23 -1
- package/dist/src/certification/json-schema.d.ts.map +1 -1
- package/dist/src/certification/json-schema.js +10 -2
- package/dist/src/certification/lifecycle.d.ts +14 -0
- package/dist/src/certification/lifecycle.d.ts.map +1 -0
- package/dist/src/certification/lifecycle.js +326 -0
- package/dist/src/certification/operator-catalog.d.ts +8 -0
- package/dist/src/certification/operator-catalog.d.ts.map +1 -1
- package/dist/src/certification/operator-catalog.js +189 -5
- package/dist/src/certification/report.d.ts.map +1 -1
- package/dist/src/certification/report.js +13 -0
- package/dist/src/certification/run.d.ts.map +1 -1
- package/dist/src/certification/run.js +1 -0
- package/dist/src/certification/schema-index.d.ts +25 -0
- package/dist/src/certification/schema-index.d.ts.map +1 -1
- package/dist/src/certification/schema-index.js +27 -0
- package/dist/src/certification/target.d.ts +9 -1
- package/dist/src/certification/target.d.ts.map +1 -1
- package/dist/src/certification/target.js +54 -2
- package/dist/src/eval/cli.js +4 -2
- package/dist/src/eval/corpus.d.ts +7 -0
- package/dist/src/eval/corpus.d.ts.map +1 -1
- package/dist/src/eval/corpus.js +16 -0
- package/dist/src/eval/drivers/deterministic.d.ts.map +1 -1
- package/dist/src/eval/drivers/deterministic.js +13 -0
- package/dist/src/eval/grade.d.ts.map +1 -1
- package/dist/src/eval/grade.js +20 -1
- package/dist/src/eval/report.d.ts +5 -0
- package/dist/src/eval/report.d.ts.map +1 -1
- package/dist/src/eval/report.js +3 -0
- package/dist/src/eval/runner.d.ts +6 -0
- package/dist/src/eval/runner.d.ts.map +1 -1
- package/dist/src/eval/runner.js +21 -7
- package/dist/src/eval/standalone-corpus.d.ts +3 -0
- package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
- package/dist/src/eval/standalone-corpus.js +699 -0
- package/dist/src/eval/types.d.ts +25 -0
- package/dist/src/eval/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +29 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +41 -21
- package/dist/src/provenance.d.ts +44 -0
- package/dist/src/provenance.d.ts.map +1 -0
- package/dist/src/provenance.js +37 -0
- package/dist/src/resources/styles.d.ts.map +1 -1
- package/dist/src/resources/styles.js +36 -3
- package/dist/src/styles.d.ts +2 -0
- package/dist/src/styles.d.ts.map +1 -1
- package/dist/src/styles.js +29 -3
- package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
- package/dist/src/tools/apply-style-preset.js +14 -2
- package/dist/src/tools/get-style.d.ts.map +1 -1
- package/dist/src/tools/get-style.js +24 -13
- package/dist/src/tools/list-services.d.ts.map +1 -1
- package/dist/src/tools/list-services.js +16 -1
- package/package.json +11 -2
|
@@ -286,8 +286,46 @@ export function buildOperatorTools() {
|
|
|
286
286
|
readOnly: false,
|
|
287
287
|
sampleOutput: { appPackageId: "app-001", uri: "honua://apps/app-001" },
|
|
288
288
|
},
|
|
289
|
+
{
|
|
290
|
+
// OPTIONAL mutation-profile tool (geospatial-mcp `mutation` profile) — matched
|
|
291
|
+
// as an advertised-only known gap, never round-tripped by the read-only path;
|
|
292
|
+
// the mutating-round-trip contract drives its insert→update→delete lifecycle.
|
|
293
|
+
// This fixture opts into the optional mutation profile so that path stays
|
|
294
|
+
// certified; the real honua-server surface does NOT advertise this tool —
|
|
295
|
+
// Honua does not support AI operational data editing (honua-server ADR-0028).
|
|
296
|
+
name: "honua_edit_features",
|
|
297
|
+
title: "Edit features",
|
|
298
|
+
description: "Apply feature inserts, updates, and deletes to a published editable layer.",
|
|
299
|
+
inputSchema: OBJECT_SCHEMA({
|
|
300
|
+
serviceId: { type: "string", minLength: 1 },
|
|
301
|
+
layerId: { type: "integer", minimum: 0 },
|
|
302
|
+
adds: {
|
|
303
|
+
type: "array",
|
|
304
|
+
items: OBJECT_SCHEMA({ attributes: { type: "object" } }, ["attributes"]),
|
|
305
|
+
},
|
|
306
|
+
updates: {
|
|
307
|
+
type: "array",
|
|
308
|
+
items: OBJECT_SCHEMA({ objectId: { type: "integer" }, attributes: { type: "object" } }, [
|
|
309
|
+
"objectId",
|
|
310
|
+
"attributes",
|
|
311
|
+
]),
|
|
312
|
+
},
|
|
313
|
+
deletes: { type: "array", items: { type: "integer" } },
|
|
314
|
+
}, ["serviceId", "layerId"]),
|
|
315
|
+
outputSchema: OBJECT_SCHEMA({
|
|
316
|
+
addResults: { type: "array", items: EDIT_RESULT_SCHEMA },
|
|
317
|
+
updateResults: { type: "array", items: EDIT_RESULT_SCHEMA },
|
|
318
|
+
deleteResults: { type: "array", items: EDIT_RESULT_SCHEMA },
|
|
319
|
+
}),
|
|
320
|
+
readOnly: false,
|
|
321
|
+
sampleOutput: { addResults: [], updateResults: [], deleteResults: [] },
|
|
322
|
+
},
|
|
289
323
|
];
|
|
290
324
|
}
|
|
325
|
+
const EDIT_RESULT_SCHEMA = OBJECT_SCHEMA({ objectId: { type: "integer" }, success: { type: "boolean" } }, [
|
|
326
|
+
"objectId",
|
|
327
|
+
"success",
|
|
328
|
+
]);
|
|
291
329
|
export function buildOperatorResources() {
|
|
292
330
|
return [
|
|
293
331
|
{
|
|
@@ -347,6 +385,88 @@ function validateToolArgs(tool, args) {
|
|
|
347
385
|
fieldPath: e.instancePath || undefined,
|
|
348
386
|
}));
|
|
349
387
|
}
|
|
388
|
+
/** Feature-store key seeded so the pagination + mutation contracts have real rows. */
|
|
389
|
+
const SEED_SERVICE = "svc-parks";
|
|
390
|
+
const SEED_LAYER = 0;
|
|
391
|
+
function seedFeatureStore() {
|
|
392
|
+
const layer = new Map();
|
|
393
|
+
// OBJECTID 1 first so the existing single-feature round-trip fixture is stable.
|
|
394
|
+
const names = ["Central Park", "Riverside Park", "Lincoln Park", "Golden Gate Park", "Prospect Park"];
|
|
395
|
+
names.forEach((name, i) => layer.set(i + 1, { OBJECTID: i + 1, NAME: name }));
|
|
396
|
+
return new Map([[`${SEED_SERVICE}::${SEED_LAYER}`, layer]]);
|
|
397
|
+
}
|
|
398
|
+
function newCatalogState() {
|
|
399
|
+
return { features: seedFeatureStore(), nextObjectId: 1000, jobs: new Map(), nextJobSeq: 1 };
|
|
400
|
+
}
|
|
401
|
+
/** Number of polls before the job lifecycle mock reports `Succeeded`. */
|
|
402
|
+
const JOB_SUCCESS_AFTER_POLLS = 3;
|
|
403
|
+
/** Stateful tool handlers keyed by tool name; absent ⇒ echo the tool's sampleOutput. */
|
|
404
|
+
function buildToolHandlers(state) {
|
|
405
|
+
const handlers = new Map();
|
|
406
|
+
handlers.set("honua_query_features", (args) => {
|
|
407
|
+
const key = `${String(args.serviceId)}::${Number(args.layerId)}`;
|
|
408
|
+
const layer = state.features.get(key) ?? new Map();
|
|
409
|
+
let rows = [...layer.values()].sort((a, b) => Number(a.OBJECTID) - Number(b.OBJECTID));
|
|
410
|
+
if (Array.isArray(args.objectIds)) {
|
|
411
|
+
const wanted = new Set(args.objectIds.map((v) => Number(v)));
|
|
412
|
+
rows = rows.filter((r) => wanted.has(Number(r.OBJECTID)));
|
|
413
|
+
}
|
|
414
|
+
const total = rows.length;
|
|
415
|
+
const offset = decodeFeatureOffset(args);
|
|
416
|
+
const limit = clampLimit(args.limit);
|
|
417
|
+
const page = rows.slice(offset, offset + limit);
|
|
418
|
+
const nextOffset = offset + page.length;
|
|
419
|
+
const out = {
|
|
420
|
+
features: page.map((attributes) => ({ attributes })),
|
|
421
|
+
count: page.length,
|
|
422
|
+
totalCount: total,
|
|
423
|
+
resultOffset: offset,
|
|
424
|
+
};
|
|
425
|
+
if (nextOffset < total) {
|
|
426
|
+
out.nextCursor = encodeCursor(nextOffset);
|
|
427
|
+
out.exceededTransferLimit = true;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
out.exceededTransferLimit = false;
|
|
431
|
+
}
|
|
432
|
+
return out;
|
|
433
|
+
});
|
|
434
|
+
handlers.set("honua_edit_features", (args) => {
|
|
435
|
+
const key = `${String(args.serviceId)}::${Number(args.layerId)}`;
|
|
436
|
+
let layer = state.features.get(key);
|
|
437
|
+
if (!layer) {
|
|
438
|
+
layer = new Map();
|
|
439
|
+
state.features.set(key, layer);
|
|
440
|
+
}
|
|
441
|
+
const addResults = asArray(args.adds).map((add) => {
|
|
442
|
+
const objectId = state.nextObjectId++;
|
|
443
|
+
layer.set(objectId, { ...(add.attributes ?? {}), OBJECTID: objectId });
|
|
444
|
+
return { objectId, success: true };
|
|
445
|
+
});
|
|
446
|
+
const updateResults = asArray(args.updates).map((u) => {
|
|
447
|
+
const objectId = Number(u.objectId);
|
|
448
|
+
const existing = layer.get(objectId);
|
|
449
|
+
if (!existing) {
|
|
450
|
+
return { objectId, success: false };
|
|
451
|
+
}
|
|
452
|
+
layer.set(objectId, { ...existing, ...(u.attributes ?? {}), OBJECTID: objectId });
|
|
453
|
+
return { objectId, success: true };
|
|
454
|
+
});
|
|
455
|
+
const deleteResults = asArray(args.deletes).map((raw) => {
|
|
456
|
+
const objectId = Number(raw);
|
|
457
|
+
return { objectId, success: layer.delete(objectId) };
|
|
458
|
+
});
|
|
459
|
+
return { addResults, updateResults, deleteResults };
|
|
460
|
+
});
|
|
461
|
+
handlers.set("honua_execute_plan", () => {
|
|
462
|
+
const seq = state.nextJobSeq++;
|
|
463
|
+
const jobId = `job-${String(seq).padStart(3, "0")}`;
|
|
464
|
+
const resultPackageId = `res-${jobId}`;
|
|
465
|
+
state.jobs.set(jobId, { jobId, polls: 0, resultPackageId });
|
|
466
|
+
return { jobId, status: "Submitted" };
|
|
467
|
+
});
|
|
468
|
+
return handlers;
|
|
469
|
+
}
|
|
350
470
|
/**
|
|
351
471
|
* Build the low-level MCP `Server` for the mock operator catalog. Auth is
|
|
352
472
|
* enforced per request from `extra.authInfo` (populated by the HTTP transport
|
|
@@ -358,6 +478,8 @@ export function createOperatorCatalogServer(options = {}) {
|
|
|
358
478
|
const tools = buildOperatorTools();
|
|
359
479
|
const resources = buildOperatorResources();
|
|
360
480
|
const toolsByName = new Map(tools.map((t) => [t.name, t]));
|
|
481
|
+
const state = newCatalogState();
|
|
482
|
+
const handlers = buildToolHandlers(state);
|
|
361
483
|
const server = new Server({ name: "honua", version: "operator-mcp-mock" }, {
|
|
362
484
|
capabilities: { tools: { listChanged: true }, resources: { listChanged: true } },
|
|
363
485
|
instructions: "Mock honua /mcp operator surface for offline certification.",
|
|
@@ -391,9 +513,11 @@ export function createOperatorCatalogServer(options = {}) {
|
|
|
391
513
|
if (violations.length > 0) {
|
|
392
514
|
return toolErrorResult(invalidArgumentError(violations));
|
|
393
515
|
}
|
|
516
|
+
const handler = handlers.get(tool.name);
|
|
517
|
+
const output = handler ? handler(request.params.arguments ?? {}) : tool.sampleOutput;
|
|
394
518
|
return {
|
|
395
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
396
|
-
structuredContent:
|
|
519
|
+
content: [{ type: "text", text: JSON.stringify(output) }],
|
|
520
|
+
structuredContent: output,
|
|
397
521
|
isError: false,
|
|
398
522
|
};
|
|
399
523
|
});
|
|
@@ -408,11 +532,27 @@ export function createOperatorCatalogServer(options = {}) {
|
|
|
408
532
|
if (!extra.authInfo) {
|
|
409
533
|
throw new McpError(ErrorCode.InvalidRequest, UNAUTHENTICATED_ERROR.error.message, UNAUTHENTICATED_ERROR);
|
|
410
534
|
}
|
|
411
|
-
const
|
|
535
|
+
const uri = request.params.uri;
|
|
536
|
+
// Dynamic job lifecycle resource: honua://jobs/{id} advances a documented
|
|
537
|
+
// Submitted→Running→Succeeded state machine on each poll.
|
|
538
|
+
const jobMatch = /^honua:\/\/jobs\/([^/]+)$/.exec(uri);
|
|
539
|
+
if (jobMatch) {
|
|
540
|
+
return readJobResource(state, jobMatch[1], uri);
|
|
541
|
+
}
|
|
542
|
+
// Dynamic result package (produced by a Succeeded job) or the seeded packages.
|
|
543
|
+
const resultMatch = /^honua:\/\/results\/([^/]+)$/.exec(uri);
|
|
544
|
+
if (resultMatch && !resources.some((r) => r.uri === uri)) {
|
|
545
|
+
return jsonResource(uri, {
|
|
546
|
+
resultPackageId: resultMatch[1],
|
|
547
|
+
status: "Ready",
|
|
548
|
+
artifacts: [{ kind: "FeatureService", uri: `honua://services/svc-pub-${resultMatch[1]}` }],
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
const resource = resources.find((r) => r.uri === uri);
|
|
412
552
|
if (!resource) {
|
|
413
|
-
throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${
|
|
553
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`, {
|
|
414
554
|
code: "not_found",
|
|
415
|
-
error: { kind: "UnknownDataset", message: `Unknown resource: ${
|
|
555
|
+
error: { kind: "UnknownDataset", message: `Unknown resource: ${uri}` },
|
|
416
556
|
});
|
|
417
557
|
}
|
|
418
558
|
return {
|
|
@@ -421,6 +561,50 @@ export function createOperatorCatalogServer(options = {}) {
|
|
|
421
561
|
});
|
|
422
562
|
return server;
|
|
423
563
|
}
|
|
564
|
+
function readJobResource(state, jobId, uri) {
|
|
565
|
+
const job = state.jobs.get(jobId);
|
|
566
|
+
if (!job) {
|
|
567
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown job: ${jobId}`, {
|
|
568
|
+
code: "not_found",
|
|
569
|
+
error: { kind: "UnknownProcess", message: `Unknown job: ${jobId}` },
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
job.polls++;
|
|
573
|
+
const succeeded = job.polls >= JOB_SUCCESS_AFTER_POLLS;
|
|
574
|
+
const status = succeeded ? "Succeeded" : "Running";
|
|
575
|
+
const fraction = Math.min(job.polls / JOB_SUCCESS_AFTER_POLLS, 1);
|
|
576
|
+
const body = {
|
|
577
|
+
jobId: job.jobId,
|
|
578
|
+
status,
|
|
579
|
+
progress: { fraction: Number(fraction.toFixed(4)), message: `step ${job.polls}` },
|
|
580
|
+
};
|
|
581
|
+
if (succeeded) {
|
|
582
|
+
body.resultPackageId = job.resultPackageId;
|
|
583
|
+
body.resultUri = `honua://results/${job.resultPackageId}`;
|
|
584
|
+
}
|
|
585
|
+
return jsonResource(uri, body);
|
|
586
|
+
}
|
|
587
|
+
function jsonResource(uri, body) {
|
|
588
|
+
return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(body) }] };
|
|
589
|
+
}
|
|
590
|
+
function asArray(value) {
|
|
591
|
+
return Array.isArray(value) ? value : [];
|
|
592
|
+
}
|
|
593
|
+
function clampLimit(raw) {
|
|
594
|
+
const n = Number(raw);
|
|
595
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
596
|
+
return 100;
|
|
597
|
+
}
|
|
598
|
+
return Math.min(Math.floor(n), 1000);
|
|
599
|
+
}
|
|
600
|
+
/** Resolve a feature-page offset from an explicit `resultOffset` or a page `cursor`. */
|
|
601
|
+
function decodeFeatureOffset(args) {
|
|
602
|
+
if (typeof args.cursor === "string") {
|
|
603
|
+
return decodeCursor(args.cursor);
|
|
604
|
+
}
|
|
605
|
+
const off = Number(args.resultOffset);
|
|
606
|
+
return Number.isFinite(off) && off >= 0 ? Math.floor(off) : 0;
|
|
607
|
+
}
|
|
424
608
|
function toolErrorResult(error) {
|
|
425
609
|
return {
|
|
426
610
|
content: [{ type: "text", text: error.error.message }],
|
|
@@ -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,
|
|
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"}
|
|
@@ -14,6 +14,18 @@ export function renderMarkdown(report) {
|
|
|
14
14
|
lines.push(`- Backend: \`${report.protocol.backend}\`${report.protocol.honuaTransport ? ` (Honua transport: \`${report.protocol.honuaTransport}\`)` : ""}`);
|
|
15
15
|
lines.push(`- Standard: \`${report.standard.source}\` (index ${report.standard.indexDate}, ${report.standard.dialect})`);
|
|
16
16
|
lines.push("");
|
|
17
|
+
const p = report.provenance;
|
|
18
|
+
lines.push("## Provenance");
|
|
19
|
+
lines.push("");
|
|
20
|
+
lines.push("| Field | Value |");
|
|
21
|
+
lines.push("| --- | --- |");
|
|
22
|
+
lines.push(`| Target | ${p.targetUrl} |`);
|
|
23
|
+
lines.push(`| Auth mode | \`${p.authMode}\` |`);
|
|
24
|
+
lines.push(`| Negotiated protocol | \`${p.protocolVersion ?? "n/a"}\` |`);
|
|
25
|
+
lines.push(`| Tools advertised | ${p.toolCount} |`);
|
|
26
|
+
lines.push(`| Suite git SHA | \`${p.suiteGitSha}\` (${p.suiteGitShaSource}) |`);
|
|
27
|
+
lines.push(`| Generated | \`${report.generatedAt}\` |`);
|
|
28
|
+
lines.push("");
|
|
17
29
|
lines.push("## Summary");
|
|
18
30
|
lines.push("");
|
|
19
31
|
lines.push("| Metric | Value |");
|
|
@@ -27,6 +39,7 @@ export function renderMarkdown(report) {
|
|
|
27
39
|
lines.push(`| Resources discovered | ${s.resourcesDiscovered} |`);
|
|
28
40
|
lines.push(`| Prompts discovered | ${s.promptsDiscovered} |`);
|
|
29
41
|
lines.push(`| Contracts checked | ${s.contractsPassed} / ${s.contractsChecked} passed |`);
|
|
42
|
+
lines.push(`| Contracts skipped | ${s.contractsSkipped} |`);
|
|
30
43
|
lines.push(`| Known gaps | ${s.knownGaps} |`);
|
|
31
44
|
lines.push(`| Failures | ${s.failures} |`);
|
|
32
45
|
lines.push("");
|
|
@@ -1 +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,
|
|
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,CAe9B;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"}
|
|
@@ -16,6 +16,7 @@ export async function certifyTarget(target, env = process.env) {
|
|
|
16
16
|
backend: target.backend,
|
|
17
17
|
surface: target.serverLabel,
|
|
18
18
|
honuaTransport: target.honuaTransport,
|
|
19
|
+
authMode: target.authMode,
|
|
19
20
|
connectUnauthenticated: target.supportsUnauthenticatedPass ? () => target.connectUnauthenticated() : undefined,
|
|
20
21
|
env,
|
|
21
22
|
});
|
|
@@ -51,6 +51,31 @@ export interface SchemaIndex {
|
|
|
51
51
|
* lives here in code instead.
|
|
52
52
|
*/
|
|
53
53
|
export declare const REFERENCE_TOOL_ALIASES: Readonly<Record<string, string>>;
|
|
54
|
+
/**
|
|
55
|
+
* Platform-gated degradations (SDK-local, NOT part of the vendored standard).
|
|
56
|
+
*
|
|
57
|
+
* Some reference-shape tools require standard properties that are *platform
|
|
58
|
+
* bindings* — identifiers that only have meaning against a Honua
|
|
59
|
+
* publishing/authoring surface. The platform-free (standalone) front door
|
|
60
|
+
* (issue #369) has no such surface, so it intentionally degrades these tools to
|
|
61
|
+
* a non-error, client-side projection and legitimately omits the binding
|
|
62
|
+
* identifiers, keeping only the taxonomy essence.
|
|
63
|
+
*
|
|
64
|
+
* This maps an advertised reference tool name to the required standard
|
|
65
|
+
* properties it may shed WITHOUT counting as non-conformant on a platform-free
|
|
66
|
+
* target. It is deliberately narrow: it never relaxes read-addressing required
|
|
67
|
+
* properties (e.g. `honua_query_features` still must require serviceId+layerId),
|
|
68
|
+
* only true platform-authoring bindings. The vendored `index.json` and schema
|
|
69
|
+
* files are byte-exact copies of the upstream standard and must never be
|
|
70
|
+
* hand-edited to encode this, so the decision lives here in code instead.
|
|
71
|
+
*
|
|
72
|
+
* `honua_apply_style_preset`: the reference shape binds a StyleRef preset onto a
|
|
73
|
+
* PUBLISHED layer (serviceId+layerId) via the server OGC API – Styles authoring
|
|
74
|
+
* surface (ADR-0048). The platform-free surface resolves a preset for
|
|
75
|
+
* client-side application keyed on `styleId` alone (ADR-0028: never mutates
|
|
76
|
+
* server state), so it sheds serviceId+layerId.
|
|
77
|
+
*/
|
|
78
|
+
export declare const PLATFORM_FREE_DEGRADATIONS: Readonly<Record<string, readonly string[]>>;
|
|
54
79
|
export type JsonSchema = Record<string, unknown>;
|
|
55
80
|
export declare const SCHEMA_ROOT: URL;
|
|
56
81
|
/** Load and cache the machine-readable schema index. */
|
|
@@ -1 +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"}
|
|
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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAElF,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"}
|
|
@@ -12,6 +12,33 @@ import { fileURLToPath } from "node:url";
|
|
|
12
12
|
export const REFERENCE_TOOL_ALIASES = {
|
|
13
13
|
honua_dry_run_plan: "honua_validate_plan",
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Platform-gated degradations (SDK-local, NOT part of the vendored standard).
|
|
17
|
+
*
|
|
18
|
+
* Some reference-shape tools require standard properties that are *platform
|
|
19
|
+
* bindings* — identifiers that only have meaning against a Honua
|
|
20
|
+
* publishing/authoring surface. The platform-free (standalone) front door
|
|
21
|
+
* (issue #369) has no such surface, so it intentionally degrades these tools to
|
|
22
|
+
* a non-error, client-side projection and legitimately omits the binding
|
|
23
|
+
* identifiers, keeping only the taxonomy essence.
|
|
24
|
+
*
|
|
25
|
+
* This maps an advertised reference tool name to the required standard
|
|
26
|
+
* properties it may shed WITHOUT counting as non-conformant on a platform-free
|
|
27
|
+
* target. It is deliberately narrow: it never relaxes read-addressing required
|
|
28
|
+
* properties (e.g. `honua_query_features` still must require serviceId+layerId),
|
|
29
|
+
* only true platform-authoring bindings. The vendored `index.json` and schema
|
|
30
|
+
* files are byte-exact copies of the upstream standard and must never be
|
|
31
|
+
* hand-edited to encode this, so the decision lives here in code instead.
|
|
32
|
+
*
|
|
33
|
+
* `honua_apply_style_preset`: the reference shape binds a StyleRef preset onto a
|
|
34
|
+
* PUBLISHED layer (serviceId+layerId) via the server OGC API – Styles authoring
|
|
35
|
+
* surface (ADR-0048). The platform-free surface resolves a preset for
|
|
36
|
+
* client-side application keyed on `styleId` alone (ADR-0028: never mutates
|
|
37
|
+
* server state), so it sheds serviceId+layerId.
|
|
38
|
+
*/
|
|
39
|
+
export const PLATFORM_FREE_DEGRADATIONS = {
|
|
40
|
+
honua_apply_style_preset: ["serviceId", "layerId"],
|
|
41
|
+
};
|
|
15
42
|
/**
|
|
16
43
|
* Resolved root of the vendored schema tree.
|
|
17
44
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { AuthMode } from "../provenance.js";
|
|
2
3
|
import { type ProxyOptions } from "../proxy.js";
|
|
3
4
|
/**
|
|
4
5
|
* Certification target selection.
|
|
@@ -14,13 +15,20 @@ import { type ProxyOptions } from "../proxy.js";
|
|
|
14
15
|
* Reuses the proxy's upstream connection logic verbatim.
|
|
15
16
|
* - `stdio-proxy` : spawns the `honua-mcp-proxy` binary against an upstream and
|
|
16
17
|
* certifies through stdio, proving the proxy chain end-to-end.
|
|
18
|
+
* - `standalone` : the PLATFORM-FREE front door (issue #369). Certifies the
|
|
19
|
+
* direct-SDK `createServer` surface against an in-process fixture
|
|
20
|
+
* of a PLAIN public FeatureServer (recorded census layer, no
|
|
21
|
+
* Honua surfaces). Proves the tools certify green against a
|
|
22
|
+
* non-Honua endpoint, with Honua-only tools skip-with-reason.
|
|
17
23
|
*/
|
|
18
|
-
export type CertTargetMode = "offline" | "remote" | "stdio-proxy";
|
|
24
|
+
export type CertTargetMode = "offline" | "remote" | "stdio-proxy" | "standalone";
|
|
19
25
|
export interface CertificationTarget {
|
|
20
26
|
mode: CertTargetMode;
|
|
21
27
|
backend: "mock-upstream" | "live";
|
|
22
28
|
/** Human-readable label for the certified surface (artifact provenance). */
|
|
23
29
|
serverLabel: string;
|
|
30
|
+
/** How the authenticated client reached the surface (recorded in provenance). */
|
|
31
|
+
authMode: AuthMode;
|
|
24
32
|
honuaTransport: string | null;
|
|
25
33
|
/** Authenticated, connected MCP client. */
|
|
26
34
|
client: Client;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAEjF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,QAAQ,CAAC;IACnB,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,CAQtF;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,CAYhH"}
|
|
@@ -2,14 +2,17 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
+
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
6
|
+
import { createClientFromEnv, createServer } from "../index.js";
|
|
5
7
|
import { connectUpstream } from "../proxy.js";
|
|
8
|
+
import { createStandaloneFixtureClient } from "./census-fixture-client.js";
|
|
6
9
|
import { startMockUpstream } from "./mock-upstream.js";
|
|
7
10
|
export function resolveTargetMode(env = process.env) {
|
|
8
11
|
const raw = (env.HONUA_MCP_CERT_TARGET ?? "offline").trim().toLowerCase();
|
|
9
|
-
if (raw === "offline" || raw === "remote" || raw === "stdio-proxy") {
|
|
12
|
+
if (raw === "offline" || raw === "remote" || raw === "stdio-proxy" || raw === "standalone") {
|
|
10
13
|
return raw;
|
|
11
14
|
}
|
|
12
|
-
throw new Error(`HONUA_MCP_CERT_TARGET must be "offline", "remote",
|
|
15
|
+
throw new Error(`HONUA_MCP_CERT_TARGET must be "offline", "remote", "stdio-proxy", or "standalone", received "${raw}"`);
|
|
13
16
|
}
|
|
14
17
|
/** Resolve the remote `/mcp` endpoint + credentials from the environment. */
|
|
15
18
|
export function resolveRemoteProxyOptions(env) {
|
|
@@ -50,8 +53,51 @@ export async function openCertificationTarget(env = process.env) {
|
|
|
50
53
|
if (mode === "stdio-proxy") {
|
|
51
54
|
return openStdioProxyTarget(env);
|
|
52
55
|
}
|
|
56
|
+
if (mode === "standalone") {
|
|
57
|
+
return openStandaloneTarget(env);
|
|
58
|
+
}
|
|
53
59
|
return openOfflineTarget(env);
|
|
54
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Open the platform-free standalone target: the direct-SDK `createServer` surface,
|
|
63
|
+
* reached over an in-memory MCP transport. By default it is wired to an in-process
|
|
64
|
+
* fixture of a plain public FeatureServer (recorded census layer, no Honua
|
|
65
|
+
* surfaces) — deterministic, no network, gates CI. When `HONUA_BASE_URL` is set
|
|
66
|
+
* (the scheduled live variant) it drives a REAL `HonuaClient` against that public
|
|
67
|
+
* FeatureServer/OGC endpoint instead. Either way there is no transport auth
|
|
68
|
+
* boundary, so the auth contracts skip-with-reason and the Honua-only style tools
|
|
69
|
+
* degrade to structured "not available on this target" results.
|
|
70
|
+
*/
|
|
71
|
+
async function openStandaloneTarget(env) {
|
|
72
|
+
const live = typeof env.HONUA_BASE_URL === "string" && env.HONUA_BASE_URL.length > 0;
|
|
73
|
+
const client_ = live ? createClientFromEnv(env) : createStandaloneFixtureClient();
|
|
74
|
+
const server = createServer(client_);
|
|
75
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
76
|
+
const client = new Client({ name: "honua-mcp-certifier", version: "1.0.0" });
|
|
77
|
+
await server.connect(serverTransport);
|
|
78
|
+
await client.connect(clientTransport);
|
|
79
|
+
return {
|
|
80
|
+
mode: "standalone",
|
|
81
|
+
backend: live ? "live" : "mock-upstream",
|
|
82
|
+
serverLabel: live
|
|
83
|
+
? `honua-mcp standalone → ${env.HONUA_BASE_URL} (live public FeatureServer, no Honua surfaces)`
|
|
84
|
+
: "honua-mcp standalone → plain public FeatureServer (recorded census fixture, no Honua surfaces)",
|
|
85
|
+
authMode: "anonymous",
|
|
86
|
+
honuaTransport: env.HONUA_TRANSPORT ?? null,
|
|
87
|
+
client,
|
|
88
|
+
// A plain FeatureServer has no MCP transport auth boundary — the auth
|
|
89
|
+
// contracts skip-with-reason rather than assert an enforcement that a
|
|
90
|
+
// platform-free target does not (and should not) provide.
|
|
91
|
+
supportsUnauthenticatedPass: false,
|
|
92
|
+
connectUnauthenticated() {
|
|
93
|
+
return Promise.reject(new Error("standalone target has no auth boundary"));
|
|
94
|
+
},
|
|
95
|
+
async close() {
|
|
96
|
+
await client.close().catch(() => { });
|
|
97
|
+
await server.close().catch(() => { });
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
55
101
|
async function openOfflineTarget(env) {
|
|
56
102
|
const mock = await startMockUpstream();
|
|
57
103
|
const authedClients = [];
|
|
@@ -61,6 +107,7 @@ async function openOfflineTarget(env) {
|
|
|
61
107
|
mode: "offline",
|
|
62
108
|
backend: "mock-upstream",
|
|
63
109
|
serverLabel: "honua /mcp operator catalog (in-process streamable-HTTP mock)",
|
|
110
|
+
authMode: "bearer",
|
|
64
111
|
honuaTransport: env.HONUA_TRANSPORT ?? null,
|
|
65
112
|
client,
|
|
66
113
|
supportsUnauthenticatedPass: true,
|
|
@@ -77,6 +124,9 @@ async function openOfflineTarget(env) {
|
|
|
77
124
|
},
|
|
78
125
|
};
|
|
79
126
|
}
|
|
127
|
+
function resolveAuthMode(options) {
|
|
128
|
+
return options.authToken ? "bearer" : options.apiKey ? "api-key" : "anonymous";
|
|
129
|
+
}
|
|
80
130
|
async function openRemoteTarget(env) {
|
|
81
131
|
const options = resolveRemoteProxyOptions(env);
|
|
82
132
|
const clients = [];
|
|
@@ -86,6 +136,7 @@ async function openRemoteTarget(env) {
|
|
|
86
136
|
mode: "remote",
|
|
87
137
|
backend: "live",
|
|
88
138
|
serverLabel: `live honua /mcp (${options.remoteUrl})`,
|
|
139
|
+
authMode: resolveAuthMode(options),
|
|
89
140
|
honuaTransport: env.HONUA_TRANSPORT ?? null,
|
|
90
141
|
client,
|
|
91
142
|
supportsUnauthenticatedPass: true,
|
|
@@ -149,6 +200,7 @@ async function openStdioProxyTarget(env) {
|
|
|
149
200
|
mode: "stdio-proxy",
|
|
150
201
|
backend,
|
|
151
202
|
serverLabel: `honua-mcp-proxy → ${backend === "live" ? upstreamUrl : "in-process mock /mcp"}`,
|
|
203
|
+
authMode: upstreamToken ? "bearer" : env.HONUA_API_KEY ? "api-key" : "anonymous",
|
|
152
204
|
honuaTransport: env.HONUA_TRANSPORT ?? null,
|
|
153
205
|
client,
|
|
154
206
|
supportsUnauthenticatedPass: true,
|
package/dist/src/eval/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { resolveCorpus } from "./corpus.js";
|
|
@@ -37,7 +37,7 @@ import { runEval } from "./runner.js";
|
|
|
37
37
|
* chain), and live remote MCP runs when HONUA_MCP_REMOTE_URL is set. Keys are
|
|
38
38
|
* never hardcoded — they come from the environment.
|
|
39
39
|
*/
|
|
40
|
-
const CORPUS_NAMES = ["analyst", "operator", "northstar", "all"];
|
|
40
|
+
const CORPUS_NAMES = ["analyst", "operator", "northstar", "standalone", "all"];
|
|
41
41
|
function parseArgs(argv) {
|
|
42
42
|
let artifactOnly = false;
|
|
43
43
|
let offline = false;
|
|
@@ -92,7 +92,9 @@ async function main() {
|
|
|
92
92
|
forceOffline: offline,
|
|
93
93
|
...(drivers.length > 0 ? { drivers: selectDrivers(drivers) } : {}),
|
|
94
94
|
...(corpus ? { corpus: corpusForName(corpus) } : {}),
|
|
95
|
+
...(corpus === "standalone" ? { forceStandaloneSurface: true } : {}),
|
|
95
96
|
});
|
|
97
|
+
mkdirSync(outDir, { recursive: true });
|
|
96
98
|
const jsonPath = resolve(outDir, "mcp-eval-results.json");
|
|
97
99
|
const mdPath = resolve(outDir, "mcp-eval-results.md");
|
|
98
100
|
writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
@@ -24,4 +24,11 @@ export declare const CORPUS: Scenario[];
|
|
|
24
24
|
* is set explicitly.
|
|
25
25
|
*/
|
|
26
26
|
export declare function resolveCorpus(env?: NodeJS.ProcessEnv): Scenario[];
|
|
27
|
+
/**
|
|
28
|
+
* Whether a corpus selection targets the PLATFORM-FREE standalone surface (a plain
|
|
29
|
+
* public FeatureServer fixture) rather than the Honua operator/analyst surface.
|
|
30
|
+
* The offline runner uses this to pick the census fixture client so the standalone
|
|
31
|
+
* corpus resolves against real recorded non-Honua data.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isStandaloneCorpus(env?: NodeJS.ProcessEnv): boolean;
|
|
27
34
|
//# sourceMappingURL=corpus.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,EAkG5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,EAAE,CAiB9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEhF"}
|
package/dist/src/eval/corpus.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NORTHSTAR_CORPUS } from "./northstar-corpus.js";
|
|
2
2
|
import { OPERATOR_CORPUS } from "./operator-corpus.js";
|
|
3
|
+
import { STANDALONE_CORPUS } from "./standalone-corpus.js";
|
|
3
4
|
/**
|
|
4
5
|
* Held-out corpus of GIS workflows for the cross-model eval (honua-server #1956).
|
|
5
6
|
*
|
|
@@ -126,9 +127,24 @@ export function resolveCorpus(env = process.env) {
|
|
|
126
127
|
return OPERATOR_CORPUS;
|
|
127
128
|
case "northstar":
|
|
128
129
|
return NORTHSTAR_CORPUS;
|
|
130
|
+
case "standalone":
|
|
131
|
+
return STANDALONE_CORPUS;
|
|
129
132
|
case "all":
|
|
133
|
+
// `all` spans the Honua-surface corpora only. The platform-free standalone
|
|
134
|
+
// corpus targets a DIFFERENT surface (a plain FeatureServer fixture) and is
|
|
135
|
+
// selected explicitly via `standalone`; mixing it here would run its
|
|
136
|
+
// census-specific scenarios against an operator surface.
|
|
130
137
|
return [...CORPUS, ...OPERATOR_CORPUS, ...NORTHSTAR_CORPUS];
|
|
131
138
|
default:
|
|
132
139
|
return CORPUS;
|
|
133
140
|
}
|
|
134
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether a corpus selection targets the PLATFORM-FREE standalone surface (a plain
|
|
144
|
+
* public FeatureServer fixture) rather than the Honua operator/analyst surface.
|
|
145
|
+
* The offline runner uses this to pick the census fixture client so the standalone
|
|
146
|
+
* corpus resolves against real recorded non-Honua data.
|
|
147
|
+
*/
|
|
148
|
+
export function isStandaloneCorpus(env = process.env) {
|
|
149
|
+
return (env.HONUA_EVAL_CORPUS ?? "").trim().toLowerCase() === "standalone";
|
|
150
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deterministic.d.ts","sourceRoot":"","sources":["../../../../src/eval/drivers/deterministic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9G;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,MAAM,EAAG,eAAe,CAAU;IAE3C,WAAW,IAAI,OAAO;IAIhB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"deterministic.d.ts","sourceRoot":"","sources":["../../../../src/eval/drivers/deterministic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9G;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,MAAM,EAAG,eAAe,CAAU;IAE3C,WAAW,IAAI,OAAO;IAIhB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAoDzF"}
|
|
@@ -18,6 +18,19 @@ export class DeterministicDriver {
|
|
|
18
18
|
const steps = [];
|
|
19
19
|
const answerParts = [];
|
|
20
20
|
let errorCount = 0;
|
|
21
|
+
// Ambiguity/refusal scenarios: the ideal client asks for clarification rather
|
|
22
|
+
// than guessing. Honoring this deterministically lets the offline control
|
|
23
|
+
// exercise refusal/clarification behavior, not just happy-path trajectories.
|
|
24
|
+
if (scenario.clarify) {
|
|
25
|
+
return {
|
|
26
|
+
scenarioId: scenario.id,
|
|
27
|
+
modelId: this.id,
|
|
28
|
+
steps,
|
|
29
|
+
finalAnswer: scenario.clarify.question,
|
|
30
|
+
clarificationRequested: true,
|
|
31
|
+
errorCount,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
21
34
|
for (const planned of scenario.script) {
|
|
22
35
|
if (!advertised.has(planned.tool)) {
|
|
23
36
|
// The scripted tool is not on the surface — record a driver-visible error
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,CAgEvF"}
|