Prepare the server locally
Run this once before adding it to Claude Code.
git clone https://github.com/neatobandit0/qt-pilot.git ~/.claude/plugins/qt-pilot
pip install -r ~/.claude/plugins/qt-pilot/requirements.txtRegister it in Claude Code
claude mcp add qt-pilot -- python3 /path/to/qt-pilot/server/main.pyReplace any placeholder paths in the command with the real path on your machine.
Make your agent remember this setup
qt-pilot's config, env vars, and the gotchas you hit — recalled in every future Claude Code, Cursor, and Codex session.
npx conare@latestFree · one command · indexes the sessions already on disk. Set up in the browser instead →
What it does
- Launch Qt applications headlessly via Xvfb
- Capture screenshots for visual verification
- Simulate user interactions like clicks, hovers, and keyboard input
- Discover and introspect widgets by object name
- Monitor application health and capture stderr
Tools 11
launch_appLaunch a Qt application headlessly.capture_screenshotCapture the current window.click_widgetClick a widget by its object name.hover_widgetHover over a widget.type_textType text into a widget or focused widget.press_keySimulate a key press with optional modifiers.find_widgetsList widgets matching a name pattern.get_widget_infoGet detailed widget information.get_app_statusCheck if the application is still running and get diagnostics.wait_for_idleWait for the Qt event queue to settle after actions.close_appClose the running application.Try it
Original README from neatobandit0/qt-pilot
Qt Pilot
An MCP server for headless Qt/PySide6 GUI testing. Enables AI assistants like Claude to visually test and interact with Qt desktop applications.
Repository: github.com/neatobandit0/qt-pilot
Features
- Launch Qt apps headlessly via Xvfb virtual display
- Capture screenshots for visual verification
- Simulate interactions: clicks, hovers, keyboard input
- Widget discovery by object name
- App health monitoring with stderr capture
- Full Qt introspection via QTest and Qt APIs
Installation
From GitHub
git clone https://github.com/neatobandit0/qt-pilot.git ~/.claude/plugins/qt-pilot
pip install -r ~/.claude/plugins/qt-pilot/requirements.txt
Manual Installation
Copy the plugin to your Claude plugins directory:
cp -r qt-pilot ~/.claude/plugins/
Then add to your ~/.claude.json:
{
"mcpServers": {
"qt-pilot": {
"type": "stdio",
"command": "python3",
"args": ["/path/to/qt-pilot/server/main.py"]
}
}
}
Dependencies
pip install mcp PySide6
Also requires Xvfb for headless display:
# Debian/Ubuntu
sudo apt install xvfb
# RHEL/CentOS/Fedora
sudo yum install xorg-x11-server-Xvfb
# macOS (via Homebrew)
brew install xquartz
MCP Tools
`launch_app`
Launch a Qt application headlessly.
# Script mode
launch_app(script_path="/path/to/test_gui.py")
# Module mode
launch_app(module="myapp.main", working_dir="/path/to/project")
`capture_screenshot`
Capture the current window.
capture_screenshot(output_path="/tmp/screenshot.png")
`click_widget`
Click a widget by its object name.
click_widget(widget_name="submit_button", button="left")
`hover_widget`
Hover over a widget.
hover_widget(widget_name="menu_item")
`type_text`
Type text into a widget or focused widget.
type_text(text="hello world", widget_name="search_input")
type_text(text="hello") # Types into currently focused widget
`press_key`
Simulate a key press with optional modifiers.
press_key(key="Enter")
press_key(key="S", modifiers=["Ctrl"]) # Ctrl+S
press_key(key="Tab")
`find_widgets`
List widgets matching a name pattern.
find_widgets(name_pattern="*") # All named widgets
find_widgets(name_pattern="btn_*") # Widgets starting with "btn_"
`get_widget_info`
Get detailed widget information.
get_widget_info(widget_name="submit_button")
# Returns: type, visible, enabled, size, position, text, checked state, etc.
`get_app_status`
Check if the application is still running and get diagnostics.
get_app_status()
# Returns: {"running": true, "exit_code": null, "stderr": "", "display": ":99"}
`wait_for_idle`
Wait for the Qt event queue to settle after actions.
click_widget(widget_name="load_button")
wait_for_idle(timeout=5.0) # Wait for async operations to complete
capture_screenshot()
`close_app`
Close the running application.
close_app()
Requirements for Target Applications
For widget interactions to work, your Qt application must:
Set object names on interactive widgets:
button = QPushButton("Click Me") button.setObjectName("my_button") # Required for widget discoveryUse QApplication (not QCoreApplication)
Show at least one window
Architecture
┌─────────────────────────────┐
│ AI Assistant (Claude) │
└─────────────┬───────────────┘
│ MCP Protocol (stdio)
▼
┌─────────────────────────────┐
│ MCP Server (main.py) │
│ - Tool definitions │
│ - Process management │
└─────────────┬───────────────┘
│ Unix Socket (IPC)
▼
┌─────────────────────────────┐
│ Test Harness (harness.py) │
│ - Runs inside Xvfb │
│ - QTest interactions │
│ - Widget introspection │
├─────────────────────────────┤
│ Your Qt Application │
└─────────────────────────────┘
Example Workflow
# 1. Launch a test app
launch_app(module="myapp.main", working_dir="/path/to/project")
# 2. List available widgets
find_widgets()
# 3. Interact with the UI
click_widget(widget_name="login_button")
wait_for_idle()
# 4. Type into a field
type_text(text="[email protected]", widget_name="email_input")
press_key(key="Tab")
type_text(text="password123", widget_name="password_input")
# 5. Submit and capture result
click_widget(widget_name="submit_button")
wait_for_idle(timeout=3.0)
capture_screenshot(output_path="/tmp/result.png")
# 6. Clean up
close_app()
Troubleshooting
"Widget not found"
- Ensure the widget has
setObjectName()called - Use
find_widgets()to list available widget names