diff --git a/package-lock.json b/package-lock.json index 4dc1ed7..4ad3272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "1.0.3", "@playwright/browser-chromium": "1.49.1", + "@playwright/test": "^1.51.0", "playwright": "1.49.1" }, "bin": { @@ -46,6 +47,51 @@ "node": ">=18" } }, + "node_modules/@playwright/test": { + "version": "1.51.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/@playwright/test/-/test-1.51.0.tgz", + "integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==", + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.51.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/test/node_modules/playwright": { + "version": "1.51.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/playwright/-/playwright-1.51.0.tgz", + "integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.51.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@playwright/test/node_modules/playwright-core": { + "version": "1.51.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/playwright-core/-/playwright-core-1.51.0.tgz", + "integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@types/node": { "version": "20.17.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", diff --git a/package.json b/package.json index 041d1a0..258d2b9 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,17 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "1.0.3", - "playwright": "1.49.1", - "@playwright/browser-chromium": "1.49.1" + "@playwright/browser-chromium": "1.49.1", + "@playwright/test": "^1.51.0", + "playwright": "1.49.1" }, - "keywords": ["playwright", "automation", "AI", "Claude", "Model Context Protocol"], + "keywords": [ + "playwright", + "automation", + "AI", + "Claude", + "Model Context Protocol" + ], "devDependencies": { "@types/node": "^20.10.5", "shx": "^0.3.4", diff --git a/src/toolsHandler.ts b/src/toolsHandler.ts index 4ce8f68..487c093 100644 --- a/src/toolsHandler.ts +++ b/src/toolsHandler.ts @@ -16,7 +16,7 @@ async function ensureBrowser() { if (!browser) { browser = await chromium.launch({ headless: false }); const context = await browser.newContext({ - viewport: { width: 1920, height: 1080 }, + viewport: { width: 1366, height: 768 }, deviceScaleFactor: 1, }); @@ -41,7 +41,7 @@ export async function handleToolCall( name: string, args: any, server: any -): Promise<{ toolResult: CallToolResult }> { +): Promise { // Check if the tool requires browser interaction const requiresBrowser = BROWSER_TOOLS.includes(name); // Check if the tool requires api interaction @@ -67,23 +67,19 @@ export async function handleToolCall( waitUntil: args.waitUntil || "load" }); return { - toolResult: { - content: [{ - type: "text", - text: `Navigated to ${args.url} with ${args.waitUntil || "load"} wait`, - }], - isError: false, - }, + content: [{ + type: "text", + text: `Navigated to ${args.url} with ${args.waitUntil || "load"} wait`, + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Navigation failed: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Navigation failed: ${(error as Error).message}`, + }], + isError: true, }; } @@ -98,13 +94,11 @@ export async function handleToolCall( const element = await page!.$(args.selector); if (!element) { return { - toolResult: { - content: [{ - type: "text", - text: `Element not found: ${args.selector}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Element not found: ${args.selector}`, + }], + isError: true, }; } screenshotOptions.element = element; @@ -143,9 +137,9 @@ export async function handleToolCall( // Handle base64 storage if (args.storeBase64 !== false) { screenshots.set(args.name, base64Screenshot); - server.notification({ - method: "notifications/resources/list_changed", - }); + // server.notification({ + // method: "notifications/resources/list_changed", + // }); responseContent.push({ type: "image", @@ -155,20 +149,16 @@ export async function handleToolCall( } return { - toolResult: { - content: responseContent, - isError: false, - }, + content: responseContent, + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Screenshot failed: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Screenshot failed: ${(error as Error).message}`, + }], + isError: true, }; } } @@ -176,23 +166,19 @@ export async function handleToolCall( try { await page!.click(args.selector); return { - toolResult: { - content: [{ - type: "text", - text: `Clicked: ${args.selector}`, - }], - isError: false, - }, + content: [{ + type: "text", + text: `Clicked: ${args.selector}`, + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to click ${args.selector}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to click ${args.selector}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -201,23 +187,19 @@ export async function handleToolCall( await page!.waitForSelector(args.selector); await page!.fill(args.selector, args.value); return { - toolResult: { - content: [{ - type: "text", - text: `Filled ${args.selector} with: ${args.value}`, - }], - isError: false, - }, + content: [{ + type: "text", + text: `Filled ${args.selector} with: ${args.value}`, + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to type ${args.selector}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to type ${args.selector}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -226,23 +208,19 @@ export async function handleToolCall( await page!.waitForSelector(args.selector); await page!.selectOption(args.selector, args.value); return { - toolResult: { - content: [{ - type: "text", - text: `Selected ${args.selector} with: ${args.value}`, - }], - isError: false, - }, + content: [{ + type: "text", + text: `Selected ${args.selector} with: ${args.value}`, + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to select ${args.selector}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to select ${args.selector}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -251,23 +229,19 @@ export async function handleToolCall( await page!.waitForSelector(args.selector); await page!.hover(args.selector); return { - toolResult: { - content: [{ - type: "text", - text: `Hovered ${args.selector}`, - }], - isError: false, - }, + content: [{ + type: "text", + text: `Hovered ${args.selector}`, + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to hover ${args.selector}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to hover ${args.selector}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -289,31 +263,27 @@ export async function handleToolCall( Object.assign(console, originalConsole); return { result, logs }; } catch (error) { - Object.assign(console, originalConsole); + Object.assign(console, originalConsole); throw error; } }, args.script); return { - toolResult: { - content: [ - { - type: "text", - text: `Execution result:\n${JSON.stringify(result.result, null, 2)}\n\nConsole output:\n${result.logs.join('\n')}`, - }, - ], - isError: false, - }, + content: [ + { + type: "text", + text: `Execution result:\n${result.result?JSON.stringify(result.result, null, 2):""}\n\nConsole output:\n${result.logs.join('\n')}`, + }, + ], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Script execution failed: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Script execution failed: ${(error as Error).message}`, + }], + isError: true, }; } @@ -322,32 +292,28 @@ export async function handleToolCall( var response = await apiContext!.get(args.url); return { - toolResult: { - content: [{ - type: "text", - text: `Performed GET Operation ${args.url}`, - }, - { - type: "text", - text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, - }, - { - type: "text", - text: `Response code ${response.status()}` - } - ], - isError: false, + content: [{ + type: "text", + text: `Performed GET Operation ${args.url}`, }, + { + type: "text", + text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, + }, + { + type: "text", + text: `Response code ${response.status()}` + } + ], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to perform GET operation on ${args.url}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to perform GET operation on ${args.url}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -362,31 +328,27 @@ export async function handleToolCall( var response = await apiContext!.post(args.url, data); return { - toolResult: { - content: [{ - type: "text", - text: `Performed POST Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, - }, - { - type: "text", - text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, - }, - { - type: "text", - text: `Response code ${response.status()}` - }], - isError: false, + content: [{ + type: "text", + text: `Performed POST Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, + }, + { + type: "text", + text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, }, + { + type: "text", + text: `Response code ${response.status()}` + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to perform POST operation on ${args.url}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to perform POST operation on ${args.url}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -401,30 +363,26 @@ export async function handleToolCall( var response = await apiContext!.put(args.url, data); return { - toolResult: { - content: [{ - type: "text", - text: `Performed PUT Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, - }, { - type: "text", - text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, - }, - { - type: "text", - text: `Response code ${response.status()}` - }], - isError: false, + content: [{ + type: "text", + text: `Performed PUT Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, + }, { + type: "text", + text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, }, + { + type: "text", + text: `Response code ${response.status()}` + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to perform PUT operation on ${args.url}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to perform PUT operation on ${args.url}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -433,27 +391,23 @@ export async function handleToolCall( var response = await apiContext!.delete(args.url); return { - toolResult: { - content: [{ - type: "text", - text: `Performed delete Operation ${args.url}`, - }, - { - type: "text", - text: `Response code ${response.status()}` - }], - isError: false, + content: [{ + type: "text", + text: `Performed delete Operation ${args.url}`, }, + { + type: "text", + text: `Response code ${response.status()}` + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to perform delete operation on ${args.url}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to perform delete operation on ${args.url}: ${(error as Error).message}`, + }], + isError: true, }; } @@ -468,41 +422,35 @@ export async function handleToolCall( var response = await apiContext!.patch(args.url, data); return { - toolResult: { - content: [{ - type: "text", - text: `Performed PATCH Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, - }, { - type: "text", - text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, - }, { - type: "text", - text: `Response code ${response.status()}` - }], - isError: false, - }, + content: [{ + type: "text", + text: `Performed PATCH Operation ${args.url} with data ${JSON.stringify(args.value, null, 2)}`, + }, { + type: "text", + text: `Response: ${JSON.stringify(await response.json(), null, 2)}`, + }, { + type: "text", + text: `Response code ${response.status()}` + }], + isError: false, }; } catch (error) { return { - toolResult: { - content: [{ - type: "text", - text: `Failed to perform PATCH operation on ${args.url}: ${(error as Error).message}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Failed to perform PATCH operation on ${args.url}: ${(error as Error).message}`, + }], + isError: true, }; } default: return { - toolResult: { - content: [{ - type: "text", - text: `Unknown tool: ${name}`, - }], - isError: true, - }, + content: [{ + type: "text", + text: `Unknown tool: ${name}`, + }], + isError: true, }; } } @@ -514,4 +462,4 @@ export function getConsoleLogs(): string[] { export function getScreenshots(): Map { return screenshots; -} \ No newline at end of file +}