Content
WPFPreviewMCP
A general-purpose MCP server for replic browser-based Preview of Claude Code (MCP that observes and controls browsers) for WPF desktop applications.
Since WPF does not have an observation layer equivalent to the Chrome DevTools Protocol,
a small **agent** is injected into the target process to access the Visual Tree, DependencyProperty,
screenshots, logs, C# evaluation, etc. (Snoop-style).
## Features
- **Zero-touch injection**: Using `DOTNET_STARTUP_HOOKS`, inject the agent into the target app **without modifying its source code** (.NET 5+ WPF apps only).
- **In-process**: Allows access to DependencyProperty effective values, bindings, and arbitrary C# execution,
which is not possible with external UI Automation.
- **MCP server, agent, and sample app all use .NET 10 / C#**.
## Operating Environment
- **OS**: Windows (WPF / `.Forms` dependent, so Windows only).
- **.NET SDK**: .NET 10 SDK (required for building and running).
- **Target .NET 5 or later WPF desktop apps (for zero-touch
.NET Framework old WPF requires agent reference or DLL injection.
- **Client**: MCP-compatible clients (e.g., Claude Code).
## Configuration
```
src/WpfPreview.Agent … Observation and operation agent that runs in the target process (injected DLL)
src/WpfPreview.Mcp … stdio MCP server (publishes preview_* tools)
samples/DemoApp … Sample WPF app for injection verification (agent not referenced)
```
```
Claude ──stdio──▶ WpfPreview.Mcp ──HTTP(localhost)──▶ WpfPreview.Agent(in target process)
└ Dispatcher / Visual Tree
```
The agent registers itself at startup with `%TEMP%/wpfpreview/<pid>.json` (port/title).
The MCP server enumerates these to discover targets (`serverId` = process ID).
## Building
```sh
dotnet build -c Debug
```
## Provided Tools (preview_*)
| Tool | Function |
|---|---|
| `preview_start` | Launch exe with agent injection using `DOTNET_STARTUP_HOOKS` (no modification required) |
| `preview_list` | List running apps with agents |
| `preview_stop` | Terminate target process |
| `` | Visual Tree snapshot (uid/type/name/AutomationId/role/text/screen coordinates) |
|_screenshot` | Window PNG. `mode='wpf'` (default, `RenderTargetBitmap`mode='native'` (`PrintWindow` for D3DImage/video preview, etc.) |
| `pect` | DependencyProperty effective values, value sources, bindings, coordinates (equivalent to Web computed styles) |
| `preview_click` AutomationPeer Invoke/Toggle/Select |
| `preview_fill` | Set values in TextBox, etc. |
| `preview | Resize window |
| `preview_console_logs` | Trace/Console output, unhandled exceptions |
| `preview_network` | communication history (best effort) |
| `preview_eval` |# expressions in the process (globals: `App`/`Main`/`Windows`/`Find(selector)`) |
### Selectors
Name` … matches `x:Name` or `AutomationId `Type[index]` … by type name, Nth item (., `TextBox[0]`)
- `uid:eN` … uid numbered by `preview_snapshot`
### XAML Source Line Mapping (in debug builds)
If the target app is a **debug build**ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO=1` is set before launch,
`preview_snapshot` and `preview_inspect` will include **element definition source XAML file:line**
(`source`/`sourceUri`/`sourceLine`/`sourcePosition`).
Uses `System.Windows.Diagnostics.VisualDiagnostics.GetXamlSourceInfo` (same mechanism as Snoop).
- If using `preview_start`, **this environment variable is set automatically**.
- In release builds or if information is not available, `source` will be null (best-effort, no impact on other features).
- Example: `Button GreetButton → source: "MainWindow.xaml:10"`
## Registering with ClaudeAdd the following to `..json` (in the project root) or `~/.claude.json`:
```json
{
"mcpServers": {
"wpf-preview": {
"command": "dotnet",
"args": ["<repository-pathsrc\\WpfPreview.Mcp\\bin\\Debug\\.0\\wpfpreview-mcp.dll"],
"env": {
"WPFPREVIEW_AGENT_DLL": "<repository-path>\\WpfPreview.Agent\\bin\\Debug\\net10.0-windows\\WpfPreview.Agent.dll"
}
}
}
}
```
`WPFPREVIEW_AGENT_DLL` is the location of the DLL injected as an agent by `preview_start`
(default probing from build output is attempted if not set).
## Manual Injection (without MCP)
```sh
DOTNET_STARTUP_HOOKS="…/WpfPreview.Agent.dll" path/to/YourApp.exe
# From another shell
curl http://127.0.0.1:<port>/snapshot
```
## Security
- Agent's HTTP is limited to `127.0.0.1` (loopback only a random port.
- A session token is generated at launch to a discovery file (same user's `%TEMP%` only MCP reads this and sends it in the `X-WpfPreview` header. **Token mismatch results in 401**,
and `Origin`/`Referer` (i.e., browser-originated) requests are rejected with **403** to block local and blind attacks from other processes.
- `preview_eval` allows arbitrary C# execution in the target process (RCE by design).
Protected by token authentication; use with trusted parties only.
## Limitations
- Zero-touch injection is **.NET 5+ only** (.NET Framework old WPF requires agent reference or DLL injection).
- `preview_network` only covers `HttpClient` (custom sockets/gRPC not supported).
- `preview_eval` assumes synchronous expressions (evaluated on the UI thread).
- `mode='wpf'` cannot capture hardware rendering like D3DImage/video previews (will appear white/transparent).
Use `mode='native'` in such cases. Native mode includes OS chrome like title bars and uses real pixels for DPI;
minimized windows cannot be captured.
## License
[MIT License](LICENSE)