From 5c2e11017d23dfe2fc95351ab5bc8188f19d4c03 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 15 Apr 2025 18:01:59 -0700 Subject: [PATCH] chore: convert console resource to tool (#193) --- README.md | 6 +++++ src/context.ts | 17 +++++++++------ src/index.ts | 11 ++++------ src/{resources => tools}/console.ts | 34 +++++++++++++++++++---------- tests/basic.spec.ts | 34 +++++++++++------------------ tests/capabilities.spec.ts | 9 +++----- tests/console.spec.ts | 16 +++++++------- 7 files changed, 66 insertions(+), 61 deletions(-) rename src/{resources => tools}/console.ts (62%) diff --git a/README.md b/README.md index 4bfc48f..95f5fcd 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,12 @@ server.connect(transport); - Parameters: - `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a` +### Console + +- **browser_console_messages** + - Description: Returns all console messages + - Parameters: None + ### Files and Media - **browser_file_upload** diff --git a/src/context.ts b/src/context.ts index 11b81d5..c9ecb06 100644 --- a/src/context.ts +++ b/src/context.ts @@ -111,18 +111,21 @@ export class Context { if (this._currentTab === tab) this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)]; - const browser = this._browser; - if (this._browserContext && !this._tabs.length) { - void this._browserContext.close().then(() => browser?.close()).catch(() => {}); - this._browser = undefined; - this._browserContext = undefined; - } + if (this._browserContext && !this._tabs.length) + void this.close(); } async close() { if (!this._browserContext) return; - await this._browserContext.close(); + const browserContext = this._browserContext; + const browser = this._browser; + this._browserContext = undefined; + this._browser = undefined; + + await browserContext?.close().then(async () => { + await browser?.close(); + }).catch(() => {}); } private async _ensureBrowserContext() { diff --git a/src/index.ts b/src/index.ts index d052d8c..33cd7f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,7 @@ import fs from 'fs'; import { createServerWithTools } from './server'; import common from './tools/common'; +import console from './tools/console'; import files from './tools/files'; import install from './tools/install'; import keyboard from './tools/keyboard'; @@ -28,15 +29,14 @@ import pdf from './tools/pdf'; import snapshot from './tools/snapshot'; import tabs from './tools/tabs'; import screen from './tools/screen'; -import { console as consoleResource } from './resources/console'; import type { Tool, ToolCapability } from './tools/tool'; -import type { Resource } from './resources/resource'; import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; import type { LaunchOptions } from 'playwright'; const snapshotTools: Tool[] = [ ...common(true), + ...console, ...files(true), ...install, ...keyboard(true), @@ -48,6 +48,7 @@ const snapshotTools: Tool[] = [ const screenshotTools: Tool[] = [ ...common(false), + ...console, ...files(false), ...install, ...keyboard(false), @@ -57,10 +58,6 @@ const screenshotTools: Tool[] = [ ...tabs(false), ]; -const resources: Resource[] = [ - consoleResource, -]; - type Options = { browser?: string; userDataDir?: string; @@ -115,7 +112,7 @@ export async function createServer(options?: Options): Promise { name: 'Playwright', version: packageJSON.version, tools, - resources, + resources: [], browserName, userDataDir, launchOptions, diff --git a/src/resources/console.ts b/src/tools/console.ts similarity index 62% rename from src/resources/console.ts rename to src/tools/console.ts index 3ba4c95..29132e5 100644 --- a/src/resources/console.ts +++ b/src/tools/console.ts @@ -14,22 +14,32 @@ * limitations under the License. */ -import type { Resource } from './resource'; +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; -export const console: Resource = { +import type { Tool } from './tool'; + +const consoleSchema = z.object({}); + +const console: Tool = { + capability: 'core', schema: { - uri: 'browser://console', - name: 'Page console', - mimeType: 'text/plain', + name: 'browser_console_messages', + description: 'Returns all console messages', + inputSchema: zodToJsonSchema(consoleSchema), }, - - read: async (context, uri) => { + handle: async context => { const messages = await context.currentTab().console(); const log = messages.map(message => `[${message.type().toUpperCase()}] ${message.text()}`).join('\n'); - return [{ - uri, - mimeType: 'text/plain', - text: log - }]; + return { + content: [{ + type: 'text', + text: log + }], + }; }, }; + +export default [ + console, +]; diff --git a/tests/basic.spec.ts b/tests/basic.spec.ts index a37f9d6..eb6d29a 100644 --- a/tests/basic.spec.ts +++ b/tests/basic.spec.ts @@ -211,14 +211,10 @@ test('browser_type', async ({ client }) => { submit: true, }, }); - const resource = await client.readResource({ - uri: 'browser://console', - }); - expect(resource.contents).toEqual([{ - uri: 'browser://console', - mimeType: 'text/plain', - text: '[LOG] Key pressed: Enter , Text: Hi!', - }]); + expect(await client.callTool({ + name: 'browser_console_messages', + arguments: {}, + })).toHaveTextContent('[LOG] Key pressed: Enter , Text: Hi!'); }); test('browser_type (slowly)', async ({ client }) => { @@ -238,19 +234,15 @@ test('browser_type (slowly)', async ({ client }) => { slowly: true, }, }); - const resource = await client.readResource({ - uri: 'browser://console', - }); - expect(resource.contents).toEqual([{ - uri: 'browser://console', - mimeType: 'text/plain', - text: [ - '[LOG] Key pressed: H Text: ', - '[LOG] Key pressed: i Text: H', - '[LOG] Key pressed: ! Text: Hi', - '[LOG] Key pressed: Enter Text: Hi!', - ].join('\n'), - }]); + expect(await client.callTool({ + name: 'browser_console_messages', + arguments: {}, + })).toHaveTextContent([ + '[LOG] Key pressed: H Text: ', + '[LOG] Key pressed: i Text: H', + '[LOG] Key pressed: ! Text: Hi', + '[LOG] Key pressed: Enter Text: Hi!', + ].join('\n')); }); test('browser_resize', async ({ client }) => { diff --git a/tests/capabilities.spec.ts b/tests/capabilities.spec.ts index e3ee91f..ea05526 100644 --- a/tests/capabilities.spec.ts +++ b/tests/capabilities.spec.ts @@ -20,6 +20,7 @@ test('test snapshot tool list', async ({ client }) => { const { tools } = await client.listTools(); expect(new Set(tools.map(t => t.name))).toEqual(new Set([ 'browser_click', + 'browser_console_messages', 'browser_drag', 'browser_file_upload', 'browser_hover', @@ -47,6 +48,7 @@ test('test vision tool list', async ({ visionClient }) => { const { tools: visionTools } = await visionClient.listTools(); expect(new Set(visionTools.map(t => t.name))).toEqual(new Set([ 'browser_close', + 'browser_console_messages', 'browser_file_upload', 'browser_install', 'browser_navigate_back', @@ -70,12 +72,7 @@ test('test vision tool list', async ({ visionClient }) => { test('test resources list', async ({ client }) => { const { resources } = await client.listResources(); - expect(resources).toEqual([ - expect.objectContaining({ - uri: 'browser://console', - mimeType: 'text/plain', - }), - ]); + expect(resources).toEqual([]); }); test('test capabilities', async ({ startClient }) => { diff --git a/tests/console.spec.ts b/tests/console.spec.ts index 947eea7..f5f617a 100644 --- a/tests/console.spec.ts +++ b/tests/console.spec.ts @@ -16,7 +16,7 @@ import { test, expect } from './fixtures'; -test('browser://console', async ({ client }) => { +test('browser_console_messages', async ({ client }) => { await client.callTool({ name: 'browser_navigate', arguments: { @@ -24,12 +24,12 @@ test('browser://console', async ({ client }) => { }, }); - const resource = await client.readResource({ - uri: 'browser://console', + const resource = await client.callTool({ + name: 'browser_console_messages', + arguments: {}, }); - expect(resource.contents).toEqual([{ - uri: 'browser://console', - mimeType: 'text/plain', - text: '[LOG] Hello, world!\n[ERROR] Error', - }]); + expect(resource).toHaveTextContent([ + '[LOG] Hello, world!', + '[ERROR] Error', + ].join('\n')); });