gpui-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@gpui-mcptake a screenshot of the current app window"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
gpui-mcp
Give MCP agents eyes and hands inside a GPUI app.
Agents read the live UI as a semantic tree, then click, type, hover, drag, scroll and wait on real state. They can also take screenshots and diffs, record video, measure frame cost, and edit HTML-authored interfaces while the app runs. It works on Windows 11, macOS, and Linux.
Setup
Install the MCP server and add it to your MCP client:
cargo install --git https://github.com/themixednuts/gpui-mcp --locked gpui-mcp-server{ "mcpServers": { "gpui": { "command": "gpui-mcp" } } }Add the bridge and its GPUI build to your app:
[dependencies]
gpui = "=0.2.2"
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "16c9aa7ea6d897a8044d9501cde1b295256722f2", features = ["font-kit", "wayland", "x11"] }
gpui-mcp = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main" }
[patch.crates-io]
gpui = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main" }
[patch."https://github.com/zed-industries/zed"]
gpui = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main" }Both patches are required. They keep your app, gpui_platform, and the bridge
on one GPUI build. They can go away once the additions in the
vendor patch inventory land upstream.
Install the bridge when you open a window:
use gpui_mcp::{AppId, BridgeConfig, BridgeHandle};
let bridge = BridgeHandle::install(
window,
cx,
BridgeConfig::new(AppId::new("my-app")?, "My App"),
)?;That is the whole integration. The client discovers running apps on its own. See the demo for a complete window.
Related MCP server: Electron MCP Server
Making your UI agent-ready
The tree is built from your rendered elements and their accessibility data. Most problems come from the items below.
Keep the
BridgeHandlealive. Store it in your root view. Dropping it stops the bridge.Give elements an
.id(...). Only elements with an id become nodes. Text inside an element without one becomes part of its nearest ancestor's label, and that text cannot be clicked, waited on or asserted on separately. Clickable elements already need an id in GPUI.Keep ids unique among siblings. When ids collide, the nodes get longer identities qualified by their path, and exact duplicates are dropped with a
DuplicateIddiagnostic. Find children throughparent; don't rely on the shape of an id.Name controls that have no text. An icon button's label is otherwise its glyph, such as
⚙. Use.aria_label("Settings"), and use.role(...)when the role can't be inferred, as with tabs.State disabled and read-only explicitly.
enabledcomes only from.aria_disabled(true). A grey control with no click handler still reportsenabled: true. Mark read-only inputs with.aria_read_only(true).Redact secrets.
.frame_redacted(true)on an element with an id withholds its text and value from the bridge, and from any labels derived from them.Check the diagnostics.
get_ui_treereturns adiagnosticslist that reports omitted, duplicate and orphaned nodes.
div()
.id("settings")
.aria_label("Settings")
.on_click(cx.listener(|this, _, _, cx| this.open_settings(cx)))
.child(svg().path("icons/settings.svg").size_4())For HTML-authored interfaces that agents can also edit live, see the visual builder guide and the showcase.
What agents can do
Area | Tools |
Discover |
|
Act |
|
Verify |
|
Pixels |
|
Performance |
|
Live edit |
|
Prefer the element tools over coordinates. All coordinates are logical pixels relative to the window.
GPUI Kit, gpui-pre and gpui-ce
Besides Zed's own GPUI, the bridge works with two GPUI releases on crates.io:
GPUI crate | Used by | Supported versions | Bridge feature |
| GPUI Kit, | 0.3.5, 0.3.6, 0.3.7 |
|
| The community fork and apps built on it | 0.2.2 |
|
Each needs a small set of GPUI patches, which this repository provides. The recipes below pull in a patched copy from here.
GPUI Kit 0.7.0 (gpui-pre 0.3.7)
Add this to your workspace's Cargo.toml:
[dependencies]
gpui-kit = "=0.7.0"
gpui-mcp = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main", default-features = false, features = ["gpui-pre"] }
[patch.crates-io]
gpui-pre = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main" }Call gpui_kit::init(cx), open the window with gpui_kit::open_window, and
install the bridge as usual. The bridge accepts Kit's Window and App
directly. To see it working, run the Kit demo:
cargo run --manifest-path examples/gpui-kit/Cargo.tomlgpui-ce 0.2.2
Add this to your workspace's Cargo.toml:
[dependencies]
gpui-ce = "=0.2.2"
gpui_ce_platform = "=0.1.0"
gpui-mcp = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main", default-features = false, features = ["gpui-ce"] }
[patch.crates-io]
gpui-ce = { git = "https://github.com/themixednuts/gpui-mcp", branch = "main" }gpui-ce is imported as gpui, so install the bridge as usual when you open
a window.
Another version, or your own GPUI patches
The [patch] recipes above always give you the version this repository
vendors. If your app is on an older supported version, or you carry GPUI
patches of your own, keep your own patched copy instead:
From a checkout of this repository, write a patched copy into your app. Pass
--crate gpui-ceforgpui-ce.cargo xtask vendor --crate gpui-pre --version 0.3.5 --output /path/to/your-app/vendor/gpui-preApply your own patches to that copy, if you have any.
Point your workspace at it:
[patch.crates-io] gpui-pre = { path = "vendor/gpui-pre" }
The patches are in vendor/patches/, one folder per crate
and version. Each version has two:
automation.patchhas everything the bridge needs.font-fallback.patchis an unrelated font fix. Add--without font-fallbackto skip it.
CI builds and tests the bridge against every supported version, so these patches are kept working.
Things to know
Use one backend per app. Every
gpui-mcpdependency in a GPUI Kit orgpui-ceapp needsdefault-features = false. Zed apps that turn off default features must addfeatures = ["zed"].Kit components that publish accessibility info work with all the tools. Custom-drawn components only show what they annotate.
In Kit 0.7.0, disabled controls report
enabled: trueand read-only inputs don't reportread_only. Kit doesn't publish these states yet.gpui-mcp-htmlonly supports the Zed backend for now.
Measuring frame cost
Injected input costs the same as real input. The server waits for the frames that input caused and adds none of its own.
Call mark_frames, perform the interaction, then call get_frame_report. For
every frame since the mark it gives:
the full
Window::drawtime (draw_ms), split into the app's share (app_draw_ms) and the bridge's (bridge_ms);p50, p95 and max for each;
every view that rendered and why, plus the cached views that replayed instead.
The render causes are:
notified: the view, or a view inside it, calledcx.notify()ancestor_rendered: a cached view around it renderedrefresh: the window was refreshedfirst_draw: the view had nothing cached yetlayout_changed: its bounds, content mask, or text style changeduncached: it is not embedded with.cached(...)
A hover inside an Entity::cached region should show that region notified
and its siblings replaying. Anything else shows where a caching boundary leaks.
get_frame_stats averages over the same frames. record_performance reports a
fixed interval. In process, use Automation::mark_frames and
Automation::frame_report.
bridge_ms is the work the bridge adds to a draw: finishing the accessibility
tree, building the observed frame, and painting highlights. The semantic tree
is converted off the UI thread when a client reads it.
Platform notes
Linux: exact-window capture currently requires X11.
Windows: screenshots take a short burst of compositor samples and return the newest, because Windows Graphics Capture can return a stale frame first. The one-second deadline covers waiting on the compositor, not readback, so even very large windows capture from debug builds.
Security
Only enable automation in development, testing, or another trusted environment. See SECURITY.md.
License
Apache-2.0.
This server cannot be deployed
Maintenance
Related MCP Connectors
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A Model Context Protocol server for Wix AI tools
remote debug iOS/Android/Unity/Godot/Flutter/RN/Web on real-device.ui-tree/screenshots/taps,tests.
Related MCP Servers
- AlicenseCqualityDmaintenanceA bridge between iOS simulators and the Model Context Protocol, enabling programmatic control of iOS simulators through standardized communication interfaces.1244TypeScriptMIT
- AlicenseBqualityBmaintenanceA Model Context Protocol server that provides comprehensive Electron application automation, debugging, and observability capabilities through Chrome DevTools Protocol integration.4450 npm71MIT
- AlicenseBqualityBmaintenanceEnables AI assistants to automate and test Tauri desktop applications through the Model Context Protocol. It provides tools for app management, UI interaction, and state inspection across multiple platforms without requiring CDP dependencies.142,167 npm1MIT
- FlicenseNot gradedqualityDmaintenanceA local testing tool for APIs, web browsers, and phone UI using the Model Context Protocol.-