From 8c1c19fc6c3722aa4755eb7f8bfcd8789e383715 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Fri, 16 Oct 2026 21:38:44 +0300 Subject: [PATCH 1/2] feat(dx): headless L2 MCP end to end through querya-mcp (#1487, part 4) headless_app.sh gets --seed-sql (apply SQL to the profile database between two starts) and --run (a command while the app is up). headless_mcp_check.sh seeds a fixture SQLite connection, turns the MCP server on, shares it, and mcp_stdio_check.py speaks MCP to the app through the real querya-mcp. --- .github/workflows/headless-smoke.yml | 24 ++- .../mcp_stdio_check.cpython-314.pyc | Bin 0 -> 11037 bytes scripts/dev/headless_app.sh | 97 +++++++++-- scripts/dev/headless_mcp_check.sh | 58 +++++++ scripts/dev/mcp_stdio_check.py | 161 ++++++++++++++++++ 5 files changed, 321 insertions(+), 19 deletions(-) create mode 100644 scripts/dev/__pycache__/mcp_stdio_check.cpython-314.pyc create mode 100755 scripts/dev/headless_mcp_check.sh create mode 100755 scripts/dev/mcp_stdio_check.py diff --git a/.github/workflows/headless-smoke.yml b/.github/workflows/headless-smoke.yml index eb6785c4..fcc80d8c 100644 --- a/.github/workflows/headless-smoke.yml +++ b/.github/workflows/headless-smoke.yml @@ -2,13 +2,19 @@ name: Headless smoke (L2) # Builds the Linux bundle and runs the real app under Xvfb in portable mode with # scripts/dev/headless_app.sh: it has to start, show a window and stay up. The -# screenshot is kept as an artifact. Not part of ci.yml: a Linux build takes +# screenshot is kept as an artifact. Then the MCP path end to end +# (scripts/dev/headless_mcp_check.sh): the app with its MCP server on, a shared +# fixture connection, and a client reading it through the real querya-mcp. Not part of ci.yml: a Linux build takes # minutes, so this runs when the script or this file changes, weekly, and on # demand. on: pull_request: paths: - scripts/dev/headless_app.sh + - scripts/dev/headless_mcp_check.sh + - scripts/dev/mcp_stdio_check.py + - lib/core/mcp/** + - packages/querya_mcp_bridge/** - .github/workflows/headless-smoke.yml schedule: - cron: "0 4 * * 2" @@ -33,7 +39,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev \ - libsecret-1-dev xvfb xdotool imagemagick + libsecret-1-dev xvfb xdotool imagemagick sqlite3 - name: Setup Flutter uses: subosito/flutter-action@v2 @@ -57,6 +63,20 @@ jobs: --log build/headless/app.log \ --wait 90 --keep 5 + - name: Build querya-mcp + run: | + cd packages/querya_mcp_bridge + dart pub get + dart compile exe bin/querya_mcp.dart -o "$RUNNER_TEMP/querya-mcp" + + - name: MCP end to end under Xvfb + run: | + mkdir -p build/headless + scripts/dev/headless_mcp_check.sh \ + --mcp "$RUNNER_TEMP/querya-mcp" \ + --bundle build/linux/x64/release/bundle \ + --log build/headless/mcp-app.log + - name: Keep the screenshot and the log if: always() uses: actions/upload-artifact@v4 diff --git a/scripts/dev/__pycache__/mcp_stdio_check.cpython-314.pyc b/scripts/dev/__pycache__/mcp_stdio_check.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18663043562a243d41362bdd3939100cbef01a29 GIT binary patch literal 11037 zcmbt4X>1$UnQzG9O;Myo>#(kobwqM>*_LBRQtil=gY!7wOg9_zHod`&P?T^i$g%cS_nqT{U zj~tR#lYy`rZrtFW{+D26to7Cf#W#ym`NFnBgs256ogm zD>GB_nnzaB<4P0EFq|bfk7w%Ogmt9ZCj1nZA3i?HiQxdJMAFT)6p2kub1z(uiPANp zb;cikp@mbXMed4ljT1P3C@6*%PLBDfIYH)*hlE%_uq06HIC#pjLJ*SJ;$nH%l(oP@``B}RIO`>oay zQ4Gj*Ga(wqGf#@Ra5xyA;;sahX$lttDT@+42iPrLwM7W%G0m~;2@}Qv1oIfwf`zS z`6GbKjxJ7wt*>!(AQu&6SqyY@`??VOcuMYq06PTQtRsFPaJCrX3E#K=394B#o?#sV zOzmZ|%mpJVf&9QqI$$cjm)OaE)~m~Yci{Kj_$gQw#={60hYVn-7MaER#ge48kuZ{mmI(&lvb(k6w=AcOg9=QSQKZeb0x**Uz`uYQ zGY>p`D08k+*S3N1np3ho3pg`)nhmM}HAYx9LKBH8v=t>uWfk$NqM9aS7vx}Ee1v=m zbTRz4CZXzKW&zkyc-me@vxm!lfsxrm=6ZLUt!d~wa+d*)myjb>Bftb)uCh@n;=jwN ztSp8u$SB&gQUMMy_k@I*$$)S~txa=7o6i>v2Nj>MttS-m3nBRkJfw%z!34P)s*h($ zqVsn5t?u=T*42vE#lR1PcZ2I~M^@X8tW_M93Sr1yooZABX-WhH1q5*>5*}%`s8%^P ziI9LUQw^gL5I_*vQ=|P-1;Szq1GK(iAPu@jnMTbH8d5cS=p$lYkd$VfRE5L!;#E;b z+FfYOoJ9j&E!=jNN*E<09|Gi??&@`S{afz(jRNOf|Es%i*W9Z4=iNW4`BBYgan;SE zH;yj4mo7gv=yr9@vEQ&fFp{!zc(8AjltE+LG`rT#WpA0wHj9t0un+Z&WrTTmWb9Ys zZ>Q+F9}y;GNT8{C>$mVQ3v9O&-F6bIos`~qMhHM9nqj$Z1qD�G>QX$$tL*ZQGWI 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 exit 2; } @@ -41,6 +52,8 @@ while [ $# -gt 0 ]; do --keep) keep_s="${2:-}"; shift 2 ;; --log) log_out="${2:-}"; shift 2 ;; --keep-data) keep_data=1; shift ;; + --seed-sql) seed_sql="${2:-}"; shift 2 ;; + --run) run_cmd="${2:-}"; shift 2 ;; -h | --help) usage; exit 0 ;; *) die "unknown option: $1 (see --help)" ;; esac @@ -54,6 +67,10 @@ fi [ -n "$bundle" ] || die "no bundle found; build one (flutter build linux) or pass --bundle DIR" [ -x "$bundle/querya_desktop" ] || die "$bundle/querya_desktop is missing or not executable" command -v Xvfb >/dev/null 2>&1 || die "Xvfb is not installed (apt-get install xvfb)" +if [ -n "$seed_sql" ]; then + [ -f "$seed_sql" ] || die "--seed-sql: $seed_sql is not a file" + command -v sqlite3 >/dev/null 2>&1 || die "--seed-sql needs the sqlite3 command (apt-get install sqlite3)" +fi if [ -n "$screenshot" ] && ! command -v import >/dev/null 2>&1; then die "--screenshot needs ImageMagick's 'import' (apt-get install imagemagick)" fi @@ -125,13 +142,32 @@ cp -a "$bundle" "$work/bundle" || die "could not copy $bundle" export QUERYA_PORTABLE=1 export NO_AT_BRIDGE=1 +# Own endpoint file, so that a second app on the machine (or a leftover one) +# can never be the one querya-mcp talks to. +export QUERYA_MCP_ENDPOINT="$work/mcp-endpoint.json" + runner=() if command -v dbus-run-session >/dev/null 2>&1; then runner=(dbus-run-session --) fi -${runner[@]+"${runner[@]}"} "$work/bundle/querya_desktop" >"$work/app.log" 2>&1 & -app_pid=$! -say "started querya_desktop (pid $app_pid)" + +start_app() { + ${runner[@]+"${runner[@]}"} "$work/bundle/querya_desktop" >>"$work/app.log" 2>&1 & + app_pid=$! + say "started querya_desktop (pid $app_pid)" +} + +stop_app() { + [ -n "$app_pid" ] || return 0 + kill -TERM "$app_pid" 2>/dev/null + for _ in $(seq 1 20); do + kill -0 "$app_pid" 2>/dev/null || break + sleep 0.5 + done + kill -KILL "$app_pid" 2>/dev/null + wait "$app_pid" 2>/dev/null + app_pid="" +} have_window_tool=0 if command -v xdotool >/dev/null 2>&1 || command -v xwininfo >/dev/null 2>&1; then @@ -147,21 +183,37 @@ window_up() { fi } -deadline=$((SECONDS + wait_s)) -shown=0 -while [ "$SECONDS" -lt "$deadline" ]; do - kill -0 "$app_pid" 2>/dev/null || fail_with_log "the app exited before showing a window" +wait_for_window() { + local deadline=$((SECONDS + wait_s)) shown=0 + while [ "$SECONDS" -lt "$deadline" ]; do + kill -0 "$app_pid" 2>/dev/null || fail_with_log "the app exited before showing a window" + if [ "$have_window_tool" -eq 1 ]; then + if window_up; then shown=1; break; fi + fi + sleep 1 + done if [ "$have_window_tool" -eq 1 ]; then - if window_up; then shown=1; break; fi + [ "$shown" -eq 1 ] || fail_with_log "no window after ${wait_s}s" + say "window is up" + else + say "no xdotool / xwininfo: only checking that the process stays alive" fi - sleep 1 -done +} -if [ "$have_window_tool" -eq 1 ]; then - [ "$shown" -eq 1 ] || fail_with_log "no window after ${wait_s}s" - say "window is up" -else - say "no xdotool / xwininfo: only checking that the process stays alive" +start_app +wait_for_window + +if [ -n "$seed_sql" ]; then + # The first start created and migrated the profile database. Let it settle, + # stop the app (it holds the file open), seed, and start again. + sleep 2 + stop_app + db="$(find "$work/bundle" -name querya.db -print -quit 2>/dev/null)" + [ -n "$db" ] || fail_with_log "the profile database querya.db was not created" + sqlite3 "$db" <"$seed_sql" || fail_with_log "could not apply $seed_sql to $db" + say "seeded $(basename "$seed_sql") into $db" + start_app + wait_for_window fi end=$((SECONDS + keep_s)) @@ -180,5 +232,16 @@ if [ -n "$screenshot" ]; then fi fi +if [ -n "$run_cmd" ]; then + export QUERYA_HEADLESS_DB + QUERYA_HEADLESS_DB="$(find "$work/bundle" -name querya.db -print -quit 2>/dev/null)" + export QUERYA_HEADLESS_DATA="$work/bundle/QueryaData" + say "running: $run_cmd" + if ! bash -c "$run_cmd"; then + fail_with_log "--run command failed" + fi + kill -0 "$app_pid" 2>/dev/null || fail_with_log "the app died while the command ran" +fi + say "ok" exit 0 diff --git a/scripts/dev/headless_mcp_check.sh b/scripts/dev/headless_mcp_check.sh new file mode 100755 index 00000000..1167e047 --- /dev/null +++ b/scripts/dev/headless_mcp_check.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# L2 headless MCP end to end: the real app under Xvfb with its MCP server on, +# one fixture SQLite connection shared, and a client talking MCP to it through +# the real `querya-mcp` bridge. +# +# scripts/dev/headless_mcp_check.sh --mcp PATH [--bundle DIR] [--log FILE] +# +# --mcp PATH the querya-mcp executable (dart compile exe +# packages/querya_mcp_bridge/bin/querya_mcp.dart -o querya-mcp) +# --bundle DIR built bundle, as for headless_app.sh +# --log FILE the app's output, as for headless_app.sh +# +# Needs what headless_app.sh needs, plus sqlite3 and python3. Exit codes are +# headless_app.sh's: 0 all checks passed, 1 something failed, 2 usage / tools. +set -euo pipefail + +here="$(cd "$(dirname "$0")" && pwd)" +mcp="" +pass=() +while [ $# -gt 0 ]; do + case "$1" in + --mcp) mcp="${2:-}"; shift 2 ;; + --bundle | --log) pass+=("$1" "${2:-}"); shift 2 ;; + -h | --help) sed -n '2,/^set -euo/p' "$0" | sed '$d' | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "headless_mcp_check: unknown option: $1" >&2; exit 2 ;; + esac +done +[ -x "$mcp" ] || { echo "headless_mcp_check: --mcp must point at the querya-mcp executable" >&2; exit 2; } +command -v python3 >/dev/null 2>&1 || { echo "headless_mcp_check: python3 is missing" >&2; exit 2; } +command -v sqlite3 >/dev/null 2>&1 || { echo "headless_mcp_check: sqlite3 is missing" >&2; exit 2; } + +tmp="$(mktemp -d "${TMPDIR:-/tmp}/querya-mcp-check.XXXXXX")" +trap 'rm -rf "$tmp"' EXIT + +# The database the model will query. +sqlite3 "$tmp/fixture.db" <<'SQL' +CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price REAL NOT NULL); +INSERT INTO items (name, price) VALUES ('alpha', 10), ('beta', 20), ('gamma', 30); +SQL + +# What the settings page would store: the connection, the server on, the +# connection shared. A second connection stays unshared, so the check can see +# that sharing is opt-in. +cat >"$tmp/seed.sql" < Date: Fri, 16 Oct 2026 21:45:40 +0300 Subject: [PATCH 2/2] docs(dev): L2 MCP check, seed/run options, what each level needs, extension-author pieces (#1487) --- docs/dev-containers.md | 64 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/docs/dev-containers.md b/docs/dev-containers.md index 1863a220..56bbd796 100644 --- a/docs/dev-containers.md +++ b/docs/dev-containers.md @@ -130,6 +130,15 @@ command and each runnable in a container or on a CI runner | L0 | unit, widget, e2e and guard tests | `flutter test --exclude-tags=golden` | | L1 | an extension against a real database, no GUI | `querya-ext-tester ./ext --connect '{...}' --sandbox` (see *Testing an extension*) | | L2 | the real app under a virtual display, in portable mode | `scripts/dev/headless_app.sh` | +| L2 + MCP | the same, with the MCP server on and a client reading a fixture through `querya-mcp` | `scripts/dev/headless_mcp_check.sh --mcp PATH` | + +What each level needs from the database stack (start only that, by service name): + +| Level | Containers | +|-------|------------| +| L0 | none; the live MySQL test needs `docker compose --profile live-tests up -d --wait mysql-live` | +| L1 | the database the extension talks to, e.g. `docker compose up -d --wait postgres` (or `clickhouse`, `redis`, `mongo`) | +| L2, L2 + MCP | none: the fixture is a throw-away SQLite file | L1 needs a tester built from 0.4.19 or newer: older ones exited 0 whatever the result. @@ -157,9 +166,36 @@ The [Headless smoke (L2)](../.github/workflows/headless-smoke.yml) workflow buil the Linux bundle and runs the script (on changes to it, weekly, and on demand), keeping the screenshot and the log as an artifact. -Whether L2 should grow a real "headless Querya" (core services without a window, -so that `querya-mcp` does not need a running Desktop) is left open until this -exists. +Two more options let a script drive the running app: + +- `--seed-sql FILE` applies SQL to the profile database (`querya.db`). The app + starts once to create and migrate it, is stopped, `sqlite3` applies the file, + and the app starts again. Use the same `app_settings` keys and tables the UI + writes, so a test needs no code in the app. +- `--run COMMAND` runs a command once the window is up and keeps the app alive + meanwhile; its exit code becomes the script's. The command gets + `QUERYA_MCP_ENDPOINT` (this run's own endpoint file, so no other instance can + answer), `QUERYA_HEADLESS_DB` and `QUERYA_HEADLESS_DATA`. + +### L2 + MCP: `scripts/dev/headless_mcp_check.sh` + +```bash +dart compile exe packages/querya_mcp_bridge/bin/querya_mcp.dart -o build/querya-mcp +scripts/dev/headless_mcp_check.sh --mcp build/querya-mcp --bundle build/linux/x64/release/bundle +``` + +It creates a fixture SQLite database with two connections, turns the MCP server +on and shares only one of them (the same settings keys the Preferences page +writes), starts the app under Xvfb and runs `scripts/dev/mcp_stdio_check.py`: a +small MCP client that talks to the real `querya-mcp` and checks `initialize`, +`tools/list`, that only the shared connection is listed, the table, its columns +and data, that a `DELETE` is refused and changes nothing, and that the unshared +connection is refused. It needs `sqlite3` and `python3` besides what L2 needs. +The same step runs in the Headless smoke workflow. + +A window under Xvfb is all the MCP server needs, so there is no separate +"headless Querya" mode: `querya-mcp` still needs a running Desktop, and the +virtual display provides it. ## Run the app in the container @@ -245,11 +281,27 @@ See [extension-testing.md](extension-testing.md) for the options, reports and the GitHub Action. The exit code is 0 on success, 1 when a check failed and 2 on a usage error. +## For extension authors + +You do not need this repository to write a database-driver extension. Start from +[QueryaHub/extension-template](https://github.com/QueryaHub/extension-template) +(a Rust driver with a dev container, a test database and CI). The pieces it uses: + +- the **Dev Container Feature** `ghcr.io/queryahub/features/querya-ext-tester` + installs `querya-ext-tester` from a release, checked against `SHA256SUMS.txt` + (see [extension-testing.md](extension-testing.md#dev-container-feature)); +- **test database images** `ghcr.io/queryahub/querya-testdb-{postgres,mysql,mongo,clickhouse,redis}:` + are the `docker/` seeds baked into the official images, no bind mounts. + Built from `docker//Dockerfile.testdb`; a new `Dockerfile.testdb` adds a + database to the build by itself. + ## Known gaps -- **The extension-author container** (Dev Container Feature, test database - images, template repository, registry CI) is not done yet; it is tracked in - [#1487](https://github.com/QueryaHub/Querya-Desktop/issues/1487). +- **The extension registry** (registry CI and `StaticRegistryRepository`) and the + release hooks that publish the Feature and the images are not done yet; they + are tracked in [#1487](https://github.com/QueryaHub/Querya-Desktop/issues/1487). + Until then the images are published by hand with the *Test database images* + workflow. ## Troubleshooting