Skip to content

Commit b4ab27c

Browse files
Added tools for install,uninstall, terminateapp and listApps-android
Co-authored-by: SrinivasanTarget <[email protected]>
1 parent 9a4d653 commit b4ab27c

File tree

10 files changed

+204
-32
lines changed

10 files changed

+204
-32
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@
6262
"typescript": "^5.8.3"
6363
}
6464
}
65+

src/server.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@ registerResources(server);
1414
registerTools(server);
1515

1616
// Handle client connection and disconnection events
17-
server.on("connect", (event) => {
18-
console.log("Client connected:", event.session);
17+
server.on('connect', event => {
18+
console.log('Client connected:', event.session);
1919
});
2020

21-
server.on("disconnect", async (event) => {
22-
console.log("Client disconnected:", event.session);
21+
server.on('disconnect', async event => {
22+
console.log('Client disconnected:', event.session);
2323
// Only try to clean up if there's an active session
2424
if (hasActiveSession()) {
2525
try {
26-
console.log("Active session detected on disconnect, cleaning up...");
26+
console.log('Active session detected on disconnect, cleaning up...');
2727
const deleted = await safeDeleteSession();
2828
if (deleted) {
29-
console.log("Session cleaned up successfully on disconnect.");
29+
console.log('Session cleaned up successfully on disconnect.');
3030
}
3131
} catch (error) {
32-
console.error("Error cleaning up session on disconnect:", error);
32+
console.error('Error cleaning up session on disconnect:', error);
3333
}
3434
} else {
35-
console.log("No active session to clean up on disconnect.");
35+
console.log('No active session to clean up on disconnect.');
3636
}
3737
});
3838

src/tools/create-session.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { z } from 'zod';
55
import fs from 'fs';
66
import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
77
import { XCUITestDriver } from 'appium-xcuitest-driver';
8-
import { setSession, hasActiveSession, safeDeleteSession } from './sessionStore.js';
8+
import {
9+
setSession,
10+
hasActiveSession,
11+
safeDeleteSession,
12+
} from './sessionStore.js';
913

1014
// Define capabilities type
1115
interface Capabilities {
@@ -45,7 +49,9 @@ export default function createSession(server: any): void {
4549
try {
4650
// Check if there's an existing session and clean it up first
4751
if (hasActiveSession()) {
48-
console.log('Existing session detected, cleaning up before creating new session...');
52+
console.log(
53+
'Existing session detected, cleaning up before creating new session...'
54+
);
4955
await safeDeleteSession();
5056
}
5157

src/tools/delete-session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function deleteSession(server: any): void {
1717
execute: async (): Promise<any> => {
1818
try {
1919
const deleted = await safeDeleteSession();
20-
20+
2121
if (deleted) {
2222
return {
2323
content: [
@@ -43,4 +43,4 @@ export default function deleteSession(server: any): void {
4343
}
4444
},
4545
});
46-
}
46+
}

src/tools/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import setValue from './interactions/setValue.js';
1515
import getText from './interactions/getText.js';
1616
import screenshot from './interactions/screenshot.js';
1717
import activateApp from './interactions/activateApp.js';
18+
import installApp from './interactions/installApp.js';
19+
import uninstallApp from './interactions/uninstallApp.js';
1820
import terminateApp from './interactions/terminateApp.js';
21+
import listApps from './interactions/listApps.js';
1922

2023
export default function registerTools(server: FastMCP): void {
2124
selectPlatform(server);
@@ -29,12 +32,15 @@ export default function registerTools(server: FastMCP): void {
2932
scrollToElement(server);
3033

3134
activateApp(server);
35+
installApp(server);
36+
uninstallApp(server);
3237
terminateApp(server);
38+
listApps(server);
3339
findElement(server);
3440
clickElement(server);
3541
setValue(server);
3642
getText(server);
37-
screenshot(server);
43+
//screenshot(server);
3844
generateTest(server);
3945
console.log('All tools registered');
4046
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
2+
import { z } from 'zod';
3+
import { getDriver, getPlatformName } from '../sessionStore.js';
4+
5+
export default function installApp(server: FastMCP): void {
6+
const schema = z.object({
7+
path: z.string().describe('Path to the app file to install'),
8+
});
9+
10+
server.addTool({
11+
name: 'appium_installApp',
12+
description: 'Install an app on the device from a file path.',
13+
parameters: schema,
14+
execute: async (args: z.infer<typeof schema>) => {
15+
const { path } = args;
16+
const driver = await getDriver();
17+
if (!driver) {
18+
throw new Error('No driver found');
19+
}
20+
try {
21+
const platform = getPlatformName(driver);
22+
const params =
23+
platform === 'Android' ? { appPath: path } : { app: path };
24+
await (driver as any).execute('mobile: installApp', params);
25+
return {
26+
content: [
27+
{
28+
type: 'text',
29+
text: 'App installed successfully',
30+
},
31+
],
32+
};
33+
} catch (err: any) {
34+
return {
35+
content: [
36+
{
37+
type: 'text',
38+
text: `Failed to install app. err: ${err.toString()}`,
39+
},
40+
],
41+
};
42+
}
43+
},
44+
});
45+
}

src/tools/interactions/listApps.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
2+
import { z } from 'zod';
3+
import { getDriver, getPlatformName } from '../sessionStore.js';
4+
import { InstalledApp } from '../robot.js';
5+
6+
async function listAppsFromDevice(): Promise<InstalledApp[]> {
7+
const driver = await getDriver();
8+
if (!driver) {
9+
throw new Error('No driver found');
10+
}
11+
12+
const platform = getPlatformName(driver);
13+
if (platform === 'iOS') {
14+
throw new Error('listApps is not yet implemented for iOS');
15+
}
16+
17+
const appPackages = await driver.adb.adbExec([
18+
'shell',
19+
'cmd',
20+
'package',
21+
'list',
22+
'packages',
23+
]);
24+
25+
const apps: InstalledApp[] = appPackages
26+
.split('package:')
27+
.filter((s: any) => s.trim())
28+
.map((s: any) => ({
29+
packageName: s.trim(),
30+
appName: '',
31+
}));
32+
33+
return apps;
34+
}
35+
36+
export default function listApps(server: FastMCP): void {
37+
const schema = z.object({});
38+
39+
server.addTool({
40+
name: 'appium_list_apps',
41+
description: 'List all installed apps on the device.',
42+
parameters: schema,
43+
execute: async () => {
44+
try {
45+
const apps = await listAppsFromDevice();
46+
return {
47+
content: [
48+
{
49+
type: 'text',
50+
text: `Installed apps: ${JSON.stringify(apps, null, 2)}`,
51+
},
52+
],
53+
};
54+
} catch (err: any) {
55+
return {
56+
content: [
57+
{
58+
type: 'text',
59+
text: `Failed to list apps. err: ${err.toString()}`,
60+
},
61+
],
62+
};
63+
}
64+
},
65+
});
66+
}
Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
2-
import { getDriver } from '../sessionStore.js';
32
import { z } from 'zod';
3+
import { getDriver, getPlatformName } from '../sessionStore.js';
44

55
export default function terminateApp(server: FastMCP): void {
6-
const terminateAppSchema = z.object({
7-
id: z.string().describe('The app id'),
6+
const schema = z.object({
7+
id: z
8+
.string()
9+
.describe('App identifier (package name for Android, bundle ID for iOS)'),
810
});
911

1012
server.addTool({
11-
name: 'appium_terminate_app',
12-
description: 'Terminate app by id',
13-
parameters: terminateAppSchema,
14-
annotations: {
15-
readOnlyHint: false,
16-
openWorldHint: false,
17-
},
18-
execute: async (args: { id: string }, context: any): Promise<any> => {
19-
const driver = getDriver();
13+
name: 'appium_terminateApp',
14+
description: 'Terminate an app on the device.',
15+
parameters: schema,
16+
execute: async (args: z.infer<typeof schema>) => {
17+
const { id } = args;
18+
const driver = await getDriver();
2019
if (!driver) {
2120
throw new Error('No driver found');
2221
}
23-
2422
try {
25-
await driver.terminateApp(args.id);
23+
const platform = getPlatformName(driver);
24+
const params =
25+
platform === 'Android' ? { appId: id } : { bundleId: id };
26+
await (driver as any).execute('mobile: terminateApp', params);
2627
return {
2728
content: [
2829
{
2930
type: 'text',
30-
text: `App ${args.id} terminated correctly.`,
31+
text: 'App terminated successfully',
3132
},
3233
],
3334
};
@@ -36,11 +37,11 @@ export default function terminateApp(server: FastMCP): void {
3637
content: [
3738
{
3839
type: 'text',
39-
text: `Error terminating the app ${args.id}: ${err.toString()}`,
40+
text: `Failed to terminate app. err: ${err.toString()}`,
4041
},
4142
],
4243
};
4344
}
4445
},
4546
});
46-
}
47+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
2+
import { z } from 'zod';
3+
import { getDriver, getPlatformName } from '../sessionStore.js';
4+
5+
export default function uninstallApp(server: FastMCP): void {
6+
const schema = z.object({
7+
id: z
8+
.string()
9+
.describe('App identifier (package name for Android, bundle ID for iOS)'),
10+
});
11+
12+
server.addTool({
13+
name: 'appium_uninstallApp',
14+
description: 'Uninstall an app from the device.',
15+
parameters: schema,
16+
execute: async (args: z.infer<typeof schema>) => {
17+
const { id } = args;
18+
const driver = await getDriver();
19+
if (!driver) {
20+
throw new Error('No driver found');
21+
}
22+
try {
23+
const platform = getPlatformName(driver);
24+
const params =
25+
platform === 'Android' ? { appId: id } : { bundleId: id };
26+
await (driver as any).execute('mobile: removeApp', params);
27+
return {
28+
content: [
29+
{
30+
type: 'text',
31+
text: 'App uninstalled successfully',
32+
},
33+
],
34+
};
35+
} catch (err: any) {
36+
return {
37+
content: [
38+
{
39+
type: 'text',
40+
text: `Failed to uninstall app. err: ${err.toString()}`,
41+
},
42+
],
43+
};
44+
}
45+
},
46+
});
47+
}

src/tools/sessionStore.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ export async function safeDeleteSession(): Promise<boolean> {
4949
try {
5050
console.log('Deleting current session');
5151
await driver.deleteSession();
52-
52+
5353
// Clear the session from store
5454
driver = null;
5555
sessionId = null;
56-
56+
5757
console.log('Session deleted successfully.');
5858
return true;
5959
} catch (error) {

0 commit comments

Comments
 (0)