1. 为什么我要把 ADB 塞进 MCP Server
做 Android 开发的朋友大概都有这种体验:调试时在终端和 IDE 之间来回切,adb logcat、adb shell dumpsys、adb install敲到手酸,日志刷屏还得手动 grep。更麻烦的是,现在 AI 助手已经能帮我写代码、读日志、分析崩溃栈了,但它够不着我的真机——它看不到adb devices的输出,也没法自己点一下屏幕截图看看 UI 长什么样。
MCP(Model Context Protocol)就是来解决这个"够不着"的问题的。它是一套开放协议,把外部工具包装成 AI 助手能理解的标准接口。你写一个 MCP Server,把 ADB 命令暴露成一个个 tool,AI 助手就能像调用函数一样调用它们:你说"看看现在连着几台设备",它自己去跑adb devices -l;你说"把最近 100 行 ERROR 日志捞出来",它自己执行adb logcat -d -t 100再帮你过滤。
但这里有个现实问题:当你同时接 Claude Code、Cline、Codex 好几个工具时,每个工具都要单独配一遍 API Key、Base URL、Model ID,通道分散、Key 满天飞,改一个地方要同步改五份配置。我试过用 TaoToken 做统一通道,一个 Key 走所有工具,MCP Server 这边只管 ADB 逻辑,模型调用全部收敛到一处,配置清爽很多。
这篇就按"从零搭建"的节奏走:先讲清楚 MCP Server 的骨架长什么样,再给出config.toml和 TaoToken 统一通道的配置片段,最后用adb devices、截图、点击这几条命令,一步步验证 AI 助手真的能操控你的 Android 设备。适合有 Node.js 基础、想让 AI 助手接管真机调试的 Android 开发者。
2. 前置准备:Node 环境、ADB 与 TaoToken 统一通道
动手之前,先把三样东西备齐:Node.js 运行时、可用的 ADB、以及一个能统一管理模型调用的通道。前两样是 MCP Server 跑起来的基础,第三样决定了你后面接多个 AI 工具时会不会被 Key 搞疯。
2.1 确认 ADB 可用并授权设备
先确认 ADB 在 PATH 里,设备能被识别:
which adb adb version adb devices -l正常输出类似:
List of devices attached R5CT90XXXXX device product:beyond1q model:SM_G9730 device:beyond1q transport_id:1如果设备那一列显示unauthorized,说明手机上的 USB 调试授权弹窗还没点"允许"。拔插一次数据线,在手机上确认授权即可。这一步不做,后面所有 ADB 命令都会卡在权限上。
2.2 初始化 MCP Server 项目
新建目录并装官方 SDK:
mkdir adb_mcp && cd adb_mcp npm init -y npm install @modelcontextprotocol/sdkpackage.json里把入口和 bin 补上,方便后面用命令直接启动:
{ "name": "adb-mcp-server", "version": "1.0.0", "description": "MCP Server for Android Debug Bridge (ADB) operations", "main": "adb-mcp-server.js", "bin": { "adb-mcp-server": "./adb-mcp-server.js" }, "scripts": { "start": "node adb-mcp-server.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" } }2.3 用 TaoToken 收敛模型调用通道
MCP Server 本身只负责执行 ADB 命令,它不直接调模型。真正调模型的是 Claude Code、Cline、Codex 这些客户端。问题在于:每个客户端都要填 Base URL、API Key、Model ID,工具一多就乱。
TaoToken 在这里的角色是统一通道:你只维护一份 Key,所有客户端都指向同一个 Base URL,换模型、换额度只改一处。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (这个不加 UTM,配置里直接写)。
先去控制台建一个 Key:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后在 API Keys 页面生成:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。生成的 Key 形如sk-xxxxxxxx,先复制到剪贴板,下一步配置要用。
注意:Key 只显示一次,丢了就得重新生成。别把它硬编码进会提交到 Git 的文件里,用环境变量或本地配置文件。
3. 可复制配置:config.toml 骨架与 TaoToken 通道片段
这一节是全文的核心,给你两份能直接抄的配置:一份是 MCP Server 的config.toml骨架,一份是 TaoToken 统一通道的接入片段。路径和字段名都按实际能跑通的来写,改完就能用。
3.1 MCP Server 的 config.toml 骨架
在项目根目录建config.toml,把 ADB 路径、超时、允许的命令白名单都放进去,避免硬编码:
# adb_mcp/config.toml [server] name = "adb-server" version = "1.0.0" transport = "stdio" [adb] # ADB 可执行文件路径,Windows 下类似 C:\\platform-tools\\adb.exe binary = "adb" # 单条命令超时(毫秒) timeout_ms = 15000 # 默认目标设备序列号,留空则用第一台 default_serial = "" [security] # 命令白名单,只有列在这里的子命令才允许执行 allowed_commands = [ "devices", "logcat", "install", "shell", "exec-out", "push", "pull" ] # 禁止的危险操作 blocked_patterns = ["rm -rf", "format", "fastboot erase"] [logging] level = "info" file = "./adb_mcp.log"这份骨架里,allowed_commands和blocked_patterns是安全底线。AI 助手再聪明也可能生成意料之外的命令,白名单能兜住大部分风险。timeout_ms设 15 秒是因为adb install大 APK 时可能慢,设太短会误判超时。
3.2 TaoToken 统一通道的接入片段
MCP Server 跑起来后,客户端要连模型。以 Claude Code 为例,它的配置走settings.json,把 Base URL 和 Key 指向 TaoToken:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929" } }如果你用 Cline,配置在 VS Code 的settings.json里,字段名不同但三件套一样:
{ "cline.apiProvider": "anthropic", "cline.apiKey": "sk-你的TaoToken密钥", "cline.baseUrl": "https://taotoken.net/api", "cline.model": "claude-sonnet-4-5-20250929" }Codex 走~/.codex/auth.json,结构是这样:
{ "OPENAI_API_KEY": "sk-你的TaoToken密钥", "OPENAI_BASE_URL": "https://taotoken.net/api", "model": "gpt-5-codex" }三件套记牢:Base URL 填https://taotoken.net/api,Key 填 TaoToken 生成的,Model ID 按你实际要用的模型填。不管接几个工具,这三样都指向同一处,改模型只改这里。
3.3 把 MCP Server 注册到客户端
以 Claude Code 为例,用命令行把 ADB MCP Server 加进去:
claude mcp add adb node /Users/你的用户名/adb_mcp/adb-mcp-server.js参数说明:adb是 Server 名称,node是运行命令,最后是adb-mcp-server.js的绝对路径。加完后用claude mcp list确认注册成功。
Claude Desktop 则编辑~/Library/Application Support/Claude/claude_desktop_config.json:
{ "mcpServers": { "adb": { "command": "node", "args": ["/Users/你的用户名/adb_mcp/adb-mcp-server.js"] } } }改完重启客户端。到这里,模型通道(TaoToken)和工具通道(MCP Server)就都通了。
4. 验证请求:用 adb devices、截图、点击确认 AI 真的操控了设备
配置写完不算数,得看到 AI 助手真的把命令执行下去、结果回传上来。这一节用三条递进的命令验证:先列设备,再截图,最后点击。
4.1 核心代码:把 ADB 命令包装成 MCP tool
先看adb-mcp-server.js的关键部分。Server 初始化声明能力和工具列表:
#!/usr/bin/env node const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js'); const { exec } = require('child_process'); const { promisify } = require('util'); const execPromise = promisify(exec); const server = new Server( { name: 'adb-server', version: '1.0.0' }, { capabilities: { tools: {} } } ); const tools = [ { name: 'adb-devices', description: 'List connected Android devices', inputSchema: { type: 'object', properties: {} } }, { name: 'adb-screenshot', description: 'Take a screenshot and save to local path', inputSchema: { type: 'object', properties: { savePath: { type: 'string', description: 'Local path to save screenshot' } }, required: ['savePath'] } }, { name: 'adb-tap', description: 'Tap on screen at given coordinates', inputSchema: { type: 'object', properties: { x: { type: 'number', description: 'X coordinate' }, y: { type: 'number', description: 'Y coordinate' } }, required: ['x', 'y'] } } ];请求处理里,ListToolsRequest返回工具列表,CallToolRequest按 name 分发执行:
server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result; switch (name) { case 'adb-devices': { const { stdout } = await execPromise('adb devices -l'); result = stdout; break; } case 'adb-screenshot': { await execPromise(`adb exec-out screencap -p > "${args.savePath}"`); result = `Screenshot saved to ${args.savePath}`; break; } case 'adb-tap': { const { stdout } = await execPromise(`adb shell input tap ${args.x} ${args.y}`); result = stdout || `Tapped at (${args.x}, ${args.y})`; break; } default: throw new Error(`Unknown tool: ${name}`); } return { content: [{ type: 'text', text: result }] }; } catch (error) { return { content: [{ type: 'text', text: `Error executing ${name}: ${error.message}` }], isError: true }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('ADB MCP Server running on stdio'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); });给执行权限:
chmod +x adb-mcp-server.js4.2 第一验证:让 AI 列出设备
在 Claude Code 里输入自然语言:"列出当前连接的 Android 设备"。AI 会调用adb-devices,返回类似:
List of devices attached R5CT90XXXXX device product:beyond1q model:SM_G9730 device:beyond1q transport_id:1看到设备序列号和device状态,说明 MCP Server 到 ADB 这条链路通了。如果这里返回空列表,先回到 2.1 检查adb devices -l在终端里能不能跑通。
4.3 第二验证:截图回传
输入:"截个图保存到 /tmp/screen.png"。AI 调用adb-screenshot,执行adb exec-out screencap -p > /tmp/screen.png。返回Screenshot saved to /tmp/screen.png后,本地打开这张图,应该能看到手机当前画面。
这一步验证的是exec-out通道——它比adb shell screencap更干净,不会混入换行符导致 PNG 损坏。如果你打开图片是花的,多半是用了shell而不是exec-out。
4.4 第三验证:点击屏幕
输入:"在坐标 (540, 1200) 点一下"。AI 调用adb-tap,执行adb shell input tap 540 1200。手机屏幕上对应位置应该有反应,比如点开了某个图标。
三条命令跑通,说明 AI 助手已经能通过 MCP Server 读设备状态、抓屏幕、发输入事件。再往上叠adb logcat、adb install、adb shell dumpsys meminfo,就是完整的真机调试闭环。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
配置过程中最容易卡在几个固定报错上。这一节按真实错误信息对照排查,每条都给定位思路。
5.1 401 Unauthorized
现象:客户端调用模型时返回401,日志里能看到invalid api key或authentication failed。
原因基本是 Key 不对或没生效。检查三处:一是 TaoToken 控制台里 Key 是否还在、有没有被删;二是配置文件里ANTHROPIC_API_KEY或OPENAI_API_KEY有没有多余空格、换行;三是 Base URL 是不是写成了https://taotoken.net/api,少写/api或写成别的路径都会 401。
改完配置记得重启客户端,很多工具不会热加载。
5.2 local proxy failed
现象:客户端启动时报local proxy failed或connection refused。
这通常是客户端在本地起了一个转发进程,但环境变量没配对。检查ANTHROPIC_BASE_URL是否指向https://taotoken.net/api,以及有没有残留的旧代理配置在干扰。把客户端完全退出(不是关窗口,是杀进程)再启动,让新配置生效。
5.3 reading choices 报错
现象:返回体解析失败,日志里出现reading 'choices'或cannot read property of undefined。
这是响应格式和客户端预期不匹配。常见于 Model ID 填错——比如客户端按 OpenAI 格式解析,你却填了个只支持 Anthropic 格式的模型名。回到 3.2 的三件套,确认 Model ID 和客户端类型匹配。换一个明确的模型名再试。
5.4 OAuth 相关报错
现象:提示OAuth token expired或要求重新登录。
如果你用的是走 OAuth 的客户端(比如某些版本的 Claude Code),它可能优先走 OAuth 而不是 API Key。检查配置里是否同时存在 OAuth 凭据和 API Key,两者冲突时以哪个为准因版本而异。最稳的做法是清掉 OAuth 缓存,只保留 TaoToken 的 API Key 配置。
5.5 MCP Server 侧报错
现象:AI 调用工具时返回Error executing adb-devices: ...。
这类错误在 MCP Server 的 catch 里被捕获,error.message会带出来。常见原因:ADB 不在 PATH(spawn adb ENOENT)、设备未授权(device unauthorized)、命令超时。对照 2.1 和 3.1 的配置逐项检查,config.toml里的binary路径在 Windows 下要写全。
6. 把通道固定下来,让 AI 接管真机调试
走到这里,你应该已经能在 Claude Code 或 Cline 里说一句"看看设备连没连",然后看着 AI 自己跑adb devices -l把结果贴回来。截图、点击也都能通过自然语言触发。MCP Server 负责把 ADB 能力标准化,TaoToken 负责把模型调用收敛成一条通道,两边各管一摊,配置不再散落。
几个实际用下来的经验:config.toml里的命令白名单别偷懒,AI 生成的命令偶尔会超出预期,白名单是最后一道闸;exec-out和shell要分清,截图、拉文件用exec-out,交互式命令用shell;Model ID 一定和客户端类型对齐,401 和 reading choices 十有八九是这里出的问题。
想继续往下扩,可以给 MCP Server 加adb-logcat带过滤参数、adb-push/adb-pull做文件传输、adb-shell做通用命令透传。每加一个 tool,就在tools数组里补定义、在 switch 里补分支,模式完全一样。模型通道那边,需要长期跑编码和 Agent 任务的话,可以看看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,想先验证模型效果可以直接开对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。