rename binary name to gammaray-mcp-bridge

This commit is contained in:
2026-07-08 13:26:08 +08:00
parent 83df5286c5
commit a6335429e5
10 changed files with 114 additions and 68 deletions

85
PLAN.md
View File

@@ -1,19 +1,19 @@
# QML SceneGraph MCP Bridge — Plan
# GammaRay MCP Bridge — Plan
## Goal
Build an MCP server that exposes QML SceneGraph introspection data (from GammaRay's probe) to LLMs for assisted debugging. The bridge is a **GammaRay client**: it connects to a probe injected into the target Qt/QML app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls.
Build an MCP server that exposes GammaRay probe introspection data (QML SceneGraph, QML items, and Qt Widgets) to LLMs for assisted debugging. The bridge is a **GammaRay client**: it connects to a probe injected into the target Qt app, reads the same models the GammaRay GUI uses, and translates them into MCP JSON-RPC tool calls.
## Architecture
```
Target Qt/QML App
│ GammaRay probe injected (preload/attach), loads quickinspector plugin
│ GammaRay probe injected (preload/attach), loads quickinspector + widgetinspector plugins
│ GammaRay binary protocol over TCP/local socket
│ (common/protocol.h — QDataStream-based, NOT JSON-RPC)
QML SceneGraph MCP Bridge (new project, GPL-2.0-or-later)
GammaRay MCP Bridge (new project, GPL-2.0-or-later)
│ Links gammaray_client + gammaray_common (installed from source)
│ Uses ClientConnectionManager to connect, ObjectBroker to get models
│ Connection is lazy: bridge starts without a probe, MCP client calls
@@ -99,7 +99,7 @@ Caveats of the FetchContent approach:
```cmake
cmake_minimum_required(VERSION 3.16)
project(QmlSceneGraphMcpBridge LANGUAGES CXX)
project(GammaRayMcpBridge LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20) # qtmcp requires C++20
set(CMAKE_AUTOMOC ON)
@@ -119,7 +119,7 @@ set(QT_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(QT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(qtmcp)
add_executable(qml-sg-mcp-bridge
add_executable(gammaray-mcp-bridge
src/main.cpp
src/gammaray_session.cpp # wraps ClientConnectionManager + ObjectBroker
src/gammaray_session.h
@@ -127,7 +127,7 @@ add_executable(qml-sg-mcp-bridge
src/scenegraph_tools.h
)
target_link_libraries(qml-sg-mcp-bridge PRIVATE
target_link_libraries(gammaray-mcp-bridge PRIVATE
gammaray_client # VERIFIED: no GammaRay:: namespace
gammaray_common
Qt6::Core
@@ -139,7 +139,7 @@ target_link_libraries(qml-sg-mcp-bridge PRIVATE
)
# qtmcp shared libs + stdio plugin live in build tree; make runtime find them:
set_target_properties(qml-sg-mcp-bridge PROPERTIES
set_target_properties(gammaray-mcp-bridge PROPERTIES
BUILD_RPATH "$<TARGET_FILE_DIR:Qt6::McpServer>;$<TARGET_FILE_DIR:Qt6::McpCommon>"
)
```
@@ -152,7 +152,7 @@ cmake --build build
# Run with probe libs + qtmcp runtime libs on the path:
LD_LIBRARY_PATH=$(pwd)/install-prefix/lib QT_QPA_PLATFORM=offscreen \
QT_PLUGIN_PATH=$PWD/build/lib/x86_64-linux-gnu/qt6/plugins \
./build/qml-sg-mcp-bridge --connect tcp://127.0.0.1:11732
./build/gammaray-mcp-bridge --connect tcp://127.0.0.1:11732
```
### ❗ MUST use QApplication, not QCoreApplication
@@ -173,7 +173,7 @@ int main(int argc, char **argv) {
// ... parse --connect <url> ...
QMcpServer server(QStringLiteral("stdio")); // backend name in ctor, no default
server.setInstructions("QML SceneGraph introspection via GammaRay");
server.setInstructions("QML SceneGraph & Qt Widget introspection via GammaRay");
auto *tools = new SceneGraphTools(&server); // holds GammaRay client connection
server.registerToolSet(tools, {
@@ -505,7 +505,7 @@ When in doubt, these files have the ground truth:
-**Step 1 done**: GammaRay 3.4.0 built + installed to `install-prefix/`. Protocol version 38.
-**Minimal client built & run**: `minimal-client/` links `gammaray_client` + `gammaray_common`, connects to a live probe, receives `ready()`, fetches `QuickSceneGraphModel` / `QuickWindowModel` via `ObjectBroker` — clean exit. Proves the client ABI + protocol path is viable for the bridge.
-**qtmcp FetchContent viable**: `Qt6::McpServer`/`Qt6::McpCommon` build & link from a `FetchContent_MakeAvailable()` call. No separate clone/install.
-**Bridge scaffold built & working end-to-end** (`bridge/`): `qml-sg-mcp-bridge` links GammaRay client + qtmcp, runs as a stdio MCP server. Smoke test (`/tmp/opencode/smoke_test.py`) against a live probe confirms:
-**Bridge scaffold built & working end-to-end** (`bridge/`): `gammaray-mcp-bridge` links GammaRay client + qtmcp, runs as a stdio MCP server. Smoke test against a live probe confirms:
- `initialize` → serverInfo + capabilities + protocolVersion `2024-11-05`
- `tools/list` → 15 tools registered: `connectProbe`, `connectProbeDefault`, `disconnectProbe`, `getMaterialProperties`, `getMaterialShaders`, `getNodeAdjacency`, `getNodeVertices`, `getShaderSource`, `listQuickWindows`, `listScenegraphNodes`, `probeStatus`, `selectQuickWindow`, `selectScenegraphNode`, `setRenderMode`, `setSlowMode`
- `probeStatus` (before connect) → `{"ready":false,"state":"disconnected","url":""}`
@@ -550,39 +550,38 @@ cmake --build build
```
`run.sh` sets `LD_LIBRARY_PATH` (GammaRay + qtmcp libs) and `QT_PLUGIN_PATH` (qtmcp `mcpserverbackend/libqmcpserverstdio.so`) and `QT_QPA_PLATFORM=offscreen`.
## Qt Widget support (planned)
## Qt Widget support (implemented)
GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构(`PropertyController``SelectionModelClient``RemoteModel`),大部分 QML 工具的模式直接复用。
GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构(`PropertyController``SelectionModelClient``RemoteModel`),大部分 QML 工具的模式直接复用。
### 可提供的 widget 工具
### 已实现的 widget 工具
**导航(与 QML 类似,模型名不同)**
- `listWidgets()` — 遍历 `com.kdab.GammaRay.WidgetTree` 模型,返回 widget 层次树
- `selectWidget(address)` — 通过 `SelectionModelClient` 选取 widget触发 `PropertyController` 填充子模型属性、attribute 等)
- `listTopLevelWindows()`(已有 `listQuickWindows`,可在 widget 应用中继续使用)
- `listWidgets()` — 遍历 `com.kdab.GammaRay.WidgetTree` 模型,返回 widget 层次树
- `selectWidget(address)` — 通过 `SelectionModelClient` 选取 widget触发 `PropertyController` 填充子模型属性、attribute 等)
**属性/attribute与 QML 共享读取实现)**
- `getWidgetProperties(address)` — 读取同一套 `AggregatedPropertyModel`(通过 `PropertyController`模型对象名 `com.kdab.GammaRay.Widget.<address>.properties``getItemProperties` 共享实现代码
- `getWidgetAttributes(address)` — 读取 `widgetAttributeModel``AttributeModel<QWidget, Qt::WidgetAttribute>`),列出 Qt::WidgetAttribute 值
- `getWidgetProperties(address)` — 读取同一套 `AggregatedPropertyModel`(通过 `PropertyController`),与 `getItemProperties` 共享实现代码
- `getWidgetAttributes(address)` — 读取 `widgetAttributeModel``AttributeModel<QWidget, Qt::WidgetAttribute>`),列出 Qt::WidgetAttribute 值
**视觉/分析(新功能,需 RemoteViewInterface**
- `grabWidget(address)` — 通过 `WidgetRemoteView``com.kdab.GammaRay.WidgetRemoteView`)抓取 widget 截图,异步图像传输
- `analyzePainting(address)` — 触发 `PaintAnalyzer` 捕获绘制操作
- `exportWidgetAsSvg(address)` — 通过 `WidgetInspectorInterface` 导出 SVG
- `exportWidgetAsUiFile(address)` — 导出 Qt Designer .ui 文件
- `grabWidget(address)` — 通过 `WidgetRemoteView``com.kdab.GammaRay.WidgetRemoteView`)抓取 widget 截图,异步图像传输
- `analyzePainting(address)` — 触发 `PaintAnalyzer` 捕获绘制操作
- `exportWidgetAsSvg(address)` — 通过 `WidgetInspectorInterface` 导出 SVG
- `exportWidgetAsUiFile(address)` — 导出 Qt Designer .ui 文件
**控制**
- `setInputRedirection(enabled)` — 允许将鼠标/键盘事件转发到目标 widget
- `setInputRedirection(enabled)` — 允许将鼠标/键盘事件转发到目标 widget
### 实现策略
| 难度 | 工作项 | 说明 |
|---|---|---|
| | `listWidgets()` / `selectWidget()` | 直接复用 `listQuickItems()` / `selectQuickItem()` 的代码模式,模型名改为 `com.kdab.GammaRay.WidgetTree` |
| | `getWidgetProperties()` | 与 `getItemProperties()` 完全一样,都读 `AggregatedPropertyModel`提取共享函数 |
| | `getWidgetAttributes()` | 需要读取 `widgetAttributeModel``PropertyController` 子模型),模式与 `getMaterialProperties()` 类似 |
| | `WidgetInspectorInterface` 代理 | IID `com.kdab.GammaRay.WidgetInspector`,用于 `export*`/`setInputRedirection` 等方法,类似 `QuickInspectorProxy` |
| | `grabWidget()` / `analyzePainting()` | 需要 `RemoteViewInterface``TransferImage` 异步路径,模式与 `MaterialExtensionInterface``gotShader` 信号类似 |
| ✅ 已完成 | `listWidgets()` / `selectWidget()` | 直接复用 `listQuickItems()` / `selectQuickItem()` 的代码模式,模型名改为 `com.kdab.GammaRay.WidgetTree` |
| ✅ 已完成 | `getWidgetProperties()` | 与 `getItemProperties()` 完全一样,都读 `AggregatedPropertyModel`提取共享函数 `readAggregatedPropertyModel()` |
| ✅ 已完成 | `getWidgetAttributes()` | 读取 `widgetAttributeModel``PropertyController` 子模型),模式与 `getMaterialProperties()` 类似 |
| ✅ 已完成 | `WidgetInspectorInterface` 代理 | IID `com.kdab.GammaRay.WidgetInspector`,用于 `export*`/`setInputRedirection` 等方法,类似 `QuickInspectorProxy` |
| ❌ 未实现 | `grabWidget()` / `analyzePainting()` | 需要 `RemoteViewInterface``TransferImage` 异步路径,模式与 `MaterialExtensionInterface``gotShader` 信号类似 |
### 与 QML 工具的关键区别
@@ -594,18 +593,20 @@ GammaRay 的 widget inspector 与 quick inspector 共享同一套底层架构(
## Next steps
### QML SceneGraph 工具(当前阶段
### 已完成的阶段
-**QML SceneGraph 工具**listScenegraphNodes、getNodeVertices、getNodeAdjacency、getMaterialShaders、getShaderSource、getMaterialProperties、setRenderMode、setSlowMode
-**QML Item 属性工具**listQuickItems、selectQuickItem、getItemProperties
-**Qt Widget 工具**listWidgets、selectWidget、getWidgetProperties、getWidgetAttributes
-**跨平台保护**QuickInspector 和 WidgetInspector 的代理注册均已添加条件守卫,避免在未加载对应插件的 target 进程中崩溃
-**测试套件**27 个 QML 测试 + 7 个 widget 测试,共 34 个集成测试
### 剩余工作
1. **Test `getShaderSource` async path end-to-end** — needs a QML app with real shaders (current test app uses software renderer where GeometryNodes have `nullptr` geometry).
2. **Test geometry/material tools against a QML app with real QSGGeometry data** — current test app `relayout.qml` uses software renderer so `hasGeometry: false` and empty sub-models are correct behavior.
3. **Implement `grabTexture(objectId)`**`TextureExtension` async image grab via `RemoteViewInterface` `TransferImage`. Follows the `MaterialExtensionInterface` proxy pattern.
4. **Fix `listScenegraphNodes` first-call fill stability** — single-call instead of caller needing a second call.
5. **Build `--connect` startup flag integration** with systemd service for auto-attach during development.
### Qt Widget 支持(未来阶段)
1. 创建 widget_tools.{h,cpp},注册 `listWidgets` / `selectWidget` / `getWidgetProperties` / `getWidgetAttributes` MCP 工具
2.`scenegraph_tools.cpp` 提取共享的 `AggregatedPropertyModel` 读取函数,供 QML 和 widget 工具共用
3. 创建 `WidgetInspectorInterface` 代理(类似 `QuickInspectorProxy`),处理 `export*`/`setInputRedirection`
4. 实现 `RemoteViewInterface` 异步图像传输路径grabWidget/analyzePainting
5. 添加 widget 测试应用(简单 QWidget 窗口 + 按钮/布局),编写 widget 工具的集成测试
2. **Test geometry/material tools against a QML app with real QSGGeometry data** — current test app uses software renderer so `hasGeometry: false` and empty sub-models are correct behavior.
3. **Implement `grabWidget(objectId)`**`RemoteViewInterface` async image grab via `TransferImage`. Follows the `MaterialExtensionInterface` proxy pattern.
4. **Implement `grabTexture(objectId)`**`TextureExtension` async image grab via `RemoteViewInterface` `TransferImage`.
5. **Implement `analyzePainting()` / `exportWidgetAsSvg()` / `exportWidgetAsUiFile()`** — via `WidgetInspectorInterface` proxy.
6. **Fix `listScenegraphNodes` first-call fill stability** — single-call instead of caller needing a second call.
7. **Build `--connect` startup flag integration** with systemd service for auto-attach during development.