diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..25103c1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint + + - name: Build + run: npm run build + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run tests + run: npm test diff --git a/.gitignore b/.gitignore index 3b4e8dc..7e00484 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ lib/ node_modules/ +test-results/ diff --git a/README.md b/README.md index f21ef7e..226ac13 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,41 @@ ## Playwright MCP -This package is experimental and not yet ready for production use. -It is a subject to change and will not respect semver versioning. +A Model Context Protocol (MCP) server that provides browser automation capabilities using [Playwright](https://playwright.dev). This server enables LLMs to interact with web pages through structured accessibility snapshots, bypassing the need for screenshots or visually-tuned models. + +### Key Features + +- **Fast and lightweight**: Uses Playwright’s accessibility tree, not pixel-based input. +- **LLM-friendly**: No vision models needed, operates purely on structured data. +- **Deterministic tool application**: Avoids ambiguity common with screenshot-based approaches. + +### Use Cases + +- Web navigation and form-filling +- Data extraction from structured content +- Automated testing driven by LLMs +- General-purpose browser interaction for agents + +## Requirements ### Example config +```js +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": [ + "@playwright/mcp", + ] + } + } +} +``` + +### Running headless browser (Browser without GUI). + +This mode is useful for background or batch operations. + ```js { "mcpServers": { @@ -19,22 +50,7 @@ It is a subject to change and will not respect semver versioning. } ``` -### Running headed browser (Browser with GUI). - -```js -{ - "mcpServers": { - "playwright": { - "command": "npx", - "args": [ - "@playwright/mcp" - ] - } - } -} -``` - -### Running headed browser on Linux +### Running headed browser on Linux w/o DISPLAY When running headed browser on system w/o display or from worker processes of the IDEs, you can run Playwright in a client-server manner. You'll run the Playwright server diff --git a/package-lock.json b/package-lock.json index 1a1263b..2be6094 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.19.0", + "@playwright/test": "1.52.0-alpha-2025-03-21", "@stylistic/eslint-plugin": "^3.0.1", "@types/node": "^22.13.10", "@typescript-eslint/eslint-plugin": "^8.26.1", @@ -283,6 +284,22 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.52.0-alpha-2025-03-21", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0-alpha-2025-03-21.tgz", + "integrity": "sha512-GGnn8kqjGB0rT7GThhas73r5pONnrztPlDAYrYfSO6mWB/NGIyxU3SrNY4Po8+bOnoqlBhxCdU1q+s84HKAMBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.52.0-alpha-2025-03-21" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", diff --git a/package.json b/package.json index 480abb5..145208f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "scripts": { "build": "tsc", "lint": "eslint .", - "watch": "tsc --watch" + "watch": "tsc --watch", + "test": "playwright test" }, "exports": { "./servers/server": "./lib/servers/server.js", @@ -37,6 +38,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.19.0", + "@playwright/test": "1.52.0-alpha-2025-03-21", "@stylistic/eslint-plugin": "^3.0.1", "@typescript-eslint/eslint-plugin": "^8.26.1", "@typescript-eslint/parser": "^8.26.1", diff --git a/src/context.ts b/src/context.ts index 58fbcec..e94d903 100644 --- a/src/context.ts +++ b/src/context.ts @@ -49,7 +49,10 @@ export class Context { const browser = await this._createBrowser(); this._page = await browser.newPage(); this._page.on('console', event => this._console.push(event)); - this._page.on('framenavigated', () => this._console.length = 0); + this._page.on('framenavigated', frame => { + if (!frame.parentFrame()) + this._console.length = 0 + }); })(); return this._initializePromise; } diff --git a/tsconfig.json b/tsconfig.json index 8ad3cb6..7bc09d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "esModuleInterop": true, "moduleResolution": "node", "strict": true, - "module": "ESNext", + "module": "CommonJS", "outDir": "./lib" }, "include": [