@honua/mcp-server 0.0.10-alpha.0 → 0.0.20-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +167 -0
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +33 -0
  3. package/certification/geospatial-mcp-schemas/README.md +90 -0
  4. package/certification/geospatial-mcp-schemas/common/geoprocessing-error.schema.json +45 -0
  5. package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +73 -0
  6. package/certification/geospatial-mcp-schemas/index.json +311 -0
  7. package/certification/geospatial-mcp-schemas/resources/app-package.schema.json +20 -0
  8. package/certification/geospatial-mcp-schemas/resources/app-template.schema.json +15 -0
  9. package/certification/geospatial-mcp-schemas/resources/artifact.schema.json +69 -0
  10. package/certification/geospatial-mcp-schemas/resources/deployment.schema.json +27 -0
  11. package/certification/geospatial-mcp-schemas/resources/map-package.schema.json +21 -0
  12. package/certification/geospatial-mcp-schemas/resources/map-template.schema.json +15 -0
  13. package/certification/geospatial-mcp-schemas/resources/provenance.schema.json +50 -0
  14. package/certification/geospatial-mcp-schemas/resources/published-service.schema.json +21 -0
  15. package/certification/geospatial-mcp-schemas/resources/result-package.schema.json +64 -0
  16. package/certification/geospatial-mcp-schemas/resources/style.schema.json +12 -0
  17. package/certification/geospatial-mcp-schemas/resources/theme.schema.json +12 -0
  18. package/certification/geospatial-mcp-schemas/resources/workspace.schema.json +63 -0
  19. package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +23 -0
  20. package/certification/geospatial-mcp-schemas/tools/cancel_job.schema.json +15 -0
  21. package/certification/geospatial-mcp-schemas/tools/clarify_intent.schema.json +69 -0
  22. package/certification/geospatial-mcp-schemas/tools/compose_mixed_protocol_map.schema.json +21 -0
  23. package/certification/geospatial-mcp-schemas/tools/create_app_package.schema.json +33 -0
  24. package/certification/geospatial-mcp-schemas/tools/create_map_package.schema.json +41 -0
  25. package/certification/geospatial-mcp-schemas/tools/execute_plan.schema.json +64 -0
  26. package/certification/geospatial-mcp-schemas/tools/geocode_address.schema.json +36 -0
  27. package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +30 -0
  28. package/certification/geospatial-mcp-schemas/tools/ground_candidates.schema.json +66 -0
  29. package/certification/geospatial-mcp-schemas/tools/list_capabilities.schema.json +21 -0
  30. package/certification/geospatial-mcp-schemas/tools/list_layers.schema.json +14 -0
  31. package/certification/geospatial-mcp-schemas/tools/plan_analysis.schema.json +19 -0
  32. package/certification/geospatial-mcp-schemas/tools/preview_app_package.schema.json +15 -0
  33. package/certification/geospatial-mcp-schemas/tools/preview_map_package.schema.json +17 -0
  34. package/certification/geospatial-mcp-schemas/tools/preview_package.schema.json +40 -0
  35. package/certification/geospatial-mcp-schemas/tools/propose_operation.schema.json +28 -0
  36. package/certification/geospatial-mcp-schemas/tools/publish_result.schema.json +29 -0
  37. package/certification/geospatial-mcp-schemas/tools/publish_service.schema.json +59 -0
  38. package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +54 -0
  39. package/certification/geospatial-mcp-schemas/tools/refine_map_package.schema.json +22 -0
  40. package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +55 -0
  41. package/certification/geospatial-mcp-schemas/tools/resolve_entity.schema.json +30 -0
  42. package/certification/geospatial-mcp-schemas/tools/solve_route.schema.json +39 -0
  43. package/certification/geospatial-mcp-schemas/tools/validate_package.schema.json +35 -0
  44. package/certification/geospatial-mcp-schemas/tools/validate_plan.schema.json +61 -0
  45. package/dist/src/certification/certifier.d.ts +119 -0
  46. package/dist/src/certification/certifier.d.ts.map +1 -0
  47. package/dist/src/certification/certifier.js +410 -0
  48. package/dist/src/certification/cli.d.ts +3 -0
  49. package/dist/src/certification/cli.d.ts.map +1 -0
  50. package/dist/src/certification/cli.js +48 -0
  51. package/dist/src/certification/contracts.d.ts +62 -0
  52. package/dist/src/certification/contracts.d.ts.map +1 -0
  53. package/dist/src/certification/contracts.js +174 -0
  54. package/dist/src/certification/fixture-client.d.ts +4 -0
  55. package/dist/src/certification/fixture-client.d.ts.map +1 -0
  56. package/dist/src/certification/fixture-client.js +113 -0
  57. package/dist/src/certification/fixtures.d.ts +32 -0
  58. package/dist/src/certification/fixtures.d.ts.map +1 -0
  59. package/dist/src/certification/fixtures.js +64 -0
  60. package/dist/src/certification/json-schema.d.ts +49 -0
  61. package/dist/src/certification/json-schema.d.ts.map +1 -0
  62. package/dist/src/certification/json-schema.js +164 -0
  63. package/dist/src/certification/mock-upstream.d.ts +29 -0
  64. package/dist/src/certification/mock-upstream.d.ts.map +1 -0
  65. package/dist/src/certification/mock-upstream.js +96 -0
  66. package/dist/src/certification/operator-catalog.d.ts +82 -0
  67. package/dist/src/certification/operator-catalog.d.ts.map +1 -0
  68. package/dist/src/certification/operator-catalog.js +466 -0
  69. package/dist/src/certification/report.d.ts +4 -0
  70. package/dist/src/certification/report.d.ts.map +1 -0
  71. package/dist/src/certification/report.js +90 -0
  72. package/dist/src/certification/run.d.ts +28 -0
  73. package/dist/src/certification/run.d.ts.map +1 -0
  74. package/dist/src/certification/run.js +31 -0
  75. package/dist/src/certification/schema-index.d.ts +69 -0
  76. package/dist/src/certification/schema-index.d.ts.map +1 -0
  77. package/dist/src/certification/schema-index.js +82 -0
  78. package/dist/src/certification/target.d.ts +40 -0
  79. package/dist/src/certification/target.d.ts.map +1 -0
  80. package/dist/src/certification/target.js +167 -0
  81. package/dist/src/eval/cli.d.ts +3 -0
  82. package/dist/src/eval/cli.d.ts.map +1 -0
  83. package/dist/src/eval/cli.js +116 -0
  84. package/dist/src/eval/corpus.d.ts +27 -0
  85. package/dist/src/eval/corpus.d.ts.map +1 -0
  86. package/dist/src/eval/corpus.js +134 -0
  87. package/dist/src/eval/drivers/anthropic.d.ts +14 -0
  88. package/dist/src/eval/drivers/anthropic.d.ts.map +1 -0
  89. package/dist/src/eval/drivers/anthropic.js +120 -0
  90. package/dist/src/eval/drivers/bedrock.d.ts +54 -0
  91. package/dist/src/eval/drivers/bedrock.d.ts.map +1 -0
  92. package/dist/src/eval/drivers/bedrock.js +178 -0
  93. package/dist/src/eval/drivers/deterministic.d.ts +17 -0
  94. package/dist/src/eval/drivers/deterministic.d.ts.map +1 -0
  95. package/dist/src/eval/drivers/deterministic.js +51 -0
  96. package/dist/src/eval/drivers/index.d.ts +34 -0
  97. package/dist/src/eval/drivers/index.d.ts.map +1 -0
  98. package/dist/src/eval/drivers/index.js +74 -0
  99. package/dist/src/eval/drivers/openai.d.ts +14 -0
  100. package/dist/src/eval/drivers/openai.d.ts.map +1 -0
  101. package/dist/src/eval/drivers/openai.js +136 -0
  102. package/dist/src/eval/grade.d.ts +14 -0
  103. package/dist/src/eval/grade.d.ts.map +1 -0
  104. package/dist/src/eval/grade.js +56 -0
  105. package/dist/src/eval/northstar-corpus.d.ts +3 -0
  106. package/dist/src/eval/northstar-corpus.d.ts.map +1 -0
  107. package/dist/src/eval/northstar-corpus.js +194 -0
  108. package/dist/src/eval/operator-corpus.d.ts +3 -0
  109. package/dist/src/eval/operator-corpus.d.ts.map +1 -0
  110. package/dist/src/eval/operator-corpus.js +193 -0
  111. package/dist/src/eval/report.d.ts +104 -0
  112. package/dist/src/eval/report.d.ts.map +1 -0
  113. package/dist/src/eval/report.js +130 -0
  114. package/dist/src/eval/runner.d.ts +22 -0
  115. package/dist/src/eval/runner.d.ts.map +1 -0
  116. package/dist/src/eval/runner.js +116 -0
  117. package/dist/src/eval/types.d.ts +93 -0
  118. package/dist/src/eval/types.d.ts.map +1 -0
  119. package/dist/src/eval/types.js +10 -0
  120. package/dist/src/helpers.d.ts +11 -0
  121. package/dist/src/helpers.d.ts.map +1 -1
  122. package/dist/src/helpers.js +22 -0
  123. package/dist/src/index.d.ts +7 -0
  124. package/dist/src/index.d.ts.map +1 -1
  125. package/dist/src/index.js +30 -4
  126. package/dist/src/proxy.d.ts +46 -0
  127. package/dist/src/proxy.d.ts.map +1 -0
  128. package/dist/src/proxy.js +124 -0
  129. package/dist/src/resources/styles.d.ts +29 -0
  130. package/dist/src/resources/styles.d.ts.map +1 -0
  131. package/dist/src/resources/styles.js +43 -0
  132. package/dist/src/styles.d.ts +110 -0
  133. package/dist/src/styles.d.ts.map +1 -0
  134. package/dist/src/styles.js +118 -0
  135. package/dist/src/tools/apply-style-preset.d.ts +27 -0
  136. package/dist/src/tools/apply-style-preset.d.ts.map +1 -0
  137. package/dist/src/tools/apply-style-preset.js +29 -0
  138. package/dist/src/tools/count-features.d.ts.map +1 -1
  139. package/dist/src/tools/count-features.js +5 -4
  140. package/dist/src/tools/get-extent.d.ts.map +1 -1
  141. package/dist/src/tools/get-extent.js +6 -4
  142. package/dist/src/tools/get-style.d.ts +24 -0
  143. package/dist/src/tools/get-style.d.ts.map +1 -0
  144. package/dist/src/tools/get-style.js +28 -0
  145. package/dist/src/tools/query-features.d.ts.map +1 -1
  146. package/dist/src/tools/query-features.js +4 -1
  147. package/package.json +19 -4
@@ -0,0 +1,74 @@
1
+ import { AnthropicDriver } from "./anthropic.js";
2
+ import { BedrockDriver } from "./bedrock.js";
3
+ import { DeterministicDriver } from "./deterministic.js";
4
+ import { OpenAiDriver } from "./openai.js";
5
+ export { AnthropicDriver, BedrockDriver, DeterministicDriver, OpenAiDriver };
6
+ /**
7
+ * Resolve the set of model drivers to run.
8
+ *
9
+ * The deterministic control is always included. Claude (Anthropic API) and GPT
10
+ * drivers are added when their API keys are present; the Claude-via-Bedrock driver
11
+ * is added when `HONUA_EVAL_BEDROCK` is set (its auth comes from the AWS credential
12
+ * chain, which can't be sniffed from a single env var). This gates live runs behind
13
+ * env without hardcoding any credentials. CI runs the deterministic driver only —
14
+ * a green, reproducible artifact with no model/API calls.
15
+ */
16
+ export function resolveDrivers(options = {}) {
17
+ const env = options.env ?? process.env;
18
+ const drivers = [new DeterministicDriver()];
19
+ const anthropic = new AnthropicDriver({ apiKey: env.ANTHROPIC_API_KEY, model: env.HONUA_EVAL_ANTHROPIC_MODEL });
20
+ if (options.includeUnavailable || anthropic.isAvailable()) {
21
+ drivers.push(anthropic);
22
+ }
23
+ const openai = new OpenAiDriver({ apiKey: env.OPENAI_API_KEY, model: env.OPENAI_MODEL });
24
+ if (options.includeUnavailable || openai.isAvailable()) {
25
+ drivers.push(openai);
26
+ }
27
+ const bedrock = new BedrockDriver({
28
+ enabled: env.HONUA_EVAL_BEDROCK,
29
+ model: env.HONUA_EVAL_BEDROCK_MODEL,
30
+ region: env.AWS_REGION,
31
+ });
32
+ if (options.includeUnavailable || bedrock.isAvailable()) {
33
+ drivers.push(bedrock);
34
+ }
35
+ return drivers;
36
+ }
37
+ /**
38
+ * Build the driver set for an explicit `--driver` selection. The deterministic
39
+ * control is always included (it is the CI gate). Named live drivers are always
40
+ * constructed and run — even if their key/opt-in is absent — so an explicit
41
+ * request surfaces an honest `driverError` rather than being silently dropped.
42
+ */
43
+ export function selectDrivers(names, options = {}) {
44
+ const env = options.env ?? process.env;
45
+ const drivers = [new DeterministicDriver()];
46
+ for (const name of names) {
47
+ switch (name) {
48
+ case "deterministic":
49
+ break; // already included
50
+ case "anthropic":
51
+ drivers.push(new AnthropicDriver({ apiKey: env.ANTHROPIC_API_KEY, model: env.HONUA_EVAL_ANTHROPIC_MODEL }));
52
+ break;
53
+ case "openai":
54
+ drivers.push(new OpenAiDriver({ apiKey: env.OPENAI_API_KEY, model: env.OPENAI_MODEL }));
55
+ break;
56
+ case "bedrock":
57
+ drivers.push(new BedrockDriver({ enabled: "1", model: env.HONUA_EVAL_BEDROCK_MODEL, region: env.AWS_REGION }));
58
+ break;
59
+ default: {
60
+ const exhaustive = name;
61
+ throw new Error(`unknown driver: ${String(exhaustive)}`);
62
+ }
63
+ }
64
+ }
65
+ return drivers;
66
+ }
67
+ /** Parse/validate a `--driver` value into a known driver name. */
68
+ export function parseDriverName(value) {
69
+ const known = ["deterministic", "anthropic", "openai", "bedrock"];
70
+ if (known.includes(value)) {
71
+ return value;
72
+ }
73
+ throw new Error(`unknown --driver "${value}" (expected one of: ${known.join(", ")})`);
74
+ }
@@ -0,0 +1,14 @@
1
+ import type { ModelDriver, Scenario, WorkflowContext, WorkflowTranscript } from "../types.js";
2
+ export interface OpenAiDriverOptions {
3
+ apiKey?: string | undefined;
4
+ model?: string;
5
+ }
6
+ export declare class OpenAiDriver implements ModelDriver {
7
+ readonly vendor: "openai";
8
+ readonly id: string;
9
+ private readonly apiKey;
10
+ constructor(options?: OpenAiDriverOptions);
11
+ isAvailable(): boolean;
12
+ runWorkflow(scenario: Scenario, ctx: WorkflowContext): Promise<WorkflowTranscript>;
13
+ }
14
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../../src/eval/drivers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAkD9G,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAa,YAAW,WAAW;IAC9C,QAAQ,CAAC,MAAM,EAAG,QAAQ,CAAU;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAEhC,OAAO,GAAE,mBAAwB;IAK7C,WAAW,IAAI,OAAO;IAIhB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CA4EzF"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * GPT client driver for the cross-model eval (honua-server #1956).
3
+ *
4
+ * Gated behind OPENAI_API_KEY and a dynamically-imported `openai` SDK — neither
5
+ * is a dependency of this package, so the offline CI path never needs them. This
6
+ * is the "any client" counterpart to the Claude driver: the SAME corpus and the
7
+ * SAME grading are driven by a GPT model over the identical MCP catalog, so the
8
+ * artifact can compare success / clarification / edit rates across vendors.
9
+ *
10
+ * Live cross-model runs are tracked under honua-io/honua-server#1956.
11
+ */
12
+ const ASK_USER_FUNCTION = {
13
+ type: "function",
14
+ function: {
15
+ name: "ask_user",
16
+ description: "Call this ONLY if the request is too ambiguous to proceed. Provide the clarifying question you would ask.",
17
+ parameters: {
18
+ type: "object",
19
+ properties: { question: { type: "string" } },
20
+ required: ["question"],
21
+ additionalProperties: false,
22
+ },
23
+ },
24
+ };
25
+ const SYSTEM_PROMPT = "You are a GIS analyst working through the Honua MCP tools. Complete the user's task by calling the available tools. " +
26
+ "Ground every step in real tool results — do not invent service names, layers, or fields. " +
27
+ "If, and only if, the request is genuinely ambiguous and you cannot proceed, call ask_user. " +
28
+ "When finished, give a concise final answer that references what the tools returned.";
29
+ const MAX_ITERATIONS = 8;
30
+ /**
31
+ * Default GPT model for live cross-model runs.
32
+ *
33
+ * `gpt-5.5` is OpenAI's generally-available flagship reasoning model as of
34
+ * July 2026 (the apex of the public Chat Completions catalog; see
35
+ * developers.openai.com/api/docs/models/all). The newer GPT-5.6 "Sol" family was
36
+ * still preview-gated to a handful of orgs at that date and not callable by an
37
+ * ordinary API key, so it is NOT the default — set `OPENAI_MODEL=gpt-5.6-sol`
38
+ * (or any other id) to override once you have access. The resolved id is recorded
39
+ * per-driver in the results artifact (`models[].model`) so published comparisons
40
+ * are attributable to an exact model.
41
+ */
42
+ const DEFAULT_MODEL = "gpt-5.5";
43
+ export class OpenAiDriver {
44
+ vendor = "openai";
45
+ id;
46
+ apiKey;
47
+ constructor(options = {}) {
48
+ this.id = options.model ?? process.env.OPENAI_MODEL ?? DEFAULT_MODEL;
49
+ this.apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
50
+ }
51
+ isAvailable() {
52
+ return typeof this.apiKey === "string" && this.apiKey.length > 0;
53
+ }
54
+ async runWorkflow(scenario, ctx) {
55
+ const steps = [];
56
+ const transcript = {
57
+ scenarioId: scenario.id,
58
+ modelId: this.id,
59
+ steps,
60
+ finalAnswer: "",
61
+ clarificationRequested: false,
62
+ errorCount: 0,
63
+ };
64
+ let OpenAI;
65
+ try {
66
+ const specifier = "openai";
67
+ const mod = (await import(specifier));
68
+ OpenAI = mod.default;
69
+ }
70
+ catch {
71
+ transcript.driverError = "openai SDK is not installed (required for live GPT eval runs)";
72
+ return transcript;
73
+ }
74
+ if (!this.apiKey) {
75
+ transcript.driverError = "OPENAI_API_KEY is not set";
76
+ return transcript;
77
+ }
78
+ const client = new OpenAI({ apiKey: this.apiKey });
79
+ const tools = [
80
+ ...ctx.tools.map((t) => ({
81
+ type: "function",
82
+ function: { name: t.name, description: t.description, parameters: t.inputSchema },
83
+ })),
84
+ ASK_USER_FUNCTION,
85
+ ];
86
+ const messages = [
87
+ { role: "system", content: SYSTEM_PROMPT },
88
+ { role: "user", content: scenario.prompt },
89
+ ];
90
+ try {
91
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
92
+ const response = await client.chat.completions.create({ model: this.id, messages, tools });
93
+ const message = response.choices[0]?.message;
94
+ if (!message) {
95
+ transcript.driverError = "no choice returned by the model";
96
+ return transcript;
97
+ }
98
+ const toolCalls = message.tool_calls ?? [];
99
+ if (toolCalls.length === 0) {
100
+ transcript.finalAnswer = message.content ?? "";
101
+ return transcript;
102
+ }
103
+ messages.push({ role: "assistant", content: message.content ?? null, tool_calls: toolCalls });
104
+ for (const call of toolCalls) {
105
+ const args = safeParse(call.function.arguments);
106
+ if (call.function.name === "ask_user") {
107
+ transcript.clarificationRequested = true;
108
+ transcript.finalAnswer = message.content ?? "";
109
+ return transcript;
110
+ }
111
+ const result = await ctx.callTool(call.function.name, args);
112
+ steps.push({ tool: call.function.name, args, isError: result.isError });
113
+ if (result.isError) {
114
+ transcript.errorCount++;
115
+ }
116
+ messages.push({ role: "tool", tool_call_id: call.id, content: result.text });
117
+ }
118
+ }
119
+ transcript.driverError = `exceeded ${MAX_ITERATIONS} tool-use iterations without finishing`;
120
+ return transcript;
121
+ }
122
+ catch (err) {
123
+ transcript.driverError = err instanceof Error ? err.message : String(err);
124
+ return transcript;
125
+ }
126
+ }
127
+ }
128
+ function safeParse(raw) {
129
+ try {
130
+ const parsed = JSON.parse(raw);
131
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
132
+ }
133
+ catch {
134
+ return {};
135
+ }
136
+ }
@@ -0,0 +1,14 @@
1
+ import type { Scenario, ScenarioGrade, WorkflowTranscript } from "./types.js";
2
+ /** True if `sequence` appears as an ordered (not necessarily contiguous) subsequence of `steps`. */
3
+ export declare function isOrderedSubsequence(steps: string[], sequence: string[]): boolean;
4
+ /**
5
+ * Grade a transcript against a scenario's declarative success criteria.
6
+ *
7
+ * Outcomes:
8
+ * - `error`: the driver failed to run (unavailable model, exception).
9
+ * - `clarified`: the driver asked a clarifying question instead of completing.
10
+ * - `pass`: all criteria satisfied.
11
+ * - `fail`: completed but violated one or more criteria.
12
+ */
13
+ export declare function grade(scenario: Scenario, transcript: WorkflowTranscript): ScenarioGrade;
14
+ //# sourceMappingURL=grade.d.ts.map
@@ -0,0 +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,CA2CvF"}
@@ -0,0 +1,56 @@
1
+ /** True if `sequence` appears as an ordered (not necessarily contiguous) subsequence of `steps`. */
2
+ export function isOrderedSubsequence(steps, sequence) {
3
+ let i = 0;
4
+ for (const step of steps) {
5
+ if (i < sequence.length && step === sequence[i]) {
6
+ i++;
7
+ }
8
+ }
9
+ return i === sequence.length;
10
+ }
11
+ /**
12
+ * Grade a transcript against a scenario's declarative success criteria.
13
+ *
14
+ * Outcomes:
15
+ * - `error`: the driver failed to run (unavailable model, exception).
16
+ * - `clarified`: the driver asked a clarifying question instead of completing.
17
+ * - `pass`: all criteria satisfied.
18
+ * - `fail`: completed but violated one or more criteria.
19
+ */
20
+ export function grade(scenario, transcript) {
21
+ const base = {
22
+ scenarioId: scenario.id,
23
+ modelId: transcript.modelId,
24
+ errorCount: transcript.errorCount,
25
+ };
26
+ if (transcript.driverError) {
27
+ return { ...base, outcome: "error", violations: [transcript.driverError] };
28
+ }
29
+ if (transcript.clarificationRequested) {
30
+ return { ...base, outcome: "clarified", violations: ["driver requested clarification"] };
31
+ }
32
+ const violations = [];
33
+ const calledTools = transcript.steps.map((s) => s.tool);
34
+ const calledSet = new Set(calledTools);
35
+ const { criteria } = scenario;
36
+ for (const required of criteria.requiredTools) {
37
+ if (!calledSet.has(required)) {
38
+ violations.push(`required tool not called: ${required}`);
39
+ }
40
+ }
41
+ if (criteria.expectedToolSequence && !isOrderedSubsequence(calledTools, criteria.expectedToolSequence)) {
42
+ violations.push(`expected tool sequence not observed: ${criteria.expectedToolSequence.join(" -> ")}`);
43
+ }
44
+ for (const forbidden of criteria.forbiddenTools ?? []) {
45
+ if (calledSet.has(forbidden)) {
46
+ violations.push(`forbidden tool called: ${forbidden}`);
47
+ }
48
+ }
49
+ const answer = transcript.finalAnswer.toLowerCase();
50
+ for (const fragment of criteria.answerMustInclude ?? []) {
51
+ if (!answer.includes(fragment.toLowerCase())) {
52
+ violations.push(`answer missing expected content: "${fragment}"`);
53
+ }
54
+ }
55
+ return { ...base, outcome: violations.length === 0 ? "pass" : "fail", violations };
56
+ }
@@ -0,0 +1,3 @@
1
+ import type { Scenario } from "./types.js";
2
+ export declare const NORTHSTAR_CORPUS: Scenario[];
3
+ //# sourceMappingURL=northstar-corpus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"northstar-corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/northstar-corpus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiF3C,eAAO,MAAM,gBAAgB,EAAE,QAAQ,EAyHtC,CAAC"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * NORTH-STAR corpus — the P1 gate measure (honua-server #1948, MCP initiative P1.7).
3
+ *
4
+ * These are the three canonical end-to-end workflows the Phase-1 gate is defined
5
+ * against: "all 3 north-star workflows pass via a vanilla MCP client". Encoding
6
+ * them as eval scenarios turns that gate into a mechanical pass/fail signal that
7
+ * runs on the exact same grading (required-tool selection, ordered subsequence,
8
+ * forbidden-tool avoidance, answer-substring evidence) as every other corpus.
9
+ *
10
+ * The tool names are the reference-implementation (`honua_*`) names for the
11
+ * standard geospatial-mcp tools (see geospatial-mcp `spec/schemas/index.json`).
12
+ * Some required tools are P1 deliverables that DO NOT yet ship on every surface:
13
+ *
14
+ * - `honua_publish_result` — standard `publish_result`, reference status
15
+ * `known-gap` (promote a job/result package to a
16
+ * hosted layer). Lands with P1.
17
+ * - `honua_apply_style_preset` — standard `apply_style_preset`, `known-gap`
18
+ * (styling a published layer). Lands with P1.
19
+ *
20
+ * Against a server that predates the P1 tools these scenarios are EXPECTED to
21
+ * fail — that failure is the gate signal, not a harness bug. The report makes it
22
+ * explicit which required tools a surface is missing:
23
+ * - `catalog.unresolvedRequiredTools` names every required tool the live
24
+ * `tools/list` did not advertise, corpus-wide.
25
+ * - each per-scenario `results[].missingTools` names the tools THAT scenario
26
+ * requires but the surface lacks (the gate dashboard, per workflow).
27
+ * The deterministic offline driver degrades HONESTLY: when its scripted ideal
28
+ * trajectory reaches a tool the surface does not advertise it records a
29
+ * `driverError` ("scripted tool not advertised …") and the scenario grades as
30
+ * `error`, never a silent pass. No fixture responses are faked to make an
31
+ * unbuilt tool look green.
32
+ *
33
+ * LIVE-GATED like the operator corpus: selected only via
34
+ * `HONUA_EVAL_CORPUS=northstar` (or `--corpus northstar`), and included in
35
+ * `all`. It is NOT part of the default analyst corpus, so the offline CI gate
36
+ * (`test:eval`, analyst-only) is untouched. Run it live against a P1 surface
37
+ * with `--driver bedrock` + `HONUA_MCP_REMOTE_URL=<server>/mcp` + credentials.
38
+ */
39
+ /**
40
+ * The generic mutation escape-hatch. Every north-star workflow must compose the
41
+ * structured lifecycle (plan → validate → execute → publish) rather than reach
42
+ * for `honua_propose_operation`, so forbidding it turns "did the client use the
43
+ * intended workflow?" into a graded criterion.
44
+ */
45
+ const GENERIC_MUTATION_TOOL = "honua_propose_operation";
46
+ /** A small, embedded address CSV for the geocode → publish workflow (10 rows). */
47
+ const ADDRESS_CSV = [
48
+ "name,street,city,state,zip",
49
+ "City Hall,1 Dr Carlton B Goodlett Pl,San Francisco,CA,94102",
50
+ "Ferry Building,1 Ferry Building,San Francisco,CA,94111",
51
+ "Coit Tower,1 Telegraph Hill Blvd,San Francisco,CA,94133",
52
+ "Palace of Fine Arts,3601 Lyon St,San Francisco,CA,94123",
53
+ "SFMOMA,151 3rd St,San Francisco,CA,94103",
54
+ "Oracle Park,24 Willie Mays Plaza,San Francisco,CA,94107",
55
+ "Golden Gate Park,501 Stanyan St,San Francisco,CA,94117",
56
+ "Exploratorium,Pier 15 Embarcadero,San Francisco,CA,94111",
57
+ "Chase Center,1 Warriors Way,San Francisco,CA,94158",
58
+ "Sutro Baths,1004 Point Lobos Ave,San Francisco,CA,94121",
59
+ ].join("\n");
60
+ /** Parsed address rows the geocoder batch call replays (without the header). */
61
+ const ADDRESS_ROWS = ADDRESS_CSV.split("\n")
62
+ .slice(1)
63
+ .map((line) => {
64
+ const [, street, city, state, zip] = line.split(",");
65
+ return `${street}, ${city}, ${state} ${zip}`;
66
+ });
67
+ /** The natural-language prompt for the CSV → point-layer workflow (CSV embedded). */
68
+ const CSV_GEOCODE_PROMPT = `Here is a CSV of addresses. Geocode them and publish a point layer from the results.
69
+
70
+ \`\`\`csv
71
+ ${ADDRESS_CSV}
72
+ \`\`\`
73
+
74
+ Batch-geocode every row, ingest the geocoded points as a dataset, and publish that dataset as a hosted point service. Do not reach for a raw propose_operation.`;
75
+ export const NORTHSTAR_CORPUS = [
76
+ {
77
+ id: "northstar-styled-map",
78
+ title: "Proximity analysis → published, styled map",
79
+ category: "north-star",
80
+ prompt: "Find all parcels within 500 m of a flood zone and produce a styled map of them. " +
81
+ "Resolve the parcels and flood-zone layers, plan and run the proximity analysis as a " +
82
+ "governed job, publish the result as a new layer, style it, and render a map. Use the " +
83
+ "structured analysis lifecycle — do not reach for a raw propose_operation.",
84
+ criteria: {
85
+ // Load-bearing arc of the workflow: ground the entities, execute the plan,
86
+ // publish the job artifact to a layer, and render a map of it.
87
+ requiredTools: ["honua_resolve_entity", "honua_execute_plan", "honua_publish_result", "honua_render_map"],
88
+ expectedToolSequence: ["honua_resolve_entity", "honua_execute_plan", "honua_publish_result", "honua_render_map"],
89
+ forbiddenTools: [GENERIC_MUTATION_TOOL],
90
+ },
91
+ script: [
92
+ { tool: "honua_resolve_entity", args: { text: "parcels", entityType: "layer" } },
93
+ { tool: "honua_resolve_entity", args: { text: "flood zones", entityType: "layer" } },
94
+ {
95
+ tool: "honua_plan_analysis",
96
+ args: { intent: "select parcels within 500 meters of a flood zone and produce a styled map" },
97
+ },
98
+ { tool: "honua_validate_plan", args: { plan: { planId: "northstar-styled-map-plan" } } },
99
+ { tool: "honua_execute_plan", args: { plan: { planId: "northstar-styled-map-plan" } } },
100
+ // P1 tools (reference known-gap today): promote the job artifact to a layer,
101
+ // then style it. On a pre-P1 surface these are unadvertised and the driver
102
+ // degrades to an honest driverError here rather than faking a response.
103
+ { tool: "honua_publish_result", args: { sourceId: "northstar-styled-map-result", targetKind: "layer" } },
104
+ {
105
+ tool: "honua_apply_style_preset",
106
+ args: { mapPackageId: "northstar-styled-map-package", styleId: "flood-risk" },
107
+ },
108
+ {
109
+ tool: "honua_render_map",
110
+ args: { layers: ["northstar-styled-map-layer"], bbox: [-122.55, 37.7, -122.35, 37.85] },
111
+ },
112
+ ],
113
+ },
114
+ {
115
+ id: "northstar-csv-geocode-publish",
116
+ title: "CSV of addresses → geocoded, published point layer",
117
+ category: "north-star",
118
+ prompt: CSV_GEOCODE_PROMPT,
119
+ criteria: {
120
+ // The CSV → point-layer arc as the standard defines it: batch geocode →
121
+ // ingest the resulting points → publish the source table as a service.
122
+ requiredTools: ["honua_geocode_addresses", "honua_ingest_dataset", "honua_publish_service"],
123
+ expectedToolSequence: ["honua_geocode_addresses", "honua_ingest_dataset", "honua_publish_service"],
124
+ forbiddenTools: [GENERIC_MUTATION_TOOL],
125
+ },
126
+ script: [
127
+ { tool: "honua_geocode_addresses", args: { addresses: ADDRESS_ROWS } },
128
+ {
129
+ tool: "honua_ingest_dataset",
130
+ args: {
131
+ format: "csv",
132
+ datasetName: "sf-landmarks",
133
+ data: ADDRESS_CSV,
134
+ addressColumn: "street",
135
+ },
136
+ },
137
+ {
138
+ tool: "honua_publish_service",
139
+ args: {
140
+ connectionId: "default",
141
+ schema: "public",
142
+ table: "sf_landmarks",
143
+ layerName: "SF Landmarks",
144
+ geometryType: "point",
145
+ },
146
+ },
147
+ ],
148
+ },
149
+ {
150
+ id: "northstar-buffer-stats-job",
151
+ title: "Buffer → intersect → summary statistics, run as a job",
152
+ category: "north-star",
153
+ prompt: "Buffer highways by 100 m, intersect the buffers with wetlands, and give me summary " +
154
+ "statistics for the overlap. Run it as a job and report progress. Ground the goal into " +
155
+ "an analysis plan, validate it, then execute it as a governed job and report the results " +
156
+ "the job produces. Do not reach for a raw propose_operation.",
157
+ criteria: {
158
+ // Plan entry → validate → execute-as-job. The answer must carry evidence
159
+ // that the JOB path was used and its results read back — kept loose (the
160
+ // job reference token, not exact statistic values) so live variance in the
161
+ // numbers does not flip the grade.
162
+ requiredTools: ["honua_plan_analysis", "honua_validate_plan", "honua_execute_plan"],
163
+ expectedToolSequence: [
164
+ "honua_ground_candidates",
165
+ "honua_plan_analysis",
166
+ "honua_validate_plan",
167
+ "honua_execute_plan",
168
+ ],
169
+ forbiddenTools: [GENERIC_MUTATION_TOOL],
170
+ answerMustInclude: ["job"],
171
+ },
172
+ script: [
173
+ {
174
+ tool: "honua_ground_candidates",
175
+ args: {
176
+ goal: "buffer highways by 100 meters, intersect with wetlands, and summarize the overlap",
177
+ assumptionPolicy: "UseDefaults",
178
+ },
179
+ },
180
+ {
181
+ tool: "honua_plan_analysis",
182
+ args: { intent: "buffer highways by 100m, intersect with wetlands, and compute summary statistics" },
183
+ },
184
+ { tool: "honua_validate_plan", args: { plan: { planId: "northstar-buffer-stats-plan" } } },
185
+ // Optional dry-run estimate before committing (reference advertises it with
186
+ // the same partial-plan input schema as validate_plan).
187
+ { tool: "honua_dry_run_plan", args: { plan: { planId: "northstar-buffer-stats-plan" } } },
188
+ {
189
+ tool: "honua_execute_plan",
190
+ args: { plan: { planId: "northstar-buffer-stats-plan" }, idempotencyKey: "northstar-buffer-stats-1" },
191
+ },
192
+ ],
193
+ },
194
+ ];
@@ -0,0 +1,3 @@
1
+ import type { Scenario } from "./types.js";
2
+ export declare const OPERATOR_CORPUS: Scenario[];
3
+ //# sourceMappingURL=operator-corpus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/operator-corpus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2D3C,eAAO,MAAM,eAAe,EAAE,QAAQ,EAmJrC,CAAC"}