The bridge is the wire protocol between the in-app sidekick (debug-only,
runs inside the target Android app process) and the desktop FixThis processes
(fixthis-cli and fixthis-mcp). Everything else — the MCP server, the
console UI, the persisted JSON — sits on top of it.
This document describes the wire format, the version policy, and the checklist for changing the protocol.
┌────────────────────┐ localabstract socket ┌─────────────────────────┐
│ fixthis-cli / │ ◀─────── adb forward ──────────────▶ │ Android debug app │
│ fixthis-mcp │ │ ├ AndroidX Startup │
│ (desktop, JVM) │ ┌───────────────────────────┐ │ ├ FixThis sidekick │
│ │ │ JSON-RPC-ish frames over │ │ └ in-process bridge │
│ Console (browser) │ │ length-prefixed binary │ │ ↳ Compose semantics │
└────────────────────┘ └───────────────────────────┘ └─────────────────────────┘
- The Android app does not host an HTTP or MCP server. It only exposes a local-abstract socket inside its own process.
- The desktop side talks to that socket via
adb forward tcp:<port> localabstract:fixthis_<package>. - Every desktop request carries a session token read from app-private
storage via
adb shell run-as <package> cat files/fixthis/session.json. Mismatched / missing tokens are rejected by the bridge.
The bridge speaks a length-prefixed JSON-frame protocol implemented in
fixthis-compose-sidekick/.../bridge/BridgeProtocol.kt:
- Frame header: 4 bytes, big-endian unsigned
length. - Frame body:
lengthbytes of UTF-8 JSON. - Max frame: 16 MB.
{
"id": "client-generated-id",
"token": "<session token>",
"method": "captureScreen",
"params": { ... }
}Success:
{ "id": "client-generated-id", "ok": true, "result": { ... } }Error:
{ "id": "client-generated-id", "ok": false, "error": { "code": "string", "message": "string" } }id is echoed verbatim. ok is the success/failure discriminator. error.code
is a stable machine-readable string; error.message is human-readable.
A single VERSION string identifies the protocol surface. Current value:
"1.3" (defined in
fixthis-compose-sidekick/src/main/kotlin/io/github/beyondwin/fixthis/compose/sidekick/bridge/BridgeProtocol.kt).
The same value is mirrored in three other places that must stay in sync.
A BridgeProtocolVersionSyncTest in :fixthis-mcp:test enforces equality
across all four sites at CI time and names each file with its observed value
on failure:
| File | Purpose | Constant |
|---|---|---|
fixthis-compose-sidekick/.../bridge/BridgeProtocol.kt |
Authoritative source — sidekick-emitted version. | VERSION |
fixthis-mcp/src/main/console/staleness.js |
Console-side minimum acceptable version. | MinimumSupportedProtocolVersion |
fixthis-cli/.../BridgeClient.kt |
Desktop CLI's expected version (used to refuse incompatible bridges with a clear error). | BridgeProtocolVersion |
fixthis-mcp/.../console/ServerVersionRoutes.kt |
Server-side reflection of the version, returned to the console for staleness comparison. | BridgeProtocolVersion |
Version 1.3 is the screen-integrity update. It adds nullable snapshot fields
for orientation, dimensions, density, window mode, system UI state, and
fingerprint data. Older persisted JSON remains readable because the new fields
are additive and nullable; the console skips fingerprint comparison when either
side lacks a fingerprint.
At runtime, the console fetches the bridge's reported version and compares it
to its own MinimumSupportedProtocolVersion. If they disagree, the console
shows a red staleness banner explaining what to rebuild / reinstall.
Two common triggers:
- Old sample APK on a new console. The installed debug app's sidekick
reports an older
bridgeProtocolVersionthan the console expects. Fix:bash scripts/restart-console.sh --with-app(or manually re-install the debug APK). - Old console JAR on a new sample APK. The console serves an outdated
MinimumSupportedProtocolVersion. Fix:bash scripts/restart-console.shto rebuild the JAR.
The console also compares its bundled build epoch against the running server on load and shows the same banner when the JAR itself is older than the sources, catching the case where you forgot to restart after a Kotlin edit.
Bump VERSION whenever a wire-visible change would silently misbehave on an
older partner:
- A new
methodis required to be present (vs. additive optional features). - A field is renamed or removed in a request or response.
- A field's semantics change (e.g., units, enum values, optional → required).
You do not need to bump for:
- Pure additive fields with safe defaults (older partner ignores them).
- Implementation-internal refactors that don't change the wire shape.
- Logging / error-message text changes.
Any PR that changes BridgeStatus, BridgeRequest, BridgeResponse, or any
method signature on the bridge:
-
Bump
BridgeProtocol.VERSIONinfixthis-compose-sidekick/.../bridge/BridgeProtocol.kt(e.g."1.2"→"1.3"). -
Bump
MinimumSupportedProtocolVersioninfixthis-mcp/src/main/console/staleness.jsto the same value. -
Bump
BridgeProtocolVersioninfixthis-cli/.../BridgeClient.ktto the same value. -
Bump
BridgeProtocolVersioninfixthis-mcp/.../console/ServerVersionRoutes.ktto the same value. -
Re-bundle console JS:
node scripts/build-console-assets.mjs
-
Add a
### Bridge Protocolentry toCHANGELOG.mddescribing the wire-visible change and the new version. -
Verify staleness banner behavior locally by running an old APK against the new console (manual smoke).
CI enforces mirror equality automatically via BridgeProtocolVersionSyncTest
(:fixthis-mcp:test). If you bump one site and forget another, the test
fails and lists each file with its current value, so you don't have to wait
for a smoke test to catch the drift.
The session token is short-lived and never leaves the local machine:
- Generated by the sidekick on app start, written to app-private storage.
- Read by the desktop side via
adb shell run-as <package>(Android debug signing required). - Included in every bridge request; the sidekick rejects mismatches.
This is not a remote-service auth boundary. The bridge is intended for a trusted local development machine with ADB access to a debug app — see Security model and Privacy.
- Architecture overview
- ADR-0007 — Feedback console connection recovery
- Output schema — what the bridge ultimately delivers into persisted feedback