Studio package parity: MCP, QGIS, and Console (honua-sdk-js#226)

Status: experimental, implemented for ticket honua-sdk-js#226 (parity layer on top of the package contracts from honua-sdk-js#225, see studio-package-contracts.md).

The goal is that an AI-generated spatial output — a map, dashboard, report, or app package — is portable across Honua Studio/Console, MCP clients, and the QGIS plugin because all three use the same package contracts and the same provenance shape, rather than surface-specific output formats. A package produced through MCP or QGIS is validated by the SDK with one envelope and can be opened in Console once Console supports the route.

import {
  validateStudioPackage,
  validatePackageProvenance,
  getPackageProvenance,
  chartWidgetToVegaLiteSpec,
} from "@honua/sdk-js/studio";

Provenance carried consistently

Every generated package may carry a provenance envelope (honua_package_provenance.v1) under its open [extra] index signature. The envelope records the same fields regardless of which surface generated it:

Field Meaning
origin console / studio / mcp / qgis / sdk — the producing surface.
prompt The natural-language prompt + model + capturing surface.
plan The ordered generation steps and rationale the agent followed.
dataBindings Which sources (and optionally query/layer) the output reads.
permissions Capabilities required, each flagged clientSide true/false.

getPackageProvenance(pkg) reads it off any package value (returns undefined when absent or malformed); validatePackageProvenance(pkg, { required }) validates just the envelope. MCP- and QGIS-origin packages must satisfy the same provenance shape as Console — there is no surface-specific variant.

One validation envelope for packages produced outside Console

validateStudioPackage(family, pkg, options) validates a package of any family produced by an MCP client, the QGIS plugin, or the SDK, returning the unified StudioPackageValidationResponse<T> from #225. For the map family it delegates to the established structural validateMapPackage; for the other families it runs the shared base checks (identity field, format, lifecycle) plus provenance checks when an envelope is present. Pass { requireProvenance: true } to make provenance mandatory.

const response = validateStudioPackage("dashboard", mcpDashboardPackage);
if (!response.valid) {
  for (const d of response.diagnostics) console.warn(d.code, d.message);
}

Fixtures for all four package variants — map-only, dashboard, report, app — live in test/fixtures/studio-packages/ and are shaped exactly as MCP/QGIS would emit them (provenance.origin of mcp/qgis). They are openable by Console and exercised by test/studio/studio-package-parity.test.ts.

Charts use a documented Vega-Lite subset

Dashboard and report charts use a documented, compatible subset of Vega-Lite v5 (HonuaVegaLiteChartSpec) so a chart renders identically across surfaces. The subset is bar / line / point / arc marks over a single positional encoding pair plus an optional color channel — a structural subset of a real Vega-Lite TopLevelSpec, consumable directly by vega-embed without translation. chartWidgetToVegaLiteSpec(widget, rows?) derives a spec from a generated-app chart widget:

Generated-app chartKind Vega-Lite mark Encoding
categories bar x: nominal group field, y: count
histogram bar x: binned quantitative field, y: count
time-series line x: temporal field, y: count or metric aggregate

Anything outside the subset is not silently downgraded: when a widget lacks the field its chart kind needs, chartWidgetToVegaLiteSpec returns undefined so a host degrades cleanly instead of emitting a broken spec.

Client-side preview vs. server execution

The SDK can preview the client-renderable parts of a package in a browser without a server. Capabilities that need server execution are flagged on each permission (clientSide: false) so a host reports an unsupported state cleanly rather than rendering a silent partial.

Capability Client-side preview (SDK) Requires server execution
Validate any package (validateStudioPackage)
Read/validate provenance
Render a map package (MapLibre runtime)
Derive a chart spec (chartWidgetToVegaLiteSpec)
Hydrate a dashboard/app manifest preview ✅ (with a feature loader)
Run live queries against a hosted/protected source ✅ (auth, hosting)
Render a report to PDF / static artifact ✅ (report:render)
Execute a GP / ETL / workflow package
Publish / share / embed a package ✅ (control-plane)

A host should gate UI on this matrix together with the server's StudioCapabilityManifest (hasCapability / getCapability): the manifest says what the connected server supports; this table says what the SDK can do without one.

Versioning

Every new export is tagged @experimental and is subpath-only under @honua/sdk-js/studio, outside the SDK semver contract until the matching server contracts land. The provenance format follows the established HONUA_X_PACKAGE_FORMAT_V1 = "honua_x_package.v1" pattern (HONUA_PACKAGE_PROVENANCE_FORMAT_V1 = "honua_package_provenance.v1").