diff --git a/src/server.ts b/src/server.ts index 82245a5..d8f51b3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,6 +1,7 @@ import { FastMCP } from 'fastmcp'; import registerTools from './tools/index.js'; import registerResources from './resources/index.js'; +import { hasActiveSession, safeDeleteSession } from './tools/sessionStore.js'; const server = new FastMCP({ name: 'Jarvis Appium', @@ -11,4 +12,28 @@ const server = new FastMCP({ registerResources(server); registerTools(server); + +// Handle client connection and disconnection events +server.on("connect", (event) => { + console.log("Client connected:", event.session); +}); + +server.on("disconnect", async (event) => { + console.log("Client disconnected:", event.session); + // Only try to clean up if there's an active session + if (hasActiveSession()) { + try { + console.log("Active session detected on disconnect, cleaning up..."); + const deleted = await safeDeleteSession(); + if (deleted) { + console.log("Session cleaned up successfully on disconnect."); + } + } catch (error) { + console.error("Error cleaning up session on disconnect:", error); + } + } else { + console.log("No active session to clean up on disconnect."); + } +}); + export default server; diff --git a/src/tools/create-session.ts b/src/tools/create-session.ts index f709138..def9987 100644 --- a/src/tools/create-session.ts +++ b/src/tools/create-session.ts @@ -3,10 +3,9 @@ */ import { z } from 'zod'; import fs from 'fs'; -import path from 'path'; import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver'; import { XCUITestDriver } from 'appium-xcuitest-driver'; -import { setSession, getDriver, getSessionId } from './sessionStore.js'; +import { setSession, hasActiveSession, safeDeleteSession } from './sessionStore.js'; // Define capabilities type interface Capabilities { @@ -44,6 +43,12 @@ export default function createSession(server: any): void { }, execute: async (args: any, context: any): Promise => { try { + // Check if there's an existing session and clean it up first + if (hasActiveSession()) { + console.log('Existing session detected, cleaning up before creating new session...'); + await safeDeleteSession(); + } + const { platform, capabilities: customCapabilities } = args; let defaultCapabilities: Capabilities; diff --git a/src/tools/delete-session.ts b/src/tools/delete-session.ts new file mode 100644 index 0000000..c0e8c84 --- /dev/null +++ b/src/tools/delete-session.ts @@ -0,0 +1,46 @@ +/** + * Tool to delete the current mobile session and clean up resources + */ +import { z } from 'zod'; +import { safeDeleteSession } from './sessionStore.js'; + +export default function deleteSession(server: any): void { + server.addTool({ + name: 'delete_session', + description: 'Delete the current mobile session and clean up resources.', + parameters: z.object({}), + annotations: { + destructiveHint: true, + readOnlyHint: false, + openWorldHint: false, + }, + execute: async (): Promise => { + try { + const deleted = await safeDeleteSession(); + + if (deleted) { + return { + content: [ + { + type: 'text', + text: 'Session deleted successfully.', + }, + ], + }; + } else { + return { + content: [ + { + type: 'text', + text: 'No active session found or deletion already in progress.', + }, + ], + }; + } + } catch (error: any) { + console.error(`Error deleting session`, error); + throw new Error(`Failed to delete session: ${error.message}`); + } + }, + }); +} \ No newline at end of file diff --git a/src/tools/index.ts b/src/tools/index.ts index c5b9dcb..a28e0dd 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,6 +1,7 @@ import { FastMCP } from 'fastmcp/dist/FastMCP.js'; import answerAppium from './answerAppium.js'; import createSession from './create-session.js'; +import deleteSession from './delete-session.js'; import createCloudSession from './create-cloud-session.js'; import uploadApp from './upload-app.js'; import generateLocators from './locators.js'; @@ -19,6 +20,7 @@ import terminateApp from './interactions/terminateApp.js'; export default function registerTools(server: FastMCP): void { selectPlatform(server); createSession(server); + deleteSession(server); createCloudSession(server); uploadApp(server); generateLocators(server); diff --git a/src/tools/sessionStore.ts b/src/tools/sessionStore.ts index 72243be..add983c 100644 --- a/src/tools/sessionStore.ts +++ b/src/tools/sessionStore.ts @@ -3,10 +3,15 @@ import { XCUITestDriver } from 'appium-xcuitest-driver'; let driver: any = null; let sessionId: string | null = null; +let isDeletingSession = false; // Lock to prevent concurrent deletion -export function setSession(d: any, id: string) { +export function setSession(d: any, id: string | null) { driver = d; sessionId = id; + // Reset deletion flag when setting a new session + if (d && id) { + isDeletingSession = false; + } } export function getDriver() { @@ -17,6 +22,49 @@ export function getSessionId() { return sessionId; } +export function isDeletingSessionInProgress() { + return isDeletingSession; +} + +export function hasActiveSession(): boolean { + return driver !== null && sessionId !== null && !isDeletingSession; +} + +export async function safeDeleteSession(): Promise { + // Check if there's no session to delete + if (!driver || !sessionId) { + console.log('No active session to delete.'); + return false; + } + + // Check if deletion is already in progress + if (isDeletingSession) { + console.log('Session deletion already in progress, skipping...'); + return false; + } + + // Set lock + isDeletingSession = true; + + try { + console.log('Deleting current session'); + await driver.deleteSession(); + + // Clear the session from store + driver = null; + sessionId = null; + + console.log('Session deleted successfully.'); + return true; + } catch (error) { + console.error('Error deleting session:', error); + throw error; + } finally { + // Always release lock + isDeletingSession = false; + } +} + export const getPlatformName = (driver: any): string => { if (driver instanceof AndroidUiautomator2Driver) return 'Android'; if (driver instanceof XCUITestDriver) return 'iOS';