The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the XamlMcp listing page.
XamlMcp is an open-source XAML MCP server and AI inspection toolkit for Avalonia, WPF, WinUI 3, and .NET MAUI. It lets Claude Code, Codex, GitHub Copilot, and other MCP clients inspect and drive a running application: walk the visual or logical tree, read and write properties, inspect styles and resources, capture screenshots, send input, invoke commands and automation patterns, and observe changes over an authenticated local transport.
See the XamlMcp installation and configuration guide for a focused setup path across supported frameworks and AI clients.
The current release is 1.0.0-preview.3. It supports Avalonia, modern .NET WPF,
self-contained unpackaged WinUI 3, and .NET MAUI on Windows and Android. Protocol version 2 is
stable. Native MAUI nodes, iOS, and Mac Catalyst are unsupported.
See the changelog for user-facing release history and the framework feature and tool matrix for per-tool support and capability limits. The preview release guide explains the package roles, installation options, and verification commands.
| Package | What it is |
|---|---|
XamlMcp.Avalonia | In-process agent for Avalonia apps (net8.0, Avalonia 11.3.18+ and 12.x) |
XamlMcp.Wpf | In-process agent for modern .NET WPF apps (net8.0-windows) |
XamlMcp.WinUI | In-process agent for unpackaged WinUI 3 apps (Windows App SDK 2.3.1) |
XamlMcp.Maui | Logical agent for .NET MAUI 10.0.80 (net10.0, Windows, and Android API 24+; screenshots require API 26+) |
XamlMcp.Windows.Input | Shared guarded Win32 raw-input core used by Windows framework agents |
XamlMcp.Agent.Hosting | Framework-neutral desktop discovery, authentication, and named-pipe host |
XamlMcp.Protocol | Shared JSON-RPC contract — DTOs and framing; no UI-framework dependency |
XamlMcp.Server | Stdio MCP server: a dotnet tool named xamlmcp that connects AI clients to running agents |
Prerequisite: the app you inspect can target .NET 8 or later, but the xamlmcp tool (and
building this repo or the sample from source) needs the .NET 10 SDK/runtime.
Add the agent package for your UI framework:
The agent's floor is Avalonia 11.3.18. If your project pins older references (the
current avalonia.app template pins 11.3.0) restore fails with a NU1605 package-downgrade
error — bump your Avalonia.* package references to at least 11.3.18.
Install the MCP server tool:
You can also run the server without a global installation:
Two entry points provide explicit diagnostic opt-in. Nothing listens unless you enable the agent per build (option B) or per launch (option A):
Why two shapes? [Conditional] requires a void method, so the fluent AppBuilder
overload can't use it and gates on the environment variable instead. And why not a plain
#if DEBUG inside the package? That would gate on how this package was compiled at pack
time and could never see your app's configuration at all. Both overloads instead gate
on your build configuration or your launch environment.
Know the difference: option B's call is gone from your Release binaries; option A's
code ships in Release but stays inert unless XAML_MCP=1 is present at launch — anyone
who controls the launch environment can enable it. If you need hard exclusion with the
fluent shape, wrap the .AttachXamlMcp() line in your own #if DEBUG.
Call the extension on the WPF Application in OnStartup:
The WPF method is [Conditional("DEBUG")], so the call site disappears from the consuming app's
Release build. There is no environment-enabled WPF overload. The agent uses the
Application.Dispatcher that performs attachment. Windows and controls owned by secondary WPF UI
threads are excluded.
Attach on the WinUI UI thread, explicitly register each Window, and keep the calls inside the
application's diagnostic build gate:
Attach() is not conditionally compiled: the caller owns the #if DEBUG or equivalent diagnostic
gate. One session supports one DispatcherQueue and explicitly registered windows on that queue.
The supported floor is Windows App SDK 2.3.1, target framework
net8.0-windows10.0.19041.0, Windows 10 1809 (10.0.17763.0), and win-x64.
WinUI support is unpackaged. Use WindowsPackageType=None,
WindowsAppSDKSelfContained=true, and publish to a fresh directory:
The publish output must retain the generated .xbf and .pri resources. MSIX/package identity,
certificates, Store distribution, XAML Islands, and multiple UI threads are unsupported.
Call UseXamlMcp() while building the MAUI app, inside the application's diagnostic build gate:
The same package and UseXamlMcp() call work in unpackaged Windows applications and debuggable
Android applications on MAUI 10.0.80. The package does not inspect the consumer's configuration,
so the caller must own the #if DEBUG or equivalent diagnostic gate. Android projects must set
SupportedOSPlatformVersion to 24.0 or later. Screenshots require API 26 or later.
On Windows, the agent writes a discovery file under %LOCALAPPDATA%/XamlMcp/instances/
(override the directory with XAML_MCP_DIR) and serves JSON-RPC 2.0 on a current-user named pipe.
On Android, it listens only on device loopback and stores a per-launch descriptor in app-private
storage. The server reads that descriptor through debuggable run-as access and creates an owned
adb forward only while connecting. Both transports require the per-launch token before any
inspection call.
To discover an Android app, pass its application ID to the MCP server. Select a device explicitly when more than one authorized device is online:
The Windows build prerequisites are the .NET 10 SDK and MAUI Windows workload. Android also
requires the .NET Android workload and Android SDK platform tools (adb). iOS and Mac Catalyst
are unsupported.
XamlMcp is a local stdio MCP server. Claude Code or Codex starts it when the client session needs it; you do not run a persistent server process.
This repository includes portable project configuration for both clients:
.mcp.json configures Claude Code..codex/config.toml configures Codex in a trusted checkout.Both configurations run the pinned preview directly from NuGet with dnx, so they require the
.NET 10 SDK but no global tool installation. After cloning the repository, open a new client
session and verify the registration:
To register the pinned preview for your user account or from another project, run:
Alternatively, install the tool globally and register its xamlmcp command:
If a client already has a server named xamlmcp, remove or update that entry before adding the
new one. Use claude mcp remove xamlmcp --scope <local|user|project> or
codex mcp remove xamlmcp as appropriate.
Launch an instrumented application next. Set XAML_MCP=1 when using Avalonia's fluent attachment
form. Then ask the client to call list-apps → attach(instanceId) → any tool below.
Integer PID attach remains a Windows desktop compatibility path.
| Tool | Purpose |
|---|---|
list-apps | Running instrumented apps (opaque instance id, platform, and sanitized metadata; desktop entries also include pid) |
attach | Connect to one app; reports its per-tool capability flags |
detach | Release the current app connection and its transport resources |
tree | Visual/logical tree snapshot; the source of node ids |
search | Find nodes by automation id / type / name / style class / pseudo-class / text |
ancestors | Ancestor chain of a node |
props | Properties with value, source/priority, automation patterns, command slots |
set-prop | Write a property, or clear a local value (unset) |
styles | Applied style selectors and setters (capability-gated) |
bindings | Active bindings, source/path/status/errors, DataContext origin, and template identity |
failures | Bounded binding/dispatcher failure evidence after an optional action correlation cursor |
resources | Resolved resources visible at a node or app scope |
pseudo-class | Toggle :pointerover, :pressed, … |
screenshot | PNG of a window or node, with an optional same-capture node map — returned as a real MCP image |
wait-for | Bounded polling for existence, visibility, enabled/focused state, or a property predicate |
hit-test | Deepest-first visual stack at logical coordinates, with node references and scaling |
input | Synthesized clicks, keys, text, wheel |
action | Automation patterns (invoke/toggle/select/…) and bound ICommands |
assets | Enumerate opaque framework asset identifiers (avares://, WPF pack, xamlmcp-asset:///, or maui-asset:///) |
open-asset | Read an asset (images as images, text as text) |
dialog-wait | Driver, opt-in: wait for a native dialog (file picker, message box) the app opened |
dialog-act | Driver, opt-in: act on it — set-file-name, accept, cancel, select-button |
window-list | Driver, opt-in: the app's top-level windows with state and bounds |
window-act | Driver, opt-in: activate / move / resize / close a window |
Three things AI-client authors should know:
Targeting: node tools accept either an exact {snapshotId, nodeId} target or a locator using
automationId, name, type, text, or styleClass, optionally scoped by within. A locator
must resolve uniquely unless nth is explicit; ambiguity returns typed candidates instead of
selecting a node silently.
Observation: every mutating tool (set-prop, pseudo-class, input, action)
waits a settle window (default 250 ms) and returns a digest of what changed. For rapid
sequences pass settleMs: 0 or observe: false. On Avalonia and WPF, the same result includes a
failureCursor; pass it to failures to retrieve only binding or dispatcher failures observed
after that action. Optional exact categories filter the page. When truncated is true, pass
nextCursor back to retrieve the next page without skipping evidence. Cursors expire on detach
or replacement.
Snapshots: exact node references remain usable across the current and previous three snapshots
while the node stays live. Use locators for workflows that must survive re-querying; a
stale-snapshot or stale-node error means resolve the target again.
tree, type/name/text search, ancestors, properties, resources, screenshots, routed input,
actions, observation, and packaged assets are enabled and live-verified.pseudo-class mutation return unsupported-capability.styles is enabled but deliberately degraded: WPF exposes declared styles, setters, triggers,
and value sources, not a complete applied selector cascade. Read degraded and
degradationReason in the result.bindings is enabled. It reports simple and composite binding declarations, current status and
validation errors, DataContext inheritance, templated parent, and named control/content/item/header
template slots. WPF does not retain a converter exception that escapes directly into application
code; converter failures represented in the binding expression are reported as converter-error.failures is enabled and captures bounded binding trace and dispatcher-exception evidence. It
starts capture only before the first mutation or failures query, preserves existing WPF trace
listeners, and does not mark dispatcher exceptions handled. WPF requires a process-wide trace
refresh that public APIs cannot reverse; detach removes XamlMcp's listener and restores its source
level, but the framework's internal tracing infrastructure remains initialized for that process.routed; it does not claim physical mouse,
focus, capture, or Mouse.DirectlyOver equivalence. Non-empty modifiers return
unsupported-capability because routed event construction cannot inject modifier state.
input-raw is false because the public InputManager.ProcessInput proof failed those
requirements too.RenderTargetBitmap. Separate popups must be captured by their
own tree ref; native child HWND and GPU/airspace content are outside that render.assets enumerates SDK-generated *.g.resources and opens only identifiers it issued. This is a
compiler convention—WPF has no public package-resource enumeration API—and ordinary copied
Content files are not advertised.tree, type/name/text search, ancestors, resources, screenshots, semantic actions,
command slots, unpackaged assets, and bounded observation are enabled and live-verified.tree.logical=false and props.complete=false. Property enumeration uses the documented known
dependency-property catalog; reads and writes outside it return typed errors rather than a
completeness claim.styles is enabled but degraded because WinUI does not expose a complete applied selector
cascade. Screenshots use RenderTargetBitmap; native HWND, airspace/GPU content, and separate
top-level surfaces retain the documented rendering limits.input is enabled only for mechanism: "raw"; routed injection returns
unsupported-capability. Raw click, move, wheel, key, and type use guarded SendInput: the
registered owner must be the exact foreground root, pointer coordinates must still resolve to
that HWND, and an explicit keyboard target must accept focus. These checks and injection are not
atomic, so raw input is intended only for explicitly enabled diagnostic sessions.bindings is disabled and returns unsupported-capability; WinUI does not expose the required
binding-expression graph through a complete public inspection API.failures is disabled until equivalent public framework evidence is implemented in M38.xamlmcp-asset:///
identifiers, enforce containment and byte limits, and reject traversal and reparse-point paths.tree must use scope: "logical". Node refs identify MAUI IVisualTreeElement objects only;
native handler PlatformView objects are never returned.BindableProperty fields and is incomplete by design.
Scalar, enum, color, thickness, rectangle, and point writes are supported where the target type
permits them; complex object writes return typed errors.raw; it does not advertise routed input.
Android dispatches touch, wheel, key, and text through the activity/view stack and reports
routed; it never advertises raw input.ICommand slots remain available on
both platforms. Windows maps WinUI automation providers; Android maps accessibility actions.
Android advertises only actions with a verifiable node-local postcondition; generic
click/invoke and absolute-percentage scroll are deliberately rejected. Read
props.patterns for the exact accepted verbs on one node.styles is deliberately degraded. MAUI visual states are reported as style frames, not protocol
pseudo-classes. Pseudo-class mutation/search and native tree scope return
unsupported-capability.bindings is disabled and returns unsupported-capability; MAUI does not expose the active
binding-expression and source graph required by this contract.failures is disabled until equivalent public framework evidence is implemented in M38.MauiAsset items are exposed through opaque maui-asset:/// identifiers generated by the
package's manifest target. Only identifiers issued by assets can be opened.Native file pickers and message boxes are separate Win32 windows — invisible to tree and
unreachable by input. The driver runs inside XamlMcp.Server to cover that boundary. It is
off by default and Windows-only. Enable it in the command registered with your MCP client:
If you installed the global tool, replace dnx XamlMcp.Server@1.0.0-preview.3 with xamlmcp.
Remove or update an existing xamlmcp client entry before registering the Driver-enabled command.
The choreography: click the button that opens the dialog (input/action), then
dialog-wait returns a dialog ref plus its structure (button automation ids, whether a
file-name edit exists — never control values), then dialog-act drives it semantically.
window-list/window-act manage the app's own top-level windows (close requires
confirm: true).
Scope and safety:
set-file-name paths are validated against --driver-file-roots <p1;p2;…> (default: your
user profile); anything outside is a typed path-not-allowed error.dialog-wait.PipeOptions.CurrentUserOnly.
Discovery files are written owner-only on Unix (0700 directory, 0600 file) and deleted on
graceful shutdown; the server prunes entries whose PID is dead or reused.run-as. The agent binds device loopback, and
the server creates and removes the exact ADB forward it owns. Tokens, private paths, device
ports, host ports, and raw ADB output never enter MCP results or errors.samples/SampleApp
is a small Avalonia app wired with
.AttachXamlMcp() and something for every tool to touch: a menu bar plus tabbed pages covering
selection, tree, toggle, text, picker, and scroll/virtualization controls — the same pages the
control-interaction matrix suite drives end-to-end. Run it and drive it:
samples/MauiSampleApp is the Windows/Android MAUI fixture. Agent
attachment is enabled in Debug builds, and the Windows target runs unpackaged:
Build its Android target with the installed SDK tooling:
(PowerShell: $env:XAML_MCP="1"; dotnet run --project samples/SampleApp.)
samples/WpfSampleApp
is the WPF playground. Its Debug build attaches automatically:
samples/WinUiSampleApp is the self-contained, unpackaged WinUI control
lab. It exposes stable names for inspection, styles and resources, every supported action pattern,
observation digests, screenshots, popup and secondary-window roots, text and binary assets, and a
native file dialog for Driver. Agent attachment remains inside #if DEBUG.
For unattended inspection, pass the sample's off-screen launch switch:
Run its real-process MCP coverage with:
The interactive Driver fact is opt-in because it opens a native desktop dialog:
The guarded raw-input proof is also desktop opt-in because it moves the real pointer and requires the test host to receive foreground ownership:
To verify the deployable unpackaged output, publish Release to a fresh directory:
The output must contain WinUiSampleApp.exe, WinUiSampleApp.pri, the generated .xbf files,
and both Assets/fixture.txt and Assets/fixture.bin.
The solution includes net10.0-android, so a full restore/build requires the .NET Android
workload. Device tests remain opt-in: set XAMLMCP_ANDROID_LIVE=1 for the emulator lane and also
set XAMLMCP_ANDROID_PHYSICAL=1 for the physical-device lane. Set XAMLMCP_ANDROID_DEVICE when
ADB reports more than one authorized device.
Apache-2.0 — see LICENSE.