Attach a debugger to a running Android app (JDWP via kotlin-debug-adapter, or zero-config jdb by fully-qualified name with no sources) or iOS-simulator app (lldb-dap) and read what UI and logcat cannot show — locals at a method hit, exception frames — through 14 MCP tools.
Prerequisites: Python 3.13+ (stock macOS python3 is older; uv tool install linc-spectra fetches its own Python 3.13 automatically, whereas running pip or pip3 on an older interpreter fails with No matching distribution found for linc-cli-kit).
Install with uv, pipx, or pip (Python >= 3.13):
uv tool install linc-spectrapipx install linc-spectrapip install linc-spectra
uv tool install places spectra (and linc-spectra) in ~/.local/bin; a shell without ~/.local/bin on PATH gets exit code 127 (command not found) — run uv tool update-shell and open a new shell (or add ~/.local/bin to PATH).
Then check your environment with uvx linc-spectra doctor (or spectra doctor once installed):
- What it checks:
adb,kotlin-debug-adapter,jdb,lldb-dap(macOS only),xcrun(macOS only), andpython(>= 3.13). On macOS,doctorprobes the/usr/bin/jdband/usr/bin/xcrunstubs and reports them as failed until a JDK or Xcode / Command Line Tools is installed. - Exit codes: exits
0when at least one debugger lane is usable (Android:adbpluskotlin-debug-adapterorjdb; iOS on macOS:xcrunpluslldb-dap), or exits1with ahint:line for every failed check. Pass--json(spectra doctor --json) for a single{ok, checks}JSON object on stdout.
- Android — zero-config
jdblane: requiresadb(Android SDKplatform-tools), a debuggable build (android:debuggable="true"), andjdb(ships with any JDK onPATH). Breaks on methods and exceptions by fully-qualified JVM name without local source files. - Android — full DAP lane: requires
adb, a debuggable build, local source files, andkotlin-debug-adapter. Unpack a release zip fromfwcd/kotlin-debug-adaptersoadapter/bin/kotlin-debug-adapteris onPATHor installed under~/tools/kotlin-debug-adapter/(~/tools/kotlin-debug-adapter/adapter/bin/kotlin-debug-adapter). - iOS — simulator lane (experimental): requires macOS with Xcode's
lldb-dap(checked onPATHfirst, then resolved viaxcrun -f lldb-dap) andxcrun;spectra doctorreports the macOS/usr/bin/jdband/usr/bin/xcrunstubs as failed until a JDK or Xcode is installed. Simulator only.
- Android (
device/device_serial): runadb devicesand use the value in the first (serial) column (for example,emulator-5554). - iOS Simulator (
device/device_serial): runxcrun simctl list devices bootedto find the booted simulator UDID, or pass the literalbooted.
Register the MCP server and run either the zero-config jdb flow (no local sources) or the full DAP flow:
claude mcp add linc-spectra -- spectra bellows
# Or run directly without a global install:
claude mcp add linc-spectra -- uvx linc-spectra bellows
# Zero-config flow (Android jdb — no local source checkout needed):
# spectra_method_breakpoint(device="emulator-5554", package="com.example.app", method_fqn="com.example.app.AuthRepository.login")
# -> trigger the UI in the app
# -> spectra_method_locals(session_id="<session_id>")
# Full DAP flow (Android kotlin-debug-adapter or iOS lldb-dap):
# spectra_attach(device="emulator-5554", package="com.example.app", platform="android")
# -> spectra_breakpoint(session_id="<session_id>", source_path="/abs/path/AuthRepository.kt", lines="42")
# -> spectra_continue(session_id="<session_id>")
# -> spectra_stacktrace(session_id="<session_id>")
# -> spectra_variables(session_id="<session_id>", frame_id=0) # 0 = stopped thread's top frame, or an id from spectra_stacktrace
# -> spectra_detach(session_id="<session_id>")linc-spectra exposes 14 MCP tools grouped by workflow.
| Tool | Description |
|---|---|
spectra_attach |
Attach a DAP debugger to a running debuggable app (Android JDWP or iOS Simulator LLDB) |
spectra_detach |
Detach the debugger, remove port forwards, and clean up the session |
spectra_sessions |
List all active debug sessions |
| Tool | Description |
|---|---|
spectra_breakpoint |
Set file:line breakpoints on local source files |
spectra_continue |
Resume execution and wait for the next breakpoint or stop event |
spectra_step |
Step next (over), in (into), or out (out of the current frame) |
| Tool | Description |
|---|---|
spectra_stacktrace |
Get the call stack for the stopped thread |
spectra_variables |
Inspect local and scope variables at a stack frame |
spectra_evaluate |
Evaluate an expression in the current frame context (REPL at a breakpoint) |
| Tool | Description |
|---|---|
spectra_method_breakpoint |
Arm a zero-config breakpoint on a method by fully-qualified name (<class>.<method>) with no local sources |
spectra_method_locals |
Harvest captured local variables from an armed spectra_method_breakpoint hit |
spectra_exception_breakpoint |
Arm a zero-config exception catchpoint by fully-qualified exception class name |
spectra_exception_info |
Harvest the throwing frame, thread, and locals from an armed spectra_exception_breakpoint |
| Tool | Description |
|---|---|
spectra_usage_stats |
Aggregate per-tool call counts, latency percentiles, and error rates from $LINC_HOME/mcp-calls.jsonl |
- Upstream
kotlin-debug-adapterexception breakpoints do not work: usespectra_exception_breakpoint(thejdbcatchpoint) instead. file:linebreakpoints reportverified: trueeven without local sources:kotlin-debug-adaptermarks non-existent paths as verified even though they can never bind;spectra_breakpointattaches awarningfield whensource_pathis missing locally (usespectra_method_breakpointwhen you do not have the source checkout).- One debugger per process: a target process exposes a single JDWP connection at a time — detach any active DAP session before arming a
jdbsession on the same app (and vice versa). - iOS simulator only: iOS debugging via
lldb-dapsupports booted iOS simulators on macOS only (experimental; physical iOS devices are not supported). - iOS
spectra_evaluatetakes expressions only: iOS expressions run inlldb-dap's expression-only (watch) context and LLDB commands (such as backtick-prefixed commands) are refused. jdbrenders values as text: a debuggee string containing line breaks can add a fakename = valueline to ajdbreply (spectrakeeps the first value of a name within a reply).- Bounded
jdbwaits and stepping:spectra_method_localsandspectra_exception_infowait at most180 s(wait_s0–180), and manual stepping takesmax_steps0–16. - Python >= 3.13: requires Python 3.13 or newer.
- No telemetry, no phone-home. spectra runs locally over stdio MCP and opens no network listener; it talks only to your local
adb,jdb,kotlin-debug-adapter,xcrun, andlldb-dapprocesses. - Local usage log under your control. Every MCP tool call is recorded in
$LINC_HOME/mcp-calls.jsonl(default~/.linc/mcp-calls.jsonl, rotated at 10 MB). Control logging withSPECTRA_USAGE_LOG=off|redacted|full:redacted(default) — sensitive fields (expressionandsource_path) are replaced by length-only placeholders (<redacted len=N>, no digest), expression text is scrubbed from recorded error strings, and the log file is opened owner-only (0600) withO_NOFOLLOW.off— nothing is written to disk.full— raw parameter values are logged (long strings truncated at 500 characters, byte buffers summarized by length). Opt in only on a machine you trust.
spectra_evaluateruns code inside the debuggee. By design,spectra_evaluateand thejdbtools execute inside the attached debuggable application. Identifiers that reachadb shell,xcrun simctl, orjdbstdin are validated with strictre.fullmatchpatterns before execution. See SECURITY.md for the threat model and vulnerability reporting instructions.
This repository is a release mirror of LINC's private repository — each release lands here as a single commit. Issues are welcome; pull requests are accepted by arrangement (open an issue first). See CONTRIBUTING.md.
Built by LINC Innovations. Licensed under Apache-2.0.