Skip to content

lukelmouse-github/android-debug-mcp

Repository files navigation

Android Debug MCP

中文 | English

Android Debug MCP 是一个本地 MCP server,用来让 AI agent 调试 Android debuggable app 的运行时状态。

它把屏幕查看、受控设备操作、JDWP/JDI attach、Java/Kotlin 行断点、变量读取、timeline 导出、断点证据记录等能力暴露成 MCP tools。

一句话原理

AI client 调用一个本地 Kotlin MCP server;server 通过 adb 操作设备,通过 JDWP/JDI attach 到 app,然后把断点、变量、屏幕和时序证据结构化返回给 AI。

为什么需要这个项目

AI 可以读源码,但 Android 运行时 bug 只看源码往往不够。真实问题通常依赖当前屏幕、进程、线程、调用栈、对象状态和事件顺序。

Android Debug MCP 的目标是给 AI 一条安全、可控、可复盘的运行时调试通道:

  • AI 负责判断下一步调试策略。
  • MCP server 负责可靠执行底层调试动作。
  • 结论来自断点、变量、屏幕和 timeline 证据。

核心能力

  • 列出已连接的 Android 设备。
  • 获取截图、UIAutomator 树、屏幕尺寸和当前 Activity。
  • 执行受控输入动作,例如 tap、swipe、text、back、launch,以及基于 UI 文本的点击。
  • 通过 adb forward tcp:<port> jdwp:<pid> attach 到 debuggable Android app。
  • 设置 Java/Kotlin class + line 断点,包括尚未加载的 class。
  • 等待断点命中,并查看线程、调用栈、本地变量名、类名、方法名和行号。
  • 从暂停的 stack frame 中读取指定变量和字段。
  • 在多模块项目中把 Kotlin/JVM class name 解析回可能的源码文件。
  • 记录调试会话 timeline,并导出 session.json / session.html
  • File.kt:L123 聚合断点证据,并保留全局命中顺序。
  • 执行短距离 server-side debug plan,用于确定性的重复采集循环。

安全边界

  • 只支持 Java/Kotlin 运行时调试。
  • 不支持 native 或 C++ 调试。
  • 不修改变量。
  • 不任意调用 app 方法。
  • 不执行任意 adb shell
  • 设备操作必须通过 allowlist 内的 action_execute action。
  • 断点变量读取只作用于当前暂停的事件。

这些限制是为了保证运行时证据可信。一个可以随意修改 app 状态的调试工具,很可能制造出它正在试图诊断的问题。

底层原理

flowchart LR
  AI["AI Client"] --> MCP["MCP JSON-RPC over stdio"]
  MCP --> Server["android-debug-mcp Kotlin Server"]
  Server --> ADB["adb"]
  ADB --> Device["Android Device"]
  Server --> JDI["JDI VirtualMachine"]
  JDI --> JDWP["adb forward tcp:port -> jdwp:pid"]
  JDWP --> App["Debuggable App Process"]
  Server --> Evidence["Timeline + Breakpoint Evidence"]
Loading

server 通过 stdio 使用 MCP JSON-RPC 通信。设备层操作使用 adb;运行时断点、调用栈和变量读取使用 JDI,并通过 adb 转发出来的 JDWP 端口连接 app。

attach 流程:

  1. 根据 package 或 process name 找到目标进程 pid。
  2. 把本地 TCP 端口转发到 app 的 JDWP endpoint。
  3. 使用 JDI SocketAttach attach。
  4. 使用返回的 VirtualMachine 处理断点、事件队列、stack frame 和变量。

断点行为:

  • 如果 class 已加载,server 立即安装 BreakpointRequest
  • 如果 class 尚未加载,server 先安装 ClassPrepareRequest,等 class 加载后再创建真正断点。
  • 断点使用 SUSPEND_EVENT_THREAD,因此只暂停命中的线程。

环境要求

  • jdk.jdi 模块的 JDK。
  • Android SDK platform-tools。
  • adb 在 PATH 中可用,或者设置 ANDROID_DEBUG_MCP_ADB
  • 已连接并开启 USB debugging 的 Android 设备或模拟器。
  • 目标 app 必须是 debuggable。

构建

./gradlew build
./gradlew installDist

运行 MCP server:

build/install/android-debug-mcp/bin/android-debug-mcp

server 会在 stdio 上等待 MCP JSON-RPC 消息。

AI 使用指南

推荐工作流是:你描述 Android 运行时问题,AI 选择断点和变量路径,MCP server 负责真实的设备操作、JDWP/JDI attach、断点处理、变量读取和证据记录。

1. 准备目标 App

确保 Android app 是 debuggable,并且正在已连接的设备或模拟器上运行:

adb devices

AI 至少需要这些信息:

  • packageName:目标 app 的 Android 包名。
  • projectRoot:本地源码目录,用于查找 class 和行号。
  • 一个具体的 bug、操作流程或运行时问题。

可选但有用的信息:

  • processName:当目标是子进程时需要提供。
  • deviceSerial:当连接多个设备时需要提供。
  • 可疑 class、method、路由、日志或 UI 操作步骤。

2. 安装 Codex 插件

本仓库在 codex-marketplace/ 下提供了一个可选的本地 Codex 插件。

codex plugin marketplace add /path/to/android-debug-mcp/codex-marketplace
codex plugin add android-debug-mcp@android-debug-local

让插件知道当前仓库路径:

export ANDROID_DEBUG_MCP_REPO=/path/to/android-debug-mcp

如果你的 Codex 环境没有继承 shell 环境变量,可以直接编辑 codex-marketplace/plugins/android-debug-mcp/.mcp.json,把 ${ANDROID_DEBUG_MCP_REPO...} 替换为仓库绝对路径。

3. 新开 AI 会话

安装插件后,新开一个 Codex 会话,让 MCP server 和 skill 被加载。

提示词模板:

用 $android-runtime-debug 调试我的 Android App。

packageName: com.example.app
processName: com.example.app
projectRoot: /path/to/android-project

问题:
我在首页点击 Refresh 后,列表没有更新。

请先查看当前屏幕和 UI 树,再从源码里选择精确的 Java/Kotlin 断点。命中断点后读取关键变量,每次暂停后都要 resume,最后 detach。

请保留本次 session 的 breakpoint evidence。最后按 File.kt:Lxx 汇总观察到的变量值,并使用 breakpointOrder 解释真实运行时顺序。

多设备场景:

deviceSerial: emulator-5554

子进程场景:

processName: com.example.app:worker

4. AI 通常会调用哪些工具

device_list
screen_snapshot
source_resolve
debug_attach
debug_set_breakpoint
action_execute
debug_wait_event
debug_read_variables
debug_resume
debug_breakpoint_results
timeline_export
debug_detach

如果 Kotlin 行号映射不稳定,AI 可能会使用:

debug_set_breakpoint_group

对于确定性的重复采集,AI 可能会使用短 plan:

debug_run_plan

debug_run_plan 应该保持短小。它适合减少重复的 wait/read/resume 循环,但不应该替代 AI 的判断。如果运行时证据和预期不一致,AI 应该暂停、查看证据,并交互式决定下一步。

5. 好的提问方式

用 $android-runtime-debug。
App 包名是 com.example.app,源码在 /path/to/android-project。
在 Search 页面输入关键词并按 Enter 后,结果列表为空。
请找出 query request 在哪里构造,读取运行时 query 参数,并导出 timeline HTML 报告。

如果你需要基于证据分析 bug:

用 $android-runtime-debug。
保留 breakpoint evidence。
每次断点命中后,读取足够证明状态错误原因的变量。
最后使用 breakpointOrder 和每个 File.kt:Lxx 下的变量值解释根因。

6. AI 必须遵守的规则

  • 每次断点命中后都必须调用 debug_resumedebug_detach
  • 优先读取小而明确的变量路径,不要直接展开巨大对象。
  • Kotlin coroutine、lambda、inline frame 场景下使用 autoResolveFrame=true
  • 需要长期保存报告时使用 timeline_export(includeHtml=true)
  • 需要断点证据时,最终分析前调用 debug_breakpoint_results
  • 不要要求 server 修改变量或任意调用 app 方法。
  • 不要使用任意 adb shell,请使用 action_execute

7. 不通过 Codex 直接使用 MCP

任意 MCP client 都可以启动:

build/install/android-debug-mcp/bin/android-debug-mcp

通信方式是 stdio。client 应先调用 initialize,再调用 tools/list,最后用 tools/call 调具体工具。

典型调试流程

列出设备:

{"name": "device_list", "arguments": {}}

获取当前屏幕状态:

{
  "name": "screen_snapshot",
  "arguments": {
    "includeScreenshot": true,
    "includeUiTree": true
  }
}

解析源码位置:

{
  "name": "source_resolve",
  "arguments": {
    "className": "com.example.app.feature.HomeFragment",
    "projectRoot": "/path/to/android-project",
    "maxResults": 20
  }
}

Attach 到 app:

{
  "name": "debug_attach",
  "arguments": {
    "packageName": "com.example.app",
    "processName": "com.example.app"
  }
}

设置断点:

{
  "name": "debug_set_breakpoint",
  "arguments": {
    "sessionId": "session-...",
    "className": "com.example.app.feature.HomeFragment",
    "line": 123
  }
}

触发目标代码路径:

{
  "name": "action_execute",
  "arguments": {
    "sessionId": "session-...",
    "action": {
      "type": "tap_text",
      "text": "Refresh",
      "match": "exact"
    }
  }
}

等待断点命中:

{
  "name": "debug_wait_event",
  "arguments": {
    "sessionId": "session-...",
    "timeoutMs": 15000,
    "includeLocals": false,
    "includeScreen": true
  }
}

读取变量:

{
  "name": "debug_read_variables",
  "arguments": {
    "sessionId": "session-...",
    "frameId": 0,
    "paths": ["this", "request", "this.state"],
    "autoResolveFrame": true,
    "frameSearchLimit": 20,
    "maxDepth": 1,
    "maxFields": 20,
    "maxArrayItems": 20,
    "maxStringLength": 1000
  }
}

恢复执行:

{"name": "debug_resume", "arguments": {"sessionId": "session-..."}}

每次断点命中后,都必须 resume 或 detach。

读取断点证据:

{
  "name": "debug_breakpoint_results",
  "arguments": {
    "sessionId": "session-...",
    "maxHits": 200
  }
}

导出 timeline:

{
  "name": "timeline_export",
  "arguments": {
    "sessionId": "session-...",
    "includeHtml": true
  }
}

Detach:

{"name": "debug_detach", "arguments": {"sessionId": "session-..."}}

设备操作

常用 allowlisted actions:

{"type": "tap", "x": 300, "y": 1200}
{"type": "swipe", "x1": 610, "y1": 2100, "x2": 610, "y2": 700, "durationMs": 500}
{"type": "text", "text": "hello"}
{"type": "keyevent", "keyCode": 66}
{"type": "back"}
{"type": "home"}
{"type": "enter"}
{"type": "launch", "packageName": "com.example.app"}
{"type": "pull_to_refresh"}
{"type": "tap_text", "text": "Refresh", "match": "exact"}
{"type": "tap_content_desc", "contentDescription": "Back"}
{"type": "scroll_to_text", "text": "Load more", "direction": "down", "maxSwipes": 8}
{"type": "tap_bounds", "bounds": {"left": 10, "top": 20, "right": 180, "bottom": 88}}

候选断点组

当 Kotlin 行号映射不稳定时,可以一次安装多个候选行,让 server 只在目标变量可读时停下来。

{
  "name": "debug_set_breakpoint_group",
  "arguments": {
    "sessionId": "session-...",
    "name": "request-state",
    "candidates": [
      {"className": "com.example.app.feature.HomeFragment", "line": 120},
      {"className": "com.example.app.feature.HomeFragment", "line": 128},
      {"className": "com.example.app.feature.HomeFragment", "line": 136}
    ],
    "requiredPaths": ["request", "this.state"],
    "autoResumeUntilReadable": true,
    "frameSearchLimit": 20
  }
}

短 Debug Plan 示例

{
  "name": "debug_run_plan",
  "arguments": {
    "sessionId": "session-...",
    "plan": {
      "name": "one-off-refresh-trace",
      "setup": [
        {
          "kind": "line_breakpoint",
          "className": "com.example.app.feature.HomeFragment",
          "line": 123
        }
      ],
      "trigger": [
        {
          "kind": "android_action",
          "action": {
            "type": "tap_text",
            "text": "Refresh",
            "match": "exact"
          }
        }
      ],
      "onEvent": [
        {
          "match": {"event": "breakpoint_hit"},
          "actions": [
            {"kind": "snapshot", "depth": 12},
            {
              "kind": "read_variables",
              "paths": ["request", "this.state"],
              "autoResolveFrame": true,
              "frameSearchLimit": 20
            },
            {"kind": "resume"}
          ]
        }
      ],
      "limits": {
        "timeoutMs": 60000,
        "maxEvents": 20
      }
    }
  }
}

工具列表

Tool 用途
device_list 列出 adb 设备
screen_snapshot 获取截图、UI 树、屏幕尺寸和当前 Activity
action_execute 执行受控 Android 输入动作
source_resolve 将 JVM/Kotlin class name 解析为源码候选
debug_attach Attach 到 debuggable Android 进程
debug_detach Detach 并移除 adb forward
debug_set_breakpoint 设置 class + line 断点
debug_clear_breakpoint 删除断点
debug_list_breakpoints 查看断点状态
debug_wait_event 等待断点命中
debug_read_variables 从暂停 frame 读取变量
debug_set_breakpoint_group 设置候选断点行
debug_clear_breakpoint_group 清理候选断点组
debug_list_breakpoint_groups 查看候选断点组
debug_resume 恢复暂停事件
debug_frame_snapshot 缓存当前调用栈
debug_get_snapshot 读取缓存的 frame snapshot
debug_breakpoint_results 按源码行读取断点证据
debug_clear_breakpoint_results 清理断点证据
debug_run_plan 运行短 server-side debug plan
debug_plan_events 读取或等待 plan 进度事件
debug_get_plan_report 读取当前或最终 plan 报告
debug_abort_plan 中止运行中的 plan
debug_pause_plan 请求 plan 在下次事件处暂停
timeline_get 读取 session timeline
timeline_export 导出 session.json 和可选 session.html
timeline_clear 清理 timeline

开发检查

./gradlew -q build
./gradlew -q installDist

列出 MCP tools:

printf 'Content-Length: 58\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}Content-Length: 58\r\n\r\n{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | build/install/android-debug-mcp/bin/android-debug-mcp

常见问题

  • pid not found:app 进程没有运行,或 processName 不正确。
  • no_location:该行不可执行、本地源码和 APK 不匹配,或 Kotlin 行号映射发生偏移。
  • no suspended breakpoint event:需要先调用 debug_wait_event,或让 debug plan pause/yield。
  • vm_in_plan:当前有 plan 正在运行;应先读取 plan event、暂停 plan 或中止 plan,再执行会改变 session 状态的操作。
  • 变量输出过大:降低渲染限制,或使用 projection/artifact 选项。

设计原则

  • AI 决定下一步调试策略。
  • server 执行可靠的底层运行时操作。
  • 运行时结论应该来自断点、变量、屏幕和 timeline 证据。
  • Debug plan 是短距离加速器,不是提前写死的推理脚本。
  • 默认行为保持 read-only,除非是明确的 UI input action 和 resume/detach。

About

android-debug-mcp

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors