Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
作者による説明(英語の原文)
Run Android tests on real phones, or lease one: install, shell, screenshot, logcat.
この3つを1つのスコアにまとめることはしません。意見、測定、人気はそれぞれ別のものだからです。
このサーバーのやさしい解説はまだ書いていません。以下はレジストリから読み取った事実です。
使っているAIアプリを選んで、設定をコピーしてください。
SAIR_API_KEY, SAIR_API_URL「秘密」と表示された値は、あなたのアカウントの鍵です。絶対に人に教えたり、チャットに貼り付けたりしないでください。
どのアプリを使っていますか?
上でアプリを選んでください。選んだアプリを覚えておくので、次からはボタンに「…にインストール」と表示されます。
リンク先は作者自身のファイルです。私たちが置いたり再パッケージしたりしたものではありません。ファイルを開くと、Claude Desktopがインストールします。
ターミナルでこれを実行します
動かす前に、空欄の値を入力してください。
ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
これはパソコンで動くサーバーですが、ChatGPTがつなげるのはインターネット上のサーバーだけです。ほかのアプリを選んでください。
選んだアプリは、この端末のCookieに保存されます。アカウントは不要です。
SAIR_API_URL envこの設定は、作者のレジストリ登録情報から作成しています。READMEと違う場合は、READMEに従ってください。
パソコンで動くサーバーです。インストールすると、ツール一覧がAIアプリに表示されます。
誰でも見られる情報をチェックしており、コードは読んでいません。当てはまらない項目は数に入れていません。 · 2026年11月6日にチェック
レジストリ名は、リポジトリを所有するGitHubアカウント compscidr で確認済みです
このリポジトリはまだGitHubから読み取っていません
このリポジトリはまだGitHubから読み取っていません
これはパソコンで動くため、インターネット上のエンドポイントはありません
パソコンで動き、代わりにローカルの設定を使います
パソコンで動くサーバーです。ツール一覧を読み取れるのは、インターネット上のエンドポイントがある場合だけです
レジストリでこのカテゴリにある有効なすべてのサーバーと比較しています。スターは、公開者がリポジトリの所有者である場合だけ数えます。
当てはまる基本チェック1項目のうち1項目に合格しました。コードは読んでいません。キーやトークンを追加する前に、サーバーがどんなアクセスを求めているかを確認し、必要な権限だけを持つキーを使ってください。
入力が必要なもの:SAIR_API_KEY
あなたのパソコンで動きます。必要なときにAIアプリが起動し、データは作者のサーバーを通りません(サーバー自体がオンラインサービスを呼び出す場合を除きます)。
GitHubリポジトリの最終更新は–、最新バージョン(0.1.36)がレジストリに届いたのは昨日です。
Claude Desktop、Claude Code、Cursor、VS CodeなどのMCP対応アプリで使えます。パソコンにMCPBも必要です。
同じカテゴリには:Spec Driven Development, Remote Desktop Commander, Zoo MCP
同じカテゴリで、ほかの作者が作ったサーバー。スターの多い順。
カードの見方:★ GitHubスターi3つの柱:レビュー · チェック · 利用状況i更新=最新バージョンをレジストリに公開した日i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
An MCP server that provides access to the Zoo API for various CAD operations and tools.
作者のリポジトリから書かれたとおりにコピーしたものです。PopNx MCPは内容を確認・推奨していません。HTML、画像、スクリプトは取り除いています。リンクはこのサイトの外へ移動します。
SAIR lets CI pipelines safely share physical Android devices. It provides device locking, ADB protocol translation, and per-runner isolation so multiple jobs never collide on the same device.
Without SAIR, every CI job that can see a phone uses it at the same time:
CI job 1 ──┐
├──▶ adb ──▶ Phone ✗ both jobs install / uninstall the same APK CI job 2 ──┘ ✗ one job's reboot or settings change breaks the other's tests mid-run ✗ connectedCheck runs on every attached phone, including ones another job is using
With SAIR, a job must hold a lock before it sees a device, and waiting jobs queue:
CI job 1 ──┐ ┌──▶ Phone A (locked by job 1)
├──▶ Proxy ───────┤ CI job 2 ──┘ ▲ └──▶ Phone B (locked by job 2) │ locks CI job 3 ··· Orchestrator: job 3 waits in the queue until a phone is released
This repo is also a GitHub Action that puts sair-acquire, sair-release, sair-wifi, sair-bluetooth, sair-locks and sair-label on PATH. The tools need an orchestrator account: sign up at sair.run to get an API key, store it as the repository secret SAIR_API_KEY, and set the repository variable SAIR_PROXY_URL to your bench's proxy (see Run). Then:
steps:
The full workflow example explains each step.
A runner with no proxy of its own (e.g. GitHub-hosted, using phones other accounts share) can have the action start one, at the same release as the action. Leave SAIR_PROXY_URL unset; the proxy listens on the default localhost:8550, and its log is at $RUNNER_TEMP/sair-proxy.log:
- uses: compscidr/sair@v0.1.18
with: proxy: true env: SAIR_API_KEY: ${{ secrets.SAIR_API_KEY }}
Used at a branch or SHA rather than a release tag, the action warns and starts the latest release instead.
sair-mcp is an MCP server (stdio) for coding agents. It can run an app's instrumented tests on a real phone in one call, or lease a phone and install APKs, run shell commands, take screenshots and read logcat on it. It talks to the orchestrator's agent API with an API key from sair.run. The agent's machine needs no proxy or adb.
Install it with go install github.com/compscidr/sair/cmd/sair-mcp@latest, take sair-mcp from a release tarball, or use sair-mcp.mcpb from a release (an MCP Bundle for clients such as Claude Desktop). Then add it to your agent, e.g. Claude Code:
claude mcp add sair -e SAIR_API_KEY=sair_... -- sair-mcp
or any client's JSON config:
{ "mcpServers": { "sair": { "command": "sair-mcp", "env": { "SAIR_API_KEY": "sair_..." } } } }
| Tool | Does |
|---|---|
run_tests | Uploads app_apk and test_apk (paths on the agent's machine), waits for the run, returns per-test results, instrument output and a logcat tail |
list_devices | Lists the phones the key may use: serial, model, sdk (API level), shared, in use |
lease_device | Takes a phone (serial, wait_s optional) and returns the lease id |
install_apk | Installs an APK on the leased phone, returns its package |
shell | Runs a command, returns output and exit code |
screenshot | Returns a PNG of the screen |
logcat | Returns the most recent lines (default 500) |
release_device | Ends the lease: uninstalls what it installed and frees the phone |
The server heartbeats the leases it takes, so an agent that thinks for a while between calls keeps its phone. A lease still ends at the plan's maximum (free 15 min, solo 1 h, team 2 h, enterprise 4 h). On a phone shared by another user, shell commands are limited to a safe set (see the orchestrator README). SAIR_API_URL points it at another orchestrator (default https://api.sair.run).
It is listed in the official MCP registry as io.github.compscidr/sair. Every release publishes it: the release workflow builds sair-mcp.mcpb, fills server.json's version, download URL and SHA-256 (the values in the repo are placeholders), and publishes it with mcp-publisher, logged in through GitHub OIDC. Check it with curl "https://registry.modelcontextprotocol.io/v0/servers?search=io.github.compscidr/sair".
curl -fsSL https://raw.githubusercontent.com/compscidr/sair/main/install.sh | bash
This detects your OS and architecture, downloads the latest release, and installs all binaries to ~/.local/bin.
adb installed on the machine with Android devices# 1. Start a real ADB server on a non-default port (5038), so it doesn't
adb -P 5038 start-server
export SAIR_API_KEY=your-api-key
sair-proxy & sair-device-source
The proxy connects to the hosted orchestrator at orchestrator.sair.run by default (with TLS). Override with ORCHESTRATOR_ADDR for local development.
Acquire a device lock (blocks until one is available), run your tests, then release:
ACQUIRE_OUTPUT=$(sair-acquire)
eval "$ACQUIRE_OUTPUT" ./gradlew connectedCheck sair-release
After eval, stock adb automatically talks to the proxy through the scoped port and only sees the locked devices.
When the bench sets WIFI_SSID on the device source, Wi-Fi is managed for you:
--no-wifi hands it over *offline* instead, verified the same way rather than trusting the previous release. --wifi-band 2.4|5|6 (also sair-wifi on --band) joins on that GHz band: the phone scans, the suggestion is pinned to the strongest BSSID of WIFI_SSID it sees in the band, and the join fails if it sees none. The phone's cmd wifi status shows the BSSID and frequency it joined.adb shell and every saved network is forgotten and the radio is toggled, and the reset waits until the phone is offline with the radio back on. The next job never inherits the last one's network. The phone then rejoins the bench Wi-Fi, so it is online between leases and app updates can install (see the Play Store below); the next acquire puts it in the state that job asked for either way..test packages included, with their app data and signed-in accounts), and the browser and mail apps are force-stopped; see BASELINE_PACKAGES, RESET_FORCE_STOP and RESET_CLEAR_PACKAGES. What was removed is written to the proxy log and the lock's log on the orchestrator, naming any Wi-Fi suggestion that went with a package. This runs before the Wi-Fi reset, so a suggestion a test app leaked for the bench network is gone before the reset waits for the phone to be offline.BASELINE_PACKAGES has no effect on this reset, but a local or guest-key lock on the same phone still resets to the baseline, so list apps to keep in BASELINE_PACKAGES. What was removed goes to the proxy log and the bench events, not the lock's log, which is on the holder's proxy. Without that list (it could not be read, the device source is older than the proxy, or no relay got that far) nothing is uninstalled: the proxy logs a warning and reports a failed package reset. Leftovers can then survive until a local lock, because the next borrower's list includes them, as it does when a relay under a new lock arrives before the previous lock's reset (the proxy warns, naming what it suspects).if: always() release step, its runner died, or the proxy was down when the lock expired, is cleaned up by the next job, whose lock log then shows sair: uninstalled … (left by an earlier job). This is local locks only: a relayed lock's next job cleans up nothing.am instrument or a leftover uiautomator server can leave a UiAutomation registered, failing every later instrumentation with UiAutomationService … already registered!. A uiautomator dump probe detects it; sair then kills the shell user's app_process/uiautomator processes, which unregisters it, and reboots only if the probe still fails (never a phone with a secure lock). The lock log gets sair: cleared a leaked UiAutomation registration (killed … | rebooted).dumpsys power says Awake, dumpsys window policy says the keyguard is not showing), with up to three attempts since a one-shot dismiss can race on some phones. A phone whose keyguard stays up fails the acquire with screen locked; that means a PIN, pattern or password is set, which sair cannot dismiss, so set the phone's screen lock to Swipe or None. The reason is also reported as the device's warning (proxy /status, device report) until a wake succeeds. A phone with stay_on_while_plugged_in off is logged as a warning, since it will sleep and re-lock between steps. Android 4.x phones (no wm) are verified through their own mShowingLockscreen field; a dump in a format sair does not know is logged once and treated as unlocked.REBOOT_UPTIME_HOURS (24 by default), while the phone is unacquired: the reset waits for the boot and the wake above before the next acquire of that phone can proceed, and the phone is not offered while it is down. The lock log gets sair: rebooted after release (uptime …) and the proxy's /status lists each phone's last_reboot. This runs before the Wi-Fi reset, so the offline wait is not spent twice. A phone with a PIN, pattern or password is never rebooted this way, since it would come back on "unlock to start" and need a human; the device source logs that with the fix. The wake and the reboot run whether or not the bench manages Wi-Fi.svc bluetooth disable, wait until dumpsys bluetooth_manager says the adapter is fully OFF, svc bluetooth enable, wait until it says ON (30 s each). A BLE stack an earlier job wedged (stuck advertisers, a scanner that neve冒頭だけを表示しています。続きはリポジトリで読めます