@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,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-model MCP workflow eval — shared types (honua-server #1956, WS-H).
|
|
3
|
+
*
|
|
4
|
+
* The eval proves the north-star claim "any client → any workflow": a held-out
|
|
5
|
+
* corpus of GIS workflows is driven through the honua MCP surface by DIFFERENT
|
|
6
|
+
* client LLMs (Claude AND a GPT model), and end-to-end success / clarification /
|
|
7
|
+
* edit rates are recorded per model. A deterministic (scripted) driver provides
|
|
8
|
+
* an offline control that runs in CI with no model/API calls.
|
|
9
|
+
*/
|
|
10
|
+
/** A tool as advertised by the MCP surface, handed to a model driver. */
|
|
11
|
+
export interface ToolDescriptor {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** Result of a single `tools/call` over the MCP surface. */
|
|
17
|
+
export interface ToolCallResult {
|
|
18
|
+
isError: boolean;
|
|
19
|
+
/** Flattened text content of the tool result (what a model would read back). */
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
/** The MCP surface a driver may exercise while solving a workflow. */
|
|
23
|
+
export interface WorkflowContext {
|
|
24
|
+
tools: ToolDescriptor[];
|
|
25
|
+
callTool(name: string, args: Record<string, unknown>): Promise<ToolCallResult>;
|
|
26
|
+
}
|
|
27
|
+
/** One step a driver took: a tool call and whether it errored. */
|
|
28
|
+
export interface TranscriptStep {
|
|
29
|
+
tool: string;
|
|
30
|
+
args: Record<string, unknown>;
|
|
31
|
+
isError: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** What a driver produced for one scenario. */
|
|
34
|
+
export interface WorkflowTranscript {
|
|
35
|
+
scenarioId: string;
|
|
36
|
+
modelId: string;
|
|
37
|
+
steps: TranscriptStep[];
|
|
38
|
+
finalAnswer: string;
|
|
39
|
+
/** The driver asked the user a clarifying question instead of completing. */
|
|
40
|
+
clarificationRequested: boolean;
|
|
41
|
+
/** Tool calls that returned isError (a proxy for self-correction / edits). */
|
|
42
|
+
errorCount: number;
|
|
43
|
+
/** Driver-level failure (timeout, exception, model unavailable). */
|
|
44
|
+
driverError?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Declarative success criteria graded against a transcript. */
|
|
47
|
+
export interface SuccessCriteria {
|
|
48
|
+
/** Tools that must be called at least once (any order). */
|
|
49
|
+
requiredTools: string[];
|
|
50
|
+
/** An ordered subsequence of tool names that must appear in order. */
|
|
51
|
+
expectedToolSequence?: string[];
|
|
52
|
+
/** Tools that must never be called (e.g. write/destructive tools). */
|
|
53
|
+
forbiddenTools?: string[];
|
|
54
|
+
/** Case-insensitive substrings the final answer must contain. */
|
|
55
|
+
answerMustInclude?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* SEMANTIC assertions (issue #369) — grade the MEANING of the answer, not just
|
|
58
|
+
* the tool trajectory. Ride on the deterministic control against grounded
|
|
59
|
+
* fixtures (known feature counts, known geographic facts), so a wrong number or
|
|
60
|
+
* a hallucinated place name fails the scenario.
|
|
61
|
+
*/
|
|
62
|
+
/** Case-insensitive substrings the final answer must NOT contain (anti-hallucination / wrong-answer guard). */
|
|
63
|
+
answerMustNotInclude?: string[];
|
|
64
|
+
/** Regular-expression sources the final answer must ALL match (case-insensitive) — exact numeric / value checks. */
|
|
65
|
+
answerMustMatch?: string[];
|
|
66
|
+
/**
|
|
67
|
+
* The prompt is deliberately ambiguous or unsupported: a correct client asks a
|
|
68
|
+
* clarifying question (or refuses) instead of guessing. When true, a `clarified`
|
|
69
|
+
* transcript grades as `pass` and a completed transcript grades as `fail`.
|
|
70
|
+
*/
|
|
71
|
+
expectClarification?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** One GIS workflow in the held-out corpus. */
|
|
74
|
+
export interface Scenario {
|
|
75
|
+
id: string;
|
|
76
|
+
title: string;
|
|
77
|
+
category: string;
|
|
78
|
+
/** The natural-language task handed to the client LLM. */
|
|
79
|
+
prompt: string;
|
|
80
|
+
criteria: SuccessCriteria;
|
|
81
|
+
/**
|
|
82
|
+
* Scripted "ideal client" trajectory used by the deterministic offline driver.
|
|
83
|
+
* Live LLM drivers ignore this and plan their own tool calls.
|
|
84
|
+
*/
|
|
85
|
+
script: {
|
|
86
|
+
tool: string;
|
|
87
|
+
args: Record<string, unknown>;
|
|
88
|
+
}[];
|
|
89
|
+
/**
|
|
90
|
+
* Ambiguity/refusal scenarios: the deterministic driver asks this clarifying
|
|
91
|
+
* question INSTEAD of running the script (an empty script is typical). Live LLM
|
|
92
|
+
* drivers decide for themselves whether to clarify from the prompt alone. Pair
|
|
93
|
+
* with `criteria.expectClarification: true`.
|
|
94
|
+
*/
|
|
95
|
+
clarify?: {
|
|
96
|
+
question: string;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Per-scenario grade for one model. */
|
|
100
|
+
export type GradeOutcome = "pass" | "fail" | "clarified" | "error";
|
|
101
|
+
export interface ScenarioGrade {
|
|
102
|
+
scenarioId: string;
|
|
103
|
+
modelId: string;
|
|
104
|
+
outcome: GradeOutcome;
|
|
105
|
+
/** Human-readable reasons a grade was not `pass`. */
|
|
106
|
+
violations: string[];
|
|
107
|
+
errorCount: number;
|
|
108
|
+
}
|
|
109
|
+
/** A driver wraps a single client model (or the deterministic control). */
|
|
110
|
+
export interface ModelDriver {
|
|
111
|
+
/** Stable identifier recorded in the artifact, e.g. "claude-opus-4-8". */
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly vendor: "anthropic" | "openai" | "bedrock" | "deterministic";
|
|
114
|
+
/** Whether this driver can actually run (key + SDK present). */
|
|
115
|
+
isAvailable(): boolean;
|
|
116
|
+
runWorkflow(scenario: Scenario, ctx: WorkflowContext): Promise<WorkflowTranscript>;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/eval/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAChF;AAED,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,sBAAsB,EAAE,OAAO,CAAC;IAChC,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sEAAsE;IACtE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,+GAA+G;IAC/G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,oHAAoH;IACpH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,+CAA+C;AAC/C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;IAC1D;;;;;OAKG;IACH,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC;AAED,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,qDAAqD;IACrD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC;IACtE,gEAAgE;IAChE,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACpF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-model MCP workflow eval — shared types (honua-server #1956, WS-H).
|
|
3
|
+
*
|
|
4
|
+
* The eval proves the north-star claim "any client → any workflow": a held-out
|
|
5
|
+
* corpus of GIS workflows is driven through the honua MCP surface by DIFFERENT
|
|
6
|
+
* client LLMs (Claude AND a GPT model), and end-to-end success / clarification /
|
|
7
|
+
* edit rates are recorded per model. A deterministic (scripted) driver provides
|
|
8
|
+
* an offline control that runs in CI with no model/API calls.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/src/helpers.d.ts
CHANGED
|
@@ -10,6 +10,17 @@ export declare function jsonText(result: unknown): {
|
|
|
10
10
|
}>;
|
|
11
11
|
};
|
|
12
12
|
export declare function metadataErrorText(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Coerce a feature `count` from a query response into a usable value.
|
|
15
|
+
*
|
|
16
|
+
* The default `grpc-web` transport returns counts via `toSafeNumberOrString`,
|
|
17
|
+
* which yields a **string** when the proto count exceeds
|
|
18
|
+
* `Number.MAX_SAFE_INTEGER` (chosen specifically to avoid precision loss). Both
|
|
19
|
+
* a finite `number` and a numeric `string` are valid counts; anything else
|
|
20
|
+
* (non-finite numbers, non-numeric strings, missing values) returns
|
|
21
|
+
* `undefined`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function coerceCount(value: unknown): number | string | undefined;
|
|
13
24
|
export declare function encodeServiceId(serviceId: string): string;
|
|
14
25
|
export declare function decodeServiceId(encoded: string): string;
|
|
15
26
|
export declare function parseLayerId(value: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMtE,eAAO,MAAM,cAAc,iIAMjB,CAAC;AAQX,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAKjF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7C,YAAY,EAAE,gBAAgB,GAAG,SAAS,GACzC,gBAAgB,GAAG,SAAS,CA8B9B;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAE5F;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAqBd"}
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMtE,eAAO,MAAM,cAAc,iIAMjB,CAAC;AAQX,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAKjF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7C,YAAY,EAAE,gBAAgB,GAAG,SAAS,GACzC,gBAAgB,GAAG,SAAS,CA8B9B;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAE5F;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAWvE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAqBd"}
|
package/dist/src/helpers.js
CHANGED
|
@@ -55,6 +55,28 @@ export function jsonText(result) {
|
|
|
55
55
|
export function metadataErrorText() {
|
|
56
56
|
return METADATA_ERROR_MESSAGE;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Coerce a feature `count` from a query response into a usable value.
|
|
60
|
+
*
|
|
61
|
+
* The default `grpc-web` transport returns counts via `toSafeNumberOrString`,
|
|
62
|
+
* which yields a **string** when the proto count exceeds
|
|
63
|
+
* `Number.MAX_SAFE_INTEGER` (chosen specifically to avoid precision loss). Both
|
|
64
|
+
* a finite `number` and a numeric `string` are valid counts; anything else
|
|
65
|
+
* (non-finite numbers, non-numeric strings, missing values) returns
|
|
66
|
+
* `undefined`.
|
|
67
|
+
*/
|
|
68
|
+
export function coerceCount(value) {
|
|
69
|
+
if (typeof value === "number") {
|
|
70
|
+
return Number.isFinite(value) ? value : undefined;
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === "string") {
|
|
73
|
+
// Accept canonical base-10 integer strings (optionally signed). These are
|
|
74
|
+
// the large-count representation the grpc adapter emits to preserve
|
|
75
|
+
// precision beyond Number.MAX_SAFE_INTEGER.
|
|
76
|
+
return /^-?\d+$/.test(value.trim()) ? value.trim() : undefined;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
58
80
|
export function encodeServiceId(serviceId) {
|
|
59
81
|
return encodeURIComponent(serviceId);
|
|
60
82
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -12,5 +12,36 @@ export interface RuntimeOptions {
|
|
|
12
12
|
export declare const SERVER_VERSION: string;
|
|
13
13
|
export declare function resolveRuntimeOptions(env: NodeJS.ProcessEnv): RuntimeOptions;
|
|
14
14
|
export declare function createClientFromEnv(env?: NodeJS.ProcessEnv): HonuaClient;
|
|
15
|
+
/**
|
|
16
|
+
* The PLATFORM-FREE standalone MCP surface (issue #369).
|
|
17
|
+
*
|
|
18
|
+
* This is the direct-SDK operator surface: nine read-only geospatial tools wired
|
|
19
|
+
* straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
|
|
20
|
+
* over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
|
|
21
|
+
* Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
|
|
22
|
+
* FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
|
|
23
|
+
* no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
|
|
24
|
+
* and what the platform-free certification lane + eval corpus exercise.
|
|
25
|
+
*
|
|
26
|
+
* Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
|
|
27
|
+
* a `/rest/services` catalog) degrade gracefully on a plain target: they return a
|
|
28
|
+
* structured "not available on this target" result rather than crashing or
|
|
29
|
+
* returning misleading empty data (see `capability.ts`).
|
|
30
|
+
*
|
|
31
|
+
* The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
|
|
32
|
+
* mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
|
|
33
|
+
* mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
|
|
34
|
+
* deployment. Standalone is the front door; the proxy is the upgrade path.
|
|
35
|
+
*/
|
|
15
36
|
export declare function createServer(client: HonuaClient): McpServer;
|
|
37
|
+
/**
|
|
38
|
+
* Run the platform-free standalone server end-to-end over stdio.
|
|
39
|
+
*
|
|
40
|
+
* Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
|
|
41
|
+
* transport/auth) and exposes {@link createServer} — the nine read-only tools —
|
|
42
|
+
* to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
|
|
43
|
+
* `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
|
|
44
|
+
* required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
|
|
45
|
+
*/
|
|
46
|
+
export declare function runStandalone(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
16
47
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAgBtF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAMD,eAAO,MAAM,cAAc,QAAyB,CAAC;AA2DrD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,cAAc,CAgD5E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,WAAW,CASrF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,aAyF/C;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF"}
|
package/dist/src/index.js
CHANGED
|
@@ -6,10 +6,13 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import * as layerSchemaResource from "./resources/layer-schema.js";
|
|
8
8
|
import * as servicesResource from "./resources/services.js";
|
|
9
|
+
import * as stylesResource from "./resources/styles.js";
|
|
10
|
+
import * as applyStylePreset from "./tools/apply-style-preset.js";
|
|
9
11
|
import * as countFeatures from "./tools/count-features.js";
|
|
10
12
|
import * as describeLayer from "./tools/describe-layer.js";
|
|
11
13
|
import * as explainCapabilityGap from "./tools/explain-capability-gap.js";
|
|
12
14
|
import * as getExtent from "./tools/get-extent.js";
|
|
15
|
+
import * as getStyle from "./tools/get-style.js";
|
|
13
16
|
import * as listServices from "./tools/list-services.js";
|
|
14
17
|
import * as queryFeatures from "./tools/query-features.js";
|
|
15
18
|
import * as statistics from "./tools/statistics.js";
|
|
@@ -113,33 +116,76 @@ export function createClientFromEnv(env = process.env) {
|
|
|
113
116
|
retry: options.retryMaxRetries > 0 ? { maxRetries: options.retryMaxRetries } : undefined,
|
|
114
117
|
});
|
|
115
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* The PLATFORM-FREE standalone MCP surface (issue #369).
|
|
121
|
+
*
|
|
122
|
+
* This is the direct-SDK operator surface: nine read-only geospatial tools wired
|
|
123
|
+
* straight onto `@honua/sdk-js`, which speaks plain Esri GeoServices / OGC API
|
|
124
|
+
* over standard REST paths (`/rest/services`, `/FeatureServer/{id}/query`, …).
|
|
125
|
+
* Point it at ANY public FeatureServer / OGC endpoint (e.g. a `services.arcgis.com`
|
|
126
|
+
* FeatureServer) with ZERO Honua-server assumptions — no admin API, no `/healthz`,
|
|
127
|
+
* no `/mcp` catalog. This is what the `honua-mcp` bin runs (see {@link runStandalone}),
|
|
128
|
+
* and what the platform-free certification lane + eval corpus exercise.
|
|
129
|
+
*
|
|
130
|
+
* Tools that need a Honua-only surface (server-side styling via OGC API – Styles,
|
|
131
|
+
* a `/rest/services` catalog) degrade gracefully on a plain target: they return a
|
|
132
|
+
* structured "not available on this target" result rather than crashing or
|
|
133
|
+
* returning misleading empty data (see `capability.ts`).
|
|
134
|
+
*
|
|
135
|
+
* The Honua-ENHANCED surface — the full `/mcp` operator catalog with governed
|
|
136
|
+
* mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
|
|
137
|
+
* mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
|
|
138
|
+
* deployment. Standalone is the front door; the proxy is the upgrade path.
|
|
139
|
+
*/
|
|
116
140
|
export function createServer(client) {
|
|
117
141
|
const server = new McpServer({
|
|
118
142
|
name: "honua",
|
|
119
143
|
version: SERVER_VERSION,
|
|
120
144
|
});
|
|
121
145
|
// ── Tools ──────────────────────────────────────────────────────
|
|
122
|
-
server.tool("honua_list_services", "Discover all available feature services. Set includeDetails=true for descriptions, layer counts, and spatial references.", listServices.schema.shape, async (args) => listServices.execute(client, listServices.schema.parse(args)));
|
|
146
|
+
server.tool("honua_list_services", "Discover all available feature services on any FeatureServer folder. Set includeDetails=true for descriptions, layer counts, and spatial references. Degrades gracefully when the target has no service catalog.", listServices.schema.shape, async (args) => listServices.execute(client, listServices.schema.parse(args)));
|
|
123
147
|
server.tool("honua_describe_layer", "Get full schema for a layer — fields, geometry type, extent, relationships.", describeLayer.schema.shape, async (args) => describeLayer.execute(client, describeLayer.schema.parse(args)));
|
|
124
148
|
server.tool("honua_query_features", "Query features with attribute filters, spatial filters, field selection, and pagination. returnGeometry defaults to false to save tokens.", queryFeatures.schema.shape, async (args) => queryFeatures.execute(client, queryFeatures.schema.parse(args)));
|
|
125
149
|
server.tool("honua_count_features", "Count features matching a filter without returning data. Use before querying to check cardinality.", countFeatures.schema.shape, async (args) => countFeatures.execute(client, countFeatures.schema.parse(args)));
|
|
126
150
|
server.tool("honua_get_extent", "Get the spatial bounding box of features matching a filter.", getExtent.schema.shape, async (args) => getExtent.execute(client, getExtent.schema.parse(args)));
|
|
127
151
|
server.tool("honua_statistics", "Compute aggregate statistics (count, sum, avg, min, max, stddev) on a field, optionally grouped.", statistics.schema.shape, async (args) => statistics.execute(client, statistics.schema.parse(args)));
|
|
128
|
-
server.tool("honua_explain_capability_gap", "Explain whether a
|
|
152
|
+
server.tool("honua_explain_capability_gap", "Explain whether a source/protocol supports a capability and suggest a safe fallback when it does not.", explainCapabilityGap.schema.shape, async (args) => explainCapabilityGap.execute(client, explainCapabilityGap.schema.parse(args)));
|
|
153
|
+
server.tool("honua_get_style", "Resolve a style from a server's OGC API – Styles surface. Pass styleId for the canonical StyleRef (style_id, title, encodings incl. inlined MapLibre); omit it to list available styles. On a plain FeatureServer with no styling surface, reports a structured 'not available on this target' result.", getStyle.schema.shape, async (args) => getStyle.execute(client, getStyle.schema.parse(args)));
|
|
154
|
+
server.tool("honua_apply_style_preset", "Resolve a style preset and its MapLibre stylesheet for client-side application. Read-only: returns the stylesheet to apply locally; it does not mutate server state. Degrades gracefully on a target with no styling surface.", applyStylePreset.schema.shape, async (args) => applyStylePreset.execute(client, applyStylePreset.schema.parse(args)));
|
|
129
155
|
// ── Resources ──────────────────────────────────────────────────
|
|
130
156
|
server.resource("services-catalog", servicesResource.uri, async (uri) => servicesResource.read(client));
|
|
131
157
|
server.resource("layer-schema", new ResourceTemplate(layerSchemaResource.uriTemplate, { list: undefined }), async (uri, params) => layerSchemaResource.read(client, params.encodedServiceId, params.layerId));
|
|
158
|
+
server.resource("styles-catalog", stylesResource.uri, async (uri) => stylesResource.readCatalog(client));
|
|
159
|
+
server.resource("style", new ResourceTemplate(stylesResource.uriTemplate, { list: undefined }), async (uri, params) => stylesResource.read(client, params.styleId));
|
|
132
160
|
return server;
|
|
133
161
|
}
|
|
134
|
-
|
|
135
|
-
|
|
162
|
+
/* v8 ignore start -- live-process entry: wires the stdio transport to a real
|
|
163
|
+
SDK client against HONUA_BASE_URL; exercised by running the bin, not unit tests.
|
|
164
|
+
The unit-testable logic (option resolution, client construction, server wiring)
|
|
165
|
+
lives in the exported functions above and is covered there. */
|
|
166
|
+
/**
|
|
167
|
+
* Run the platform-free standalone server end-to-end over stdio.
|
|
168
|
+
*
|
|
169
|
+
* Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
|
|
170
|
+
* transport/auth) and exposes {@link createServer} — the nine read-only tools —
|
|
171
|
+
* to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
|
|
172
|
+
* `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
|
|
173
|
+
* required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
|
|
174
|
+
*/
|
|
175
|
+
export async function runStandalone(env = process.env) {
|
|
176
|
+
const client = createClientFromEnv(env);
|
|
136
177
|
const server = createServer(client);
|
|
137
178
|
const transport = new StdioServerTransport();
|
|
138
179
|
await server.connect(transport);
|
|
139
180
|
}
|
|
181
|
+
/** `honua-mcp` bin entry — the platform-free standalone server. */
|
|
182
|
+
async function main() {
|
|
183
|
+
await runStandalone();
|
|
184
|
+
}
|
|
140
185
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
141
186
|
main().catch((err) => {
|
|
142
187
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
143
188
|
process.exit(1);
|
|
144
189
|
});
|
|
145
190
|
}
|
|
191
|
+
/* v8 ignore stop */
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-proving provenance for certification & eval artifacts (A5 provability).
|
|
3
|
+
*
|
|
4
|
+
* A published report is only evidence if a reader can verify WHAT was tested and
|
|
5
|
+
* WITH WHICH code. Every artifact carries a `provenance` block naming the target
|
|
6
|
+
* surface, the negotiated MCP protocol version, the advertised tool count, the
|
|
7
|
+
* auth mode, the git SHA of the certification/eval suite, and a timestamp. That
|
|
8
|
+
* turns "we pass" into "here is the exact, reproducible run that passed".
|
|
9
|
+
*/
|
|
10
|
+
/** How the eval/cert authenticated to the surface (proves the run was authed). */
|
|
11
|
+
export type AuthMode = "bearer" | "api-key" | "anonymous" | "none" | "unknown";
|
|
12
|
+
/** The reproducibility fingerprint attached to every artifact. */
|
|
13
|
+
export interface SuiteProvenance {
|
|
14
|
+
/** Git SHA of the certification/eval suite that produced this artifact. */
|
|
15
|
+
suiteGitSha: string;
|
|
16
|
+
/** Where the SHA came from (CI env, local git, or unresolved). */
|
|
17
|
+
suiteGitShaSource: "env" | "git" | "unknown";
|
|
18
|
+
/** Human-readable target surface / URL under test. */
|
|
19
|
+
targetUrl: string;
|
|
20
|
+
/** MCP protocol version negotiated with the surface, when observable. */
|
|
21
|
+
protocolVersion: string | null;
|
|
22
|
+
/** Number of tools the surface advertised. */
|
|
23
|
+
toolCount: number;
|
|
24
|
+
/** How the run authenticated to the surface. */
|
|
25
|
+
authMode: AuthMode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the git SHA of the running suite. Prefers the CI-provided `GITHUB_SHA`
|
|
29
|
+
* (or an explicit `HONUA_SUITE_GIT_SHA` override), then falls back to
|
|
30
|
+
* `git rev-parse HEAD`, then to `"unknown"` so an artifact is always self-labeled
|
|
31
|
+
* even outside a git checkout.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveSuiteGitSha(env?: NodeJS.ProcessEnv): {
|
|
34
|
+
sha: string;
|
|
35
|
+
source: SuiteProvenance["suiteGitShaSource"];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Read the negotiated MCP protocol version off a connected client, if its
|
|
39
|
+
* transport exposes one (the streamable-HTTP client transport does after
|
|
40
|
+
* `initialize`). Returns `null` for transports that do not surface it
|
|
41
|
+
* (stdio / in-memory), so the field is honest rather than fabricated.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readNegotiatedProtocolVersion(client: unknown): string | null;
|
|
44
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/provenance.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/E,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,iBAAiB,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;IAC7C,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC,mBAAmB,CAAC,CAAC;CAC9C,CAiBA;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI5E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the git SHA of the running suite. Prefers the CI-provided `GITHUB_SHA`
|
|
4
|
+
* (or an explicit `HONUA_SUITE_GIT_SHA` override), then falls back to
|
|
5
|
+
* `git rev-parse HEAD`, then to `"unknown"` so an artifact is always self-labeled
|
|
6
|
+
* even outside a git checkout.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveSuiteGitSha(env = process.env) {
|
|
9
|
+
const fromEnv = (env.HONUA_SUITE_GIT_SHA ?? env.GITHUB_SHA ?? "").trim();
|
|
10
|
+
if (fromEnv.length > 0) {
|
|
11
|
+
return { sha: fromEnv, source: "env" };
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const sha = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
17
|
+
}).trim();
|
|
18
|
+
if (sha.length > 0) {
|
|
19
|
+
return { sha, source: "git" };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// not a git checkout / git unavailable
|
|
24
|
+
}
|
|
25
|
+
return { sha: "unknown", source: "unknown" };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read the negotiated MCP protocol version off a connected client, if its
|
|
29
|
+
* transport exposes one (the streamable-HTTP client transport does after
|
|
30
|
+
* `initialize`). Returns `null` for transports that do not surface it
|
|
31
|
+
* (stdio / in-memory), so the field is honest rather than fabricated.
|
|
32
|
+
*/
|
|
33
|
+
export function readNegotiatedProtocolVersion(client) {
|
|
34
|
+
const transport = client.transport;
|
|
35
|
+
const version = transport?.protocolVersion;
|
|
36
|
+
return typeof version === "string" && version.length > 0 ? version : null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Transport-symmetric stdio proxy for the honua MCP surface (honua-server #1950).
|
|
6
|
+
*
|
|
7
|
+
* The honua server exposes a single MCP catalog over streamable-HTTP/SSE at
|
|
8
|
+
* `/mcp`. Claude-Desktop-style clients speak stdio. Rather than reimplementing
|
|
9
|
+
* the tool/resource catalog (the older `@honua/mcp-server` discovery surface did
|
|
10
|
+
* exactly that, which is how the two halves drifted apart), this proxy bridges a
|
|
11
|
+
* local stdio MCP client to the remote HTTP-SSE MCP server: it connects upstream
|
|
12
|
+
* as an MCP client, then re-exposes the *same* catalog downstream over stdio.
|
|
13
|
+
*
|
|
14
|
+
* Because every request and notification is forwarded verbatim, the stdio
|
|
15
|
+
* surface is transport-symmetric with the HTTP-SSE surface by construction —
|
|
16
|
+
* identical tools, identical input/output schemas, identical resources and
|
|
17
|
+
* prompts, and live `list_changed` notifications. There is one source-of-truth
|
|
18
|
+
* catalog (the server's `/mcp`); the SDK proxies it.
|
|
19
|
+
*/
|
|
20
|
+
export interface ProxyOptions {
|
|
21
|
+
/** Absolute URL of the remote honua MCP endpoint (e.g. https://demo.honua.io/mcp). */
|
|
22
|
+
remoteUrl: string;
|
|
23
|
+
/** Optional bearer token for the remote MCP surface. */
|
|
24
|
+
authToken?: string | undefined;
|
|
25
|
+
/** Optional API key (sent as x-api-key) for deployments that require it. */
|
|
26
|
+
apiKey?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
export declare function resolveProxyOptions(env?: NodeJS.ProcessEnv): ProxyOptions;
|
|
29
|
+
/** Build request headers for the upstream connection from the resolved options. */
|
|
30
|
+
export declare function buildUpstreamHeaders(options: ProxyOptions): Record<string, string>;
|
|
31
|
+
/** Connect an upstream MCP client to the remote honua /mcp over streamable HTTP. */
|
|
32
|
+
export declare function connectUpstream(options: ProxyOptions): Promise<Client>;
|
|
33
|
+
/**
|
|
34
|
+
* Build a low-level MCP server that forwards every request and `list_changed`
|
|
35
|
+
* notification to the already-connected upstream client. The proxy advertises
|
|
36
|
+
* exactly the upstream's name, version, and capabilities, so the downstream
|
|
37
|
+
* (stdio) surface mirrors the upstream (HTTP-SSE) surface.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createProxyServer(upstream: Client): Server;
|
|
40
|
+
/**
|
|
41
|
+
* Run the stdio proxy end-to-end: resolve the remote `/mcp` from the
|
|
42
|
+
* environment, connect upstream, and expose the mirrored catalog over stdio.
|
|
43
|
+
* Also used by the deprecated `honua-mcp` bin, which now delegates here.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runProxy(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAiBnE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAqBtF;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASlF;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+C1D;AAOD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBlF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, PromptListChangedNotificationSchema, ReadResourceRequestSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import { SERVER_VERSION } from "./index.js";
|
|
9
|
+
export function resolveProxyOptions(env = process.env) {
|
|
10
|
+
const remoteUrl = env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL;
|
|
11
|
+
if (!remoteUrl) {
|
|
12
|
+
throw new Error("HONUA_MCP_REMOTE_URL environment variable is required (the remote honua /mcp endpoint to proxy).");
|
|
13
|
+
}
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = new URL(remoteUrl);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error(`HONUA_MCP_REMOTE_URL must be a valid absolute URL: ${remoteUrl}`);
|
|
20
|
+
}
|
|
21
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22
|
+
throw new Error(`HONUA_MCP_REMOTE_URL must use http or https: ${remoteUrl}`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
remoteUrl: parsed.toString(),
|
|
26
|
+
authToken: env.HONUA_MCP_AUTH_TOKEN,
|
|
27
|
+
apiKey: env.HONUA_API_KEY,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Build request headers for the upstream connection from the resolved options. */
|
|
31
|
+
export function buildUpstreamHeaders(options) {
|
|
32
|
+
const headers = {};
|
|
33
|
+
if (options.authToken) {
|
|
34
|
+
headers.Authorization = `Bearer ${options.authToken}`;
|
|
35
|
+
}
|
|
36
|
+
if (options.apiKey) {
|
|
37
|
+
headers["x-api-key"] = options.apiKey;
|
|
38
|
+
}
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
/** Connect an upstream MCP client to the remote honua /mcp over streamable HTTP. */
|
|
42
|
+
export async function connectUpstream(options) {
|
|
43
|
+
const headers = buildUpstreamHeaders(options);
|
|
44
|
+
const transport = new StreamableHTTPClientTransport(new URL(options.remoteUrl), {
|
|
45
|
+
requestInit: Object.keys(headers).length > 0 ? { headers } : undefined,
|
|
46
|
+
});
|
|
47
|
+
const client = new Client({ name: "honua-mcp-stdio-proxy", version: SERVER_VERSION });
|
|
48
|
+
await client.connect(transport);
|
|
49
|
+
return client;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build a low-level MCP server that forwards every request and `list_changed`
|
|
53
|
+
* notification to the already-connected upstream client. The proxy advertises
|
|
54
|
+
* exactly the upstream's name, version, and capabilities, so the downstream
|
|
55
|
+
* (stdio) surface mirrors the upstream (HTTP-SSE) surface.
|
|
56
|
+
*/
|
|
57
|
+
export function createProxyServer(upstream) {
|
|
58
|
+
const upstreamInfo = upstream.getServerVersion() ?? { name: "honua", version: SERVER_VERSION };
|
|
59
|
+
const upstreamCapabilities = upstream.getServerCapabilities() ?? {};
|
|
60
|
+
const server = new Server({ name: upstreamInfo.name, version: upstreamInfo.version }, {
|
|
61
|
+
capabilities: upstreamCapabilities,
|
|
62
|
+
instructions: upstream.getInstructions(),
|
|
63
|
+
});
|
|
64
|
+
// ── Tools ──────────────────────────────────────────────────────
|
|
65
|
+
if (upstreamCapabilities.tools) {
|
|
66
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request) => upstream.listTools(request.params));
|
|
67
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => upstream.callTool(request.params));
|
|
68
|
+
}
|
|
69
|
+
// ── Resources ──────────────────────────────────────────────────
|
|
70
|
+
if (upstreamCapabilities.resources) {
|
|
71
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (request) => upstream.listResources(request.params));
|
|
72
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async (request) => upstream.listResourceTemplates(request.params));
|
|
73
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => upstream.readResource(request.params));
|
|
74
|
+
}
|
|
75
|
+
// ── Prompts ────────────────────────────────────────────────────
|
|
76
|
+
if (upstreamCapabilities.prompts) {
|
|
77
|
+
server.setRequestHandler(ListPromptsRequestSchema, async (request) => upstream.listPrompts(request.params));
|
|
78
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => upstream.getPrompt(request.params));
|
|
79
|
+
}
|
|
80
|
+
// ── list_changed notification forwarding ───────────────────────
|
|
81
|
+
// Keeps the stdio surface live: when the server's catalog changes, the
|
|
82
|
+
// downstream client is told, exactly as an HTTP-SSE client would be.
|
|
83
|
+
upstream.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
84
|
+
await server.sendToolListChanged();
|
|
85
|
+
});
|
|
86
|
+
upstream.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
87
|
+
await server.sendResourceListChanged();
|
|
88
|
+
});
|
|
89
|
+
upstream.setNotificationHandler(PromptListChangedNotificationSchema, async () => {
|
|
90
|
+
await server.sendPromptListChanged();
|
|
91
|
+
});
|
|
92
|
+
return server;
|
|
93
|
+
}
|
|
94
|
+
/* v8 ignore start -- live-process entry: wires the stdio transport to a real
|
|
95
|
+
remote /mcp upstream; exercised by running the proxy, not by unit tests. The
|
|
96
|
+
unit-testable logic (option/header resolution, upstream connect, catalog
|
|
97
|
+
forwarding) lives in the exported functions above and is covered there. */
|
|
98
|
+
/**
|
|
99
|
+
* Run the stdio proxy end-to-end: resolve the remote `/mcp` from the
|
|
100
|
+
* environment, connect upstream, and expose the mirrored catalog over stdio.
|
|
101
|
+
* Also used by the deprecated `honua-mcp` bin, which now delegates here.
|
|
102
|
+
*/
|
|
103
|
+
export async function runProxy(env = process.env) {
|
|
104
|
+
const options = resolveProxyOptions(env);
|
|
105
|
+
const upstream = await connectUpstream(options);
|
|
106
|
+
const server = createProxyServer(upstream);
|
|
107
|
+
// Tear down both ends together so a dropped upstream surfaces to the client.
|
|
108
|
+
const shutdown = async () => {
|
|
109
|
+
await server.close().catch(() => { });
|
|
110
|
+
await upstream.close().catch(() => { });
|
|
111
|
+
};
|
|
112
|
+
upstream.onclose = () => {
|
|
113
|
+
void shutdown();
|
|
114
|
+
};
|
|
115
|
+
const transport = new StdioServerTransport();
|
|
116
|
+
await server.connect(transport);
|
|
117
|
+
}
|
|
118
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
119
|
+
runProxy().catch((err) => {
|
|
120
|
+
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/* v8 ignore stop */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HonuaClient } from "@honua/sdk-js";
|
|
2
|
+
/** URI for the styles catalog resource (`honua://styles`). */
|
|
3
|
+
export declare const uri = "honua://styles";
|
|
4
|
+
/** URI template for a single style resource (`honua://styles/{styleId}`). */
|
|
5
|
+
export declare const uriTemplate = "honua://styles/{styleId}";
|
|
6
|
+
/**
|
|
7
|
+
* Read the styles catalog: the list of styles available on the server, each
|
|
8
|
+
* with its stable styleId, title, and canonical URI (`honua://styles/{id}`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function readCatalog(client: HonuaClient): Promise<{
|
|
11
|
+
contents: {
|
|
12
|
+
uri: string;
|
|
13
|
+
mimeType: "application/json";
|
|
14
|
+
text: string;
|
|
15
|
+
}[];
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Read a single style as a `StyleRef` projection (style_id / title /
|
|
19
|
+
* description / style_version / encodings / legend_url), resolved against the
|
|
20
|
+
* server's `/ogc/styles/{styleId}` surface.
|
|
21
|
+
*/
|
|
22
|
+
export declare function read(client: HonuaClient, styleId: string): Promise<{
|
|
23
|
+
contents: {
|
|
24
|
+
uri: string;
|
|
25
|
+
mimeType: "application/json";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/resources/styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQjD,8DAA8D;AAC9D,eAAO,MAAM,GAAG,mBAAmB,CAAC;AAEpC,6EAA6E;AAC7E,eAAO,MAAM,WAAW,6BAA6B,CAAC;AAEtD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW;;;;;;GAyBpD;AAeD;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM;;;;;;GAoB9D"}
|