flutter_mcp_ui_core 0.6.5
flutter_mcp_ui_core: ^0.6.5 copied to clipboard
Core models, constants, and utilities for Flutter MCP UI system. Shared foundation for renderer and generator packages.
0.6.5 - 2026-08-23 #
Added — location action type (UI DSL §4.25, Location Profile) #
ActionTypes.locationandActionTypes.locationTypes, its own list for the same reason aspaymentTypes: the Profile is not implied by Core, so a runtime that does not claim it fails the action visibly rather than dropping it as an unknown type. It is still inall, or a validator built from this class would reject a conformant document.- The generated widget schema carries
locationin the action-type enum, so a document using it loads. The enum is derived from §17.2.2 — this is a regeneration, not a hand edit.
payment is declared (DSL §4.24, Payment Profile) #
ActionTypes.payment, carried in its own paymentTypes list. The Payment
Profile is not implied by Core, so grouping it with the version lists would
have said the wrong thing; it is still in all, because a validator built
from this class has to accept a conformant document.
DefinitionValidator reads the action catalog instead of a copy of it #
The known-type set was a literal that stopped at v1.1, so validateAction
reported submit, event, identity, sound.* and media.* as unknown
types while the runtime executed all of them. It now reads ActionTypes.all,
which is also why payment needed no second edit here.
Regenerated #
widgets_schema.g.dart — the action-type enum inside the widget schema is
what the runtime's load gate checks, so a document carrying payment would
have been rejected before rendering without this.
0.6.4 - 2026-08-11 #
The constants describe the registry again #
WidgetTypes was missing 37 canonical widget types — view, accordion,
fileInput, qrCode, link, menu, the animation family, the v1.4 advanced
set. isValidType is built from categories, and both are exported, so
UIValidator and DefinitionValidator rejected valid 1.4 documents and
WidgetRegistry filed those types under no category at all. All 37 are
declared and categorised.
ActionTypes gained the canonical grouped forms §17.3.4 names — channel,
permission, identity — which existed only in their dotted legacy spellings
here, plus submit (§4.22, resolved by the widget rather than the dispatcher)
and event (§4.23).
The generated schemas caught up with their source #
widgets_schema.g.dart and the app / page / theme constants are emitted from
the yaml registry, and the latter three had not been regenerated since the
asset-reference work: they still described AssetRef as five fixed schemes,
with no object form, no open scheme set and no Color definition. They now
match configs/. Registry-side changes carried in the same regeneration:
box accepts decoratedBox / constrainedBox, progressBar accepts
circularProgressIndicator and defaults indicatorType to circular, and
TableRow is a named shape.
Tests #
The completeness tests counted category lengths (layout == 29,
coreTypes == 10). Any 29 layout widgets satisfied that, which is how 37
types went missing under a green suite. They now read the registry and
§17.2.2 and compare against them.
0.6.3 - 2026-08-08 — an edge is {from, to} #
The widget-spec registry described a graph edge as {source, target, label}
while §10.12 / §10.13 and the generated reference say {from, to}. Both
spellings render — the runtime reads the canonical pair first and falls back to
the legacy one — so nothing was broken; an author simply could not tell which
was the standard, and the two documents disagreed in the same package.
The prose now states the standard and names the legacy spelling as legacy, which is the rule everywhere else in this spec: the standard is what is offered, compatibility is what is kept.
Reported while measuring the published cut.
0.6.2 - 2026-08-08 — a topology may come from the server #
networkGraph.nodes / edges are typed array<object> | binding, matching
every other data widget in the family (heatmap.data, dataTable.rows,
kanban.columns). The runtime already resolved a bound value there; the
property table said literal-only, so an author reading the contract wrote the
topology into the document by hand — on a platform whose whole shape is "the
server draws the dashboard", that was the one widget that could not be fed.
Schema only: the generated widgets_schema.g.dart widens, nothing narrows, and
every document that validated before still validates.
Raised by a reader who took the property table first and asked whether the gap was intent or omission rather than filing it as a defect.
0.6.1 - 2026-08-07 — BoxSpacing, and one definition deliberately parked #
BoxSpacing — box.margin was declared as a bare EdgeInsets, so the M3
spacing token the widget has resolved since runtime 0.4.4 was rejected at
authoring time while padding accepted it. One type now covers both slots:
the token, the {token} object form, and every EdgeInsets spelling.
The string branch stays open. "16px" is not a token and resolves to no
inset, so catching it here would be better than catching it on screen — but
box.padding has admitted any string since 1.4, and a runtime validates
documents at load, so tightening the pattern would stop an already-published
bundle from opening. The runtime reports the unresolved value instead.
Differential against the published registry: 20 value shapes × 2 slots, 0 narrowed, 7 widened, 33 identical. No bundle that opens today stops.
ValidationConfig is written and parked, not landed.
textInput.validation emits unconstrained because the type is referenced and
undefined — so defining it constrains a slot that accepted anything, and
every published bundle carrying a validation block the definition did not
admit would stop opening. The finished definition sits at
configs/widget/_ValidationConfig.yaml (the _ prefix keeps codegen off it)
for a release allowed to break bundles. The runtime half of that work needs no
registry change and shipped separately.
This leaves the 27 named types requested against 1.4.1 at 26 landed, 1 parked.
0.6.0 - 2026-08-05 — the registry declares what the runtime honours (spec 1.4.1) #
The embedded schema is regenerated from spec 1.4.1. Three things changed in it.
330 properties across 80 widgets were promoted. They were read by the runtime and named nowhere, so an author could not know they existed, an editor could not offer them, and a validator was right to reject them. Each was judged in order: a variant spelling of a declared property stays out (§17.3.2 governs those), a name that breaks §17.1 is corrected rather than declared, a duplicate of something the widget already has stays out, and what remains must actually be applied by the factory. 15 alias spellings and 6 semantic duplicates were rejected on those grounds and are recorded in the spec's changelog.
Four narrowings — this is why the release is a minor. A document that validated before can now be rejected:
linear.distributiondeclaresspaceBetween/spaceAround/spaceEvenlyonly. The kebab spellings left the canonical surface (§17.3.1a was rewritten: legacy values are no longer carried in theenum, which had made the naming rule unenforceable).qrCode.errorCorrectiondeclareslow/medium/quartile/high; the QR standard'sL/M/Q/Hare runtime-only.otpInput.autoSubmitis gone;onCompleteis the declared name.- Option objects require
value. Without it the runtime falls back to an empty string, so two entries collide and selecting one selects both. Measured first: 278 object-form options in the document corpus, none missing it.
Named types are defined. EdgeInsets existed under no name at all — it was
expanded inline at every use, so a consumer resolving types by name could not
check a padding or margin slot, and the emptiness looked like a pass. It is
a primitive now, with a binding branch per edge ({left: "{{w}}", top: 8} is
the shape documents write). Fifteen item shapes (Option, NavItem, Column,
Tab, Segment, …) gained definitions too; they declare the keys the runtime
reads and keep accepting further ones.
Also in this cut — Color is checked wherever a color is taken #
The primitive was written and then not used. 0.5.1 fixed Color itself —
the CSS-name branch had an inline (?i:) flag that ECMA-262 does not have, so
it constrained nothing and tomato validated. That fix reached 17 of the 55
slots that take a color. The other 38 were declared as a bare
{"type": "string"}, so the same document that was rejected on text.color
passed on box.decoration.color, avatar.backgroundColor, divider.color,
headerBar.backgroundColor, markdown.linkColor, every NavigationStyle
color, and the rest. All 55 reference the primitive now, including the three
that carry colors inside another shape — Gradient.colors (whose own
description had said "plus Gradient stops" while the items stayed open
strings), BackgroundImage.colorFilter.color, and heatmap.colorRange, which
had no schema at all.
rgb() / rgba() are accepted. §5.3.4 rates functional notation SHOULD
and the theme parser had always read it, but the primitive had no branch for
it — so a theme written the way the spec describes failed validation. The
prose and the primitive are one set again: §5.3.4 now lists all five spellings
with their ratings, names the ten basic colors it had only alluded to, and
states that no other CSS keyword is a color here.
Measured before landing: 6,912 documents, 8,008 widget instances carrying a
color. Two values fail that did not before, both signature.penColor: "textOnSurface" in a 2026-04 backup tree — a slot name that does not exist
(onSurface does), which is exactly what the narrowing is for.
0.5.1 - 2026-08-03 — the schema now checks what it always declared #
isMcpUiDslWidgetType / mcpUiDslWidgetTypes are exported. A host that
registers extension widgets needs to tell its own additions apart from the
spec's set; without that distinction a validator ends up calling a host's own
widget a malformed document.
Color accepted every string. Its CSS-name branch used an inline (?i:)
flag, which ECMA-262 — the regex dialect JSON Schema uses — does not have, so
the pattern was not a valid regex and the branch constrained nothing.
color: "tomato" validated and then drew nothing, which is worse than being
rejected. Now spelled as a character class per letter, and pinned by
test/schema/color_primitive_test.dart, which checks both directions: every
spelling parseColor resolves is accepted, and nothing else is.
dragTarget.builder is Required=yes in §2.10.4 and was optional in the
registry, so the runtime threw on a document the schema had accepted.
Patch, deliberately. The 1.4 cut already announced that the schema narrows;
that is what made 0.5.0 a minor. This does not narrow it a second time — it
applies that same cut to the slots it had missed. A second floor bump would
make every consumer migrate twice for one change.
X[] is not a notation the generator parses, and 51 property slots across 45
widgets used it — linear, stack and form among them. Those slots emitted
a description and no constraint, so children: "hello" validated. The registry
had been mixing two notations (22 slots on array<X>, checked; 51 on X[],
not) and the emitted schema looks the same either way. Normalized to
array<X>; those slots now require an array of the declared element type.
Once children were actually checked, a class of contract disagreements surfaced underneath — places where a document could be written per the prose and rejected by the schema, or accepted by the schema and dropped by the runtime. §17.3.1a already settles which way those go: a value the runtime accepts and the schema rejects is a contract disagreeing with itself.
Coloris a primitive now, and accepts what the runtime accepts. It had been#RRGGBBonly. The runtime also takes all 40 Material 3 scheme slots (primary,onSurfaceVariant,error, …) — the only spelling that follows light/dark mode, and the one the theme chapter tells authors to use — plus ten CSS names and 3-digit hex. Everycolorproperty was rejecting the adaptive form.Alignmentlisted only the directional names;topLeft…bottomRightare registered aliases the runtime resolves.BoxShadowrequiredoffset: {dx, dy}and rejected the flatoffsetX/offsetYpair the resolver reads, along withblur/spread.- Aliases the factories read and the registry never declared:
markdown.text←content,lottieAnimation.src←source, andbox←constrained. dataTable.rowsandfileExplorer.itemstake a literal array; both were declaredbindingonly.codeEditor.codewas required even thoughbindingalone supplies the content.graph.dataandmarkdown.textwere truncated by the drafting tool and adopted verbatim — the union's| bindinghad spilled intodefault:and therequiredcolumn intodescription:, so both declared that the property does not accept a binding. Eleven more type strings carried markdown backticks the generator emits verbatim.box.width/heightareDimension: the runtime accepts{value, unit}, so the registry was the narrow side.lazywas defined twice and the shorter copy won, so the schema carried onlychild—content,trigger,onLoadandonErrorwere absent despite being documented. Restored under its registered Utility category.
Six action types appeared when the generator stopped transcribing §17.2.2
and started reading it: client.storage.set / .get / .remove and
client.notification, all documented in §4.12 with examples, plus
identity.promote / identity.release, which nothing had reported because no
document under test used them. The generator now also cross-checks the register
against the set it used to emit, so a type disappearing from §17.2.2 is a
warning rather than a document rejected weeks later.
0.5.0 - 2026-08-03 — Asset reference axis, 23 widgets, prose enums (spec 1.4) #
The embedded schema constant is regenerated from spec 1.4 after the largest vocabulary cut since the Composition Profile.
Changed #
- README states 1.4 rather than 1.3, and its example compiles: it called
ThemeDefinition.lightFromSeed(no such method —defaultLight(seedHex:)) and constructed aUIDefinitionthat does not exist.
Changed — narrowing, which is why this is a minor rather than a patch #
AssetRefslots reject a bare string carrying no scheme.image.src(and itssource/backgroundImagealiases),avatar.srcandlottieAnimation.srcwere typed as plain strings while the prose saidAssetRef; they now reference the primitive. A document whose source is absent expresses that with a binding, not""— spec §6.12.2a.iconslots takeIconRef. Theiconwidget documented three forms while the eight other icon slots were bare strings, so a codepoint object could not be written outsideiconat all.IconRefstates the rule once (name · codepoint · anyAssetRef) and every slot references it. A bare string carrying no known scheme is still read as a name, so the named form is unchanged.- Thirteen string properties declare their values as
enum. They listed them indescriptiononly, sotext.variantrejected a typo whilebutton.variantaccepted one.linear.distributioncarries its kebab spellings in the enum as well — the runtime has accepted them since v1.0 and no document said so (spec §17.3.1a).
Every widget in this workspace was checked against both the old and the new schema before the change landed: 4,476 asset and icon widgets, zero newly invalid.
Added #
AssetRefobject form —{uri, origin?}, read through MCPresources/read. No new scheme was minted for "an asset the server holds": MCP already reads an arbitrary resource uri, andOriginalready says which server. The scheme pattern also opens from a closed enumeration to any RFC 3986 scheme, matching the opennessOriginwas written with.- 23 widget definitions. Core:
fileInputmultiSelectcomboboxotpInputdateTimePickeraccordionpopovermenucontextMenubreadcrumbpaginationlink. Advanced:qrCodebarcodepdfViewerdiffViewerrichTextEditorsplitterresizablekanbanganttspreadsheet. Client:voiceInput. - 20 widget aliases (§17.3.1) and 21 properties on existing widgets.
navigation.openUrl— Core had no way out of the application.
0.4.3 - 2026-07-28 — Composition Profile in the embedded schemas (spec 1.4) #
Added — entry & identity value types (spec 1.4 §8.9) #
EntryContext,EntryIssuer,EntryNotice,IdentityContext,IdentityState,IdentitySubjectKind— how a definition was reached and who is looking at it. They live here, with the other spec value types, so authoring tools, validators and non-runtime consumers can name them without depending on the Flutter runtime; the runtime re-exports them and owns the behaviour (session, binding resolution, launch route).IdentityPromotion/PromotionOutcome—promoted·declined·unavailable·failed. A host returning "no identity" for all three would leave a document unable to tell "you declined, try again" from "this host cannot sign you in", which is a distinction every established credential API preserves.ActionTypes.identityPromote/identityRelease, av14Typeslist, andisIdentityAction. This registry mirrors spec §17.2.2 — adding the actions to the runtime without adding them here would leave the canonical name list disagreeing with the spec it claims to mirror.
EntryNotice.fromWire folds an unrecognised notice kind onto advisory rather than dropping it, so a resolver newer than the runtime never loses a message.
Fixed #
ApplicationDefinition.routeswas typedMap<String, String>, andfromConfigcast every value withas String. Since v1.4 a route target may be aDefinitionSourceobject naming another origin, so an application with a composed route threw during parsing. Routes now keep their declared shape. The same narrowing existed on the runtime side and is fixed there too.
Changed #
app/page/themeschemas re-versioned to 1.4. The 1.4 spec tree was seeded from 1.3 and these three kept 1.3$ids, titles and cross-references, while only the widget schema had moved — so the machine-readable half of the release described the previous version.configs_codegenwas pinned to1.3independently ofspec_codegen's--spec-version; it now carries a_specVersionconstant that moves with it.RouteValuewidened to aDefinitionSource($defs/Originadded). The 1.4 prose widened routes in §1.2.1 but the config that generates the schema was never updated, so a validator would have rejected a valid composed route.
Changed — widgets schema #
widgets_schema.g.dartregenerated — the embeddedwidgets.schema.jsonnow carries theviewwidget (utilitycategory,Compositionprofile,since: v1.4) with itssource/props/fallback/loading/onError/themeproperties. Sourced fromspecs/mcp_ui_dsl/spec/1.4/widgets/utility/view.yamland emitted bytools/spec_codegen, whose default--spec-versionmoved to1.4in the same change.- Additive: every pre-existing widget definition is byte-identical, so bundles that never use
vieware unaffected. A runtime that does not implement the Composition Profile treatsviewas an unknown widget type (error placeholder, no crash) per spec §18.7.3.
0.4.2 - 2026-07-19 — client.mcpStream channel type in the embedded page schema (spec 1.3) #
Changed #
page_schema.g.dartregenerated — theChannelDefinition.typeenumeration now listsclient.mcpStreamalongside the existing five channel types (client.watchFile,client.watchDirectory,client.systemMonitor,client.poll,client.websocket). Sourced fromspecs/mcp_ui_dsl/spec/1.3/configs/page/ChannelDefinition.yamland embedded by the codegen. Additive and version-neutral:typestays an open string, so bundles using the prior five types are unaffected, and themajor.minorDSL-version gate (MCPUIDSLVersion) is unchanged.channel_definition.dartdartdoc lists the new type. First consumer: theclient.mcpStreamruntime channel (flutter_mcp_ui_runtime) — the first channel that carries an MCP-server-pushed live stream.mcp_bundlefloor raised^0.4.0 → ^0.4.8(floors-at-latest on cut; no new symbol used).
0.4.1 - 2026-05-23 — common widget property fanout (spec 1.3.4) + template validate fix + mcp_bundle 0.4.0 cascade #
Changed #
widgets_schema.g.dartregenerated — every widget def in the embeddedwidgets.schema.jsonnow admits the commonclick: Actionandtooltip: stringproperties (spec 1.3.4 §2.2). Sourced fromspecs/mcp_ui_dsl/spec/1.3/widgets/_common.yamland merged by the codegen into each widget's effective property set; a widget-declared same-named property still wins. Additive — bundles that omitclick/tooltipare unaffected.mcp_bundlecaret bumped from^0.3.0to^0.4.0. The downstream bundle package switched itsUiSection.pagesrepresentation from a list to a map (Map<String, PageDefinition>) to align withmcp_ui_dsl 1.3 app.schema.json. flutter_mcp_ui_core does not call that field directly, so the only consumer change here is the caret bump; consumers of this package should bump to^0.4.1.theme_schema.g.dartdescription regenerated to English-only (drift removed from the upstreamtheme.schema.json).
Fixed #
TemplateDefinition.validatenow skips declared-type checks when the supplied argument is a binding expression ("{{...}}"). Previously a template param declaredtype: booleanrejected every expression-bound argument (always a String at validate time), causinguseinvocations to fall throughtemplateRegistry.resolveand surface the runtime'sTemplate not found:placeholder. Spec §9.3.1 mandates only required / default / enum / validator — strict type rejection is not required and expressions must be exempt regardless. Non-expression arguments still take the type path unchanged. Regression:test/models/tc_template_definition_test.dart(12/12).
0.4.0 - 2026-05-03 - Spec ↔ implementation alignment (1.3.3) #
- App / page / theme JSON schema mirrored as generated Dart constants alongside the existing widgets schema.
ApplicationDefinitiontyped i18n fields,TemplateLibraryintegrity,ColorSchemespec-spelling acceptance — all backward compatible.
0.3.2 - 2026-05-02 - M3 token shorthand + ResponsiveValue schema #
Schema additions to support widget-side consumption of the M3 token domains declared in 0.3.0. Backward-compatible — existing bundles continue to validate.
- M3 token shorthand on
text.variant/box.padding/card.shape/card.elevation/button.elevation/icon.size/icon.sizeToken. $defs.ResponsiveValueformalised for per-form-factor property overrides.14_Responsive_Events.mdrewritten on M3 5-class (compact/medium/expanded/large/extraLarge+embedded);xs/sm/md/lg/xllabels removed.{{runtime.breakpoint}}renamed to{{runtime.formFactor}}.
0.3.1 - 2026-04-30 - TemplateDefinition spec alignment #
Changed (breaking — pre-launch spec alignment) #
TemplateDefinitionwidget tree wrapper field renamedbody→contentto align with MCP UI DSL 1.3 §9.2.2 (canonical key perspecs/mcp_ui_dsl/spec/1.3/09_Templates.md).paramsfield name retained — it is canonical at both the definition site and theusewidget invocation site, keeping the API symmetric. Previous wire format (body) is removed; bundles must emitcontent.
0.3.0 - 2026-04-28 - MCP UI DSL 1.3 (Material 3 + DTCG) #
Changed (breaking) #
ThemeDefinitionrewritten to the canonical 14-token-domain spec (color, typography, spacing, shape, elevation, motion, density, breakpoints, border, opacity, focus ring, z-index, components) with optional light/dark mode overrides. Replaces the 1.2 5-sectionThemeConfig(no alias).- Color — Material 3 28-role plus 6 semantic roles, surface tonal scale (
surfaceContainerLowest..Highest,surfaceBright/Dim). Deprecatedbackground/surfaceVariant/textOn*names removed;inverseOnSurfacerenamed toonInverseSurface. - Typography — M3 15-role (display/headline/title/body/label × L/M/S). Legacy
h1–h6,subtitle1/2,body1/2,caption,button,overlineremoved. - Spacing — 9-step 8pt grid (
xxs/xs/sm/md/lg/xl/2xl/3xl/4xl) plus 4 layout aliases. - Shape — M3 7-family with
ShapeCorner.uniform/perCorner(RTL-aware). - Elevation — 6-level with shadow + optional surface tint, tonal fallback.
- License changed from Apache-2.0 to MIT.
Added #
- HCT seed palettes —
SeedPalette.lightFromSeed/darkFromSeedderive a 28-role palette viamaterial_color_utilities. - DTCG codec —
ThemeDefinition.toDtcg()/fromDtcg()round-trip the entire theme through W3C Design Tokens Community Group format. Compatible with Tokens Studio, Style Dictionary, Claude Design exports. - M3 standard state-layer opacities (hover 0.08, focus 0.12, pressed 0.16, disabled 0.38).
- New dependency:
mcp_bundle ^0.3.0.
0.2.3 #
- Bug fixes
0.2.2 #
0.2.1 #
Bug Fixes #
- Fixed ThemeConfig color keys to match MCP UI DSL v1.0 specification (textOnPrimary instead of onPrimary)
- Implemented comprehensive theme validation in UIValidator
- Added color format validation for #RRGGBB and #AARRGGBB formats
- Fixed theme validation to check all 10 required colors (5 background + 5 text colors)
- Added validation for theme mode (light, dark, system)
- Added typography, spacing, borderRadius, and elevation validation
- Fixed nested theme validation path issues
- Removed UIDefinition model that didn't follow MCP UI DSL spec
- Updated UIValidator to validate ApplicationConfig and PageConfig instead of UIDefinition
0.2.0 #
Refactoring #
- Major internal refactoring for improved maintainability
- Enhanced code organization and structure
- Improved type safety and validation
- Better separation of concerns
0.1.0 #
Initial Release #
- Core models for UI definitions (
UIDefinition,WidgetConfig,ActionConfig) - Constants for 77+ supported widget types across 9 categories
- Comprehensive validation framework with
UIValidator - Type-safe property key constants
- Utility functions for JSON manipulation and type conversion
- Property helpers for style extraction and validation
- DSL version management (v1.0.0)
- Structured exception hierarchy for error handling
- Full support for MCP UI DSL v1.0 specification