| title | Collaborative UI Debugging with AvaloniaMcp & DevTools |
|---|---|
| description | Comprehensive guide for developers and AI coding assistants collaborating on desktop UI debugging, visual tree inspection, and runtime diagnostics. |
| outline | deep |
Local LLM Server Manager includes an in-app debugging mode designed for live, collaborative problem-solving between software developers and AI coding assistants (such as Antigravity).
By uniting Avalonia F12 DevTools for human visual inspection with the AvaloniaMcp Model Context Protocol server for AI-driven programmatic inspection, teams can diagnose visual defects, inspect runtime data contexts, catch silent XAML binding errors, and verify fixes in real time.
Collaborative debugging operates on a dual-channel architecture:
- Human Developer Channel: The developer interacts with the running desktop application, evaluates ergonomics and visual aesthetics, and uses built-in F12 DevTools to inspect layout bounds, active styles, and element trees interactively.
- AI Assistant Channel: The AI coding assistant connects through the
AvaloniaMcpprotocol server over a local named pipe, querying visual trees, serialized ViewModel state, and binding diagnostic logs via structured JSON-RPC tool calls.
flowchart TD
subgraph DevWorkspace["DEVELOPER WORKSPACE"]
D1["Runs application in Debug mode: `dotnet run -c Debug`"]
D2["Inspects visual layout & controls via F12 DevTools"]
D3["Reports visual anomalies or UX issues in dialogue"]
end
subgraph RuntimeProcess["AVALONIA RUNTIME PROCESS (PID: {pid})"]
R1["AppBuilder.Configure<App>()\n.UseMcpDiagnostics()\n.LogToTrace()"]
R2["Named Pipe Endpoint: avalonia-mcp-{pid}"]
R3["Process Discovery Metadata: %TEMP%/avalonia-mcp/{pid}.json"]
R4["UI Diagnostic Logger (Binding Error Trace Capture)"]
end
subgraph McpServer["AVALONIA MCP SERVER (avaloniamcp)"]
M1[".NET Global Tool: dotnet avalonia-mcp"]
M2["Exposes 15 Diagnostic Tools over stdio"]
end
subgraph AIAssistant["AI CODING ASSISTANT (ANTIGRAVITY / CLAUDE)"]
A1["Discovers running app instance: discover_apps"]
A2["Inspects visual/logical trees, DataContexts & bindings"]
A3["Mutates properties live to test layout hypotheses"]
A4["Captures element screenshots for visual verification"]
A5["Formulates and applies codebase fixes directly"]
end
DevWorkspace -->|Interactive Visual Inspection| RuntimeProcess
RuntimeProcess -->|Named Pipe JSON-RPC| McpServer
McpServer -->|MCP Protocol stdio| AIAssistant
AIAssistant -->|Automated Fixes & Verification| DevWorkspace
All debugging and diagnostic bridges are conditionally compiled:
- Conditional Project References:
AvaloniaMcp.DiagnosticsandAvalonia.Diagnosticsare included inLocalLLMServerManager.csprojonly when'$(Configuration)' == 'Debug'. - Preprocessor Directives: Initialization calls (
UseMcpDiagnostics()andthis.AttachDevTools()) are wrapped in#if DEBUGpreprocessor blocks. - Release Cleanliness: In
Releasebuilds, no named pipe server is started, no discovery files are generated, F12 DevTools cannot be activated, and the compiled binaries contain zero runtime overhead or external attack surface. - IPC Security: Named pipes bind exclusively to local OS inter-process communication (
%TEMP%/avalonia-mcp/) accessible only by the current authenticated user session.
To enable collaborative debugging, compile and launch the project under the Debug configuration.
Run the following command from the repository root:
dotnet run -c DebugWhen launched in Debug configuration, the application executes the following startup sequence:
- AppBuilder Diagnostics: In
Program.cs,BuildAvaloniaApp()invokes.UseMcpDiagnostics()and.LogToTrace(). - Named Pipe Creation:
AvaloniaMcp.Diagnosticsallocates a local named pipe identified asavalonia-mcp-{pid}where{pid}is the process ID of the running application. - Discovery Metadata: A JSON discovery file is written to
%TEMP%/avalonia-mcp/{pid}.jsoncontaining the process ID, application name, start time, and named pipe endpoint. - DevTools Attachment: In
Views/MainWindow.axaml.cs, the window constructor callsthis.AttachDevTools(), registering theF12global shortcut. - Diagnostic Logging: The
UiDiagnosticLoggerregisters trace listeners to intercept Avalonia binding warnings and errors.
The built-in Avalonia DevTools provide immediate visual inspection without requiring external browsers or agents.
- Ensure the desktop application window is focused.
- Press F12.
- A separate Avalonia DevTools diagnostic window opens.
- Visual Tree: Displays every rendered visual primitive (e.g.,
Border,ContentPresenter,TextBlock,LayoutTransformControl). Use this to determine actual render sizes, margins, padding, clipping rectangles, and alignment. - Logical Tree: Displays controls as declared in high-level XAML markup (e.g.,
Button,ListBox,Grid), making it straightforward to match UI elements with their source.axamlfiles. - Pointer Selection: Click the crosshair icon in the DevTools toolbar, then click any element in the main application window to jump directly to that element in the tree.
- Selecting any node in the tree shows all registered Avalonia properties in the right-hand panel.
- Property Precedence: Observe whether a property value originates from a local assignment, an active style setter, an inherited value, or default metadata.
- Live Property Modification: Double-click editable values (such as
Width,Height,Margin,HorizontalAlignment,Background, orIsVisible) to change them at runtime. This allows rapid verification of layout fixes before editing source code.
- The Styles tab lists every style rule currently evaluated against the selected control.
- Active rules are highlighted, while overridden or unmatched rules are dimmed.
- Pseudo-Class Tracking: Observe dynamic pseudo-classes such as
:pointerover,:pressed,:focus, and:disabledupdate live as you interact with the UI.
- The Events tab records routed events (pointer moved, pointer pressed, key down) bubbling or tunneling through the tree.
- Use this to diagnose why a button click is not reaching an expected handler or if an invisible overlay is intercepting pointer input.
AI assistants connect to the running application using the open-source avaloniamcp Model Context Protocol server.
Install the avaloniamcp global .NET tool from NuGet:
dotnet tool install -g avaloniamcpTo update an existing installation to the latest release:
dotnet tool update -g avaloniamcpStart the server using standard stdio communication:
dotnet avalonia-mcpThe server automatically monitors %TEMP%/avalonia-mcp/, discovers any active Avalonia application running with .UseMcpDiagnostics(), and bridges MCP tool calls directly to the application's UI thread via the local named pipe.
To configure Antigravity, Claude Desktop, Cursor, or other MCP-compatible AI clients, add the server to your configuration file (e.g., mcpServers block):
{
"mcpServers": {
"avalonia_mcp": {
"command": "dotnet",
"args": ["avalonia-mcp"]
}
}
}You can verify the connection manually using the avaloniamcp CLI:
# Discover running Avalonia processes
dotnet avalonia-mcp cli discover_apps
# List open windows
dotnet avalonia-mcp cli list_windows
# Check for active binding errors
dotnet avalonia-mcp cli get_binding_errors
# Inspect top-level visual elements
dotnet avalonia-mcp cli get_visual_tree --maxDepth 3avaloniamcp exposes 15 specialized tools to AI assistants, categorized into five operational domains:
| Category | Tool Name | Parameters | Description |
|---|---|---|---|
| Inspection | list_windows |
None | Lists all open windows, titles, dimensions, positions, and window states. |
get_visual_tree |
maxDepth (optional) |
Returns the complete rendered visual hierarchy with element types, names, bounds, and visibility. | |
get_logical_tree |
maxDepth (optional) |
Returns the logical hierarchy matching the developer's XAML markup declarations. | |
find_control |
name, typeName, text (optional) |
Fast lookup of UI elements by name (#Name), control type, or displayed text. |
|
get_control_properties |
controlId |
Dumps all registered Avalonia properties, current values, types, and inheritance sources for an element. | |
| Data & Bindings | get_data_context |
controlId (optional) |
Serializes the bound ViewModel properties and values into clean JSON. |
get_binding_errors |
None | Retrieves all active and logged Avalonia binding errors, including target properties and source paths. | |
| Visual & Styles | take_screenshot |
controlId (optional) |
Captures a high-resolution base64 PNG of the entire window or a specific control for visual analysis. |
get_applied_styles |
controlId |
Inspects matching style selectors, active setters, and pseudo-classes (:pointerover, :pressed). |
|
get_resources |
controlId (optional) |
Enumerates XAML resources (brushes, colors, geometry, templates) accessible at the element's scope. | |
get_focused_element |
None | Returns the currently focused control and its keyboard tab navigation index. | |
| Interaction | click_control |
controlId |
Programmatically triggers a click event and executes bound ICommand handlers. |
input_text |
controlId, text |
Types text into TextBox or other editable input controls. |
|
set_property |
controlId, propertyName, value |
Mutates control properties at runtime to test layout fixes live on the UI thread. | |
| Discovery | discover_apps |
None | Discovers all running Avalonia applications instrumented with AvaloniaMcp.Diagnostics. |
Returns an array of active desktop windows, indicating whether each window is active, minimized, normal, or maximized, along with screen coordinates and dimensions (X, Y, Width, Height).
Generates a structured tree representation of every visual element. Each node includes a unique controlId, type name (e.g., Avalonia.Controls.Button), element name, bounds, and visibility state. Limiting maxDepth prevents overwhelming output on deeply nested layouts.
Summarizes the UI hierarchy from the perspective of XAML logical parenting. This view omits internal layout primitives (such as internal borders and presenters), making it easier to reason about high-level component organization.
Allows the AI assistant to search across the UI hierarchy in a single step. For example, calling find_control(name: "HuggingFaceSearchBox") or find_control(text: "Download") quickly returns the target controlId without traversing the entire tree.
Fetches the full property dictionary of a specific control. Includes layout metrics (Margin, Padding, HorizontalAlignment, ActualWidth), state properties (IsEnabled, IsVisible), and control-specific configurations.
Inspects the ViewModel instance bound to the target control. The server traverses the object graph and serializes properties, collections, and commands into JSON. If a control has no local DataContext, it inherits and inspects the ancestor context.
Queries the in-app diagnostic log for binding failures. This captures silent failures where Avalonia encounters a missing ViewModel property, invalid cast, or null path element during evaluation.
Renders the specified control or the entire window into an off-screen render target and returns a base64-encoded PNG image. This enables multimodal AI models to visually inspect alignment, clipping, contrast, and layout rendering.
Returns the cascade of styles matching the control. Identifies active selectors, applied setters, and pseudo-classes, pinpointing whether an unintended theme rule or local style is overriding expected colors or margins.
Inspects local and inherited XAML ResourceDictionary trees. Enables verifying whether brush keys (e.g., AccentColorBrush, SystemControlBackgroundBaseMediumBrush) resolve to intended color definitions.
Returns the element currently holding keyboard focus. Useful for diagnosing keyboard navigation bugs, focus trapping, or broken tab indexing.
Dispatches a synthetic pointer click event on the UI thread for the target control. Verifies whether button commands execute correctly and updates the UI state accordingly.
Sets text on editable controls, raising corresponding text change and binding notification events to simulate user data entry.
Dispatches a property update directly to the Avalonia property system on the UI thread. The AI assistant can test candidate values (e.g., changing Width from NaN to 200, or IsVisible from false to true) and immediately verify the visual outcome before modifying files.
Scans local IPC registration files in %TEMP%/avalonia-mcp/ and reports all detectable Avalonia instances with their process IDs and executable paths.
In XAML-based frameworks, data binding errors fail silently by default to prevent application crashes during render loops. However, silent failures lead to empty lists, unresponsive buttons, and blank labels that are difficult to diagnose from application logs alone.
- Path Typo: The XAML binding
{Binding ModelTitel}references a misspelled property (ModelTitle). - Missing Notification: A ViewModel property lacks
SetProperty(ref _field, value)or[ObservableProperty], preventing UI updates when values change. - Null Path Navigation: Binding
{Binding SelectedEngine.Config.Port}fails becauseSelectedEngineorConfigis null during initialization. - Type Conversion Mismatch: Binding a string to an enum property without an appropriate
IValueConverter.
To capture these issues, LocalLLMServerManager includes a dedicated UiDiagnosticLogger service:
- Trace Interception: Hooks into Avalonia's internal
Trace.ListenersandLoggersystem, filtering forLogEventLevel.WarningandLogEventLevel.Erroron theBindinglog category. - Ring Buffer Storage: Stores recent entries in a bounded circular buffer (capped at 500 entries) to prevent unbounded memory growth during long debugging sessions.
- Structured Records: Each diagnostic entry captures:
- Timestamp (UTC)
- Target control type and name
- Bound target property name
- Source path expression
- Full exception or warning message
When get_binding_errors is called, the AI assistant receives structured error information:
[
{
"timestamp": "2026-09-18T20:15:32.410Z",
"target": "Avalonia.Controls.Button #FilterLoraButton",
"property": "Command",
"sourcePath": "ToggleLoraFilterCommand",
"message": "Could not find property 'ToggleLoraFilterCommand' on 'CivitaiSearchViewModel'."
}
]This precise output immediately indicates that CivitaiSearchViewModel lacks the expected command or named it differently (e.g., FilterLoraCommand), eliminating guesswork.
The diagram below illustrates the typical workflow between the developer, the running application, the avaloniamcp server, and the AI assistant:
sequenceDiagram
autonumber
actor Dev as Developer
participant App as Avalonia App (Debug)
participant MCP as avaloniamcp Server
participant AI as Antigravity AI
Dev->>App: Launch via `dotnet run -c Debug`
App->>MCP: Register named pipe in %TEMP%/avalonia-mcp/
Dev->>App: Interacts with UI (e.g. Hugging Face / CivitAI Hub)
Dev->>AI: Reports symptom: "The CivitAI LoRA toggle button does not activate"
rect rgb(30, 40, 60)
Note over AI,MCP: Autonomous AI Inspection Loop
AI->>MCP: `discover_apps()`
MCP-->>AI: Returns active process ID
AI->>MCP: `find_control(name: "FilterLoraButton")`
MCP-->>AI: Returns controlId "c-1048"
AI->>MCP: `get_control_properties(controlId: "c-1048")`
MCP-->>AI: Returns IsEnabled: false, Classes: []
AI->>MCP: `get_binding_errors()`
MCP-->>AI: Returns Command binding error on ToggleLoraFilterCommand
AI->>MCP: `get_data_context(controlId: "c-1048")`
MCP-->>AI: Returns ViewModel state (FilterType: Checkpoint)
end
AI->>Dev: Explains root cause: XAML bound to non-existent command
AI->>App: Applies code fix in CivitaiSearchViewModel.cs & XAML
AI->>AI: Runs `dotnet test`, `npm run lint`, `npx tsc --noEmit`
Dev->>App: Validates fix live in the desktop UI
- Reproduction: The developer reproduces a UI glitch or unexpected state in the running application.
- Report: The developer describes the observation to the AI assistant (e.g., "The Hugging Face search box doesn't submit when pressing Enter" or "The status badge is clipped").
- Targeted Inspection:
- The AI assistant calls
find_controlto locate the relevant element. - Calls
get_data_contextto inspect current ViewModel state. - Calls
get_binding_errorsto check for silent binding failures.
- The AI assistant calls
- Visual Verification: If the issue involves alignment or styling, the AI assistant calls
take_screenshotorget_applied_styles. - Interactive Prototyping: The AI assistant can invoke
set_propertyto verify whether a proposed property change resolves the issue live. - Codebase Modification: The AI assistant updates the appropriate
.axamlor.csfile in the repository. - Verification: The AI assistant runs automated quality gates (
npm run lint,npx tsc --noEmit,dotnet test). - Confirmation: The developer reviews the running UI and confirms the fix.
| Action | Command / Shortcut | Purpose |
|---|---|---|
| Launch in Debug Mode | dotnet run -c Debug |
Launches app with DevTools and AvaloniaMcp active. |
| Toggle DevTools | F12 (in app) | Opens the interactive Avalonia DevTools inspector window. |
| Install MCP Tool | dotnet tool install -g avaloniamcp |
Installs the global MCP server on developer machines. |
| Update MCP Tool | dotnet tool update -g avaloniamcp |
Updates avaloniamcp to the latest version. |
| Test MCP Connection | dotnet avalonia-mcp cli discover_apps |
Lists active Avalonia instances available for debugging. |
| Inspect Binding Errors | dotnet avalonia-mcp cli get_binding_errors |
Dumps current binding diagnostic log directly to terminal. |
| Run Unit Tests | dotnet test |
Executes solution test suite. |
| Build Documentation | npm run docs:build |
Verifies VitePress documentation builds cleanly with zero errors. |
| Lint Codebase | npm run lint |
Runs ESLint across TypeScript and tooling scripts. |
| Typecheck Codebase | npx tsc --noEmit |
Runs TypeScript typechecker. |