技术小黑屋

一看就会,为 AI 编程 Agent 撸一个 MCP 服务

通过 MCP (Model Context Protocol) 让 AI 助手直接调用 ADB 命令操作 Android 设备,实现日志查看、应用安装、性能分析等自动化操作。

MCP 协议说明

MCP 是 Anthropic 推出的开放协议,用于连接 AI 助手与外部工具。MCP Server 将特定工具包装成标准化接口,让 AI 能够理解和调用。

架构如下:

1
Claude Desktop/API → MCP Server → ADB Commands → Android Device

实现步骤

初始化项目

1
2
3
4
mkdir adb_mcp
cd adb_mcp
npm init -y
npm install @modelcontextprotocol/sdk

配置 package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "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"
  },
  "keywords": ["mcp", "adb", "android", "debug"],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  }
}

核心代码实现

创建 adb-mcp-server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/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);

// 创建 MCP Server 实例
const server = new Server(
  {
    name: 'adb-server',
    version: '1.0.0'
  },
  {
    capabilities: {
      tools: {}
    }
  }
);

// 定义工具列表
const tools = [
  {
    name: 'adb-logcat',
    description: 'Get Android device logs',
    inputSchema: {
      type: 'object',
      properties: {
        lines: {
          type: 'number',
          description: 'Number of log lines to retrieve',
          default: 100
        }
      }
    }
  },
  {
    name: 'adb-devices',
    description: 'List connected Android devices',
    inputSchema: {
      type: 'object',
      properties: {}
    }
  },
  {
    name: 'adb-install',
    description: 'Install APK on connected device',
    inputSchema: {
      type: 'object',
      properties: {
        apkPath: {
          type: 'string',
          description: 'Path to the APK file to install'
        }
      },
      required: ['apkPath']
    }
  },
  {
    name: 'adb-app-info',
    description: 'Get app package information',
    inputSchema: {
      type: 'object',
      properties: {
        packageName: {
          type: 'string',
          description: 'Android package name (e.g., com.example.app)'
        }
      },
      required: ['packageName']
    }
  },
  {
    name: 'adb-meminfo',
    description: 'Get app memory usage information',
    inputSchema: {
      type: 'object',
      properties: {
        packageName: {
          type: 'string',
          description: 'Android package name (e.g., com.example.app)'
        }
      },
      required: ['packageName']
    }
  },
  {
    name: 'adb-clear-data',
    description: 'Clear app data and cache',
    inputSchema: {
      type: 'object',
      properties: {
        packageName: {
          type: 'string',
          description: 'Android package name (e.g., com.example.app)'
        }
      },
      required: ['packageName']
    }
  }
];

// 处理工具列表请求
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return { tools };
});

// 处理工具执行请求
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    let result;

    switch (name) {
      case 'adb-logcat': {
        const lines = args.lines || 100;
        const { stdout } = await execPromise(`adb logcat -d -t ${lines}`);
        result = stdout;
        break;
      }

      case 'adb-devices': {
        const { stdout } = await execPromise('adb devices -l');
        result = stdout;
        break;
      }

      case 'adb-install': {
        if (!args.apkPath) {
          throw new Error('apkPath is required');
        }
        const { stdout } = await execPromise(`adb install -r "${args.apkPath}"`);
        result = stdout;
        break;
      }

      case 'adb-app-info': {
        if (!args.packageName) {
          throw new Error('packageName is required');
        }
        const { stdout } = await execPromise(`adb shell dumpsys package ${args.packageName}`);
        result = stdout;
        break;
      }

      case 'adb-meminfo': {
        if (!args.packageName) {
          throw new Error('packageName is required');
        }
        const { stdout } = await execPromise(`adb shell dumpsys meminfo ${args.packageName}`);
        result = stdout;
        break;
      }

      case 'adb-clear-data': {
        if (!args.packageName) {
          throw new Error('packageName is required');
        }
        const { stdout } = await execPromise(`adb shell pm clear ${args.packageName}`);
        result = stdout;
        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);
});

赋予执行权限:

1
chmod +x adb-mcp-server.js

关键实现说明

Server 初始化

1
2
3
4
const server = new Server(
  { name: 'adb-server', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

声明服务器名称、版本和支持的能力类型。

工具定义

每个工具包含三个部分:

  • name: 唯一标识符
  • description: 功能描述,AI 根据此判断调用时机
  • inputSchema: JSON Schema 格式的参数定义

示例:

1
2
3
4
5
6
7
8
9
10
11
{
  name: 'adb-install',
  description: 'Install APK on connected device',
  inputSchema: {
    type: 'object',
    properties: {
      apkPath: { type: 'string', description: 'Path to the APK file' }
    },
    required: ['apkPath']
  }
}

请求处理

MCP 定义两种请求类型:

ListToolsRequest - 列出所有可用工具:

1
2
3
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return { tools };
});

CallToolRequest - 执行具体工具:

1
2
3
4
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  // 根据 name 执行相应 ADB 命令
});

命令执行

使用 child_process 执行 ADB 命令:

1
const { stdout } = await execPromise(`adb devices -l`);

配置方式

Claude Desktop 配置

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json

1
2
3
4
5
6
7
8
{
  "mcpServers": {
    "adb": {
      "command": "node",
      "args": ["/Users/你的用户名/Documents/self_host/adb_mcp/adb-mcp-server.js"]
    }
  }
}

配置完成后重启 Claude Desktop。

Claude Code CLI 配置

使用 Claude Code 命令行工具添加 MCP Server:

1
claude -p mcp add adb node /Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js

参数说明:

  • adb: MCP Server 名称
  • node: 运行命令
  • 最后是 adb-mcp-server.js 的完整路径

Gemini CLI 配置

编辑 Gemini CLI 配置文件 ~/.gemini/mcp_config.json

1
2
3
4
5
6
7
8
{
  "mcpServers": {
    "adb": {
      "command": "node",
      "args": ["/Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js"]
    }
  }
}

:Gemini CLI 的 MCP 配置可能因版本而异,建议以官方文档为准。

Copilot CLI 配置

编辑 Copilot CLI 配置文件 ~/.github-copilot/mcp_servers.json

1
2
3
4
5
6
{
  "adb": {
    "command": "node",
    "args": ["/Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js"]
  }
}

:Copilot CLI 的 MCP 配置可能因版本而异,建议以官方文档为准。


使用示例

在 Claude 中直接使用自然语言:

1
查看连接的 Android 设备
1
获取最近 50  logcat 日志
1
查看 com.android.chrome 的内存使用情况
1
清除 com.example.app 的数据

Claude 会自动调用对应的 MCP 工具执行操作。


应用场景

日志分析

传统方式:

1
2
adb logcat -d > log.txt
# 手动搜索错误信息

使用 MCP:

1
获取最近的 logcat 日志,找出所有 ERROR 级别信息

Claude 自动执行并分析结果。

性能监控

传统方式:

1
2
adb shell dumpsys meminfo com.example.app
# 手动分析输出数据

使用 MCP:

1
查看 com.example.app 的内存使用,分析是否有内存泄漏

批量操作

1
列出所有连接的设备,在每个设备上安装 /path/to/app.apk

Claude 自动处理设备列表和批量安装。


扩展功能

添加截图

1
2
3
4
5
6
7
8
9
10
11
{
  name: 'adb-screenshot',
  description: 'Take a screenshot',
  inputSchema: {
    type: 'object',
    properties: {
      savePath: { type: 'string', description: 'Path to save screenshot' }
    },
    required: ['savePath']
  }
}

执行命令:

1
await execPromise(`adb exec-out screencap -p > ${args.savePath}`);

添加性能监控

1
2
3
4
5
{
  name: 'adb-top',
  description: 'Get CPU usage',
  inputSchema: { type: 'object', properties: {} }
}

添加文件传输

1
2
3
4
5
6
7
8
9
10
11
12
{
  name: 'adb-push',
  description: 'Push file to device',
  inputSchema: {
    type: 'object',
    properties: {
      localPath: { type: 'string' },
      remotePath: { type: 'string' }
    },
    required: ['localPath', 'remotePath']
  }
}

注意事项

权限检查

确保 ADB 已添加到系统 PATH:

1
2
which adb
adb version

设备授权

使用前确认设备已连接并授权:

1
adb devices

如果显示 unauthorized,需在设备上确认 USB 调试授权。

错误处理

生产环境应添加:

  • 详细日志记录
  • 设备断开连接处理
  • 命令超时机制

安全性

  • 避免在不信任的环境使用
  • 注意 APK 路径注入风险
  • 考虑添加命令白名单

参考资源





快来解锁最新版 Typora,新用户券后仅需 84 元!
如何便宜的购买 office 365 ?
新版赤友 NTFS 助手来袭,正版超低价