UI Composer Tools
UI Composer tools work with local Composer extension packs and blueprint inputs. They do not inspect a running WPF target and should be used before catalog, validation, rendering, preview compile, or apply workflows.
Contract Compatibility
Composer currently supports these v1 contracts and fails closed when schemaVersion is missing or different:
| Contract | Supported version | Compatibility policy |
|---|---|---|
| UI pack | wpfdevtools.ui-pack.v1 |
Reads the artifact as-is; install/import workflows copy pack files instead of rewriting them. |
| UI block | wpfdevtools.ui-block.v1 |
Resolves pack-qualified block kinds from enabled packs only. |
| UI recipe | wpfdevtools.ui-recipe.v1 |
Expands recipes only after the declared required packs are available. |
| UI blueprint | wpfdevtools.ui-blueprint.v1 |
Requires packs[], primaryPack, and pack-qualified block kinds; optional resourceVariants selects pack-owned resource variants. |
| Source lock | wpfdevtools.source-lock.v1 |
Preserves provenance metadata for loaded packs. |
| Pack install manifest | wpfdevtools.pack-install-manifest.v1 |
Records copied pack installation metadata without changing the pack artifact. |
| Composer project | wpfdevtools.composer-project.v1 |
Reserved for project-local Composer configuration. |
This contract is still pre-release. Beta builds may intentionally correct v1 shapes without legacy aliases; packs, validators, docs, and the extension-pack creator must move together. Stable-release compatibility policy starts with the first public stable contract.
Data-driven visual foundation
- Native WPF layout and media come from the explicit
core@0.1.0pack withrole="layout-pack". Use qualified kinds such ascore.stack,core.grid,core.rowDefinition,core.columnDefinition,core.gridCell,core.scrollViewer,core.border,core.image,core.text, andcore.template. core.scrollViewerhosts one generic child and exposes independent WPF horizontal and vertical scrollbar policies. Use a bounded grid row or column when scrolling must occur, and verify the final runtime offset or visible-item change through MCP rather than inferring scrolling from a static screenshot.core.imagerenders a project-owned image through an application-local pack URI or a simple binding. Create a literal file before apply; the reviewedprojectIntegrationPlanandapply_ui_project_integrationflow declare it as a WPF Resource. The block requires an accessible automation name and rejects external, file-system, UNC, and traversal URIs.core.gridsupports real rows, columns, grid cells, spanning, alignment, and WPFgridLengthvalues. Layout behavior is pack data, not an engine primitive or WPF UI special case.- The built-in WPF UI visual set includes
wpfui.autoSuggestBox,wpfui.numberBox,wpfui.toggleSwitch,wpfui.progressBar, andwpfui.progressRing, plus configurable typography, margin, padding, alignment, width, and window content constraints. Title-bar actions accept the search input or buttons. Use the linearprogressBarfor bounded progress, sequences, or side-by-side comparison; use the compactprogressRingfor ongoing activity when a long track is unnecessary. - The pack-owned
wpfui.editorialCardblock provides a horizontal editorial surface with media, copy, arbitrary content, and action slots. Its optionalmediaSourceaccepts a project-owned application-local pack URI or WPF binding; external and file-system URIs remain blocked. Leave it unset and populate the optional media slot for an accessible symbol fallback. This remains pack data with no Composer engine special case, so third-party packs can expose equivalent media patterns through their own contracts and renderers. - Property contracts can expose
minimum,maximum,integer,thickness, andgridLengthconstraints. SlotallowedKindsaccepts exact qualified kinds,*, or<pack-id>.*; optional non-negative integerminItemsandmaxItemsdeclare child-count bounds, with zero minimum and no maximum when omitted.xamlItemTemplateapplies a declared wrapper to each child. - Renderer templates can wrap optional slot-only XAML in non-nested
{{?slot.name}}...{{/slot.name}}sections. Composer omits the whole section when the declared slot has no children, preventing empty property elements or layout containers. - Renderer templates can wrap non-nested property-dependent XAML in
{{?property.name}}...{{/property.name}}when a declared property has a non-empty effective value, or{{^property.name}}...{{/property.name}}when it does not. This keeps control-specific precedence in extension-pack data instead of Composer code. - Extension blocks can declare
authoringRoles, while a slot can declare one data-onlyadjacencyAdvisoryusingchildRole,whenProperty,whenValues, requireditemSpacingProperty, and optionalchildMarginProperty. When adjacent matching children have no effective horizontal separation, validation emits the genericAdjacentContentWithoutSeparationwarning at the second child and includes exact repair paths. Results are capped at 32; the engine never infers a pack, block kind, control library, or property name. - Packs may expose named
resourceVariantswith a default and a pack-ownedappearance(light,dark, orneutral). A blueprint selects variants by pack id, so Composer never needs library-specific theme logic. A block property may declarevisualRoleassurface; validation then emitsSurfaceThemeContrastRiskat the exact property path when an explicit surface conflicts with a selected theme-styled subtree. - Third-party renderers that emit a pack XML namespace declare safe structural preview metadata in
pack.json. Composer generates preview types from that metadata and returnsPreviewContractMissingwhen a used custom namespace has no contract. Native-only third-party renderers need no stub contract. Packs cannot provide arbitrary preview C#. - Preview metadata remains pack-neutral: use
tabControlortabItemfor semantic subclasses that receive native base-targeted styles. Do not redeclare members inherited from any selectedbaseKind; use native properties such asWindow.Content, sizing, commands, items, and tab state directly in renderer XAML. Composer rejects shadow declarations that would disconnect authored values from the native visual tree, commands, styles, or templates. Renderer attributes whose entire value is an unset property token are omitted; explicit empty strings and literal empty attributes are preserved. Leave inheritable visual properties such asForegroundunset unless the blueprint explicitly overrides the active theme. - Any blueprint node may declare a stable
elementNameandautomationId. Composer validates safe syntax and tree-wide uniqueness, then persists them as WPFx:NameandAutomationProperties.AutomationIdon the renderer root. Duplicate values fail withDuplicateElementNameorDuplicateAutomationId; a conflicting pack-owned root identity fails instead of silently rewriting a renderer contract.
For an original app, query get_ui_block_catalog with includeRecipes=false first and choose a creative brief from available capabilities. Request recipes later as optional accelerators; do not let the first recipe determine the app concept.
Composer observability
Composer tool responses include an observability object with local structured logs, per-call metrics, top diagnostic codes, and a privacy policy summary. This data is returned only in the MCP response or pack import plan; it is not exported to a remote service by default. Set WPFDEVTOOLS_COMPOSER_TELEMETRY_DISABLED=true to make that disabled policy explicit in hosted environments.
The observability payload does not include blueprint JSON, generated XAML, full user file content, secrets, or absolute local paths. Logs keep stable diagnostic codes plus short remediation text so agents can debug validation, render dry-run, apply, security rejection, rollback, preview compile, and pack import paths without copying project content.
list_ui_block_packs
Lists installed UI block packs from built-in, project-local, and user-global roots. Each entry includes kind, themeTokens, resourceVariants, role, required, counts, provenance, readiness metadata, and available block kinds. resourceVariants.defaultVariant and its ordered variant ids/appearances are the authoritative pack-neutral resource choices. role is the pack-kind-derived suggested blueprint role, while required=true marks a default required declaration; required=false never permits omitting a pack whose blocks the blueprint uses. Use the top-level allowedPackRoles as the authoritative pack-neutral values for blueprint packs[].role; do not guess a role from a pack id.
Request options:
projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
The tool omits absolute pack root paths from its public payload. Use structuredContent as the canonical result and treat content[0].text as a compact fallback.
Caller-selected roots must resolve to local non-network storage. UNC, device, and other remote roots are rejected before filesystem probing. On Windows, every existing path component is checked before directory discovery; a reparse-point ancestor or root is rejected before existence checks or enumeration can follow it. Discovery is bounded by pack count and directory/file traversal limits, so narrow roots remain preferable.
import_ui_block_pack
Validates a normalized extension-pack ZIP and, after explicit approval, installs it only under <projectRoot>/.wpfdevtools/packs. The default dry-run returns the pack identity, archive SHA256, destination root, and relative file plan without writing.
Request options:
archivePath: required absolute local path to the reviewed normalized pack ZIP.projectRoot: required absolute local WPF project root; this is the only write boundary.dryRun: defaults totrue. Review the archive hash and complete file plan first.reviewedArchiveSha256: required whendryRun=false; copyarchiveSha256exactly from the reviewed dry-run response. The import fails if the archive changes between review and write.confirmImport: must betruewhendryRun=false.allowOverwrite: defaults tofalse; enable only for a reviewed replacement of the same pack id and version.
Non-dry-run imports also require WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true, WPFDEVTOOLS_MCP_ALLOW_PROJECT_WRITES=true, and an exact WPFDEVTOOLS_MCP_ALLOWED_PROJECT_ROOTS match. Compressed archives are capped at 64 MiB before hashing; hashing uses cancellable asynchronous I/O. The importer reuses one open archive handle for review binding and extraction, rejects unsafe archive entries, invalid pack contracts, destination reparse points, and writes outside the project-local registry. It never edits project files, package references, resources, XAML, code-behind, or ViewModels.
get_ui_block_catalog
Returns block catalog entries from enabled Composer packs. Use it after list_ui_block_packs when an agent needs concrete block kinds, properties, slot names, allowedKinds, declared child-count bounds, renderer availability, or source hint summaries before creating a blueprint.
Request options:
packIds: optional pack id filter, such as["sample"].category: optional block category filter.authoringRole: optional case-insensitive exact filter over pack-definedauthoringRoles. Derive roles from the intended visual structure, such asoverlay-layoutorcarousel, before choosing convenience blocks.kindPrefix: optional pack-qualified kind prefix.composableOnly: when true, returns only blocks with an available renderer template.kind: optional exact pack-qualified block kind for single-block detail.includeRecipes: when true, also returns recipe catalog entries for use withexpand_ui_recipe.compact: when true, returns the brief discovery projection with identity, the pack-authored block description, category, property names, required or numeric-boundedpropertyContracts, preview warnings, slot bounds, renderer availability,compositionSkeleton, and pack-definedauthoringRoles. Each compact property contract preserves its type, required flag, bounded allowed-value sample/count, numeric bounds, integer flag, and format. An omittedmaximumormaxItemsremains unbounded.allowedValueQuery: optional case-insensitive substring search over allowed string values. Use it with an exactkindandcompact=false; omittingkindreturnsCatalogExactKindRequiredinstead of the full catalog. The query is limited to 128 characters.
Catalog entries include source hint paths only. They do not copy third-party source code into tool output.
Use compact=true for broad discovery, then query each selected exact kind with compact=false before setting unfamiliar properties. Full mode remains the default and retains descriptions, complete property contracts, slots, and source hints.
Large pack-owned vocabularies always remain bounded, including exact-kind detail. Each property reports the complete allowedValueCount, the current allowedValueMatchCount, up to 12 matching allowedValues, and whether those matches were truncated. To choose a value, call get_ui_block_catalog with the exact kind, compact=false, and a short allowedValueQuery such as a concept or icon name. Validation still checks the complete pack vocabulary and returns a bounded set of relevant repair values.
Pack authors can provide inert description text for blocks, properties, and slots. Properties can also provide previewWarning when structural preview may differ from final package measurement or styling. Read these pack-defined fields before choosing values; they describe renderer behavior without adding library-specific logic to Composer. Renderer identity targets are runtime-inspectable by default. A pack sets renderer.runtimeInspectable=false only for a non-element WPF object that runtime element tools cannot discover.
The response also includes authoringGuidance. Its strategy="brief-first" and creativeBriefRequired=true fields tell an Agent to establish an independent product purpose and information architecture from discovered capabilities. includeRecipes defaults to false; request recipes only later as optional accelerators or fragments.
Every catalog item includes a pack-neutral compositionSkeleton generated from
that block's own contract. It contains the exact kind, values for required
properties, and empty arrays for declared slots. Copy the compact node into a
blueprint and add children or optional properties without retyping pack-specific
kind and slot names.
Optional blueprint draft transport
Use create_ui_blueprint_draft when a multi-step workflow should avoid retransmitting and double-serializing the same document. Start with the smallest valid root or shell, store it immediately, and add descendants with compose_ui_blueprint; do not hand-author a deep complete tree in one JSON string. The tool returns an opaque draftRef without echoing the document. Its bounded aliasInventory lists up to 64 copy-ready @ElementName aliases declared by node-level elementName values; metadata fields do not create aliases. The immutable, process-local store is bounded to 32 drafts, 65,536 characters per draft, and 30 minutes per entry. References are unguessable, are never persisted, and become invalid when their MCP server process exits, their lifetime expires, or capacity eviction occurs.
Use patch_ui_blueprint_draft with a live reference to create a new immutable derived reference. For broad object changes, pass a JSON Merge Patch object: null removes an object property, nested objects merge recursively, and arrays or scalar values replace their target. For one edit, pass an exact jsonPath plus a native JSON value, or set remove=true without a value. A bare @ElementName alias selects the whole named node for replace/remove; append a relative path such as @ElementName.properties.text for a nested target. For two to 16 related edits, pass ordered operations; they run atomically against one working copy and produce one derived reference. Each reported atomic change includes its zero-based operationIndex. The source reference never changes. Every successful derivation returns a bounded changeSummary with changed paths and compact before/after values instead of echoing the full blueprint. A missing, expired, or evicted reference returns BlueprintDraftNotFound with recovery guidance.
The seven downstream tools that take blueprintJson also accept an opaque draftRef: compose_ui_blueprint, validate_ui_blueprint, render_ui_blueprint, preview_ui_blueprint, repair_ui_blueprint, apply_ui_blueprint, and apply_ui_project_integration. Direct blueprintJson remains the simplest option for one-shot workflows.
create_ui_blueprint_draft
Creates one bounded ephemeral draft. The response includes draftRef, characterCount, expiresAt, immutable=true, and exact retention metadata; it intentionally omits the stored JSON.
patch_ui_blueprint_draft
Derives a new draft in one of three mutually exclusive modes:
- Broad change: pass
draftRefandpatchJsonfor JSON Merge Patch. - Surgical change: pass
draftRef, an exact path such as$.layout.slots.children[0].properties.text, andvalue; when the target node has a unique standard blueprintelementName, use@ElementNamefor the whole node or@ElementName.properties.textfor a descendant instead of repeating its nested array path. Use bracket-quoted segments such as$.layout.properties["accent.color"]when a pack-defined property key is not a simple identifier. To delete the target, omitvalueand setremove=true. - Atomic multi-path change: pass
draftRefand anoperationsarray containing one to 16 ordered objects. Each object uses the samejsonPath/valueorremove=truecontract as surgical mode. Paths and stable aliases resolve against the result of earlier operations in the same batch. Any invalid operation rejects the complete batch with its exact$.operations[index]request path; no partial draft is retained. This all-or-nothing mode returns one immutable reference and ordered per-path change summaries.
The response returns the new reference, sourceDraftRef, retention metadata, and a compact changeSummary containing changeCount, bounded changes, and truncation metadata. Each change identifies jsonPath, changeType, and compact before/after values; atomic batches also include zero-based operationIndex. It never echoes the full blueprint. Use compose_ui_blueprint instead when inserting a catalog block into a slot array.
compose_ui_blueprint
Inserts one pack-defined compositionSkeleton into an existing blueprint slot and validates the resulting document. Use it to build nested interfaces incrementally without manually rewriting deep JSON. This operation is pack-neutral and never writes files.
Request options:
blueprintJson: current full blueprint JSON text or an opaquedraftRef.targetPath: exact slot path. Use$.layout.slots.<slot>for a root slot, include an explicit child index before each nested slot such as$.layout.slots.content[0].slots.actions, or use@ElementName.slots.actionswhen that node has a unique standard blueprintelementName. Successful responses still publish the resolved exactinsertedPath.kind: exact pack-qualified block kind fromget_ui_block_catalogwithcomposableOnly=true.elementNameandautomationId: optional standard identities assigned during insertion. Existing blueprint validation enforces safe syntax and blueprint-wide uniqueness; neither field depends on the selected extension pack.properties: optional JSON object of pack-defined values to apply during insertion. The installed block contract validates property names, types, ranges, and allowed values.insertionIndex: optional zero-based position; omit it to append.projectRootandlocalAppDataRoot: optional pack discovery roots.
Use properties when the block should be configured at insertion time; this avoids a follow-up edit through a long nested path while keeping the pack's compositionSkeleton authoritative. With raw JSON input, composed=true returns a new blueprint, compact blueprintJson, exact insertedPath, and validation result. With draft input, it returns a new immutable draftRef and omits the full document; the source draft remains unchanged. Every non-composed outcome returns success=false as an MCP error result. An invalid draft-derived candidate is still retained under candidateDraftRef, while raw input retains the existing invalidCandidate and candidateBlueprintJson recovery shape. Neither path writes project files. Ambiguous paths and non-composable blocks return actionable errors without a candidate.
Every successful response also includes a bounded insertedNodeSummary so the caller can verify same-call configuration without rendering or retrieving the full draft. It publishes the resolved exact JSON path, kind, optional elementName and automationId, total and reported property counts, truncation state, and up to 32 deterministic property entries. Each entry includes its name, JSON value kind, a compact value of at most 160 characters, and an explicit value-truncation flag.
When the target resolves to an installed block contract, targetSlotSummary reports its exact path, parent kind, slot name, allowedKinds, minItems, maxItems, existing and resulting counts, remainingCapacity, and whether capacity was exceeded. A declared but omitted empty slot is created when first composed. Unbounded maxItems and remainingCapacity are explicit JSON null values rather than omitted members. The same summary is retained with an invalid candidate, so the Agent can recover from SlotMinimumItemsNotMet or SlotMaximumItemsExceeded without another catalog lookup. These are extension-declared child-count constraints, not pixel-width predictions.
validate_ui_blueprint
Validates UI blueprint JSON against the installed Composer pack contracts. Use it after list_ui_block_packs and get_ui_block_catalog, before rendering XAML or applying generated UI.
Request options:
blueprintJson: required raw UI blueprint JSON withschemaVersionset towpfdevtools.ui-blueprint.v1, or an opaquedraftRef.targetPath: optional target XAML path used to check generated class/member collisions. Omit it to useViews/<blueprint-name>.xaml.projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
The response keeps success=true for a completed validation call and reports blueprint validity in valid. A valid response includes a flattened compositionMap with total/reported counts, truncation state, and up to 64 copy-ready slot targets. Each target reports its exact targetPath, parent path and kind, slot name, current/minimum/maximum counts, and remaining capacity. Targets use element aliases when available and bracket-quoted paths for arbitrary extension-defined slot names. Validation issues include jsonPath, code, message, repairSuggestion, and relevant allowedKinds or allowedValues. Syntactically valid JSON with an incompatible field type returns InvalidBlueprintShape at the serializer's exact path, with observedValueKind, a copy-ready expectedJsonShape, and exact replacement guidance; malformed JSON remains InvalidBlueprintJson at the root. Unknown pack-owned resource selections fail with UnknownResourceVariant; explicit surface/theme conflicts return the bounded SurfaceThemeContrastRisk warning before preview or apply. Node-level identities are checked for safe syntax, uniqueness, and GeneratedClassMemberNameCollision before rendering. blueprintSize reports currentCharacters, maximumCharacters, remainingCharacters, and utilizationPercent so an agent can simplify the document before it reaches the public input limit.
expand_ui_recipe
Expands a starter recipe into a full UI blueprint and runs blueprint validation immediately. Use get_ui_block_catalog with includeRecipes=true to discover recipe ids and inputs before calling this tool.
Request options:
recipeId: required pack-qualified recipe id, such assample.workspaceStarter.inputs: optional JSON object with recipe input values. Omitted inputs use recipe defaults when available.projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
The response includes valid, recipeId, the expanded blueprint, and the nested validation result. Built-in WPF UI starter recipes cover navigation shell, dashboard card, data grid page, and tabbed settings patterns.
The built-in catalog intentionally excludes host-backed controls such as WPF UI Snackbar and ContentDialog. Those controls require presenter, host, or runtime show behavior that a standalone layout node cannot represent safely. Runtime catalog discovery is authoritative; third-party packs may expose comparable controls only when their renderer and behavior contracts cover those requirements.
For a one-shot raw workflow, serialize the blueprint object from structuredContent to JSON text before the next Composer call, then pass it under the blueprintJson parameter name. For repeated calls, create a draft once and pass its draftRef through that same blueprintJson parameter. Do not pass the object under a parameter named blueprint.
Every Composer blueprintJson parameter accepts at most 65,536 characters. Use a compact serializer so formatting whitespace does not consume that limit. In PowerShell, serialize the structured blueprint object with $blueprint | ConvertTo-Json -Depth 100 -Compress; the explicit depth preserves nested properties in built-in recipes. Other clients should disable indentation when serializing the expanded object.
render_ui_blueprint
Runs a dry-run XAML render for a valid UI blueprint. Use it after validate_ui_blueprint or expand_ui_recipe to inspect generated XAML, required package references, and application resource setup before any file-writing apply workflow.
Request options:
blueprintJson: required raw UI blueprint JSON withschemaVersionset towpfdevtools.ui-blueprint.v1, or an opaquedraftRef.targetPath: optional target XAML path suggestion. The renderer reports it in the file plan but does not write it.projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
The response keeps success=true for a completed render call and reports render validity in valid. Successful results include xaml, requiredNuGetPackages, requiredResources, packageIntegrationGuidance, and a filePlan with wouldWriteFiles=false. Package guidance is derived from the target project and never edits project or central package files. Invalid results return validation or render issues with jsonPath, code, message, and repairSuggestion.
preview_ui_blueprint
Compiles generated UI Composer XAML in a temporary WPF preview project. Use it after render_ui_blueprint when an agent needs CI-friendly compile, host-load, or runtime scene/layout evidence before applying generated UI to a real project.
Request options:
blueprintJson: required raw UI blueprint JSON withschemaVersionset towpfdevtools.ui-blueprint.v1, or an opaquedraftRef.restoreEnabled: optional boolean that defaults to true. When false, the temporary project is built with--no-restoreso missing-restore diagnostics can be validated deterministically.startHost: optional boolean that defaults to false. When true, the temporary preview host starts after a successful build and reports generated-view load status.includeRuntimeDiagnostics: optional boolean that defaults to false. When true withstartHost=true, the tool reusesconnect,get_ui_summary(depthMode="semantic"), a boundedfind_elementslookup plan for generated names and non-generated correlation names (authoredelementNamevalues and renderer-provided rootx:Namevalues), batchedget_clipping_infofor those exact targets, andget_layout_infoagainst the temporary host. This requiresWPFDEVTOOLS_MCP_ALLOW_SENSITIVE_READS=true.compactRuntimeDiagnostics: optional boolean that defaults to true. Compact mode omits successful generated XAML and risk-free correlation details while keeping their lengths/counts, each tool outcome, failed diagnostic payload, and any correlations needed to investigate layout risk. A successful screenshot is also repeated as the early top-levelpreviewScreenshotpayload in either mode, so verbose diagnostics cannot hide its reusable resource handle. Set false only when the complete XAML, correlation, and raw diagnostic payloads are required.includeScreenshotDiagnostics: optional boolean that defaults to false. When true withstartHost=true, the tool enables runtime diagnostics and requests a screenshot only if bothWPFDEVTOOLS_MCP_ALLOW_SENSITIVE_READS=trueandWPFDEVTOOLS_MCP_ALLOW_SCREENSHOTS=true.screenshotOutputMode: optional closed value that defaults tometadata; usefileto retain a server-owned PNG and return itsresourceUriplus an exactresourceReadrequest. CallresourceRead.method(resources/read) withresourceRead.paramsin the same MCP server session, after the temporary preview host exits but before that server session ends. Other values, includingbase64, returnInvalidArgumentbefore preview work starts.screenshotMaxWidthandscreenshotMaxHeight: optional positive bounds that default to 1024. Keep these defaults for reliable visual consumption across constrained Agent image bridges; pass explicit null values only when full rendered dimensions are required for archival evidence.viewportWidthandviewportHeight: optional values for previewWindow.WidthandWindow.Heightin device-independent pixels, each bounded from 1 to 8192. Match the intended target Window dimensions to expose overflow before apply. These affect WPF layout; screenshot bounds only scale returned pixel evidence. The resulting client area is smaller when window chrome is present.visualLayoutContractJson: optional pack-neutral JSON with 1 to 16 unique exactelementNameregions. Each region declares normalizedbounds(x,y,width,heightwithin the preview root), an optionaltolerancefrom 0 to 0.25 (default 0.05), and optionalhorizontalScrollbarChrome/verticalScrollbarChromevalues ofany,hidden, orvisible. It requiresstartHost=trueand sensitive reads.runtimePackApprovalTokens: optional reviewed content-bound tokens for this request only. RequiresWPFDEVTOOLS_MCP_ALLOW_COMPOSER_RUNTIME_APPROVALS=true.correlationLookupLimit: maximum exact non-generated correlation names (authoredelementNamevalues and renderer-provided rootx:Namevalues) inspected by runtime diagnostics. It defaults to 32 and is bounded to 64. The separately bounded visual-contract names do not consume this lookup budget. Raise it only whenunresolvedCorrelationsreportsreason="lookup-budget"; other reasons require repair or a final-app check, not more lookups.projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
Example visualLayoutContractJson value:
{
"regions": [{
"elementName": "PrimaryRegion",
"bounds": { "x": 0.05, "y": 0.05, "width": 0.9, "height": 0.5 },
"tolerance": 0.05,
"horizontalScrollbarChrome": "hidden"
}]
}
Preview performs restore/build and may load third-party code, so preview_ui_blueprint is a destructive tool and requires WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true, including compile-only calls.
The tool writes only to an isolated temporary preview directory and deletes it after the compile smoke. Built-in runtime packs are trusted by release provenance and omitted from these reviews. Because NuGet build targets, control constructors, and resource markup are executable third-party dependencies, project-local and user-global packs remain structural until the matching entry in runtimePackApprovalReviews is reviewed. Each entry exposes pack identity, scope, fingerprint, selected resources, exact package closure and hashes, approvalScope="content-bound-installed-pack", approvalSource (none, request-token, or environment-token), approved, runtimeEligible, and nullable eligibilityCode/eligibilityMessage without requiring diagnostic-message parsing. An ineligible entry has no approval token; repair the reported package immutability or resource-safety issue first. When WPFDEVTOOLS_MCP_ALLOW_COMPOSER_RUNTIME_APPROVALS=true, retry with the exact eligible token in runtimePackApprovalTokens; it authorizes only that request. Operator-controlled WPFDEVTOOLS_COMPOSER_TRUSTED_RUNTIME_PACKS remains available for server-start approval. The content-bound approval token binds pack scope, canonical installed root, id, version, and fingerprint, so it cannot authorize another project or mutated pack. Every package in the runtime dependency closure must be listed with exact [version] and NuGet SHA-512 contentHash; restore uses a preview-local NuGet cache, rejects undeclared transitive packages, and verifies all hashes before build. Selected preview inputs are safety-scanned before project generation. The generic WPF framework safety rules reject external or rooted image, navigation, media, XML, and resource-dictionary locations. Resource dictionaries are limited to pack-declared application-local ResourceDictionary pack URIs; literal Image.Source and BitmapImage.UriSource values remain blocked in preview. Approved packs use their declared NuGet packages, XML namespaces, selected resource variants, and de-duplicated ordered application dictionaries. There is no pack- or library-specific branching: framework safety checks classify only WPF surfaces that can initiate I/O and do not recognize a third-party pack id, block kind, control type, or resource name.
Completed results report visualFidelity="resource-backed" for runtime-only output, "hybrid-resource-backed" for mixed runtime/stub output, or "structural" for stubs only; invalid, cancelled, or failed builds report "not-available". visualValidationGuidance and visualComparisonChecklist still require confirmation in the applied, built, and launched application. Successful compact results include generatedXamlLength, elementCorrelationCount, buildOutput, and a previewHost summary; set compactRuntimeDiagnostics=false when full generated xaml and risk-free elementCorrelations are actually needed. Correlations involved in clipping, unresolved lookup, incomplete inspection, or truncated inspection remain inline in compact mode. Runtime diagnostics are opt-in; failed payloads remain inline so recovery guidance is never hidden. layoutRiskSummary.warnings prioritizes geometrically partially visible targets before fully offscreen or unclassified overflow; geometricClippingSeverity and visibleRatio identify that geometry without claiming that meaningful pixels are missing. A successful screenshotOutputMode="file" resource appears directly in previewScreenshot, remains subject to the 24-hour and 100-resource SessionManager bounds, and is removed on expiry, eviction, or disposal. If a client displays missing or dark regions while semantic evidence is complete, follow screenshotVerificationGuidance: re-read previewScreenshot.resourceUri and verify its SHA-256 before another preview cycle. If the same decoded bytes remain sparse, retry preview_ui_blueprint with screenshotMaxWidth=1024 and screenshotMaxHeight=1024; the temporary host has already exited. Do not report a product visual failure until the verified image and semantic summary agree. Build failures map back to the blueprint root and renderer template path when available.
When visualLayoutContractJson is supplied, visualLayoutContractSummary reports matched, mismatched, and unresolved named regions, including unresolvedCount, a nullable unresolved reason, expected and measured normalized bounds, maximum bounds delta, and scrollbar-chrome mismatches. A scrollbar expectation must name a runtime element that exposes computed scrollbar visibility; otherwise the value is unavailable and the region mismatches. The contract measures only names and geometry declared by the caller; it does not recognize pack ids, control libraries, product names, or reference-image subjects. Use it to make a reference-derived composition check machine-readable before apply, then verify the final built application because preview evidence is not final visual approval.
The propertyWarnings array contains only pack-defined warnings for properties explicitly present in the submitted blueprint. Each entry reports the exact jsonPath, blockKind, propertyName, and message, so an Agent can focus final-app validation on the affected layout or styling decision instead of treating every preview limitation as equally relevant.
The elementCorrelations array maps each runtime-inspectable renderer identity target's transient or safely preserved x:Name (elementName) to an exact blueprint jsonPath and blockKind. Generated names avoid every name reserved by the active renderer templates. Blocks that explicitly declare renderer.runtimeInspectable=false remain in rendered XAML but are excluded from runtime correlation because element tools cannot discover their non-element roots. Runtime diagnostics query the generated prefix once and query distinct non-generated correlation names (authored elementName values and renderer-provided root x:Name values) exactly, using the bounded correlationLookupLimit. Keep the default to avoid unnecessary calls; raise it only to close reported lookup-budget gaps. Match those results with get_ui_summary to connect preview evidence to the authored node. Correlation metadata is never stored in the blueprint or emitted by normal render/apply; existing renderer names remain unchanged so ElementName bindings keep working.
The Window client is treated as an implicit viewport boundary. Content outside the real host client can therefore report clippingSource="window-client-viewport" even when WPF ClipToBounds is false.
When runtime diagnostics are enabled, layoutRiskSummary maps clipped correlated elements back to exact blueprint paths without relying on pack-specific kinds or slot names. Coverage is explicit: correlatedTargetCount is the number of distinct renderer correlation names, resolvedTargetCount is the number of distinct runtime element IDs associated with those names, and inspectedTargetCount is the number of those IDs successfully inspected by get_clipping_info. inspectionTruncated=true means at least one correlation was unresolved or ambiguous (one name maps to multiple exact records), a find_elements response reported searchComplete=false, or a resolved element was not inspected; duplicate or unrelated matches cannot hide missing coverage. When names are unresolved or ambiguous, unresolvedCorrelationCount reports the complete number of exact correlation records and unresolvedCorrelations returns up to 32 entries with jsonPath, blockKind, elementName, requiresActiveStateInspection, and a stable reason: ambiguous-authored-name, lookup-budget, runtime-match-ambiguous, runtime-not-realized, or search-incomplete. runtime-not-realized means the valid authored XAML was absent from the active preview runtime state, commonly because its container is inactive, deferred, or virtualized; requiresActiveStateInspection=true asks the Agent to activate that state and run focused final-runtime inspection, and does not claim that the authored element is missing. When a runtime target was resolved but not inspected, the parallel uninspectedCorrelationCount and uninspectedCorrelations fields return its exact path, kind, name, and elementId. Each bounded list has its own reportedUnresolvedCorrelationCount or reportedUninspectedCorrelationCount and unresolvedCorrelationsTruncated or uninspectedCorrelationsTruncated metadata, so an agent can inspect or simplify omitted nodes directly. warningsTruncated independently applies to the separate 32-warning output limit. attentionRequiredCount counts slivers (at most 15% visible), plus hidden targets when nearestScrollContainer.canBringTargetIntoView is not true; minimumVisibleRatio is the lowest ratio among those attention-required warnings. Warnings carry visibilityClassification and nearestScrollContainer; that nested object contains hasVisibleScrollBarChrome, isTargetClippedByViewport, and canBringTargetIntoView. The summary also reports the total clipped-element count, block kind, element identity, clipping source, directional overflow, and the runtime suggestedFix. Layout and ancestor-layout clips use RuntimeStructuralOverflowRisk, riskClassification="structural-overflow", and visibleContentRisk="unconfirmed-structural"; other unconfirmed sources use RuntimeClippingDetected, riskClassification="clipping", and visibleContentRisk="unconfirmed-clipping". When visibleContentImpact="not-determined", either value with severity="advisory" and requiresVisualConfirmation=true is an evidence prompt rather than a confirmed pixel-loss finding. Verify focused descendants or a screenshot; when pixels and final-app checks are complete, record the advisory as visually cleared instead of changing layout only to silence the measurement. Repeat clipping checks in the final built application because extension-package templates can measure differently from preview stubs. Validation also emits LargeFixedStackSpacing when a large leading margin after the first child may be distributing siblings along a core.stack; prefer core.grid Auto/star rows or columns for distribution.
Runtime diagnostics also use get_namescope. After a completed exact lookup, a registered target absent from active visual search is classified as reason="namescope-only", commonly indicating inactive or lazy content rather than a missing renderer. namescopeOnlyCorrelationCount reports the complete number; namescopeOnlyCorrelations returns up to 32 exact jsonPath, blockKind, elementName, elementId, and reason entries, with reportedNamescopeOnlyCorrelationCount and namescopeOnlyCorrelationsTruncated metadata. inspectedTargetCount counts only active-visual-search IDs; namescope-only targets contribute to resolved coverage without pretending that clipping was inspected. These classified exclusions do not alone set inspectionTruncated=true; activate and inspect them in the final app when their pixels or behavior matter.
repair_ui_blueprint
Turns validation, render, compile, or preview diagnostics into blueprint-first repair actions. Use it after validate_ui_blueprint, render_ui_blueprint, or preview_ui_blueprint returns issues.
Request options:
blueprintJson: required raw UI blueprint JSON withschemaVersionset towpfdevtools.ui-blueprint.v1, or an opaquedraftRef.diagnosticsJson: optional diagnostics JSON object or array from render or preview results.targetPath: optional target XAML path suggestion for render diagnostics only. The tool does not write it.projectRoot: optional WPF project root. When present, project-local packs are discovered from<projectRoot>/.wpfdevtools/packs.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
The response includes repairable, generatedXamlPatch=false, actionCount, and actions. Only content-equivalent duplicate guidance becomes one action; matching issueCode and jsonPath alone does not merge different messages, suggestions, values, or renderer paths. For merged equivalents, source preserves the first observation and ordered sources[] lists every contributing validation, renderer, or diagnostic source. Actions identify whether the repair belongs in the blueprint or in the pack renderer template contract. The tool never patches generated XAML directly.
apply_ui_blueprint
Produces a guarded apply plan for a UI blueprint. The default is dry-run, so agents can inspect the generated view file path, required resources, package plan, authored binding requirements, targetWindowPlan, and deterministic projectIntegrationPlan before any write is allowed.
Request options:
blueprintJson: required raw UI blueprint JSON withschemaVersionset towpfdevtools.ui-blueprint.v1, or an opaquedraftRef.projectRoot: required local WPF project root used for path planning and write allowlist checks.targetPath: optional project-root-relative target XAML file path, such asMainWindow.xamlorViews/GeneratedView.xaml. Absolute paths are rejected even when they point insideprojectRoot.dryRun: optional boolean that defaults to true.confirmApply: optional boolean that must be true for non-dry-run writes after the dry-run plan has been reviewed.includeGeneratedXaml: optional boolean that defaults to false. Leave it false for a compact plan; set it true only when the same response must include full generated XAML.render_ui_blueprintremains the dedicated XAML review path.targetWindowWidthandtargetWindowHeight: optional target Window dimensions in device-independent pixels, each from 1 to 8192. Copy the reviewedpreview_ui_blueprintviewportWidthandviewportHeightwhen exact preview-to-app sizing matters; omit either value to preserve that existing dimension.localAppDataRoot: optional root for user-global discovery. When omitted, the server uses the current user's LocalApplicationData path if available.
Responses always include generatedXamlLength; generatedXamlOmitted=true confirms that non-empty XAML was intentionally excluded. Non-dry-run writes require confirmApply=true, WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true, WPFDEVTOOLS_MCP_ALLOW_PROJECT_WRITES=true, and an exact WPFDEVTOOLS_MCP_ALLOWED_PROJECT_ROOTS match. A successful confirmed response preserves the executed file plan from the pre-write target state: a newly written file remains action="create", while an existing file remains action="update" and reports its backup path. The tool rejects paths outside projectRoot, creates a backup when updating an existing view, keeps one WPFDEVTOOLS_BLUEPRINT_SOURCE header and one preserved WPFDEVTOOLS_SAFE_SLOT envelope across repeated full-view applies, and does not run NuGet restore.
When targetPath is the existing App.xaml StartupUri and that XAML root is a Window, Composer can host a generated non-Window root without library-specific logic. The dry-run preserves the existing Window shell and replaces only its content; a pack-declared window root remains top-level. targetWindowPlan reports whether dimensions were configured, preserved, or do not apply. Other targets remain standalone views and do not claim startup integration. Confirm that the application-XAML operation includes the startup purpose before treating the generated UI as the launched surface.
The dry-run projectIntegrationPlan is pack-neutral. Its operations name exact target paths, semantic purposes, current-file preconditions, and proposed SHA-256 values for package references, application resources, startup selection, and pack-declared code-behind base types. It does not expose full existing project-file content through an ungated dry-run. The plan hash binds the reviewed semantic operations to the exact proposed content and current file state.
apply_ui_project_integration
Applies only the projectIntegrationPlan returned by the latest apply_ui_blueprint dry-run. Pass the same raw blueprintJson or opaque draftRef, projectRoot, targetPath, and pack discovery scope together with reviewedPlanHash and confirmIntegration=true.
This destructive tool requires WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true, WPFDEVTOOLS_MCP_ALLOW_PROJECT_WRITES=true, and an exact WPFDEVTOOLS_MCP_ALLOWED_PROJECT_ROOTS match. It regenerates the plan immediately before writing. Any pack, blueprint, target, or project-file change produces IntegrationPlanChanged and no write occurs.
Only plan-generated package-reference, central-package-version, project-resource, application-XAML, and code-behind-base-type operations are permitted. A project-resource operation can declare an existing project-owned application-local image referenced by rendered XAML. Package IDs, XAML namespaces, application dictionaries, and base types come from the selected packs; the engine contains no control-library branch. Existing files use atomic replacement and each returned change records backupPath plus rollbackAction. If a later operation fails, earlier changes are rolled back and the response reports whether rollback completed.
Apply-to-build workflow
apply_ui_blueprint writes reviewed view XAML; it does not silently edit the project file, application resources, code-behind, ViewModel, or startup flow. Use the returned plans as the authoritative integration checklist:
Run dry apply and review
filePlan,requiredNuGetPackages,packageIntegrationGuidance,resourcePlan,viewModelBindingContract,behaviorIntegrationContract,targetWindowPlan, andprojectIntegrationPlan.Run confirmed apply only after the project-root gates are scoped to the intended project.
When
projectIntegrationPlan.ready=true, callapply_ui_project_integrationwith its exactreviewedPlanHashonly after reviewing every operation. A stale hash fails withIntegrationPlanChanged; successful changes includebackupPathand rollback evidence. When the applied plan changes package references, the response also returnspackageRestoreRequired=trueand a concisebuildGuidancereminder.When the machine-applicable plan is not ready, follow
packageIntegrationGuidancemanually for every pack-declared package. Detection is static XML best-effort; every result reportsinspectionConfidence,inspectionReason,inspectedFiles, andinspectionLimitations, including the lack of evaluated MSBuild imports and conditions. Whenmode="project", add each returnedprojectPackageReferenceto the reported project file. Whenmode="central"becauseManagePackageVersionsCentrally=true, add the versionlessprojectPackageReferenceto the project and the matchingcentralPackageVersiontoDirectory.Packages.props. If that central file is inherited from outsideprojectRootand this project should be isolated, create a project-localDirectory.Packages.propswith this complete minimal XML:<Project><PropertyGroup><ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally></PropertyGroup></Project>. Then rerun the dry-run plan; do not edit the inherited file. Whenmode="unknown", package snippets are null: inspect the project first and do not infer either integration shape.Apply each entry in
resourcePlanmanually only when it is not covered by a ready reviewed integration plan. The plan already reflects the blueprint'sresourceVariantsselections or each pack's default. Treat the returned pack data as authoritative; do not assume a specific library namespace or dictionary.If
filePlancontainsrole="code-behind-integration"and the reviewed integration plan is not ready, use its action and the pack renderer's validatedcodeBehindBaseTypeso generated XAMLx:Classand code-behind inherit the same type.Treat
viewModelBindingContract.bindingRequirements.status="required"as an implementation gate. Requirements are derived only from authored WPF binding expressions, regardless of the pack property type, and deduplicate the normalized binding path while preserving every exact blueprint JSON usage path. Literal property values do not create ViewModel requirements. Implement every resolved path and investigate everypath-unresolvedentry; Composer deliberately reportscomposerWritesViewModelSource=false.Treat
behaviorIntegrationContract.status="required"as a release gate. Each interaction includesbindingStatus, rawcommandBinding, and a nullable parsedcommandPath. Complex valid WPF bindings remain required when their path is unresolved; resolve them in the final view. Navigation commands receivecommandParameterand must update selected application state and destination content; action commands must perform observable application behavior and expose an appropriateCanExecutepolicy. These are application contracts, not generated business logic.Restore, build, and launch the actual application separately:
dotnet restore .\YourApp.csproj dotnet build .\YourApp.csproj --no-restore dotnet run --project .\YourApp.csproj --no-buildValidate the running app, not only the structural preview. Use
connect,get_ui_summary, focused element reads, andelement_screenshot(outputMode="file"). Invoke every interaction frombehaviorIntegrationContractand verify a state or visible content change. For any diagnostic mutation, usecapture_state_snapshot,get_state_diff, andrestore_state_snapshot.
Do not approve a generated application merely because it compiles or because a button reports click-ready. A command-bound control remains incomplete until its DataContext command and observable result have been implemented and verified in the launched application.