replicant-mcp 1.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/LICENSE +21 -0
- package/README.md +386 -0
- package/dist/adapters/adb.d.ts +21 -0
- package/dist/adapters/adb.js +75 -0
- package/dist/adapters/emulator.d.ts +19 -0
- package/dist/adapters/emulator.js +72 -0
- package/dist/adapters/gradle.d.ts +20 -0
- package/dist/adapters/gradle.js +80 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/ui-automator.d.ts +23 -0
- package/dist/adapters/ui-automator.js +53 -0
- package/dist/cli/adb.d.ts +2 -0
- package/dist/cli/adb.js +256 -0
- package/dist/cli/cache.d.ts +2 -0
- package/dist/cli/cache.js +115 -0
- package/dist/cli/emulator.d.ts +2 -0
- package/dist/cli/emulator.js +181 -0
- package/dist/cli/formatter.d.ts +52 -0
- package/dist/cli/formatter.js +68 -0
- package/dist/cli/gradle.d.ts +2 -0
- package/dist/cli/gradle.js +192 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +218 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/parsers/adb-output.d.ts +4 -0
- package/dist/parsers/adb-output.js +32 -0
- package/dist/parsers/emulator-output.d.ts +9 -0
- package/dist/parsers/emulator-output.js +33 -0
- package/dist/parsers/gradle-output.d.ts +30 -0
- package/dist/parsers/gradle-output.js +80 -0
- package/dist/parsers/index.d.ts +4 -0
- package/dist/parsers/index.js +4 -0
- package/dist/parsers/ui-dump.d.ts +27 -0
- package/dist/parsers/ui-dump.js +142 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +113 -0
- package/dist/services/cache-manager.d.ts +22 -0
- package/dist/services/cache-manager.js +90 -0
- package/dist/services/device-state.d.ts +9 -0
- package/dist/services/device-state.js +26 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +3 -0
- package/dist/services/process-runner.d.ts +15 -0
- package/dist/services/process-runner.js +62 -0
- package/dist/tools/adb-app.d.ts +38 -0
- package/dist/tools/adb-app.js +68 -0
- package/dist/tools/adb-device.d.ts +31 -0
- package/dist/tools/adb-device.js +71 -0
- package/dist/tools/adb-logcat.d.ts +54 -0
- package/dist/tools/adb-logcat.js +70 -0
- package/dist/tools/adb-shell.d.ts +26 -0
- package/dist/tools/adb-shell.js +27 -0
- package/dist/tools/cache.d.ts +50 -0
- package/dist/tools/cache.js +57 -0
- package/dist/tools/emulator-device.d.ts +56 -0
- package/dist/tools/emulator-device.js +132 -0
- package/dist/tools/gradle-build.d.ts +35 -0
- package/dist/tools/gradle-build.js +40 -0
- package/dist/tools/gradle-get-details.d.ts +32 -0
- package/dist/tools/gradle-get-details.js +72 -0
- package/dist/tools/gradle-list.d.ts +30 -0
- package/dist/tools/gradle-list.js +55 -0
- package/dist/tools/gradle-test.d.ts +34 -0
- package/dist/tools/gradle-test.js +40 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/rtfm.d.ts +26 -0
- package/dist/tools/rtfm.js +70 -0
- package/dist/tools/ui.d.ts +77 -0
- package/dist/tools/ui.js +131 -0
- package/dist/types/cache.d.ts +24 -0
- package/dist/types/cache.js +14 -0
- package/dist/types/device.d.ts +11 -0
- package/dist/types/device.js +1 -0
- package/dist/types/errors.d.ts +31 -0
- package/dist/types/errors.js +43 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/package.json +64 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { CacheManager, DeviceStateManager, ProcessRunner } from "./services/index.js";
|
|
5
|
+
import { AdbAdapter, EmulatorAdapter, GradleAdapter, UiAutomatorAdapter } from "./adapters/index.js";
|
|
6
|
+
import { ReplicantError } from "./types/index.js";
|
|
7
|
+
import { cacheToolDefinition, handleCacheTool, rtfmToolDefinition, handleRtfmTool, adbDeviceToolDefinition, handleAdbDeviceTool, adbAppToolDefinition, handleAdbAppTool, adbLogcatToolDefinition, handleAdbLogcatTool, adbShellToolDefinition, handleAdbShellTool, emulatorDeviceToolDefinition, handleEmulatorDeviceTool, gradleBuildToolDefinition, handleGradleBuildTool, gradleTestToolDefinition, handleGradleTestTool, gradleListToolDefinition, handleGradleListTool, gradleGetDetailsToolDefinition, handleGradleGetDetailsTool, uiToolDefinition, handleUiTool, } from "./tools/index.js";
|
|
8
|
+
export function createServerContext() {
|
|
9
|
+
const processRunner = new ProcessRunner();
|
|
10
|
+
const adb = new AdbAdapter(processRunner);
|
|
11
|
+
return {
|
|
12
|
+
cache: new CacheManager(),
|
|
13
|
+
deviceState: new DeviceStateManager(),
|
|
14
|
+
processRunner,
|
|
15
|
+
adb,
|
|
16
|
+
emulator: new EmulatorAdapter(processRunner),
|
|
17
|
+
gradle: new GradleAdapter(processRunner),
|
|
18
|
+
ui: new UiAutomatorAdapter(adb),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function createServer(context) {
|
|
22
|
+
const server = new Server({
|
|
23
|
+
name: "replicant-mcp",
|
|
24
|
+
version: "1.0.0",
|
|
25
|
+
}, {
|
|
26
|
+
capabilities: {
|
|
27
|
+
tools: {},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
// Register tool list handler
|
|
31
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
32
|
+
tools: [
|
|
33
|
+
cacheToolDefinition,
|
|
34
|
+
rtfmToolDefinition,
|
|
35
|
+
adbDeviceToolDefinition,
|
|
36
|
+
adbAppToolDefinition,
|
|
37
|
+
adbLogcatToolDefinition,
|
|
38
|
+
adbShellToolDefinition,
|
|
39
|
+
emulatorDeviceToolDefinition,
|
|
40
|
+
gradleBuildToolDefinition,
|
|
41
|
+
gradleTestToolDefinition,
|
|
42
|
+
gradleListToolDefinition,
|
|
43
|
+
gradleGetDetailsToolDefinition,
|
|
44
|
+
uiToolDefinition,
|
|
45
|
+
],
|
|
46
|
+
}));
|
|
47
|
+
// Register tool call handler
|
|
48
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
49
|
+
const { name, arguments: args } = request.params;
|
|
50
|
+
try {
|
|
51
|
+
let result;
|
|
52
|
+
switch (name) {
|
|
53
|
+
case "cache":
|
|
54
|
+
result = await handleCacheTool(args, context.cache);
|
|
55
|
+
break;
|
|
56
|
+
case "rtfm":
|
|
57
|
+
result = await handleRtfmTool(args);
|
|
58
|
+
break;
|
|
59
|
+
case "adb-device":
|
|
60
|
+
result = await handleAdbDeviceTool(args, context);
|
|
61
|
+
break;
|
|
62
|
+
case "adb-app":
|
|
63
|
+
result = await handleAdbAppTool(args, context);
|
|
64
|
+
break;
|
|
65
|
+
case "adb-logcat":
|
|
66
|
+
result = await handleAdbLogcatTool(args, context);
|
|
67
|
+
break;
|
|
68
|
+
case "adb-shell":
|
|
69
|
+
result = await handleAdbShellTool(args, context);
|
|
70
|
+
break;
|
|
71
|
+
case "emulator-device":
|
|
72
|
+
result = await handleEmulatorDeviceTool(args, context);
|
|
73
|
+
break;
|
|
74
|
+
case "gradle-build":
|
|
75
|
+
result = await handleGradleBuildTool(args, context);
|
|
76
|
+
break;
|
|
77
|
+
case "gradle-test":
|
|
78
|
+
result = await handleGradleTestTool(args, context);
|
|
79
|
+
break;
|
|
80
|
+
case "gradle-list":
|
|
81
|
+
result = await handleGradleListTool(args, context);
|
|
82
|
+
break;
|
|
83
|
+
case "gradle-get-details":
|
|
84
|
+
result = await handleGradleGetDetailsTool(args, context);
|
|
85
|
+
break;
|
|
86
|
+
case "ui":
|
|
87
|
+
result = await handleUiTool(args, context);
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof ReplicantError) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: JSON.stringify(error.toToolError(), null, 2) }],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return server;
|
|
107
|
+
}
|
|
108
|
+
export async function runServer() {
|
|
109
|
+
const context = createServerContext();
|
|
110
|
+
const server = await createServer(context);
|
|
111
|
+
const transport = new StdioServerTransport();
|
|
112
|
+
await server.connect(transport);
|
|
113
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CacheEntry, CacheConfig } from "../types/index.js";
|
|
2
|
+
export interface CacheStats {
|
|
3
|
+
entryCount: number;
|
|
4
|
+
totalSizeBytes: number;
|
|
5
|
+
typeBreakdown: Record<string, number>;
|
|
6
|
+
config: CacheConfig;
|
|
7
|
+
}
|
|
8
|
+
export declare class CacheManager {
|
|
9
|
+
private cache;
|
|
10
|
+
private accessOrder;
|
|
11
|
+
private config;
|
|
12
|
+
constructor(config?: Partial<CacheConfig>);
|
|
13
|
+
generateId(type: string): string;
|
|
14
|
+
set<T>(id: string, data: T, type: string, ttlMs?: number): void;
|
|
15
|
+
get<T>(id: string): CacheEntry<T> | undefined;
|
|
16
|
+
clear(id: string): void;
|
|
17
|
+
clearAll(): void;
|
|
18
|
+
invalidateByType(type: string): void;
|
|
19
|
+
getStats(): CacheStats;
|
|
20
|
+
getConfig(): CacheConfig;
|
|
21
|
+
setConfig(config: Partial<CacheConfig>): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { DEFAULT_CACHE_CONFIG } from "../types/index.js";
|
|
2
|
+
import { randomBytes } from "crypto";
|
|
3
|
+
export class CacheManager {
|
|
4
|
+
cache = new Map();
|
|
5
|
+
accessOrder = [];
|
|
6
|
+
config;
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.config = { ...DEFAULT_CACHE_CONFIG, ...config };
|
|
9
|
+
}
|
|
10
|
+
generateId(type) {
|
|
11
|
+
const hash = randomBytes(4).toString("hex");
|
|
12
|
+
const timestamp = Date.now();
|
|
13
|
+
return `${type}-${hash}-${timestamp}`;
|
|
14
|
+
}
|
|
15
|
+
set(id, data, type, ttlMs) {
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
const sizeBytes = JSON.stringify(data).length;
|
|
18
|
+
// LRU eviction
|
|
19
|
+
while (this.cache.size >= this.config.maxEntries) {
|
|
20
|
+
const oldest = this.accessOrder.shift();
|
|
21
|
+
if (oldest) {
|
|
22
|
+
this.cache.delete(oldest);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const entry = {
|
|
26
|
+
data,
|
|
27
|
+
expiresAt: now + (ttlMs ?? this.config.defaultTtlMs),
|
|
28
|
+
metadata: {
|
|
29
|
+
createdAt: now,
|
|
30
|
+
type,
|
|
31
|
+
sizeBytes,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
this.cache.set(id, entry);
|
|
35
|
+
this.accessOrder.push(id);
|
|
36
|
+
}
|
|
37
|
+
get(id) {
|
|
38
|
+
const entry = this.cache.get(id);
|
|
39
|
+
if (!entry) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// Check expiration
|
|
43
|
+
if (Date.now() > entry.expiresAt) {
|
|
44
|
+
this.cache.delete(id);
|
|
45
|
+
this.accessOrder = this.accessOrder.filter((k) => k !== id);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
// Update access order for LRU
|
|
49
|
+
this.accessOrder = this.accessOrder.filter((k) => k !== id);
|
|
50
|
+
this.accessOrder.push(id);
|
|
51
|
+
return entry;
|
|
52
|
+
}
|
|
53
|
+
clear(id) {
|
|
54
|
+
this.cache.delete(id);
|
|
55
|
+
this.accessOrder = this.accessOrder.filter((k) => k !== id);
|
|
56
|
+
}
|
|
57
|
+
clearAll() {
|
|
58
|
+
this.cache.clear();
|
|
59
|
+
this.accessOrder = [];
|
|
60
|
+
}
|
|
61
|
+
invalidateByType(type) {
|
|
62
|
+
for (const [id, entry] of this.cache.entries()) {
|
|
63
|
+
if (entry.metadata.type === type) {
|
|
64
|
+
this.cache.delete(id);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
this.accessOrder = this.accessOrder.filter((id) => this.cache.has(id));
|
|
68
|
+
}
|
|
69
|
+
getStats() {
|
|
70
|
+
const typeBreakdown = {};
|
|
71
|
+
let totalSizeBytes = 0;
|
|
72
|
+
for (const entry of this.cache.values()) {
|
|
73
|
+
const type = entry.metadata.type;
|
|
74
|
+
typeBreakdown[type] = (typeBreakdown[type] ?? 0) + 1;
|
|
75
|
+
totalSizeBytes += entry.metadata.sizeBytes ?? 0;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
entryCount: this.cache.size,
|
|
79
|
+
totalSizeBytes,
|
|
80
|
+
typeBreakdown,
|
|
81
|
+
config: this.config,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
getConfig() {
|
|
85
|
+
return { ...this.config };
|
|
86
|
+
}
|
|
87
|
+
setConfig(config) {
|
|
88
|
+
this.config = { ...this.config, ...config };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Device } from "../types/index.js";
|
|
2
|
+
export declare class DeviceStateManager {
|
|
3
|
+
private currentDevice;
|
|
4
|
+
getCurrentDevice(): Device | null;
|
|
5
|
+
setCurrentDevice(device: Device): void;
|
|
6
|
+
clearCurrentDevice(): void;
|
|
7
|
+
requireCurrentDevice(): Device;
|
|
8
|
+
autoSelectIfSingle(devices: Device[]): boolean;
|
|
9
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReplicantError, ErrorCode } from "../types/index.js";
|
|
2
|
+
export class DeviceStateManager {
|
|
3
|
+
currentDevice = null;
|
|
4
|
+
getCurrentDevice() {
|
|
5
|
+
return this.currentDevice;
|
|
6
|
+
}
|
|
7
|
+
setCurrentDevice(device) {
|
|
8
|
+
this.currentDevice = device;
|
|
9
|
+
}
|
|
10
|
+
clearCurrentDevice() {
|
|
11
|
+
this.currentDevice = null;
|
|
12
|
+
}
|
|
13
|
+
requireCurrentDevice() {
|
|
14
|
+
if (!this.currentDevice) {
|
|
15
|
+
throw new ReplicantError(ErrorCode.NO_DEVICE_SELECTED, "No device selected", "Call adb-device({ operation: 'list' }) to see available devices");
|
|
16
|
+
}
|
|
17
|
+
return this.currentDevice;
|
|
18
|
+
}
|
|
19
|
+
autoSelectIfSingle(devices) {
|
|
20
|
+
if (devices.length === 1 && !this.currentDevice) {
|
|
21
|
+
this.currentDevice = devices[0];
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RunOptions {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
cwd?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface RunResult {
|
|
6
|
+
stdout: string;
|
|
7
|
+
stderr: string;
|
|
8
|
+
exitCode: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ProcessRunner {
|
|
11
|
+
private readonly defaultTimeoutMs;
|
|
12
|
+
private readonly maxTimeoutMs;
|
|
13
|
+
run(command: string, args: string[], options?: RunOptions): Promise<RunResult>;
|
|
14
|
+
private validateCommand;
|
|
15
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import { ReplicantError, ErrorCode } from "../types/index.js";
|
|
3
|
+
const BLOCKED_COMMANDS = new Set(["reboot", "shutdown", "halt", "poweroff"]);
|
|
4
|
+
const BLOCKED_PATTERNS = [
|
|
5
|
+
/^rm\s+(-[rf]+\s+)*\//, // rm -rf /
|
|
6
|
+
/^su(\s|$)/, // su
|
|
7
|
+
/^sudo(\s|$)/, // sudo
|
|
8
|
+
/\bformat\b/, // format commands
|
|
9
|
+
];
|
|
10
|
+
export class ProcessRunner {
|
|
11
|
+
defaultTimeoutMs = 30_000;
|
|
12
|
+
maxTimeoutMs = 120_000;
|
|
13
|
+
async run(command, args, options = {}) {
|
|
14
|
+
this.validateCommand(command, args);
|
|
15
|
+
const timeoutMs = Math.min(options.timeoutMs ?? this.defaultTimeoutMs, this.maxTimeoutMs);
|
|
16
|
+
try {
|
|
17
|
+
const result = await execa(command, args, {
|
|
18
|
+
timeout: timeoutMs,
|
|
19
|
+
cwd: options.cwd,
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
stdout: result.stdout,
|
|
23
|
+
stderr: result.stderr,
|
|
24
|
+
exitCode: result.exitCode ?? 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
// Check for timeout (execa v9 uses timedOut property)
|
|
29
|
+
if (typeof error === "object" &&
|
|
30
|
+
error !== null &&
|
|
31
|
+
"timedOut" in error &&
|
|
32
|
+
error.timedOut) {
|
|
33
|
+
throw new ReplicantError(ErrorCode.TIMEOUT, `Command timed out after ${timeoutMs}ms`, "Try increasing the timeout or simplifying the command");
|
|
34
|
+
}
|
|
35
|
+
// For non-zero exit code, return the result instead of throwing
|
|
36
|
+
if (typeof error === "object" &&
|
|
37
|
+
error !== null &&
|
|
38
|
+
"exitCode" in error &&
|
|
39
|
+
"stdout" in error &&
|
|
40
|
+
"stderr" in error) {
|
|
41
|
+
const execaError = error;
|
|
42
|
+
return {
|
|
43
|
+
stdout: execaError.stdout,
|
|
44
|
+
stderr: execaError.stderr,
|
|
45
|
+
exitCode: execaError.exitCode,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
validateCommand(command, args) {
|
|
52
|
+
if (BLOCKED_COMMANDS.has(command)) {
|
|
53
|
+
throw new ReplicantError(ErrorCode.COMMAND_BLOCKED, `Command '${command}' is not allowed`, "Use safe commands only");
|
|
54
|
+
}
|
|
55
|
+
const fullCommand = `${command} ${args.join(" ")}`;
|
|
56
|
+
for (const pattern of BLOCKED_PATTERNS) {
|
|
57
|
+
if (pattern.test(fullCommand)) {
|
|
58
|
+
throw new ReplicantError(ErrorCode.COMMAND_BLOCKED, `Command '${fullCommand}' is not allowed`, "Use safe commands only");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ServerContext } from "../server.js";
|
|
3
|
+
export declare const adbAppInputSchema: z.ZodObject<{
|
|
4
|
+
operation: z.ZodEnum<{
|
|
5
|
+
list: "list";
|
|
6
|
+
install: "install";
|
|
7
|
+
uninstall: "uninstall";
|
|
8
|
+
launch: "launch";
|
|
9
|
+
stop: "stop";
|
|
10
|
+
"clear-data": "clear-data";
|
|
11
|
+
}>;
|
|
12
|
+
apkPath: z.ZodOptional<z.ZodString>;
|
|
13
|
+
packageName: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type AdbAppInput = z.infer<typeof adbAppInputSchema>;
|
|
16
|
+
export declare function handleAdbAppTool(input: AdbAppInput, context: ServerContext): Promise<Record<string, unknown>>;
|
|
17
|
+
export declare const adbAppToolDefinition: {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: string;
|
|
22
|
+
properties: {
|
|
23
|
+
operation: {
|
|
24
|
+
type: string;
|
|
25
|
+
enum: string[];
|
|
26
|
+
};
|
|
27
|
+
apkPath: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
packageName: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
required: string[];
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const adbAppInputSchema = z.object({
|
|
3
|
+
operation: z.enum(["install", "uninstall", "launch", "stop", "clear-data", "list"]),
|
|
4
|
+
apkPath: z.string().optional(),
|
|
5
|
+
packageName: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
export async function handleAdbAppTool(input, context) {
|
|
8
|
+
const deviceId = context.deviceState.requireCurrentDevice().id;
|
|
9
|
+
switch (input.operation) {
|
|
10
|
+
case "install": {
|
|
11
|
+
if (!input.apkPath) {
|
|
12
|
+
throw new Error("apkPath is required for install operation");
|
|
13
|
+
}
|
|
14
|
+
await context.adb.install(deviceId, input.apkPath);
|
|
15
|
+
return { installed: input.apkPath, deviceId };
|
|
16
|
+
}
|
|
17
|
+
case "uninstall": {
|
|
18
|
+
if (!input.packageName) {
|
|
19
|
+
throw new Error("packageName is required for uninstall operation");
|
|
20
|
+
}
|
|
21
|
+
await context.adb.uninstall(deviceId, input.packageName);
|
|
22
|
+
return { uninstalled: input.packageName, deviceId };
|
|
23
|
+
}
|
|
24
|
+
case "launch": {
|
|
25
|
+
if (!input.packageName) {
|
|
26
|
+
throw new Error("packageName is required for launch operation");
|
|
27
|
+
}
|
|
28
|
+
await context.adb.launch(deviceId, input.packageName);
|
|
29
|
+
return { launched: input.packageName, deviceId };
|
|
30
|
+
}
|
|
31
|
+
case "stop": {
|
|
32
|
+
if (!input.packageName) {
|
|
33
|
+
throw new Error("packageName is required for stop operation");
|
|
34
|
+
}
|
|
35
|
+
await context.adb.stop(deviceId, input.packageName);
|
|
36
|
+
return { stopped: input.packageName, deviceId };
|
|
37
|
+
}
|
|
38
|
+
case "clear-data": {
|
|
39
|
+
if (!input.packageName) {
|
|
40
|
+
throw new Error("packageName is required for clear-data operation");
|
|
41
|
+
}
|
|
42
|
+
await context.adb.clearData(deviceId, input.packageName);
|
|
43
|
+
return { cleared: input.packageName, deviceId };
|
|
44
|
+
}
|
|
45
|
+
case "list": {
|
|
46
|
+
const packages = await context.adb.getPackages(deviceId);
|
|
47
|
+
return { packages, count: packages.length, deviceId };
|
|
48
|
+
}
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(`Unknown operation: ${input.operation}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export const adbAppToolDefinition = {
|
|
54
|
+
name: "adb-app",
|
|
55
|
+
description: "Manage applications. Operations: install, uninstall, launch, stop, clear-data, list.",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
operation: {
|
|
60
|
+
type: "string",
|
|
61
|
+
enum: ["install", "uninstall", "launch", "stop", "clear-data", "list"],
|
|
62
|
+
},
|
|
63
|
+
apkPath: { type: "string", description: "Path to APK file (for install)" },
|
|
64
|
+
packageName: { type: "string", description: "Package name (for other operations)" },
|
|
65
|
+
},
|
|
66
|
+
required: ["operation"],
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ServerContext } from "../server.js";
|
|
3
|
+
export declare const adbDeviceInputSchema: z.ZodObject<{
|
|
4
|
+
operation: z.ZodEnum<{
|
|
5
|
+
list: "list";
|
|
6
|
+
select: "select";
|
|
7
|
+
wait: "wait";
|
|
8
|
+
properties: "properties";
|
|
9
|
+
}>;
|
|
10
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type AdbDeviceInput = z.infer<typeof adbDeviceInputSchema>;
|
|
13
|
+
export declare function handleAdbDeviceTool(input: AdbDeviceInput, context: ServerContext): Promise<Record<string, unknown>>;
|
|
14
|
+
export declare const adbDeviceToolDefinition: {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: string;
|
|
19
|
+
properties: {
|
|
20
|
+
operation: {
|
|
21
|
+
type: string;
|
|
22
|
+
enum: string[];
|
|
23
|
+
};
|
|
24
|
+
deviceId: {
|
|
25
|
+
type: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
required: string[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const adbDeviceInputSchema = z.object({
|
|
3
|
+
operation: z.enum(["list", "select", "wait", "properties"]),
|
|
4
|
+
deviceId: z.string().optional(),
|
|
5
|
+
});
|
|
6
|
+
export async function handleAdbDeviceTool(input, context) {
|
|
7
|
+
switch (input.operation) {
|
|
8
|
+
case "list": {
|
|
9
|
+
const devices = await context.adb.getDevices();
|
|
10
|
+
context.deviceState.autoSelectIfSingle(devices);
|
|
11
|
+
const current = context.deviceState.getCurrentDevice();
|
|
12
|
+
return {
|
|
13
|
+
devices,
|
|
14
|
+
currentDevice: current?.id || null,
|
|
15
|
+
autoSelected: devices.length === 1,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
case "select": {
|
|
19
|
+
if (!input.deviceId) {
|
|
20
|
+
throw new Error("deviceId is required for select operation");
|
|
21
|
+
}
|
|
22
|
+
const devices = await context.adb.getDevices();
|
|
23
|
+
const device = devices.find((d) => d.id === input.deviceId);
|
|
24
|
+
if (!device) {
|
|
25
|
+
throw new Error(`Device ${input.deviceId} not found`);
|
|
26
|
+
}
|
|
27
|
+
context.deviceState.setCurrentDevice(device);
|
|
28
|
+
return { selected: device };
|
|
29
|
+
}
|
|
30
|
+
case "wait": {
|
|
31
|
+
const deviceId = input.deviceId || context.deviceState.getCurrentDevice()?.id;
|
|
32
|
+
if (!deviceId) {
|
|
33
|
+
throw new Error("No device selected. Call with deviceId or select a device first.");
|
|
34
|
+
}
|
|
35
|
+
await context.adb.waitForDevice(deviceId);
|
|
36
|
+
return { status: "device ready", deviceId };
|
|
37
|
+
}
|
|
38
|
+
case "properties": {
|
|
39
|
+
const deviceId = input.deviceId || context.deviceState.requireCurrentDevice().id;
|
|
40
|
+
const props = await context.adb.getProperties(deviceId);
|
|
41
|
+
return {
|
|
42
|
+
deviceId,
|
|
43
|
+
properties: {
|
|
44
|
+
model: props["ro.product.model"],
|
|
45
|
+
manufacturer: props["ro.product.manufacturer"],
|
|
46
|
+
sdkVersion: props["ro.build.version.sdk"],
|
|
47
|
+
androidVersion: props["ro.build.version.release"],
|
|
48
|
+
buildId: props["ro.build.id"],
|
|
49
|
+
},
|
|
50
|
+
allProperties: props,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`Unknown operation: ${input.operation}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export const adbDeviceToolDefinition = {
|
|
58
|
+
name: "adb-device",
|
|
59
|
+
description: "Manage device connections. Operations: list, select, wait, properties.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
operation: {
|
|
64
|
+
type: "string",
|
|
65
|
+
enum: ["list", "select", "wait", "properties"],
|
|
66
|
+
},
|
|
67
|
+
deviceId: { type: "string", description: "Device ID for select/wait/properties" },
|
|
68
|
+
},
|
|
69
|
+
required: ["operation"],
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ServerContext } from "../server.js";
|
|
3
|
+
export declare const adbLogcatInputSchema: z.ZodObject<{
|
|
4
|
+
lines: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
5
|
+
package: z.ZodOptional<z.ZodString>;
|
|
6
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
level: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
debug: "debug";
|
|
9
|
+
info: "info";
|
|
10
|
+
verbose: "verbose";
|
|
11
|
+
warn: "warn";
|
|
12
|
+
error: "error";
|
|
13
|
+
}>>;
|
|
14
|
+
rawFilter: z.ZodOptional<z.ZodString>;
|
|
15
|
+
since: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export type AdbLogcatInput = z.infer<typeof adbLogcatInputSchema>;
|
|
18
|
+
export declare function handleAdbLogcatTool(input: AdbLogcatInput, context: ServerContext): Promise<Record<string, unknown>>;
|
|
19
|
+
export declare const adbLogcatToolDefinition: {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: string;
|
|
24
|
+
properties: {
|
|
25
|
+
lines: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
package: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
tags: {
|
|
34
|
+
type: string;
|
|
35
|
+
items: {
|
|
36
|
+
type: string;
|
|
37
|
+
};
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
level: {
|
|
41
|
+
type: string;
|
|
42
|
+
enum: string[];
|
|
43
|
+
};
|
|
44
|
+
rawFilter: {
|
|
45
|
+
type: string;
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
since: {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|