@ddqd/droid-mcp 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # Droid MCP
2
+
3
+ М minimal MCP (Model Context Protocol) сервер для работы с Android ADB командами.
4
+
5
+ ## Описание
6
+
7
+ Этот сервер предоставляет инструменты для взаимодействия с Android устройствами через ADB. Сервер построен на официальном [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) и использует Zod для валидации входных данных.
8
+
9
+ ## Требования
10
+
11
+ - Node.js 18+
12
+ - TypeScript 5+
13
+ - ADB установлен и доступен в PATH
14
+ - Подключенное Android устройство или эмулятор
15
+
16
+ ## Зависимости
17
+
18
+ Проект использует:
19
+ - `@modelcontextprotocol/sdk` - официальный SDK для MCP серверов
20
+ - `zod` - библиотека для валидации и типизации данных
21
+
22
+ ## Установка
23
+
24
+ ```bash
25
+ npm install
26
+ npm run build
27
+ ```
28
+
29
+ ## Использование
30
+
31
+ > **📖 Примеры использования:** См. [EXAMPLES.md](EXAMPLES.md) для детальных сценариев отладки и промптов для Cursor/Windsurf.
32
+
33
+ ### Глобальная фильтрация по Package ID
34
+
35
+ Вы можете настроить автоматическую фильтрацию логов по package ID через переменную окружения `ANDROID_APP_IDS`. Это значительно уменьшит объем данных, которые получает LLM, исключив системные логи.
36
+
37
+ **Формат:** Список package ID через запятую или пробел:
38
+ ```bash
39
+ # Одно приложение
40
+ export ANDROID_APP_IDS="com.example.myapp"
41
+
42
+ # Несколько приложений
43
+ export ANDROID_APP_IDS="com.example.app1,com.example.app2"
44
+ # или
45
+ export ANDROID_APP_IDS="com.example.app1 com.example.app2"
46
+ ```
47
+
48
+ Когда `ANDROID_APP_IDS` установлена, все инструменты (`get_logcat` и `capture_session`) автоматически фильтруют логи только для запущенных процессов указанных приложений. Если приложение не запущено, выводится предупреждение, но работа продолжается без фильтра.
49
+
50
+ ### Запуск сервера
51
+
52
+ ```bash
53
+ npm start
54
+ ```
55
+
56
+ Или после сборки:
57
+
58
+ ```bash
59
+ node dist/index.js
60
+ ```
61
+
62
+ ### Доступные инструменты
63
+
64
+ #### `inspect_app_identity`
65
+
66
+ Сканирует директорию проекта и подключенное Android устройство для автоматического определения package ID (applicationId) для фильтрации логов. Ищет applicationId в файлах build.gradle и выводит список всех установленных сторонних приложений на устройстве. Показывает совпадения между проектом и устройством, помогая настроить переменную окружения `ANDROID_APP_IDS`. Параметры не требуются. Полезен для первоначальной настройки или при работе с несколькими приложениями.
67
+
68
+ **Параметры:** Нет (пустой объект)
69
+
70
+ **Что делает:**
71
+ - Сканирует `build.gradle` файлы проекта для поиска `applicationId`
72
+ - Получает список всех установленных сторонних приложений с устройства
73
+ - Показывает совпадения между проектом и устройством
74
+ - Предлагает примеры настройки `ANDROID_APP_IDS`
75
+
76
+ **Пример:**
77
+ ```json
78
+ {
79
+ "name": "inspect_app_identity"
80
+ }
81
+ ```
82
+
83
+ **Пример вывода:**
84
+ ```
85
+ === Package ID Inspection ===
86
+
87
+ [Project Source] Found in build.gradle: com.example.myapp
88
+ ✓ Currently installed on device
89
+
90
+ [Device] Third-party packages installed (15 total):
91
+ → com.example.myapp (matches gradle)
92
+ com.google.android.youtube
93
+ ru.yandex.taxi
94
+ ... and 12 more packages
95
+
96
+ [Usage] Set ANDROID_APP_IDS environment variable to filter logs:
97
+ Example: ANDROID_APP_IDS="com.example.myapp"
98
+ ```
99
+
100
+ #### `get_logcat`
101
+
102
+ Получает историю логов logcat с опциональной фильтрацией по tag. Автоматически применяет фильтр по PID, если установлена переменная `ANDROID_APP_IDS`. Полезен для просмотра недавней истории логов или отладки прошлых событий.
103
+
104
+ **Параметры:**
105
+ - `tag` (string, optional): Фильтр по tag (например, "MyApp"). При указании возвращаются только логи с этим тегом.
106
+ - `lines` (number, optional): Количество строк логов для получения. По умолчанию: 200, максимум: 2000.
107
+
108
+ **Примеры:**
109
+
110
+ ```json
111
+ {
112
+ "name": "get_logcat",
113
+ "arguments": {
114
+ "tag": "MyApp",
115
+ "lines": 500
116
+ }
117
+ }
118
+ ```
119
+
120
+ Это выполнит команду: `adb logcat -d -t 500 MyApp:*`
121
+
122
+ ```json
123
+ {
124
+ "name": "get_logcat",
125
+ "arguments": {
126
+ "lines": 200
127
+ }
128
+ }
129
+ ```
130
+
131
+ Это выполнит команду: `adb logcat -d -t 200`
132
+
133
+ **Примечание:** Если установлена переменная `ANDROID_APP_IDS`, команда автоматически добавит фильтры `--pid=<PID>` для каждого запущенного процесса указанных приложений.
134
+
135
+ #### `capture_session`
136
+
137
+ Записывает логи logcat в реальном времени, ожидая появления стоп-слова (regex pattern) или истечения таймаута. Автоматически применяет фильтр по PID, если установлена переменная `ANDROID_APP_IDS`. Поддерживает фильтрацию по tag, уровню приоритета и буферу логов. Идеально для интерактивной отладки при воспроизведении конкретных пользовательских сценариев. По умолчанию таймаут: 30 секунд (максимум: 300). Можно опционально очистить буфер перед началом для захвата только свежих логов.
138
+
139
+ **Параметры:**
140
+ - `stop_pattern` (string, required): Regex pattern или строка, при появлении которой запись остановится. Когда этот паттерн появляется в логах, захват останавливается немедленно.
141
+ - `timeout_seconds` (number, optional): Максимальное время ожидания `stop_pattern` в секундах. По умолчанию: 30, максимум: 300. Захват остановится при достижении таймаута, даже если `stop_pattern` не найден.
142
+ - `clear_buffer` (boolean, optional): Очистить буфер logcat перед началом захвата. По умолчанию: false. Полезно для начала со свежими логами.
143
+ - `tag` (string, optional): Фильтр по tag (например, "MyApp"). При указании захватываются только логи с этим тегом.
144
+ - `priority` (string, optional): Минимальный уровень приоритета логов: V (Verbose), D (Debug), I (Info), W (Warning), E (Error), F (Fatal). Захватываются только логи на этом уровне и выше.
145
+ - `buffer` (string, optional): Буфер логов для чтения: 'main' (логи приложений по умолчанию), 'system' (системные логи), 'crash' (логи крашей), 'radio' (логи радио), 'events' (логи событий), 'all' (все буферы), 'default' (основной буфер).
146
+
147
+ **Примеры:**
148
+
149
+ ```json
150
+ {
151
+ "name": "capture_session",
152
+ "arguments": {
153
+ "stop_pattern": "FATAL|Exception",
154
+ "timeout_seconds": 60,
155
+ "clear_buffer": true,
156
+ "tag": "MyApp"
157
+ }
158
+ }
159
+ ```
160
+
161
+ Это запустит запись логов, очистит буфер, будет фильтровать по tag "MyApp" и остановится при появлении "FATAL" или "Exception" или через 60 секунд.
162
+
163
+ ```json
164
+ {
165
+ "name": "capture_session",
166
+ "arguments": {
167
+ "stop_pattern": "SCENARIO_FINISHED",
168
+ "timeout_seconds": 120,
169
+ "priority": "E"
170
+ }
171
+ }
172
+ ```
173
+
174
+ Это будет записывать только ошибки (priority E и выше) до появления "SCENARIO_FINISHED" или таймаута.
175
+
176
+ **Примечание:** Если установлена переменная `ANDROID_APP_IDS`, команда автоматически добавит фильтры `--pid=<PID>` для каждого запущенного процесса указанных приложений, что значительно уменьшает объем записываемых логов.
177
+
178
+ #### `dump_view_hierarchy`
179
+
180
+ Получает иерархию View с экрана Android устройства, используя uiautomator. Возвращает структуру View с bounds, text, resource-id, content-desc и clickable атрибутами. Оптимизирован для AI-анализа с компактным JSON форматом (по умолчанию), который фильтрует пустые контейнеры для экономии токенов. Поддерживает вывод в сыром XML для отладки. Полезен для понимания UI, тестовой автоматизации и AI-агентам, которые взаимодействуют с Android приложениями.
181
+
182
+ **Параметры:**
183
+ - `format` (string, optional): Формат вывода — "xml" (сырой uiautomator XML), "json" (полное дерево), "compact" (AI-оптимизированный JSON, по умолчанию — фильтрует пустые контейнеры).
184
+ - `include_invisible` (boolean, optional): Включать non-clickable view без text/content-desc. По умолчанию: false (фильтрует пустые контейнеры). Применяется только к форматам "json" и "compact".
185
+
186
+ **Пример:**
187
+ ```json
188
+ {
189
+ "name": "dump_view_hierarchy",
190
+ "arguments": {
191
+ "format": "compact",
192
+ "include_invisible": false
193
+ }
194
+ }
195
+ ```
196
+
197
+ #### `capture_screenshot`
198
+
199
+ Получает снимок экрана Android устройства. По умолчанию сохраняет в файл и возвращает абсолютный путь. Поддерживает WebP формат (по умолчанию, AI-оптимизированный, меньший размер) и PNG (без потерь). Автоматически масштабирует для уменьшения размера при сохранении читаемости для AI. Идеален для визуальной отладки, проверки UI и документирования проблем.
200
+
201
+ **Настройка output директории:**
202
+
203
+ ```bash
204
+ # Через аргумент командной строки
205
+ node dist/index.js --output-dir /path/to/screenshots
206
+
207
+ # Через переменную окружения
208
+ export DROID_MCP_OUTPUT_DIR=/path/to/screenshots
209
+ node dist/index.js
210
+
211
+ # Если не настроено — используется .droid-mcp/screenshots в проекте
212
+ ```
213
+
214
+ **Параметры:**
215
+ - `output` (string, optional): "file" (по умолчанию, сохраняет в output директорию) или "base64" (встраивает в ответ как data URL).
216
+ - `format` (string, optional): "webp" (по умолчанию, AI-оптимизированный) или "png" (без потерь).
217
+ - `scale` (number, optional): Масштаб 0.25-1.0. По умолчанию: 0.5 (половина разрешения, ~300KB).
218
+ - `quality` (number, optional): Качество WebP 1-100. По умолчанию: 85.
219
+
220
+ **Примеры:**
221
+
222
+ Получить скриншот с настройками по умолчанию (WebP, 50% scale, файл):
223
+ ```json
224
+ {
225
+ "name": "capture_screenshot",
226
+ "arguments": {}
227
+ }
228
+ ```
229
+
230
+ Получить скриншот в PNG с полным разрешением:
231
+ ```json
232
+ {
233
+ "name": "capture_screenshot",
234
+ "arguments": {
235
+ "format": "png",
236
+ "scale": 1.0
237
+ }
238
+ }
239
+ ```
240
+
241
+ Получить скриншот как base64 data URL:
242
+ ```json
243
+ {
244
+ "name": "capture_screenshot",
245
+ "arguments": {
246
+ "output": "base64",
247
+ "format": "webp",
248
+ "scale": 0.5
249
+ }
250
+ }
251
+ ```
252
+
253
+ **Ответ (file mode):**
254
+ ```json
255
+ {
256
+ "filePath": "/absolute/path/to/screenshot_2025-01-17_14-30-25.webp",
257
+ "relativePath": "screenshot_2025-01-17_14-30-25.webp",
258
+ "format": "webp",
259
+ "sizeKb": 305,
260
+ "width": 540,
261
+ "height": 960,
262
+ "originalWidth": 1080,
263
+ "originalHeight": 1920,
264
+ "scaled": true,
265
+ "message": "Screenshot saved to /absolute/path/to/screenshot_2025-01-17_14-30-25.webp"
266
+ }
267
+ ```
268
+
269
+ ## Архитектура
270
+
271
+ - `src/index.ts` - Основной MCP сервер, использующий официальный SDK
272
+ - `src/adb.ts` - Обертка для выполнения ADB команд
273
+ - `src/types.ts` - Zod схемы для валидации параметров инструментов
274
+
275
+ ## Разработка
276
+
277
+ ```bash
278
+ # Сборка
279
+ npm run build
280
+
281
+ # Запуск
282
+ npm start
283
+ ```
284
+
285
+ ### Отладка (Debug Mode)
286
+
287
+ Для отладки сервера доступно несколько вариантов:
288
+
289
+ #### VS Code Debugger (Рекомендуется)
290
+
291
+ 1. Откройте проект в VS Code
292
+ 2. Перейдите в раздел "Run and Debug" (F5)
293
+ 3. Выберите конфигурацию "Debug droid-mcp" или "Debug droid-mcp (with ANDROID_APP_IDS)"
294
+ 4. Нажмите F5 для запуска
295
+
296
+ Конфигурация автоматически:
297
+ - Соберет проект перед запуском
298
+ - Включит детальное логирование через переменную `DROID_MCP_DEBUG`
299
+ - Настроит source maps для отладки TypeScript кода
300
+ - Позволит ставить breakpoints и инспектировать переменные
301
+
302
+ #### Ручной запуск с debug-логированием
303
+
304
+ ```bash
305
+ # Windows PowerShell
306
+ $env:DROID_MCP_DEBUG="true"; npm start
307
+
308
+ # Linux/Mac
309
+ DROID_MCP_DEBUG=true npm start
310
+ ```
311
+
312
+ #### Node.js Debugger
313
+
314
+ ```bash
315
+ # Запуск с debugger на порту 9229
316
+ node --inspect dist/index.js
317
+
318
+ # Затем откройте Chrome и перейдите на chrome://inspect
319
+ ```
320
+
321
+ **Debug-логирование включает:**
322
+ - Все вызовы инструментов MCP с параметрами
323
+ - Все ADB команды с полными командами и результатами
324
+ - Детали обработки ошибок
325
+ - Время выполнения операций
326
+ - Информацию о фильтрации по PID
327
+ - Детали работы с view hierarchy dump
328
+
329
+ ## Тестирование
330
+
331
+ Тесты проверяют базовую работоспособность MCP сервера: инициализацию, получение списка инструментов, валидацию параметров и обработку ошибок.
332
+
333
+ **Запуск тестов:**
334
+
335
+ ```bash
336
+ npm test
337
+ ```
338
+
339
+ Команда автоматически выполнит сборку проекта и запустит интеграционные тесты. Тесты используют официальный MCP Client SDK для проверки взаимодействия с сервером.
340
+
341
+ **Что проверяют тесты:**
342
+
343
+ - Инициализация сервера с корректными параметрами протокола
344
+ - Получение списка доступных инструментов (`get_logcat`, `capture_session`, `inspect_app_identity`)
345
+ - Валидация параметров инструментов (Zod схемы)
346
+ - Обработка ошибок (неизвестные инструменты, невалидные параметры)
347
+ - Структура ответов MCP протокола
348
+
349
+ **Примечание:** Тесты не требуют подключенного Android устройства, так как проверяют только протокол MCP и валидацию, а не выполнение ADB команд. Тесты, которые вызывают ADB, обрабатывают отсутствие устройства gracefully.
350
+
351
+ ## Лицензия
352
+
353
+ MIT
354
+
package/dist/adb.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ export interface AdbResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ }
5
+ export declare class AdbError extends Error {
6
+ code: number | null;
7
+ stderr: string;
8
+ constructor(message: string, code: number | null, stderr: string);
9
+ }
10
+ /**
11
+ * Execute an ADB command
12
+ * @param command - Array of command arguments (e.g., ["logcat", "-d", "-t", "200"])
13
+ * @returns Promise with stdout and stderr
14
+ * @throws AdbError if command fails
15
+ */
16
+ export declare function execAdb(command: string[]): Promise<AdbResult>;
17
+ export interface CaptureLogcatOptions {
18
+ stopPattern: string;
19
+ timeoutMs: number;
20
+ clearBuffer?: boolean;
21
+ tag?: string;
22
+ priority?: string;
23
+ buffer?: string;
24
+ pids?: string[];
25
+ }
26
+ /**
27
+ * Получает PID процесса по package ID
28
+ * @param packageId - Package ID приложения (например, "com.example.app")
29
+ * @returns PID процесса или null, если приложение не запущено
30
+ */
31
+ export declare function getPidForPackage(packageId: string): Promise<string | null>;
32
+ /**
33
+ * Получает PID для списка package ID
34
+ * @param packageIds - Массив package ID
35
+ * @returns Массив PID для запущенных процессов (только те, что найдены)
36
+ */
37
+ export declare function getPidsForPackages(packageIds: string[]): Promise<string[]>;
38
+ /**
39
+ * Получает список установленных сторонних приложений (User installed)
40
+ * @returns Массив package ID установленных приложений
41
+ */
42
+ export declare function listThirdPartyPackages(): Promise<string[]>;
43
+ /**
44
+ * Пытается найти applicationId в build.gradle файлах проекта
45
+ * @param rootDir - Корневая директория проекта (по умолчанию process.cwd())
46
+ * @returns Найденный applicationId или null
47
+ */
48
+ export declare function scanGradleForPackageId(rootDir?: string): Promise<string | null>;
49
+ /**
50
+ * Запускает logcat и слушает вывод до появления stopPattern или истечения таймаута
51
+ * @param options - Параметры захвата логов
52
+ * @returns Promise с накопленными логами
53
+ * @throws AdbError если команда не может быть выполнена
54
+ */
55
+ export declare function captureLogcatUntil(options: CaptureLogcatOptions): Promise<string>;
56
+ /**
57
+ * Оптимизированный узел view для AI-агента
58
+ */
59
+ export interface ParsedViewNode {
60
+ class: string;
61
+ bounds?: string;
62
+ id?: string;
63
+ text?: string;
64
+ desc?: string;
65
+ clickable?: boolean;
66
+ children?: ParsedViewNode[];
67
+ }
68
+ /**
69
+ * Опции для парсинга view hierarchy
70
+ */
71
+ export interface ParseViewHierarchyOptions {
72
+ includeInvisible: boolean;
73
+ compact: boolean;
74
+ }
75
+ /**
76
+ * Выполняет uiautomator dump и возвращает XML
77
+ * @returns XML строка с иерархией View
78
+ * @throws AdbError если команда не может быть выполнена
79
+ */
80
+ export declare function dumpViewHierarchy(): Promise<string>;
81
+ /**
82
+ * Парсит XML иерархии View в оптимизированное для AI дерево
83
+ * @param xml - XML строка от uiautomator dump
84
+ * @param options - Опции парсинга
85
+ * @returns Оптимизированное дерево View
86
+ */
87
+ export declare function parseViewHierarchyXml(xml: string, options: ParseViewHierarchyOptions): ParsedViewNode;
88
+ export declare function setOutputDir(dir: string): void;
89
+ export declare function getOutputDir(): string;
90
+ export interface CaptureScreenshotOptions {
91
+ format: "webp" | "png";
92
+ scale: number;
93
+ quality: number;
94
+ output: "file" | "base64";
95
+ }
96
+ export interface ScreenshotResult {
97
+ filePath?: string;
98
+ relativePath?: string;
99
+ data?: string;
100
+ format: string;
101
+ size: number;
102
+ sizeKb: number;
103
+ width: number;
104
+ height: number;
105
+ originalWidth: number;
106
+ originalHeight: number;
107
+ scaled: boolean;
108
+ }
109
+ export declare function captureScreenshot(options: CaptureScreenshotOptions): Promise<ScreenshotResult>;
110
+ //# sourceMappingURL=adb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}