From 9976c19f483bae3242b272828f6986ad196c57b4 Mon Sep 17 00:00:00 2001 From: arch3rPro Date: Sat, 30 Aug 2025 19:33:16 +0800 Subject: [PATCH] feat: add app playwright-mcp --- apps/playwright-mcp/0.0.35/data.yml | 82 ++++++++++ apps/playwright-mcp/0.0.35/docker-compose.yml | 33 +++++ apps/playwright-mcp/README.md | 140 ++++++++++++++++++ apps/playwright-mcp/README_en.md | 140 ++++++++++++++++++ apps/playwright-mcp/data.yml | 26 ++++ apps/playwright-mcp/latest/data.yml | 82 ++++++++++ apps/playwright-mcp/latest/docker-compose.yml | 33 +++++ apps/playwright-mcp/logo.png | Bin 0 -> 15328 bytes apps/safeline/9.2.4/docker-compose.yml | 10 +- 9 files changed, 541 insertions(+), 5 deletions(-) create mode 100644 apps/playwright-mcp/0.0.35/data.yml create mode 100644 apps/playwright-mcp/0.0.35/docker-compose.yml create mode 100644 apps/playwright-mcp/README.md create mode 100644 apps/playwright-mcp/README_en.md create mode 100644 apps/playwright-mcp/data.yml create mode 100644 apps/playwright-mcp/latest/data.yml create mode 100644 apps/playwright-mcp/latest/docker-compose.yml create mode 100644 apps/playwright-mcp/logo.png diff --git a/apps/playwright-mcp/0.0.35/data.yml b/apps/playwright-mcp/0.0.35/data.yml new file mode 100644 index 0000000..1bc4e6b --- /dev/null +++ b/apps/playwright-mcp/0.0.35/data.yml @@ -0,0 +1,82 @@ +additionalProperties: + formFields: + - default: 8931 + edit: true + envKey: PANEL_APP_PORT_HTTP + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ms: Port + pt-br: Porta + ru: Порт + ko: 포트 + required: true + rule: paramPort + type: number + - default: "chromium" + envKey: BROWSER_TYPE + label: + en: Browser Type + zh: 浏览器类型 + zh-Hant: 瀏覽器類型 + ja: ブラウザタイプ + ms: Jenis Pelayar + pt-br: Tipo de Navegador + ru: Тип браузера + ko: 브라우저 유형 + required: true + type: select + values: + - label: Chromium + value: "chromium" + - label: Firefox + value: "firefox" + - label: WebKit + value: "webkit" + - default: "true" + envKey: HEADLESS_MODE + label: + en: Headless Mode + zh: 无头模式 + zh-Hant: 無頭模式 + ja: ヘッドレスモード + ms: Mod Tanpa Kepala + pt-br: Modo Headless + ru: Безголовый режим + ko: 헤드리스 모드 + required: true + type: select + values: + - label: "True" + value: "true" + - label: "False" + value: "false" + - default: "1280,720" + envKey: VIEWPORT_SIZE + label: + en: Viewport Size (width,height) + zh: 视窗大小 (宽度,高度) + zh-Hant: 視窗大小 (寬度,高度) + ja: ビューポートサイズ (幅,高さ) + ms: Saiz Viewport (lebar,tinggi) + pt-br: Tamanho da Viewport (largura,altura) + ru: Размер области просмотра (ширина,высота) + ko: 뷰포트 크기 (너비,높이) + required: false + rule: paramCommon + type: text + - default: "Mozilla/5.0 (Linux; x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + envKey: USER_AGENT + label: + en: User Agent + zh: 用户代理 + zh-Hant: 用戶代理 + ja: ユーザーエージェント + ms: Ejen Pengguna + pt-br: Agente do Usuário + ru: Пользовательский агент + ko: 사용자 에이전트 + required: false + type: text \ No newline at end of file diff --git a/apps/playwright-mcp/0.0.35/docker-compose.yml b/apps/playwright-mcp/0.0.35/docker-compose.yml new file mode 100644 index 0000000..2f31960 --- /dev/null +++ b/apps/playwright-mcp/0.0.35/docker-compose.yml @@ -0,0 +1,33 @@ +services: + playwright-mcp: + image: mcr.microsoft.com/playwright/mcp:v0.0.35 + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8931" + volumes: + - ./data:/app/data + - ./data/config:/app/config + working_dir: /app + environment: + - NODE_ENV=production + - PORT=8931 + - BROWSER_TYPE=${BROWSER_TYPE:-chromium} + - HEADLESS_MODE=${HEADLESS_MODE:-true} + - VIEWPORT_SIZE=${VIEWPORT_SIZE:-1280,720} + - USER_AGENT=${USER_AGENT} + command: ["--port", "8931", "--host", "0.0.0.0"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8931/mcp"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true \ No newline at end of file diff --git a/apps/playwright-mcp/README.md b/apps/playwright-mcp/README.md new file mode 100644 index 0000000..a72ccd1 --- /dev/null +++ b/apps/playwright-mcp/README.md @@ -0,0 +1,140 @@ +# Playwright MCP + +Playwright MCP 是一个基于模型上下文协议 (MCP) 的服务器,它使用 Playwright 提供浏览器自动化功能。该服务器让大语言模型 (LLM) 能够通过结构化的可访问性快照与网页交互,而无需使用截图或视觉调优模型。 + +![](https://cdn.jsdelivr.net/gh/xiaoY233/PicList@main/public/assets/Playwright-MCP.png) + +![](https://img.shields.io/badge/Copyright-arch3rPro-ff9800?style=flat&logo=github&logoColor=white) + + +## ✨ 核心特性 + +- **🚀 快速轻量**:使用 Microsoft 官方 MCP 镜像,预配置完整 +- **🤖 LLM 友好**:无需视觉模型,完全基于结构化数据操作 +- **🎯 确定性工具应用**:避免基于截图方法常见的模糊性 +- **🌐 多浏览器支持**:支持 Chromium、Firefox 和 WebKit +- **🔧 高度可配置**:支持多种配置选项和运行模式 +- **📦 官方镜像**:使用 `mcr.microsoft.com/playwright/mcp` 官方镜像 + +## 📋 系统要求 + +- Node.js 18 或更新版本 +- Docker 环境 +- 1Panel 控制面板 + +## 🚀 快速开始 + +### 1Panel 部署 + +1. 在 1Panel 应用商店中搜索 "Playwright MCP" +2. 点击安装并配置以下参数: + - **端口**:服务访问端口(默认:8931) + - **浏览器类型**:选择 Chromium、Firefox 或 WebKit + - **无头模式**:是否启用无头模式运行 + - **视窗大小**:浏览器视窗尺寸(格式:宽度,高度) + - **用户代理**:自定义浏览器用户代理字符串 + +3. 点击确认安装 + +### 访问服务 + +安装完成后,您可以通过以下方式访问: + +- **HTTP 端点**:`http://your-server-ip:port/mcp` +- **健康检查**:`http://your-server-ip:port/mcp` + +## ⚙️ 配置说明 + +### 浏览器类型 + +- **Chromium**:推荐选择,兼容性最佳 +- **Firefox**:Gecko 引擎,适合特定测试需求 +- **WebKit**:Safari 内核,适合 Apple 生态测试 + +### 运行模式 + +- **有头模式**:显示浏览器界面,适合调试 +- **无头模式**:后台运行,适合生产环境,性能更佳 + +### 视窗配置 + +- 格式:`宽度,高度`(如:1920,1080) +- 影响页面渲染和响应式布局 +- 建议使用常见分辨率 + +## 🔌 MCP 客户端集成 + +### VS Code + +```json +{ + "mcpServers": { + "playwright": { + "url": "http://your-server:8931/mcp" + } + } +} +``` + +### Claude Desktop + +```json +{ + "mcpServers": { + "playwright": { + "url": "http://your-server:8931/mcp" + } + } +} +``` + +## 📊 性能优化 + +- 启用无头模式可减少资源消耗 +- 选择合适的视窗大小避免不必要的渲染 +- 定期清理用户数据目录 +- 监控容器资源使用情况 + +## 🐛 故障排除 + +### 常见问题 + +1. **服务无法启动** + - 检查端口是否被占用 + - 确认 Docker 容器状态 + - 查看容器日志 + +2. **浏览器启动失败** + - 验证浏览器类型配置 + - 检查系统资源是否充足 + - 确认依赖安装完成 + +3. **连接问题** + - 检查防火墙设置 + - 验证网络配置 + - 确认端口映射正确 + +### 日志查看 + +```bash +# 查看容器日志 +docker logs playwright-mcp + +# 实时跟踪日志 +docker logs -f playwright-mcp +``` + +## 🔗 相关链接 + +- [官方项目](https://github.com/microsoft/playwright-mcp) +- [Playwright 文档](https://playwright.dev/) +- [MCP 协议规范](https://modelcontextprotocol.io/introduction) +- [1Panel 文档](https://1panel.cn/docs/) + +## 📄 许可证 + +本项目遵循原项目的开源许可证。详细信息请参考 [官方仓库](https://github.com/microsoft/playwright-mcp)。 + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request 来帮助改进这个应用配置。 \ No newline at end of file diff --git a/apps/playwright-mcp/README_en.md b/apps/playwright-mcp/README_en.md new file mode 100644 index 0000000..557c777 --- /dev/null +++ b/apps/playwright-mcp/README_en.md @@ -0,0 +1,140 @@ +# Playwright MCP + +A Model Context Protocol (MCP) server that provides browser automation capabilities using Playwright. This server enables LLMs to interact with web pages through structured accessibility snapshots, bypassing the need for screenshots or visually-tuned models. + + +![](https://cdn.jsdelivr.net/gh/xiaoY233/PicList@main/public/assets/Playwright-MCP.png) + +![](https://img.shields.io/badge/Copyright-arch3rPro-ff9800?style=flat&logo=github&logoColor=white) + +## ✨ Key Features + +- **🚀 Fast and Lightweight**: Uses Microsoft official MCP image, pre-configured and ready +- **🤖 LLM-Friendly**: No vision models needed, operates purely on structured data +- **🎯 Deterministic Tool Application**: Avoids ambiguity common with screenshot-based approaches +- **🌐 Multi-Browser Support**: Supports Chromium, Firefox, and WebKit +- **🔧 Highly Configurable**: Supports various configuration options and running modes +- **📦 Official Image**: Uses `mcr.microsoft.com/playwright/mcp` official image + +## 📋 System Requirements + +- Node.js 18 or newer +- Docker environment +- 1Panel control panel + +## 🚀 Quick Start + +### 1Panel Deployment + +1. Search for "Playwright MCP" in the 1Panel app store +2. Click install and configure the following parameters: + - **Port**: Service access port (default: 8931) + - **Browser Type**: Choose Chromium, Firefox, or WebKit + - **Headless Mode**: Whether to enable headless mode + - **Viewport Size**: Browser viewport dimensions (format: width,height) + - **User Agent**: Custom browser user agent string + +3. Click confirm to install + +### Accessing the Service + +After installation, you can access via: + +- **HTTP Endpoint**: `http://your-server-ip:port/mcp` +- **Health Check**: `http://your-server-ip:port/mcp` + +## ⚙️ Configuration Guide + +### Browser Types + +- **Chromium**: Recommended choice, best compatibility +- **Firefox**: Gecko engine, suitable for specific testing needs +- **WebKit**: Safari kernel, suitable for Apple ecosystem testing + +### Running Modes + +- **Headed Mode**: Shows browser interface, suitable for debugging +- **Headless Mode**: Runs in background, suitable for production, better performance + +### Viewport Configuration + +- Format: `width,height` (e.g., 1920,1080) +- Affects page rendering and responsive layout +- Recommended to use common resolutions + +## 🔌 MCP Client Integration + +### VS Code + +```json +{ + "mcpServers": { + "playwright": { + "url": "http://your-server:8931/mcp" + } + } +} +``` + +### Claude Desktop + +```json +{ + "mcpServers": { + "playwright": { + "url": "http://your-server:8931/mcp" + } + } +} +``` + +## 📊 Performance Optimization + +- Enable headless mode to reduce resource consumption +- Choose appropriate viewport size to avoid unnecessary rendering +- Regularly clean user data directory +- Monitor container resource usage + +## 🐛 Troubleshooting + +### Common Issues + +1. **Service Won't Start** + - Check if port is already in use + - Verify Docker container status + - Check container logs + +2. **Browser Launch Fails** + - Verify browser type configuration + - Check if system resources are sufficient + - Confirm dependencies are installed + +3. **Connection Issues** + - Check firewall settings + - Verify network configuration + - Confirm port mapping is correct + +### Log Viewing + +```bash +# View container logs +docker logs playwright-mcp + +# Follow logs in real-time +docker logs -f playwright-mcp +``` + +## 🔗 Related Links + +- [Official Project](https://github.com/microsoft/playwright-mcp) +- [Playwright Documentation](https://playwright.dev/) +- [MCP Protocol Specification](https://modelcontextprotocol.io/introduction) +- [1Panel Documentation](https://1panel.cn/docs/) + +## 📄 License + +This project follows the open source license of the original project. For details, please refer to the [official repository](https://github.com/microsoft/playwright-mcp). + +## 🤝 Contributing + +Welcome to submit Issues and Pull Requests to help improve this application configuration. \ No newline at end of file diff --git a/apps/playwright-mcp/data.yml b/apps/playwright-mcp/data.yml new file mode 100644 index 0000000..af0a89e --- /dev/null +++ b/apps/playwright-mcp/data.yml @@ -0,0 +1,26 @@ +name: Playwright-MCP +tags: + - 开发工具 + - AI + - 实用工具 +title: Playwright MCP 服务器 +description: 基于 Playwright 的模型上下文协议服务器 +additionalProperties: + key: playwright-mcp + name: Playwright-MCP + tags: + - DevTool + - AI + - Tool + shortDescZh: 提供浏览器自动化功能的 MCP 服务器 + shortDescEn: Playwright MCP server that provides browser automation capabilities + description: + zh: 一个基于 Playwright 的模型上下文协议 (MCP) 服务器,为 LLM 提供浏览器自动化功能。使用结构化可访问性快照与网页交互,无需截图或视觉调优模型。 + en: A Model Context Protocol (MCP) server that provides browser automation capabilities using Playwright. This server enables LLMs to interact with web pages through structured accessibility snapshots, bypassing the need for screenshots or visually-tuned models. + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 0 + website: https://github.com/microsoft/playwright-mcp + github: https://github.com/microsoft/playwright-mcp + document: https://github.com/microsoft/playwright-mcp#readme \ No newline at end of file diff --git a/apps/playwright-mcp/latest/data.yml b/apps/playwright-mcp/latest/data.yml new file mode 100644 index 0000000..1bc4e6b --- /dev/null +++ b/apps/playwright-mcp/latest/data.yml @@ -0,0 +1,82 @@ +additionalProperties: + formFields: + - default: 8931 + edit: true + envKey: PANEL_APP_PORT_HTTP + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ms: Port + pt-br: Porta + ru: Порт + ko: 포트 + required: true + rule: paramPort + type: number + - default: "chromium" + envKey: BROWSER_TYPE + label: + en: Browser Type + zh: 浏览器类型 + zh-Hant: 瀏覽器類型 + ja: ブラウザタイプ + ms: Jenis Pelayar + pt-br: Tipo de Navegador + ru: Тип браузера + ko: 브라우저 유형 + required: true + type: select + values: + - label: Chromium + value: "chromium" + - label: Firefox + value: "firefox" + - label: WebKit + value: "webkit" + - default: "true" + envKey: HEADLESS_MODE + label: + en: Headless Mode + zh: 无头模式 + zh-Hant: 無頭模式 + ja: ヘッドレスモード + ms: Mod Tanpa Kepala + pt-br: Modo Headless + ru: Безголовый режим + ko: 헤드리스 모드 + required: true + type: select + values: + - label: "True" + value: "true" + - label: "False" + value: "false" + - default: "1280,720" + envKey: VIEWPORT_SIZE + label: + en: Viewport Size (width,height) + zh: 视窗大小 (宽度,高度) + zh-Hant: 視窗大小 (寬度,高度) + ja: ビューポートサイズ (幅,高さ) + ms: Saiz Viewport (lebar,tinggi) + pt-br: Tamanho da Viewport (largura,altura) + ru: Размер области просмотра (ширина,высота) + ko: 뷰포트 크기 (너비,높이) + required: false + rule: paramCommon + type: text + - default: "Mozilla/5.0 (Linux; x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + envKey: USER_AGENT + label: + en: User Agent + zh: 用户代理 + zh-Hant: 用戶代理 + ja: ユーザーエージェント + ms: Ejen Pengguna + pt-br: Agente do Usuário + ru: Пользовательский агент + ko: 사용자 에이전트 + required: false + type: text \ No newline at end of file diff --git a/apps/playwright-mcp/latest/docker-compose.yml b/apps/playwright-mcp/latest/docker-compose.yml new file mode 100644 index 0000000..2d088d8 --- /dev/null +++ b/apps/playwright-mcp/latest/docker-compose.yml @@ -0,0 +1,33 @@ +services: + playwright-mcp: + image: mcr.microsoft.com/playwright/mcp:latest + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8931" + volumes: + - ./data:/app/data + - ./data/config:/app/config + working_dir: /app + environment: + - NODE_ENV=production + - PORT=8931 + - BROWSER_TYPE=${BROWSER_TYPE:-chromium} + - HEADLESS_MODE=${HEADLESS_MODE:-true} + - VIEWPORT_SIZE=${VIEWPORT_SIZE:-1280,720} + - USER_AGENT=${USER_AGENT} + command: ["--port", "8931", "--host", "0.0.0.0"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8931/mcp"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true \ No newline at end of file diff --git a/apps/playwright-mcp/logo.png b/apps/playwright-mcp/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..74558c6d56ff46418d61a4bc6fa0e846c07f1a3a GIT binary patch literal 15328 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 Qc=7O*