Skip to main content
Glama
abranson
by abranson
README.md
# Sailfish Devel MCP

Host-side Model Context Protocol server for Sailfish OS development workflows.

This first iteration is dependency-free: it implements MCP over stdio directly
with Python's standard library and exposes typed tools around commands that are
easy to get wrong during day-to-day Sailfish work.

## Scope

The server currently exposes tools for:

- Sailfish device access over SSH
- device prerequisite checks and opt-in installation of missing optional tools
- defaultuser session-bus calls
- Lipstick screenshots
- touchscreen discovery and tap/swipe injection
- combined screenshot, touchscreen discovery, and touch injection workflows
- topmost window PID lookup
- process map inspection
- journal log reads
- RPM copy/install on a device
- system and user service management
- short and detached user-session command execution with the configured D-Bus environment
- desktop-entry application launch as the configured session user
- Sailfish Browser launch/debug helpers
- preflighted Docker/mb2 RPM builds through the vendored `build-sailfishos` helper
- cancellable local and remote Android/AppSupport build jobs
- installed SDK repository metadata refresh
- OBS result and build-log lookup through `osc`
- repository status and search under the configured git root
- RPM spec metadata summaries
- QML translation search and Sailfish ternary translation checks

The committed configuration template defaults are deliberately generic.
Device tools default to the placeholder SSH target `root@device`, the Sailfish
user-session bus at `/run/user/100000/dbus/user_bus_socket`, `~/git` as the
local source root, `~/OBS` as the OBS checkout root, and the vendored build
helper at
`src/sailfish_devel_mcp/vendor/build_sailfishos.py`. Put a config file at
`~/.config/sailfish-devel-mcp/config.json` or pass `--config` to provide your
real device and OBS settings. Device entries can also carry the preferred user,
architecture, and configured release label. If an installed SDK is available,
set `paths.local_sdk` to its `sdk-chroot` path; builds will use it when it has a
target matching the requested release and architecture, otherwise they fall
back to a matching tag in the third-party coderus Docker mirror. Neither a
configured device label nor mirror tag availability independently establishes
the current official SailfishOS release or SDK target.

For a device attached over USB, pass `usb` as the device argument to use the
configured default device, or `usb:<name>` when the user supplies a device
name. The MCP keeps that device's user and session metadata but connects to
`192.168.2.15` without checking or storing SSH host keys. It trusts the supplied
name and does not resolve it through DNS, so no separate validation connection
is made.

Generic Python configuration code is tracked and works in a clean checkout.
Keep all machine-specific values in `~/.config/sailfish-devel-mcp/config.json`
or its environment overrides. No Python bootstrap copy is needed.

## Host prerequisites

Use a Linux host with Python **3.10 or newer**. The server has no third-party
Python runtime dependencies. Detached job supervision uses POSIX process groups
and Linux `/proc`. The following programs are needed only for the workflows
listed; installing every optional tool is unnecessary.

| Workflow | Host requirements |
| --- | --- |
| Run from the checkout | `python3`, POSIX `sh`, `dirname`, `mkdir`, `touch`, and `date` for the logging wrapper |
| Install the Python package | `pip` and setuptools >=61 for packaging; `python3 -m pip install .` installs both CLI entry points |
| Device commands and deployment | OpenSSH `ssh`; `scp` for RPM copies and screenshot downloads; SSH credentials and a reachable device |
| Repository inspection | `git`; `rg` (ripgrep) for searches, with `grep` as a repository-search fallback |
| QML translation search | `rg`; the ternary-comment checker itself needs only Python |
| RPM builds and SDK refresh | A running Docker engine accessible to your user, `git`, and `bash`; ACL tools (`setfacl`) for Docker builds that need container write access |
| Installed SDK builds | A configured Sailfish Platform SDK `sdk-chroot`, registered architecture targets, and an appropriate Docker wrapper image; `sdk-manage`, `mb2`, `sb2`, `zypper`, RPM and build dependencies live inside the SDK |
| OBS results and logs | `osc`, with the selected server and credentials configured in your `.oscrc` |
| Android/AppSupport builds | `ssh`; a separate configured Linux build host with its Android tree and build dependencies |

Run the MCP tool `sailfish_doctor` to report local command availability, helper
provenance and installed SDK targets. It does not connect to devices, test Docker
daemon access, or install host packages. Install host tools through your Linux
distribution; the package names differ between distributions. Check RPM build
requirements in each project's spec file and Android requirements in its tree's
documentation.

The remote Android build host also needs `sh`, the selected `bash` or `sh`,
`nohup`, `setsid`, `base64`, `date`, `mkdir`, `chmod`, `cat`, `sed`, `cut`,
`sort`, `tail`, and `sleep`, plus Linux `/proc`. These support job bookkeeping
and cancellation; the build command may require additional tools.

## Device prerequisites and optional setup

Enable Sailfish developer mode and SSH access before using device tools. SSH
authentication and root access must be configured by the device owner. Set the
device's `username` and session-bus address in the local configuration: older
devices can use `nemo` rather than `defaultuser`, and the user ID can differ.
Start the graphical user session before checking Lipstick or session-bus tools.
See the [Sailfish developer-mode instructions](https://docs.sailfishos.org/Support/Help_Articles/Enabling_Developer_Mode/)
and [D-Bus reference](https://docs.sailfishos.org/Reference/Core_Areas_and_APIs/D-Bus_APIs/).

| Feature passed to `sailfish_device_setup` | Device requirements |
| --- | --- |
| `commands` | `sh` and `env`; arbitrary commands need their own executables |
| `diagnostics` | `journalctl`, `cat`, `dbus-send`, `env`; the configured session bus and Lipstick for topmost PID queries; readable `/proc/<pid>/maps` for process maps |
| `screenshot` | `dbus-send`, `env`, `sg`, `stat`, `install`, `getent`, `dirname`; Lipstick's screenshot API and access to the `privileged` group |
| `touch` | Python >=3.6 with standard-library `os`, `re`, `struct`, `sys` and `time`; `sh`, `cat`; readable `/proc/bus/input/devices` and permission to write the selected `/dev/input/event<N>` |
| `input_trace` | Optional `evdev_trace` from `mce-tools`, plus `sh`, `cat`, `sleep`; `timeout` is used when present, with a bounded fallback otherwise |
| `app_launch` | `gio`, `getent`, `cut`, `nohup`, `mkdir`, `mktemp`, `mv`, `cat`, `tail`, `sleep`, `sh`, `env`, and either `runuser` or `su`; installed desktop entries and a graphical user session |
| `browser` | `systemctl`, `pidof`, `ps`, `awk`, `grep`, `invoker`, `sailfish-browser`, `dbus-send`, `sleep`, `sh`, `env`, and either `runuser` or `su`; Sailfish Browser and its booster/session environment |
| `install` | `pkcon` for the default installer or `rpm` when explicitly selected; an SSH file-transfer subsystem compatible with the host's `scp`; package-install privileges |
| `services` | `systemctl`, `env`; permissions for the selected system or user service |

The prerequisite probe itself requires `sh` and `id`. `dbus-send` is supplied by
[`dbus`](https://github.com/sailfishos/dbus/blob/master/rpm/dbus.spec), and `gio`
is supplied by [`glib2`](https://github.com/sailfishos/glib2/blob/master/rpm/glib2.spec)
in current Sailfish packaging. Base OS tools, missing applications, permissions,
and session configuration require manual repair. Command availability and a
session socket do not prove that every device API or touch input works.

Call `sailfish_device_setup` with `install_missing` omitted or `false` to check
a device without installing anything:

```json
{"device": "phone", "features": ["touch", "app_launch"]}
```

The result lists discovered executables, missing requirements, session issues,
and optional packages that could be installed. The default features are
`commands`, `diagnostics`, `screenshot`, `touch`, `app_launch`, `install`, and
`services`. Select `input_trace` or `browser` explicitly when needed.

To install supported missing optional tools, explicitly opt in:

```json
{"device": "phone", "features": ["touch", "input_trace"], "install_missing": true}
```

Setup requires a root SSH login and `pkcon` or `zypper`. It installs only missing
`python3-base` (for Python) and `mce-tools` (for `evdev_trace`) from the device's
existing repositories, then checks again. Package dependencies are resolved by
the device package manager. It does not add repositories, enable SSH, change
accounts or permissions, or repair base OS packages. Package availability can
vary with the configured Sailfish release. The mappings follow the
[Python spec](https://github.com/sailfishos/python3/blob/master/rpm/python3.spec)
and [Sailfish touch documentation](https://docs.sailfishos.org/Reference/Architecture/#touch).

Installation returns a detached job by default. Check `sailfish_build_status`
or use `sailfish-devel-jobs wait --kind build --job-id JOB_ID`; the completed
status includes the final report under `device_setup` and its JSON `result_path`.
`sailfish_build_cancel` requests
cancellation, but interrupting SSH does not roll back a package transaction.
`wait=true` requests synchronous operation for controlled diagnostics.

## Trust and privacy

This is local development tooling intended for a trusted MCP client running on
your own workstation. It communicates over stdio and has no network listener.
The client invokes tools using your host account, SSH credentials and OBS
credentials. Tool annotations describe effects; they do not enforce permissions
or provide an approval layer.

Device command tools can execute arbitrary programs. Their default identity is
the SSH login account, often root; setting the session environment does not
change that identity. Select `run_as_user=true` when execution should use the
configured Sailfish user. Android builds intentionally accept shell commands.
Configured aliases are convenient names, not a host allowlist: explicit
`user@host` targets are also supported. Target strings cannot contain SSH
options or whitespace.

RPM builds and SDK maintenance use privileged Docker containers. Installed SDK
builds mount the host user's home directory and the SDK read-write. Build only
trusted source and images, including the optional third-party SDK mirror.
Repository path checks constrain direct file operations; they do not sandbox
build scripts or arbitrary device commands. The explicit `chmod` permission
fallback broadens file permissions; prefer ACL support and the default error
behaviour when ACL tools are unavailable.

Non-USB SSH connections follow the configured SSH host-key policy. USB mode
connects to `192.168.2.15` and deliberately disables host-key checking and storage
to support changing devices at that address. Use it only for a device connection
you trust.

By default, SSH and SCP use `~/.ssh/config` when it exists, or `-F /dev/null`
when it is absent. Both choices skip the system SSH configuration; OpenSSH's
built-in defaults, including normal non-USB host-key checks, still apply.
An explicit `paths.ssh_config` must name an existing file; a missing explicit
file remains an error. This policy applies to device and Android connections.

Tool output is returned to the MCP client and may enter a model's context.
Effective configuration includes private hostnames, usernames, source paths and
OBS mappings. Screenshots, journals, source matches and job logs can contain
private data; commands can contain credentials supplied by callers. Keep real
configuration, credentials, task notes, screenshots and runtime logs out of
source releases and public bug reports. The server's request logs record
argument names rather than tool argument values, but job logs and verbose
results retain command/output details. Configure the client's tool approvals
to suit your workflow.

## Running

From a checkout:

```sh
/path/to/sailfish-devel-mcp/bin/sailfish-devel-mcp
```

To inspect the effective configuration:

```sh
/path/to/sailfish-devel-mcp/bin/sailfish-devel-mcp --dump-config
```

Example MCP client configuration:

```json
{
  "mcpServers": {
    "sailfish-devel": {
      "command": "/path/to/sailfish-devel-mcp/bin/sailfish-devel-mcp"
    }
  }
}
```

The wrapper writes startup, shutdown, MCP request, and tool-call logs to
`~/.local/state/sailfish-devel-mcp/server.log` by default. Override the log
directory with `SAILFISH_DEVEL_MCP_LOG_DIR`. If the MCP client uses a different
server label, set `SAILFISH_DEVEL_MCP_SERVER_LABEL` in the client environment so
the wrapper startup line includes the same label.

## Configuration

Example:

```json
{
  "default_device": "phone",
  "devices": {
    "phone": {
      "ssh_target": "root@phone",
      "username": "defaultuser",
      "architecture": "aarch64",
      "release": "live",
      "user_bus_runtime_dir": "/run/user/100000",
      "user_bus_address": "unix:path=/run/user/100000/dbus/user_bus_socket"
    }
  },
  "paths": {
    "git_root": "~/git",
    "obs_root": "~/OBS",
    "ssh_config": "~/.ssh/config",
    "local_sdk": "/srv/mer/sdks/sfossdk/sdk-chroot",
    "osc_api_alias": "primary",
    "obs_servers": {
      "primary": "primary",
      "community": "community"
    }
  },
  "default_android_build_host": "android-builder",
  "android_build_hosts": {
    "android-builder": {
      "ssh_target": "user@android-build-host"
    }
  }
}
```

## Tool Notes

The server keeps local paths scoped to the configured git root, OBS checkout
root, and `/tmp` for tools that read or write files. Device access still uses
SSH, so the usual SSH prompts, permissions, and command failures are surfaced
as tool results.

Mutating tools are annotated as non-read-only:

- `sailfish_device_setup` (conservatively annotated because installation is optional)
- `sailfish_device_lipstick_screenshot`
- `sailfish_device_touch`
- `sailfish_device_touch_workflow`
- `sailfish_device_user_bus_call`
- `sailfish_device_user_session_command`
- `sailfish_device_command_start`
- `sailfish_device_command_cancel`
- `sailfish_device_install_rpm`
- `sailfish_device_restart_service`
- `sailfish_device_app_launch`
- `sailfish_device_browser_launch`
- `sailfish_build_rpm`
- `sailfish_build_cancel`
- `sailfish_android_build`
- `sailfish_android_build_cancel`
- `sailfish_sdk_refresh_metadata`

`sailfish_device_lipstick_screenshot` defaults to
`~/Pictures/Screenshots/lipstick-<timestamp>.png` under `/home/<username>`,
where `username` comes from the device config. Lipstick rejects screenshot save
paths outside the home directory. When that directory is missing, the tool
derives ownership with `stat -L`, creates `Pictures` as that owner/group with
mode `775`, and creates `Pictures/Screenshots` as that owner and the
`privileged` group with mode `755` when the group exists.

`sailfish_device_touch` supports `discover`, `tap`, and `swipe`. Discovery
prints `/proc/bus/input/devices` and can also run `evdev_trace -i` when
`include_evdev_trace` is true. Tap and swipe auto-select a likely touchscreen
input event device unless `input_device` is supplied. Coordinates are raw
input/display coordinates, so pair this tool with a current screenshot when
choosing points.

`sailfish_device_touch_workflow` wraps the common UI-debugging sequence:
capture a Lipstick screenshot, optionally list touch devices, inject a tap or
swipe, and optionally capture a second screenshot. It returns each step's
structured result separately.

`sailfish_device_user_session_command` runs an argv command with
`XDG_RUNTIME_DIR` and `DBUS_SESSION_BUS_ADDRESS` set from the configured device.
Set `run_as_user` when the command should execute as the configured Sailfish
username through `runuser` or `su`. Command tools return compact metadata and
bounded output by default; set `verbose` only when the full command, stdout, and
stderr are needed. This synchronous tool is limited to 15 seconds.

Use `sailfish_device_command_start` for GDB, tracing, or any command that might
run longer than 15 seconds. It starts a detached host-side SSH process and
returns a job id immediately, so the command survives the MCP request and stdio
server exiting. Use the CLI waiter below for long waits. For occasional checks, use
`sailfish_device_command_status` with `lines=0` and at most 15 `wait_seconds`. Terminal
failures include a bounded log excerpt automatically. Full logs remain under
the local MCP state directory in `device-commands/<job-id>/command.log`. Use
`sailfish_device_command_cancel` to ask the owning supervisor to terminate the
SSH process group.

`sailfish_device_install_rpm` accepts either `rpm_path` for one local RPM or
`rpm_paths` for a dependency set. With `rpm_paths`, the tool copies every RPM to
the remote `remote_dir` and runs one `pkcon install-local` or `rpm -Uvh`
command with all copied files, so dependencies can be resolved together.

`sailfish_device_app_launch` launches an installed desktop entry using `gio`,
as the configured device user from their passwd home directory. For example:

```json
{"device": "my-device", "desktop_file": "sailfish-browser.desktop"}
```

A basename resolves under `/usr/share/applications`; an absolute device path
ending in `.desktop` is also accepted. No URL is required. It does not kill
running apps, restart boosters, or change preferences. The process detaches
with stdin/stdout/stderr redirected, avoiding an SSH timeout when app children
retain the connection. `wait_seconds` (0–5, default 1) checks for immediate
launcher failure; `timeout` (up to 15 seconds) must exceed it.

The receipt includes `submitted`, the launch supervisor PID (not the app PID),
`launch_exit_code` if already available, and private `remote_log`/`remote_status`
paths under the user's `~/.cache/sailfish-devel-mcp/launch.*` directory. Read those
paths with device commands for later failures and remove that specific directory
when its evidence is no longer needed. Submission or `gio` exit zero does not
prove foreground activation/rendering: check Lipstick's topmost PID and a
screenshot separately. This desktop-entry path was validated manually for Browser
on a Sailfish device; it does not claim to execute all of Lipstick's icon/switcher logic.

`sailfish_device_browser_launch` stops the browser booster service and stale
browser/firejail PIDs when requested, launches Sailfish Browser through
`invoker` with the display and user-session environment, then queries Lipstick
for the topmost PID and checks whether that process has `libxul.so` mapped.

`sailfish_build_rpm` can use a configured device's `architecture` and `release`
as defaults when the call includes `device`. If `paths.local_sdk` is set, the
build defaults to `live` and first checks the installed SDK targets. `live` uses
the unversioned local target for the requested architecture, for example
`aarch64`. A named production release is used only when passed explicitly
through the tool arguments, environment, or device config; it uses a matching
versioned local target when available, and otherwise falls back to a matching
tag in the third-party `coderus/sailfishos-platform-sdk` Docker mirror. Tags in
that mirror indicate image availability; they do not identify the current
official SailfishOS release or SDK target. The wrapper image defaults to
`sailfish-sdk-build-engine:$USER` and can be overridden with
`SAILFISH_SDK_BUILD_ENGINE_IMAGE`.

Use `sailfish_build_preflight` to validate backend, image/target selection,
architectures, local RPM inputs, pull policy, VCS behavior, and artifact paths
without pulling an image or changing the project. `sailfish_build_rpm` accepts
the same `backend`, `local_sdk`, `target`, `pull_policy`, `no_vcs_apply`, and
`allow_untrusted_rpms` controls. An explicitly selected `local` backend does
not silently fall back to Docker. Asynchronous jobs use confined UUID job
directories and expose helper metadata and RPM paths through
`sailfish_build_status`; use `sailfish_build_cancel` to terminate the tracked
process group. Successful builds run the helper in quiet mode while retaining
the complete job log. Let `sailfish_build_status` wait mechanically with
`wait_seconds` and leave `lines` at `0` during routine monitoring. A terminal
failure automatically includes a bounded diagnostic excerpt; request log lines
or `verbose` output only when more detail is needed. Status waits are capped at
15 seconds so a monitoring request stays comfortably below the stdio
transport's observed lifetime limit.

Local SDK builds use the canonical helper. Ordinary builds share the base's
mb2-managed `.default` working snapshot; custom repositories, packages and local
RPMs use isolated originals. `snapshot_key` explicitly isolates a project even
without custom inputs. Supply it when your project or machine policy requires
per-project isolation. `snapshot_repository` and `snapshot_package` use the same
helper options; repeat these inputs on later builds so outdated snapshots can
be restored. Only original targets go to mb2; working `.default` children are
managed by mb2. Bases remain free of project dependencies.

`sailfish_android_build` starts a remote Android/AppSupport build on the
configured build host. Configure `android_build_hosts.<host>.project_dir` or
pass `project_dir` to point at the remote Android source tree. The tool writes
job state under the remote `state_dir`, atomically creates a per-job directory,
and starts its own process group with `nohup` and `setsid`, so the SSH session
used to launch the job can disconnect without killing the build. Set
`build_timeout` for a remote build lifetime limit. Poll with
`sailfish_android_build_status`; omit `job_id` to list recent jobs, or pass a
job id and `wait_seconds` to wait mechanically for a state change. Running jobs
omit their log by default, while failed jobs include a bounded diagnostic
excerpt. Set `lines` or `verbose` only for explicit log or command inspection.
Individual status waits are capped at 15 seconds. Use
`sailfish_android_build_cancel` to terminate the identity-checked process group.

`sailfish_sdk_refresh_metadata` starts a detached job to refresh zypper metadata in the installed SDK
main target, for example `aarch64.default`, using the same privileged Docker
wrapper style as local SDK builds. Use it when local SDK builds fail because a
package listed in repository metadata cannot be downloaded.

`sailfish_obs_results` and `sailfish_obs_buildlog` accept a friendly `server`
name configured in `paths.obs_servers`; each value is an `.oscrc` alias or API
URL passed to `osc -A`. Omit `server` to use `paths.osc_api_alias`. The advanced
`api_alias` argument accepts any raw `osc -A` alias or API URL and cannot be
combined with `server`. Keep machine- or organization-specific server mappings
in the local config rather than in the repository.

`sailfish_obs_results` can wait mechanically in intervals of at most 15 seconds
until OBS no longer reports an active state. `sailfish_obs_buildlog` returns a
bounded tail by default; set `verbose` only when the complete response is
required.

`sailfish_obs_buildlog` defaults to `osc api` with `nostream=1` so a build log
request does not become a long-running live stream. Set `nostream` to `false`
to use `osc remotebuildlog`.

Read-only tools include build preflight/status, the journal, topmost PID,
process maps, OBS lookup, repo search, spec summary, and QML checks.

## Smoke Test

```sh
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | /path/to/sailfish-devel-mcp/bin/sailfish-devel-mcp
```

## Development

Run tests without installing the package:

```sh
PYTHONPATH=src python3 -m unittest discover -s tests
```

The canonical helper lives in the `build-sailfishos` skill. Update the exact
vendored copy with:

```sh
python3 scripts/update_build_helper.py /path/to/build-sailfishos/scripts/build_sailfishos.py
```

## Compact jobs and diagnostics

RPM installation and SDK metadata refresh now return detached build-job receipts
by default. Wait for `sailfish_build_status` to finish successfully before dependent
actions; `sailfish_build_cancel` cancels these jobs. Explicit `wait=true` retains
legacy synchronous behavior. For a multi-RPM transaction, timeout bounds the whole
async job; cancellation of local SSH does not guarantee rollback of a remote RPM
transaction. Inspect package state before retrying an interrupted install.

`sailfish_obs_watch` runs `osc results --watch --fail-on-error` in a detached job,
using the selected OBS alias and package filter. It does not modify OBS sources.

For long jobs use one CLI process, without repeated model-driven status calls:

```sh
bin/sailfish-devel-jobs wait --kind build --job-id JOB_ID
bin/sailfish-devel-jobs wait --kind device --job-id JOB_ID
bin/sailfish-devel-jobs wait --kind android --host HOST --job-id JOB_ID
bin/sailfish-devel-jobs status --kind build --job-id JOB_ID
bin/sailfish-devel-jobs log --kind build --job-id JOB_ID --offset 0 --max-bytes 6000
```

The waiter emits one JSON result at completion or failure. Exit codes are 0 for
success, 1 for failure/cancellation and 124 for wait expiration (which leaves the
job running). Installed packages expose `sailfish-devel-jobs` on PATH. Android
jobs accept an optional `--state-dir` matching their creation call.

Status results include a `revision`. Send `after_revision` on an occasional later
check to receive a minimal `unchanged` receipt when nothing changed. Explicit
`lines`, `verbose`, or `log_offset` requests still return their requested detail.
Build/device log pages are capped at 12,000 bytes and return `next_offset` and
`eof`. Tails are byte bounded too, including logs containing very long lines.
Verbose status retains full job metadata; compact completion exposes helper
version, backend, targets, duration, RPM paths, rpmlint and dependency diagnostics.

`sailfish_doctor` checks configured helper provenance, local executable availability,
SDK targets and configuration presence without contacting devices or OBS. It does
not prove Docker daemon access or network connectivity. The canonical helper also
provides `--doctor` and `--refresh-metadata --target TARGET [--force-refresh]`.

The QML checker lexes strings/comments and multiline ternaries. It reports missing
branch-local `//:` and `//%` comments; it is a focused checker, not a full QML parser.

## Maintaining the helper and skills

The vendored helper is pinned by `vendor/build-helper.json` with its version,
SHA-256 and available source revision/dirty state. Verify parity without editing:

```sh
python3 scripts/update_build_helper.py /path/to/build-sailfishos/scripts/build_sailfishos.py --check
```

The version check, CLI-control tests and snapshot tests guard the interface. Update
the canonical skill first, vendor it, and run both test suites before release.
`skills/sailfish-devel` is a separate optional skill for deployment, debugging,
Android and OBS workflows. Install/symlink that directory in your skill directory;
the canonical RPM skill stays focused on building and packaging.

TDQS

B3.3/5.0

Scored across 35 tools

Disambiguation4/5

Tools are grouped by domain and most have clearly distinct purposes, such as local RPM build, Android build, OBS, device SSH commands, and QML translation checks. Some boundaries blur because sailfish_build_status/cancel are reused for multiple local async jobs despite the 'build' name, and device_user_session_command vs device_command_start require reading descriptions to distinguish short from long-running commands.

Naming Consistency4/5

All tool names use snake_case with a consistent sailfish_ prefix, which makes the set predictable. However, the suffix pattern is not uniformly verb_noun: sailfish_devices, sailfish_doctor, sailfish_spec_summary, and sailfish_obs_watch mix noun/state and action forms, though deviations are minor.

Tool Count3/5

35 tools is heavy for a single MCP server, even though the server spans several subdomains including device, build, Android, OBS, repo, SDK, spec, and QML. The breadth justifies many tools, but the surface is at the upper end and could likely be consolidated in places.

Completeness4/5

The tool set covers a wide lifecycle: build planning, local and Android builds, device inspection and command execution, OBS monitoring, SDK refresh, repo search, spec parsing, and QML translation checks. Minor gaps remain, such as RPM uninstall, explicit app stop/termination, git write operations, and signing/deployment workflows.

Maintenance

ActivityMaintained
ResponsivenessNo issues