1.3.0The path a user took, and what happened on every screen
Compose Multiplatform · Ktor · OkHttp

Every call your app makes.
In the app, in your browser,
and in your AI agent.

Inspector is a network inspector for Compose Multiplatform apps. One capture feeds an in-app overlay, a local web UI with a searchable archive, and an MCP server your coding agent can query. It is debug-only by construction, so none of it can end up in a release build.

Get started ★ Star on GitHub io.github.shafichariri:inspector-core:1.3.0
A request from the app is captured once and appears in the overlay, the daemon archive, the web UI and the MCP server your app · Android / iOS / desktop Ktor HttpClient install(Inspector) OkHttp client okHttpInterceptor() Inspector capture ring buffer · redaction ① In-app overlay GET /users · 200 · 143ms WebSocket your machine · 127.0.0.1 only ② Local daemon inspector serve :8099 ③ Web UI live tail · filter · replay Session archive ~/.inspector/sessions/ ④ MCP server inspector mcp your AI coding agent
Four surfaces, one capture

Read the same traffic wherever you're working

A row in the overlay, a row in the browser and a row an agent reads are the same record. A filter you type on the phone is a string you can hand to an agent.

The in-app overlay: a list of calls with screen, cache and state signals and marker dividers between them

In-app overlay

A draggable pill shows the latest call. Tap it and you get the full inspector, from one Compose codebase on Android, iOS and desktop.

  • One row per attempt, so redirects and retries are never collapsed
  • Foldable JSON tree, headers, cURL copy
  • Markers and app state on the same timeline
127.0.0.1:8099
The web UI: a session's calls on the left, the selected call's JSON body on the right

Web UI localhost

Every session is archived, and you can browse it at 127.0.0.1:8099, live or long after the app has closed.

  • Filter grammar: status:4xx path:/checkout since:marker("pay")
  • Replay a call, edited or re-signed by the app
  • Export a whole session as HTML, Markdown, JSON or HAR
Why did checkout fail just now?
session_summary
list_transactions status:4xx path:/checkout
get_body 7f3a · response
The first POST /checkout returned 409 cart_stale because the cart was at version 7 while the app sent 6. The retry 212ms later succeeded.
illustration

MCP server for agents

Your coding agent reads the same archive you do. It starts with a one-kilobyte summary and pulls only the bodies it needs.

  • session_summary, list_transactions, get_body, explain, timeline, export_session
  • Agents can drop markers and pull live app state
  • Failures come back as readable results the agent can correct from, not dead ends
$ inspector serve inspector: listening on http://127.0.0.1:8099 inspector: usb bridge on — a USB-attached iPhone is dialled $ tree ~/.inspector/sessions/latest 2026-08-17T05-57-58_sample_Pixel-8_debug/ ├── meta.json ├── index.jsonl # one call per line ├── markers.jsonl ├── signals.jsonl └── bodies/ $ grep '"status":409' ~/.inspector/latest/index.jsonl

Local daemon & archive

A small JVM daemon that runs on your machine and nowhere else. Sessions are plain folders and jsonl files you can grep, diff and attach to a bug report.

  • Binds to loopback only, never to the network
  • Retention by count and by size
  • Bridges a USB-attached iPhone, with nothing to configure
On the phone

A full inspector inside your app

These are real renders of the overlay at phone width, built from the same Compose code that runs on Android and iOS. Swipe through.

In the browser

Every session, searchable, on your machine

The daemon serves this page at 127.0.0.1:8099. Screenshots are from the bundled sample app.

127.0.0.1:8099
The Inspector web UI
New in 1.3.0

Follow the user from screen to screen

The path tab draws the route through the app as a line of stations, one per visit, and under it a line per visit: the calls, cache writes, analytics events and taps that happened while that screen was up, in order. It updates live as the user moves, without pulling the view away from what you are reading.

127.0.0.1:8099
The path tab: stations for each screen visit along the top, and a line per visit with its calls and signals

Needs your app to send a screen signal when it navigates. Anything else you send, under any tag, appears on the screen it happened on. A failed call is always shown, never folded away.

Since 1.2.0

Hand a whole session to someone else

Use export ▾ beside the session picker. Each export is one file, holds the whole session with no filter or page limit, and the person you send it to doesn't need Inspector installed.

.html

The web UI itself

The same page, working offline with the session embedded. Open it straight from an email.

.md

Markdown

A readable timeline and every call, ready for a ticket or a chat.

.json

JSON

Everything: calls, markers, signals and bodies, for your own tools.

.har

HAR

Just the calls, for DevTools or any HAR viewer.

An export contains whatever was captured, including credentials unless you turned redaction on. Read it before you send it.

Signals

See what the app was doing, not only what it sent

Screens, view-model state and cache contents sit on the same timeline as the traffic. You can see which screen made the call and what the app believed before and after it.

A timeline with network calls, markers and app-state signals interleaved time → screen: Cart GET /cart 200 ▸ marker "pay tapped" POST /checkout 409 CartVM: version 6→7 POST /checkout 200 screen: Receipt
Platforms

Where it runs today

Every row below says exactly what has been tested. Since 1.2.0: a physical iPhone, over the cable.

How each kind of device reaches the daemon on your machine Daemon on your machine 127.0.0.1:8099 not reachable from the network Android emulator✓ supported 10.0.2.2 iOS simulator✓ supported shared loopback iPhone (USB)✓ supported · new usbmuxd · daemon dials in Android phone (USB)◐ supported, early adb reverse tcp:8099
TargetStatusNotes
Android emulator✓ SupportedOverlay and daemon verified with the sample app.
iOS simulator✓ SupportedOverlay and daemon verified with the sample app.
Desktop (JVM)✓ SupportedThe reference sample.
iPhone, over USB✓ SupportedSince 1.2.0. Plug it in and run inspector serve, with no code change. Verified on an iPhone 12 Pro: 188 calls archived, and replay signing works across the cable. Cable only; a phone paired over Wi-Fi is skipped by design.
Android phone, over USB◐ EarlyConnects through adb reverse. The route is verified on an emulator, but no Android build has run on a physical phone yet. Reports welcome.
Production safety

Zero cost in release, and a check that proves it

Release builds swap every module for a no-op twin with an identical API. A canary check then scans your release artifacts and fails the build if any capture code made it in.

Debug builds depend on the real modules; release builds swap in no-op twins and pass a canary check debug build release build · -Pinspector=off your app your app inspector-core inspector-ui inspector-stream noop noop-ui noop-stream same public API 🛡 check-release-clean.sh no canary found → ship
Get started

Three steps to your first captured call

Requires Kotlin 2.3.20, Compose Multiplatform 1.11 and Ktor 3. The integration guide covers the rest.

Add the dependency

implementation("io.github.shafichariri:inspector-core:1.3.0")
implementation("io.github.shafichariri:inspector-ui:1.3.0")

Install it and wrap your root

val client = HttpClient(engine) {
    Inspector.install(this)   // add last
}
InspectorOverlay { App() }

Optional: run the daemon for the web UI and MCP

inspector serve          # then open http://127.0.0.1:8099
inspector mcp            # point your agent's MCP config here
Know before you install

What it doesn't do

These limits are deliberate choices, and they're listed here so you don't find them the hard way.

Not process-wideIt sees Ktor and any OkHttp client you hand it. It doesn't see WebViews or native URLSession.
Not wire-levelBodies may already be decompressed. There's no DNS, TLS or TTFB breakdown.
No WebSockets or SSEIt captures request/response traffic only.
Never in productionEnforced by the no-op swap and the canary check above.