replicant-mcp 1.5.0 → 1.6.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/dist/adapters/emulator.d.ts +2 -0
- package/dist/adapters/emulator.js +35 -5
- package/dist/parsers/adb-output.js +5 -1
- package/dist/server.js +11 -21
- package/dist/services/device-state.js +18 -9
- package/dist/services/process-runner.js +1 -1
- package/dist/tools/adb-app.d.ts +0 -1
- package/dist/tools/adb-app.js +6 -7
- package/dist/tools/adb-device.d.ts +0 -1
- package/dist/tools/adb-device.js +10 -10
- package/dist/tools/adb-logcat.d.ts +0 -3
- package/dist/tools/adb-logcat.js +26 -30
- package/dist/tools/adb-shell.d.ts +0 -1
- package/dist/tools/adb-shell.js +9 -7
- package/dist/tools/cache.js +2 -2
- package/dist/tools/emulator-device.d.ts +0 -4
- package/dist/tools/emulator-device.js +6 -6
- package/dist/tools/gradle-build.d.ts +0 -1
- package/dist/tools/gradle-build.js +3 -4
- package/dist/tools/gradle-get-details.d.ts +0 -2
- package/dist/tools/gradle-get-details.js +2 -3
- package/dist/tools/gradle-list.js +2 -4
- package/dist/tools/gradle-test.d.ts +0 -1
- package/dist/tools/gradle-test.js +3 -7
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/rtfm.js +24 -28
- package/dist/tools/ui-action.d.ts +63 -0
- package/dist/tools/ui-action.js +83 -0
- package/dist/tools/ui-capture.d.ts +43 -0
- package/dist/tools/ui-capture.js +59 -0
- package/dist/tools/ui-find.d.ts +14 -2
- package/dist/tools/ui-find.js +0 -1
- package/dist/tools/{ui.d.ts → ui-query.d.ts} +4 -77
- package/dist/tools/ui-query.js +138 -0
- package/dist/types/device.d.ts +1 -1
- package/dist/types/schemas/adb-device-output.d.ts +4 -0
- package/dist/types/schemas/adb-device-output.js +1 -1
- package/docs/contracts/replicant-mcp.contract.json +140 -130
- package/docs/rtfm/ui.md +55 -32
- package/package.json +1 -1
- package/dist/tools/ui.js +0 -252
|
@@ -10,6 +10,8 @@ export declare class EmulatorAdapter {
|
|
|
10
10
|
list(): Promise<EmulatorListResult>;
|
|
11
11
|
create(name: string, device: string, systemImage: string): Promise<void>;
|
|
12
12
|
start(avdName: string): Promise<string>;
|
|
13
|
+
private getRunningEmulatorIds;
|
|
14
|
+
private getAvdName;
|
|
13
15
|
kill(emulatorId: string): Promise<void>;
|
|
14
16
|
wipe(avdName: string): Promise<void>;
|
|
15
17
|
snapshotSave(emulatorId: string, name: string): Promise<void>;
|
|
@@ -29,6 +29,8 @@ export class EmulatorAdapter {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
async start(avdName) {
|
|
32
|
+
// Snapshot existing emulators before starting a new one
|
|
33
|
+
const existingIds = await this.getRunningEmulatorIds();
|
|
32
34
|
// Start emulator in background - don't wait for it
|
|
33
35
|
// Returns immediately, emulator boots in background
|
|
34
36
|
this.runner.runEmulator([
|
|
@@ -40,13 +42,41 @@ export class EmulatorAdapter {
|
|
|
40
42
|
});
|
|
41
43
|
// Give it a moment to register
|
|
42
44
|
await new Promise((r) => setTimeout(r, 2000));
|
|
43
|
-
// Find the
|
|
45
|
+
// Find the NEW emulator by diffing against pre-existing ones
|
|
46
|
+
const currentIds = await this.getRunningEmulatorIds();
|
|
47
|
+
const newIds = currentIds.filter((id) => !existingIds.includes(id));
|
|
48
|
+
if (newIds.length === 1) {
|
|
49
|
+
return newIds[0];
|
|
50
|
+
}
|
|
51
|
+
// Ambiguous or no new emulator — fall back to matching by AVD name
|
|
52
|
+
for (const id of currentIds) {
|
|
53
|
+
if (existingIds.includes(id))
|
|
54
|
+
continue;
|
|
55
|
+
const name = await this.getAvdName(id);
|
|
56
|
+
if (name === avdName)
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
// Last resort: check all current emulators by AVD name
|
|
60
|
+
for (const id of currentIds) {
|
|
61
|
+
const name = await this.getAvdName(id);
|
|
62
|
+
if (name === avdName)
|
|
63
|
+
return id;
|
|
64
|
+
}
|
|
65
|
+
throw new ReplicantError(ErrorCode.EMULATOR_START_FAILED, `Emulator ${avdName} failed to start`, "Check the AVD name and try again");
|
|
66
|
+
}
|
|
67
|
+
async getRunningEmulatorIds() {
|
|
44
68
|
const result = await this.runner.runAdb(["devices"]);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
const matches = result.stdout.match(/emulator-\d+/g);
|
|
70
|
+
return matches ?? [];
|
|
71
|
+
}
|
|
72
|
+
async getAvdName(emulatorId) {
|
|
73
|
+
try {
|
|
74
|
+
const result = await this.runner.runAdb(["-s", emulatorId, "emu", "avd", "name"]);
|
|
75
|
+
return result.stdout.trim().split("\n")[0].trim();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return "";
|
|
48
79
|
}
|
|
49
|
-
return match[0];
|
|
50
80
|
}
|
|
51
81
|
async kill(emulatorId) {
|
|
52
82
|
await this.runner.runAdb(["-s", emulatorId, "emu", "kill"]);
|
|
@@ -9,7 +9,11 @@ export function parseDeviceList(output) {
|
|
|
9
9
|
if (!id)
|
|
10
10
|
continue;
|
|
11
11
|
const type = id.startsWith("emulator") ? "emulator" : "physical";
|
|
12
|
-
const status = statusStr === "device"
|
|
12
|
+
const status = statusStr === "device"
|
|
13
|
+
? "online"
|
|
14
|
+
: statusStr === "unauthorized"
|
|
15
|
+
? "unauthorized"
|
|
16
|
+
: "offline";
|
|
13
17
|
devices.push({
|
|
14
18
|
id,
|
|
15
19
|
type,
|
package/dist/server.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CacheManager, DeviceStateManager, ProcessRunner, EnvironmentService, Co
|
|
|
6
6
|
import { AdbAdapter, EmulatorAdapter, GradleAdapter, UiAutomatorAdapter } from "./adapters/index.js";
|
|
7
7
|
import { ReplicantError, ErrorCode } from "./types/index.js";
|
|
8
8
|
import { VERSION } from "./version.js";
|
|
9
|
-
import { cacheInputSchema, cacheToolDefinition, handleCacheTool, rtfmInputSchema, rtfmToolDefinition, handleRtfmTool, adbDeviceInputSchema, adbDeviceToolDefinition, handleAdbDeviceTool, adbAppInputSchema, adbAppToolDefinition, handleAdbAppTool, adbLogcatInputSchema, adbLogcatToolDefinition, handleAdbLogcatTool, adbShellInputSchema, adbShellToolDefinition, handleAdbShellTool, emulatorDeviceInputSchema, emulatorDeviceToolDefinition, handleEmulatorDeviceTool, gradleBuildInputSchema, gradleBuildToolDefinition, handleGradleBuildTool, gradleTestInputSchema, gradleTestToolDefinition, handleGradleTestTool, gradleListInputSchema, gradleListToolDefinition, handleGradleListTool, gradleGetDetailsInputSchema, gradleGetDetailsToolDefinition, handleGradleGetDetailsTool,
|
|
9
|
+
import { cacheInputSchema, cacheToolDefinition, handleCacheTool, rtfmInputSchema, rtfmToolDefinition, handleRtfmTool, adbDeviceInputSchema, adbDeviceToolDefinition, handleAdbDeviceTool, adbAppInputSchema, adbAppToolDefinition, handleAdbAppTool, adbLogcatInputSchema, adbLogcatToolDefinition, handleAdbLogcatTool, adbShellInputSchema, adbShellToolDefinition, handleAdbShellTool, emulatorDeviceInputSchema, emulatorDeviceToolDefinition, handleEmulatorDeviceTool, gradleBuildInputSchema, gradleBuildToolDefinition, handleGradleBuildTool, gradleTestInputSchema, gradleTestToolDefinition, handleGradleTestTool, gradleListInputSchema, gradleListToolDefinition, handleGradleListTool, gradleGetDetailsInputSchema, gradleGetDetailsToolDefinition, handleGradleGetDetailsTool, uiQueryInputSchema, uiQueryToolDefinition, handleUiQueryTool, uiActionInputSchema, uiActionToolDefinition, handleUiActionTool, uiCaptureInputSchema, uiCaptureToolDefinition, handleUiCaptureTool, } from "./tools/index.js";
|
|
10
10
|
export function createServerContext() {
|
|
11
11
|
const environment = new EnvironmentService();
|
|
12
12
|
const processRunner = new ProcessRunner(environment);
|
|
@@ -36,7 +36,9 @@ const toolDefinitions = [
|
|
|
36
36
|
gradleTestToolDefinition,
|
|
37
37
|
gradleListToolDefinition,
|
|
38
38
|
gradleGetDetailsToolDefinition,
|
|
39
|
-
|
|
39
|
+
uiQueryToolDefinition,
|
|
40
|
+
uiActionToolDefinition,
|
|
41
|
+
uiCaptureToolDefinition,
|
|
40
42
|
];
|
|
41
43
|
async function dispatchToolCall(name, args, context) {
|
|
42
44
|
const rawArgs = args ?? {};
|
|
@@ -80,8 +82,12 @@ async function dispatchToolCall(name, args, context) {
|
|
|
80
82
|
return handleGradleListTool(parseOrThrow("gradle-list", gradleListInputSchema), context);
|
|
81
83
|
case "gradle-get-details":
|
|
82
84
|
return handleGradleGetDetailsTool(parseOrThrow("gradle-get-details", gradleGetDetailsInputSchema), context);
|
|
83
|
-
case "ui":
|
|
84
|
-
return
|
|
85
|
+
case "ui-query":
|
|
86
|
+
return handleUiQueryTool(parseOrThrow("ui-query", uiQueryInputSchema), context, context.config.getUiConfig());
|
|
87
|
+
case "ui-action":
|
|
88
|
+
return handleUiActionTool(parseOrThrow("ui-action", uiActionInputSchema), context);
|
|
89
|
+
case "ui-capture":
|
|
90
|
+
return handleUiCaptureTool(parseOrThrow("ui-capture", uiCaptureInputSchema), context, context.config.getUiConfig());
|
|
85
91
|
default:
|
|
86
92
|
throw new Error(`Unknown tool: ${name}`);
|
|
87
93
|
}
|
|
@@ -94,21 +100,7 @@ export async function createServer(context) {
|
|
|
94
100
|
capabilities: {
|
|
95
101
|
tools: {},
|
|
96
102
|
},
|
|
97
|
-
instructions: `
|
|
98
|
-
Only fall back to raw adb/gradle/emulator commands if replicant-mcp lacks a specific feature.
|
|
99
|
-
|
|
100
|
-
Tool mapping:
|
|
101
|
-
- Device management → adb-device (not \`adb devices\`)
|
|
102
|
-
- App install/launch/stop → adb-app (not \`adb install\`, \`adb shell am\`)
|
|
103
|
-
- Logs → adb-logcat (not \`adb logcat\`)
|
|
104
|
-
- Shell commands → adb-shell (not \`adb shell\`)
|
|
105
|
-
- Emulator control → emulator-device (not \`emulator\` CLI)
|
|
106
|
-
- Builds → gradle-build (not \`./gradlew\`)
|
|
107
|
-
- Tests → gradle-test (not \`./gradlew test\`)
|
|
108
|
-
- UI automation → ui (accessibility-first, screenshots auto-scaled to configured max dimension, default 800px)
|
|
109
|
-
|
|
110
|
-
Start with \`adb-device list\` to see connected devices.
|
|
111
|
-
Use \`rtfm\` for detailed documentation on any tool.`,
|
|
103
|
+
instructions: `Use these tools for Android — never raw adb/gradle/emulator commands. Auto-selects single device. Start: \`adb-device list\`. Docs: \`rtfm\`.`,
|
|
112
104
|
});
|
|
113
105
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
114
106
|
tools: toolDefinitions,
|
|
@@ -144,9 +136,7 @@ Use \`rtfm\` for detailed documentation on any tool.`,
|
|
|
144
136
|
}
|
|
145
137
|
export async function runServer() {
|
|
146
138
|
const context = createServerContext();
|
|
147
|
-
// Load configuration from REPLICANT_CONFIG if set
|
|
148
139
|
await context.config.load();
|
|
149
|
-
// Apply project root: env var takes precedence over config file
|
|
150
140
|
const projectRoot = process.env.REPLICANT_PROJECT_ROOT || context.config.get().build?.projectRoot;
|
|
151
141
|
if (projectRoot) {
|
|
152
142
|
context.gradle.setProjectPath(projectRoot);
|
|
@@ -22,21 +22,30 @@ export class DeviceStateManager {
|
|
|
22
22
|
return this.currentDevice;
|
|
23
23
|
}
|
|
24
24
|
// Try to auto-select
|
|
25
|
-
const
|
|
26
|
-
|
|
25
|
+
const allDevices = await adb.getDevices();
|
|
26
|
+
const onlineDevices = allDevices.filter((d) => d.status === "online");
|
|
27
|
+
if (allDevices.length === 0) {
|
|
27
28
|
throw new ReplicantError(ErrorCode.NO_DEVICES, "No devices connected", "Start an emulator with 'emulator-device start' or connect a USB device with debugging enabled");
|
|
28
29
|
}
|
|
29
|
-
if (
|
|
30
|
-
|
|
30
|
+
if (onlineDevices.length === 0) {
|
|
31
|
+
const statuses = allDevices.map((d) => `${d.id} (${d.status})`).join(", ");
|
|
32
|
+
const suggestion = allDevices.some((d) => d.status === "unauthorized")
|
|
33
|
+
? "Check the USB debugging authorization prompt on the device and tap 'Allow'"
|
|
34
|
+
: "Wait for the device to come online or restart it with 'adb reconnect'";
|
|
35
|
+
throw new ReplicantError(ErrorCode.DEVICE_OFFLINE, `No online devices available. Found: ${statuses}`, suggestion);
|
|
36
|
+
}
|
|
37
|
+
if (onlineDevices.length === 1) {
|
|
38
|
+
this.currentDevice = onlineDevices[0];
|
|
31
39
|
return this.currentDevice;
|
|
32
40
|
}
|
|
33
|
-
// Multiple devices - user must choose
|
|
34
|
-
const deviceList =
|
|
35
|
-
throw new ReplicantError(ErrorCode.MULTIPLE_DEVICES, `${
|
|
41
|
+
// Multiple online devices - user must choose
|
|
42
|
+
const deviceList = onlineDevices.map((d) => d.id).join(", ");
|
|
43
|
+
throw new ReplicantError(ErrorCode.MULTIPLE_DEVICES, `${onlineDevices.length} devices connected: ${deviceList}`, `Call adb-device({ operation: 'select', deviceId: '...' }) to choose one`);
|
|
36
44
|
}
|
|
37
45
|
autoSelectIfSingle(devices) {
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
const onlineDevices = devices.filter((d) => d.status === "online");
|
|
47
|
+
if (onlineDevices.length === 1 && !this.currentDevice) {
|
|
48
|
+
this.currentDevice = onlineDevices[0];
|
|
40
49
|
return true;
|
|
41
50
|
}
|
|
42
51
|
return false;
|
package/dist/tools/adb-app.d.ts
CHANGED
package/dist/tools/adb-app.js
CHANGED
|
@@ -4,7 +4,6 @@ export const adbAppInputSchema = z.object({
|
|
|
4
4
|
operation: z.enum(["install", "uninstall", "launch", "stop", "clear-data", "list"]),
|
|
5
5
|
apkPath: z.string().optional(),
|
|
6
6
|
packageName: z.string().optional(),
|
|
7
|
-
// List operation options
|
|
8
7
|
limit: z.number().min(1).max(100).optional(),
|
|
9
8
|
filter: z.string().optional(),
|
|
10
9
|
offset: z.number().min(0).optional(),
|
|
@@ -85,7 +84,7 @@ export async function handleAdbAppTool(input, context) {
|
|
|
85
84
|
}
|
|
86
85
|
export const adbAppToolDefinition = {
|
|
87
86
|
name: "adb-app",
|
|
88
|
-
description: "Manage applications.
|
|
87
|
+
description: "Manage applications.",
|
|
89
88
|
inputSchema: {
|
|
90
89
|
type: "object",
|
|
91
90
|
properties: {
|
|
@@ -93,19 +92,19 @@ export const adbAppToolDefinition = {
|
|
|
93
92
|
type: "string",
|
|
94
93
|
enum: ["install", "uninstall", "launch", "stop", "clear-data", "list"],
|
|
95
94
|
},
|
|
96
|
-
apkPath: { type: "string", description: "
|
|
97
|
-
packageName: { type: "string"
|
|
95
|
+
apkPath: { type: "string", description: "APK path" },
|
|
96
|
+
packageName: { type: "string" },
|
|
98
97
|
limit: {
|
|
99
98
|
type: "number",
|
|
100
|
-
description: "
|
|
99
|
+
description: "Default: 20, max: 100",
|
|
101
100
|
},
|
|
102
101
|
filter: {
|
|
103
102
|
type: "string",
|
|
104
|
-
description: "Filter
|
|
103
|
+
description: "Filter by name (case-insensitive)",
|
|
105
104
|
},
|
|
106
105
|
offset: {
|
|
107
106
|
type: "number",
|
|
108
|
-
description: "
|
|
107
|
+
description: "Pagination offset",
|
|
109
108
|
},
|
|
110
109
|
},
|
|
111
110
|
required: ["operation"],
|
package/dist/tools/adb-device.js
CHANGED
|
@@ -26,19 +26,19 @@ async function handleSelect(input, context) {
|
|
|
26
26
|
context.deviceState.setCurrentDevice(device);
|
|
27
27
|
return { selected: device };
|
|
28
28
|
}
|
|
29
|
+
async function resolveDeviceId(input, context) {
|
|
30
|
+
if (input.deviceId)
|
|
31
|
+
return input.deviceId;
|
|
32
|
+
const device = await context.deviceState.ensureDevice(context.adb);
|
|
33
|
+
return device.id;
|
|
34
|
+
}
|
|
29
35
|
async function handleWait(input, context) {
|
|
30
|
-
const
|
|
31
|
-
? { id: input.deviceId }
|
|
32
|
-
: await context.deviceState.ensureDevice(context.adb);
|
|
33
|
-
const deviceId = device.id;
|
|
36
|
+
const deviceId = await resolveDeviceId(input, context);
|
|
34
37
|
await context.adb.waitForDevice(deviceId);
|
|
35
38
|
return { status: "device ready", deviceId };
|
|
36
39
|
}
|
|
37
40
|
async function handleProperties(input, context) {
|
|
38
|
-
const
|
|
39
|
-
? { id: input.deviceId }
|
|
40
|
-
: await context.deviceState.ensureDevice(context.adb);
|
|
41
|
-
const deviceId = device.id;
|
|
41
|
+
const deviceId = await resolveDeviceId(input, context);
|
|
42
42
|
const props = await context.adb.getProperties(deviceId);
|
|
43
43
|
const cacheId = context.cache.generateId("device-props");
|
|
44
44
|
context.cache.set(cacheId, { deviceId, properties: props }, "device-props", CACHE_TTLS.DEVICE_PROPERTIES);
|
|
@@ -110,7 +110,7 @@ export async function handleAdbDeviceTool(input, context) {
|
|
|
110
110
|
}
|
|
111
111
|
export const adbDeviceToolDefinition = {
|
|
112
112
|
name: "adb-device",
|
|
113
|
-
description: "Manage device connections.
|
|
113
|
+
description: "Manage device connections.",
|
|
114
114
|
inputSchema: {
|
|
115
115
|
type: "object",
|
|
116
116
|
properties: {
|
|
@@ -118,7 +118,7 @@ export const adbDeviceToolDefinition = {
|
|
|
118
118
|
type: "string",
|
|
119
119
|
enum: ["list", "select", "wait", "properties", "health-check"],
|
|
120
120
|
},
|
|
121
|
-
deviceId: { type: "string"
|
|
121
|
+
deviceId: { type: "string" },
|
|
122
122
|
},
|
|
123
123
|
required: ["operation"],
|
|
124
124
|
},
|
|
@@ -28,14 +28,12 @@ export declare const adbLogcatToolDefinition: {
|
|
|
28
28
|
};
|
|
29
29
|
package: {
|
|
30
30
|
type: string;
|
|
31
|
-
description: string;
|
|
32
31
|
};
|
|
33
32
|
tags: {
|
|
34
33
|
type: string;
|
|
35
34
|
items: {
|
|
36
35
|
type: string;
|
|
37
36
|
};
|
|
38
|
-
description: string;
|
|
39
37
|
};
|
|
40
38
|
level: {
|
|
41
39
|
type: string;
|
|
@@ -43,7 +41,6 @@ export declare const adbLogcatToolDefinition: {
|
|
|
43
41
|
};
|
|
44
42
|
rawFilter: {
|
|
45
43
|
type: string;
|
|
46
|
-
description: string;
|
|
47
44
|
};
|
|
48
45
|
since: {
|
|
49
46
|
type: string;
|
package/dist/tools/adb-logcat.js
CHANGED
|
@@ -8,40 +8,36 @@ export const adbLogcatInputSchema = z.object({
|
|
|
8
8
|
rawFilter: z.string().optional(),
|
|
9
9
|
since: z.string().optional(),
|
|
10
10
|
});
|
|
11
|
+
function buildLogcatFilter(input) {
|
|
12
|
+
if (input.rawFilter)
|
|
13
|
+
return input.rawFilter;
|
|
14
|
+
if (!input.tags && !input.level)
|
|
15
|
+
return undefined;
|
|
16
|
+
const levelMap = {
|
|
17
|
+
verbose: "V",
|
|
18
|
+
debug: "D",
|
|
19
|
+
info: "I",
|
|
20
|
+
warn: "W",
|
|
21
|
+
error: "E",
|
|
22
|
+
};
|
|
23
|
+
const levelChar = input.level ? levelMap[input.level] : "V";
|
|
24
|
+
if (input.tags) {
|
|
25
|
+
return input.tags.map((tag) => `${tag}:${levelChar}`).join(" ") + " *:S";
|
|
26
|
+
}
|
|
27
|
+
return `*:${levelChar}`;
|
|
28
|
+
}
|
|
11
29
|
export async function handleAdbLogcatTool(input, context) {
|
|
12
30
|
const device = await context.deviceState.ensureDevice(context.adb);
|
|
13
31
|
const deviceId = device.id;
|
|
14
|
-
|
|
15
|
-
let filter = "";
|
|
16
|
-
if (input.rawFilter) {
|
|
17
|
-
filter = input.rawFilter;
|
|
18
|
-
}
|
|
19
|
-
else if (input.tags || input.level) {
|
|
20
|
-
const levelMap = {
|
|
21
|
-
verbose: "V",
|
|
22
|
-
debug: "D",
|
|
23
|
-
info: "I",
|
|
24
|
-
warn: "W",
|
|
25
|
-
error: "E",
|
|
26
|
-
};
|
|
27
|
-
const levelChar = input.level ? levelMap[input.level] : "V";
|
|
28
|
-
if (input.tags) {
|
|
29
|
-
filter = input.tags.map((tag) => `${tag}:${levelChar}`).join(" ") + " *:S";
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
filter = `*:${levelChar}`;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
32
|
+
const filter = buildLogcatFilter(input);
|
|
35
33
|
const output = await context.adb.logcat(deviceId, {
|
|
36
34
|
lines: input.lines,
|
|
37
|
-
filter
|
|
35
|
+
filter,
|
|
38
36
|
since: input.since,
|
|
39
37
|
package: input.package,
|
|
40
38
|
});
|
|
41
|
-
// Cache the full output and return a summary
|
|
42
39
|
const logId = context.cache.generateId("logcat");
|
|
43
40
|
context.cache.set(logId, { output, deviceId, filter }, "logcat", CACHE_TTLS.LOGCAT);
|
|
44
|
-
// Parse log lines
|
|
45
41
|
const lines = output.split("\n").filter(Boolean);
|
|
46
42
|
const errorCount = lines.filter((l) => l.includes(" E ")).length;
|
|
47
43
|
const warnCount = lines.filter((l) => l.includes(" W ")).length;
|
|
@@ -58,16 +54,16 @@ export async function handleAdbLogcatTool(input, context) {
|
|
|
58
54
|
}
|
|
59
55
|
export const adbLogcatToolDefinition = {
|
|
60
56
|
name: "adb-logcat",
|
|
61
|
-
description: "Read device logs.
|
|
57
|
+
description: "Read device logs. Returns summary with logId.",
|
|
62
58
|
inputSchema: {
|
|
63
59
|
type: "object",
|
|
64
60
|
properties: {
|
|
65
|
-
lines: { type: "number", description: "
|
|
66
|
-
package: { type: "string"
|
|
67
|
-
tags: { type: "array", items: { type: "string" }
|
|
61
|
+
lines: { type: "number", description: "Default: 100" },
|
|
62
|
+
package: { type: "string" },
|
|
63
|
+
tags: { type: "array", items: { type: "string" } },
|
|
68
64
|
level: { type: "string", enum: ["verbose", "debug", "info", "warn", "error"] },
|
|
69
|
-
rawFilter: { type: "string"
|
|
70
|
-
since: { type: "string", description: "
|
|
65
|
+
rawFilter: { type: "string" },
|
|
66
|
+
since: { type: "string", description: "e.g., '01-20 15:30:00.000'" },
|
|
71
67
|
},
|
|
72
68
|
},
|
|
73
69
|
};
|
package/dist/tools/adb-shell.js
CHANGED
|
@@ -9,7 +9,9 @@ export const adbShellInputSchema = z.object({
|
|
|
9
9
|
export async function handleAdbShellTool(input, context) {
|
|
10
10
|
const device = await context.deviceState.ensureDevice(context.adb);
|
|
11
11
|
const deviceId = device.id;
|
|
12
|
-
const
|
|
12
|
+
const ADB_SHELL_MAX_TIMEOUT = 120_000;
|
|
13
|
+
const timeout = input.timeout ? Math.min(input.timeout, ADB_SHELL_MAX_TIMEOUT) : undefined;
|
|
14
|
+
const result = await context.adb.shell(deviceId, input.command, timeout);
|
|
13
15
|
if (input.summaryOnly) {
|
|
14
16
|
const previewChars = input.previewChars ?? 200;
|
|
15
17
|
return {
|
|
@@ -41,15 +43,15 @@ export async function handleAdbShellTool(input, context) {
|
|
|
41
43
|
}
|
|
42
44
|
export const adbShellToolDefinition = {
|
|
43
45
|
name: "adb-shell",
|
|
44
|
-
description: "Execute shell commands with safety guards.
|
|
46
|
+
description: "Execute shell commands with safety guards.",
|
|
45
47
|
inputSchema: {
|
|
46
48
|
type: "object",
|
|
47
49
|
properties: {
|
|
48
|
-
command: { type: "string"
|
|
49
|
-
timeout: { type: "number", description: "
|
|
50
|
-
maxChars: { type: "number", description: "Truncate
|
|
51
|
-
summaryOnly: { type: "boolean", description: "
|
|
52
|
-
previewChars: { type: "number", description: "
|
|
50
|
+
command: { type: "string" },
|
|
51
|
+
timeout: { type: "number", description: "ms, default: 30000, max: 120000" },
|
|
52
|
+
maxChars: { type: "number", description: "Truncate output to N chars" },
|
|
53
|
+
summaryOnly: { type: "boolean", description: "Compact preview only" },
|
|
54
|
+
previewChars: { type: "number", description: "Preview length (default: 200)" },
|
|
53
55
|
},
|
|
54
56
|
required: ["command"],
|
|
55
57
|
},
|
package/dist/tools/cache.js
CHANGED
|
@@ -35,7 +35,7 @@ export async function handleCacheTool(input, cache) {
|
|
|
35
35
|
}
|
|
36
36
|
export const cacheToolDefinition = {
|
|
37
37
|
name: "cache",
|
|
38
|
-
description: "Manage the cache.
|
|
38
|
+
description: "Manage the cache. See rtfm for details.",
|
|
39
39
|
inputSchema: {
|
|
40
40
|
type: "object",
|
|
41
41
|
properties: {
|
|
@@ -43,7 +43,7 @@ export const cacheToolDefinition = {
|
|
|
43
43
|
type: "string",
|
|
44
44
|
enum: ["get-stats", "clear", "get-config", "set-config"],
|
|
45
45
|
},
|
|
46
|
-
key: { type: "string", description: "
|
|
46
|
+
key: { type: "string", description: "Key to clear (optional)" },
|
|
47
47
|
config: {
|
|
48
48
|
type: "object",
|
|
49
49
|
properties: {
|
|
@@ -32,7 +32,6 @@ export declare const emulatorDeviceToolDefinition: {
|
|
|
32
32
|
};
|
|
33
33
|
avdName: {
|
|
34
34
|
type: string;
|
|
35
|
-
description: string;
|
|
36
35
|
};
|
|
37
36
|
device: {
|
|
38
37
|
type: string;
|
|
@@ -40,15 +39,12 @@ export declare const emulatorDeviceToolDefinition: {
|
|
|
40
39
|
};
|
|
41
40
|
systemImage: {
|
|
42
41
|
type: string;
|
|
43
|
-
description: string;
|
|
44
42
|
};
|
|
45
43
|
snapshotName: {
|
|
46
44
|
type: string;
|
|
47
|
-
description: string;
|
|
48
45
|
};
|
|
49
46
|
emulatorId: {
|
|
50
47
|
type: string;
|
|
51
|
-
description: string;
|
|
52
48
|
};
|
|
53
49
|
};
|
|
54
50
|
required: string[];
|
|
@@ -115,7 +115,7 @@ export async function handleEmulatorDeviceTool(input, context) {
|
|
|
115
115
|
}
|
|
116
116
|
export const emulatorDeviceToolDefinition = {
|
|
117
117
|
name: "emulator-device",
|
|
118
|
-
description: "Manage Android emulators.
|
|
118
|
+
description: "Manage Android emulators.",
|
|
119
119
|
inputSchema: {
|
|
120
120
|
type: "object",
|
|
121
121
|
properties: {
|
|
@@ -133,11 +133,11 @@ export const emulatorDeviceToolDefinition = {
|
|
|
133
133
|
"snapshot-delete",
|
|
134
134
|
],
|
|
135
135
|
},
|
|
136
|
-
avdName: { type: "string"
|
|
137
|
-
device: { type: "string", description: "
|
|
138
|
-
systemImage: { type: "string"
|
|
139
|
-
snapshotName: { type: "string"
|
|
140
|
-
emulatorId: { type: "string"
|
|
136
|
+
avdName: { type: "string" },
|
|
137
|
+
device: { type: "string", description: "e.g., 'pixel_7'" },
|
|
138
|
+
systemImage: { type: "string" },
|
|
139
|
+
snapshotName: { type: "string" },
|
|
140
|
+
emulatorId: { type: "string" },
|
|
141
141
|
},
|
|
142
142
|
required: ["operation"],
|
|
143
143
|
},
|
|
@@ -7,7 +7,6 @@ export const gradleBuildInputSchema = z.object({
|
|
|
7
7
|
});
|
|
8
8
|
export async function handleGradleBuildTool(input, context) {
|
|
9
9
|
const { result, fullOutput } = await context.gradle.build(input.operation, input.module, input.flavor);
|
|
10
|
-
// Cache full output for later retrieval
|
|
11
10
|
const buildId = context.cache.generateId("build");
|
|
12
11
|
context.cache.set(buildId, { fullOutput, result, operation: input.operation }, "build", CACHE_TTLS.BUILD_OUTPUT);
|
|
13
12
|
return {
|
|
@@ -24,7 +23,7 @@ export async function handleGradleBuildTool(input, context) {
|
|
|
24
23
|
}
|
|
25
24
|
export const gradleBuildToolDefinition = {
|
|
26
25
|
name: "gradle-build",
|
|
27
|
-
description: "Build
|
|
26
|
+
description: "Build. Returns summary with buildId.",
|
|
28
27
|
inputSchema: {
|
|
29
28
|
type: "object",
|
|
30
29
|
properties: {
|
|
@@ -32,8 +31,8 @@ export const gradleBuildToolDefinition = {
|
|
|
32
31
|
type: "string",
|
|
33
32
|
enum: ["assembleDebug", "assembleRelease", "bundle"],
|
|
34
33
|
},
|
|
35
|
-
module: { type: "string", description: "
|
|
36
|
-
flavor: { type: "string"
|
|
34
|
+
module: { type: "string", description: "e.g., ':app'" },
|
|
35
|
+
flavor: { type: "string" },
|
|
37
36
|
},
|
|
38
37
|
required: ["operation"],
|
|
39
38
|
},
|
|
@@ -139,15 +139,14 @@ export const gradleGetDetailsToolDefinition = {
|
|
|
139
139
|
inputSchema: {
|
|
140
140
|
type: "object",
|
|
141
141
|
properties: {
|
|
142
|
-
id: { type: "string"
|
|
142
|
+
id: { type: "string" },
|
|
143
143
|
detailType: {
|
|
144
144
|
type: "string",
|
|
145
145
|
enum: ["logs", "errors", "tasks", "all"],
|
|
146
|
-
description: "Type of details to retrieve",
|
|
147
146
|
},
|
|
148
147
|
maxChars: {
|
|
149
148
|
type: "number",
|
|
150
|
-
description: "Truncate
|
|
149
|
+
description: "Truncate to N chars",
|
|
151
150
|
},
|
|
152
151
|
summaryOnly: {
|
|
153
152
|
type: "boolean",
|
|
@@ -16,10 +16,8 @@ export async function handleGradleListTool(input, context) {
|
|
|
16
16
|
}
|
|
17
17
|
case "tasks": {
|
|
18
18
|
const tasks = await context.gradle.listTasks(input.module);
|
|
19
|
-
// Cache full task list
|
|
20
19
|
const listId = context.cache.generateId("tasks");
|
|
21
20
|
context.cache.set(listId, { tasks }, "tasks", CACHE_TTLS.GRADLE_VARIANTS);
|
|
22
|
-
// Return categorized summary
|
|
23
21
|
const buildTasks = tasks.filter((t) => t.includes("assemble") || t.includes("bundle"));
|
|
24
22
|
const testTasks = tasks.filter((t) => t.includes("test") || t.includes("Test"));
|
|
25
23
|
const cleanTasks = tasks.filter((t) => t.includes("clean"));
|
|
@@ -40,7 +38,7 @@ export async function handleGradleListTool(input, context) {
|
|
|
40
38
|
}
|
|
41
39
|
export const gradleListToolDefinition = {
|
|
42
40
|
name: "gradle-list",
|
|
43
|
-
description: "Introspect project structure.
|
|
41
|
+
description: "Introspect project structure.",
|
|
44
42
|
inputSchema: {
|
|
45
43
|
type: "object",
|
|
46
44
|
properties: {
|
|
@@ -48,7 +46,7 @@ export const gradleListToolDefinition = {
|
|
|
48
46
|
type: "string",
|
|
49
47
|
enum: ["variants", "modules", "tasks"],
|
|
50
48
|
},
|
|
51
|
-
module: { type: "string", description: "
|
|
49
|
+
module: { type: "string", description: "e.g., ':app'" },
|
|
52
50
|
},
|
|
53
51
|
required: ["operation"],
|
|
54
52
|
},
|