Skip to content

Improvements / Deploy to Cloudflare button in README #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Cloudflare Playwright MCP

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/playwright-mcp/tree/main/template)

### Overview

This project leverages Playwright for automated browser testing and integrates with Cloudflare Workers, [Browser Rendering](https://developers.cloudflare.com/browser-rendering/) and [`@cloudflare/playwright`](https://github.com/cloudflare/playwright) for deployment.
Expand Down
6 changes: 6 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,10 @@ export default [
languageOptions,
rules: baseRules,
},
{
files: ['template/**/*'],
rules: {
'notice/notice': 'off',
},
},
];
12 changes: 10 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@
* limitations under the License.
*/

import type { LaunchOptions } from '@cloudflare/playwright';
import type { BrowserWorker, LaunchOptions } from '@cloudflare/playwright';
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
import type { McpAgent } from 'agents/mcp';

declare namespace Cloudflare {
interface Env {
}
}

type Options = {
/**
Expand All @@ -37,4 +43,6 @@ type Options = {
vision?: boolean;
};

export function createServer(options?: Options): Server;
export function createServer(endpoint: BrowserWorker, options?: Options): Server;

export function createMcpAgent(endpoint: BrowserWorker, options?: Options): typeof McpAgent<Cloudflare.Env, {}, {}>;
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
* limitations under the License.
*/

const { createServer } = require('./lib/index');
module.exports = { createServer };
const { createServer, createMcpAgent } = require('./lib/index');
module.exports = { createServer, createMcpAgent };
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@playwright/mcp",
"version": "0.0.7",
"description": "Playwright Tools for MCP",
"name": "@cloudflare/playwright-mcp",
"version": "0.0.1-next",
"description": "Cloudflare Playwright Tools for MCP",
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/playwright-mcp.git"
"url": "git+https://github.com/cloudflare/playwright-mcp.git"
},
"homepage": "https://playwright.dev",
"homepage": "https://github.com/cloudflare/playwright-mcp",
"engines": {
"node": ">=18"
},
Expand All @@ -30,13 +30,13 @@
}
},
"dependencies": {
"@cloudflare/playwright": "^0.0.9",
"@modelcontextprotocol/sdk": "^1.6.1",
"agents": "^0.0.45",
"commander": "^13.1.0",
"zod-to-json-schema": "^3.24.4"
},
"devDependencies": {
"@cloudflare/playwright": "^0.0.9",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.19.0",
"@stylistic/eslint-plugin": "^3.0.1",
Expand Down
14 changes: 14 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import type { Resource } from './resources/resource';
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
import type { LaunchOptions } from '@cloudflare/playwright';
import { BrowserWorker } from '@cloudflare/playwright';
import { McpAgent } from 'agents/mcp';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';

const commonTools: Tool[] = [
common.pressKey,
Expand Down Expand Up @@ -83,3 +85,15 @@ export function createServer(endpoint: BrowserWorker, options?: Options): Server
launchOptions: options?.launchOptions,
});
}

export function createMcpAgent(endpoint: BrowserWorker, options?: Options): typeof McpAgent<Env, {}, {}> {
return class PlaywrightMcpAgent extends McpAgent<Env, {}, {}> {
// we can use a Server instead of a McpServer here
// but we need to force it
server = createServer(this.env.BROWSER, options) as unknown as McpServer;

async init() {
// do nothing
}
};
}
2 changes: 1 addition & 1 deletion src/tools/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import type { ImageContent, TextContent } from '@modelcontextprotocol/sdk/types';
import type { ImageContent, TextContent } from '@modelcontextprotocol/sdk/types.d.ts';
import type { JsonSchema7Type } from 'zod-to-json-schema';
import type { Context } from '../context';

Expand Down
14 changes: 4 additions & 10 deletions src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { McpAgent } from 'agents/mcp';
import { createServer } from '.';

export class PlaywrightMCP extends McpAgent<Env, {}, {}> {
// we can use a Server instead of a McpServer here
// @ts-expect-error
server = createServer(this.env.BROWSER, { vision: false });
import { env } from 'cloudflare:workers';

async init() {
// do nothing
}
}
import { createMcpAgent } from '.';

export const PlaywrightMCP = createMcpAgent(env.BROWSER);

export default PlaywrightMCP.mount('/sse');
108 changes: 108 additions & 0 deletions template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
## Cloudflare Playwright MCP

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/playwright-mcp/tree/main/template)

### Overview

This project leverages Playwright for automated browser testing and integrates with Cloudflare Workers, [Browser Rendering](https://developers.cloudflare.com/browser-rendering/) and [`@cloudflare/playwright`](https://github.com/cloudflare/playwright) for deployment.

### Build and Deploy

Follow these steps to set up and deploy the project:

1. Install dependencies:
```bash
npm ci
```

2. Deploy to Cloudflare Workers:

```bash
npx wrangler deploy
```

### Use with Cloudflare AI Playground

![alt text](docs/imgs/playground-ai-screenshot.png)

[Cloudflare playground AI](https://playground.ai.cloudflare.com/) is a great way to test MCP servers using LLM models available in Workers AI.

- Navigate to https://playground.ai.cloudflare.com/
- Ensure model is set to `llama-3.3-70b-instruct-fp8-fast`
- In **MCP Servers**, set **URL** to `https://[my-mcp-url].workers.dev/sse`
- Click **Connect**
- Status should update to **Connected** and it should list 14 available tools

You can now start to interact with the model, and it will run necessary tools to accomplish what was requested.

> [!TIP]
> For best results, give simple instructions consisting of one single action, e. g., "Create a new todo entry", "Go to cloudflare site", "Take a screenshot"


Example of a conversation:

| Role | Message |
|------|---------|
| **User** | Go to demo.playwright.dev/todomvc |
| **Assistant** | *runs browser_navigate tool*<br> The page has been navigated to https://demo.playwright.dev/todomvc/. The page title is "React • TodoMVC". The page snapshot shows the content of the page, including a heading, a textbox, and several links. |
| **User** | Create some todo entry |
| **Assistant** | *runs browser_type tool*<br> A new todo entry "Buy groceries" has been created. The page snapshot shows the updated content of the page, including the new todo entry. |
| **User** | Nice. Now create a todo in parrot style |
| **Assistant** | *runs browser_type tool*<br> A new todo entry "Polly wants a cracker" has been created in parrot style. The page snapshot shows the updated content of the page, including the new todo entry. |
| **User** | And create another todo in yoda style |
| **Assistant** | *runs browser_type tool*<br> A new todo entry "A walk, I must take" has been created in Yoda style. The page snapshot shows the updated content of the page, including the new todo entry. |
| **User** | Take a screenshot |
| **Assistant** | *runs browser_take_screenshot tool*<br> ![alt text](docs/imgs/todomvc-screenshot-1.png) A screenshot of the current page has been taken. The screenshot is in PNG format and is 56KB in size. |
| **User** | Now click the checkbox of the parrot todo entry |
| **Assistant** | *runs browser_clickt tool*<br> The checkbox of the parrot todo entry "Polly wants a cracker" has been clicked. The page snapshot shows the updated content of the page, with the checkbox now checked. |
| **User** | Take a screenshot |
| **Assistant** | *runs browser_take_screenshot tool*<br> ![alt text](docs/imgs/todomvc-screenshot-2.png) A screenshot of the current page has been taken. The screenshot is in PNG format and is 58KB in size. |
| **User** | Remove completed todo entries |
| **Assistant** | *runs browser_clickt tool*<br> The "Clear completed" button has been clicked. The page snapshot shows the updated content of the page, with the completed todo entry "Polly wants a cracker" removed. |
| **User** | Take a final screenshot |
| **Assistant** | *runs browser_take_screenshot tool*<br> ![alt text](docs/imgs/todomvc-screenshot-3.png) A final screenshot of the current page has been taken. The screenshot is in PNG format and is 50KB in size. |

### Use with Claude Desktop

As of now, [Claude Desktop](https://claude.ai/download) only supports local MCP servers. To use `playground-mcp` with Claude Desktop we make use of [mcp-remote](https://github.com/geelen/mcp-remote), a tool that proxies remote MCP servers and exposes them locally. Use the following configuration:

1. Open the configuration file for Claude Desktop.
2. Add the following JSON snippet under the `mcpServers` section:

```json
{
"mcpServers": {
"cloudflare-playwright-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://[my-mcp-url].workers.dev/sse"
]
}
}
}
```

3. Save the configuration file and **restart** Claude Desktop to apply the changes.

This setup ensures that Claude Desktop can communicate with the Cloudflare Playwright MCP server.

Here's an example of a session opening the TODO demo app, adding "buy lemons" and doing a screenshot, taking advantage of playwright-mcp tools and Browser Rendering:

![alt text](docs/imgs/claudemcp.gif)

### Configure in VSCode

You can install the Playwright MCP server using the [VS Code CLI](https://code.visualstudio.com/docs/configure/command-line):

```bash
# For VS Code
code --add-mcp '{"name":"cloudflare-playwright","type":"sse","url":"https://[my-mcp-url].workers.dev/sse"}'
```

```bash
# For VS Code Insiders
code-insiders --add-mcp '{"name":"cloudflare-playwright","type":"sse","url":"https://[my-mcp-url].workers.dev/sse"}'
```

After installation, the Playwright MCP server will be available for use with your GitHub Copilot agent in VS Code.
24 changes: 24 additions & 0 deletions template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@cloudflare/playwright-mcp-template",
"version": "0.0.1-next",
"description": "Cloudflare Playwright Tools for MCP - Template",
"repository": {
"type": "git",
"url": "git+https://github.com/cloudflare/playwright-mcp.git"
},
"homepage": "https://github.com/cloudflare/playwright-mcp",
"engines": {
"node": ">=18"
},
"license": "Apache-2.0",
"scripts": {
"build": "tsc"
},
"dependencies": {
"@cloudflare/playwright-mcp": "^0.0.1"
},
"devDependencies": {
"typescript": "^5.8.2",
"wrangler": "^4.6.0"
}
}
7 changes: 7 additions & 0 deletions template/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { env } from 'cloudflare:workers';

import { createMcpAgent } from '@cloudflare/playwright-mcp';

export const PlaywrightMCP = createMcpAgent(env.BROWSER);

export default PlaywrightMCP.mount('/sse');
11 changes: 11 additions & 0 deletions template/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true
},
"include": [
"worker-configuration.d.ts",
"src",
]
}
Loading
Loading