Expose only public MCP support in marketplace
Constraint: The public marketplace must not contain internal MCP endpoints, shared keys, project keys, or user-token workflows. Rejected: Publishing every configured KeyInfo MCP server | internal knowledge, Firecrawl, and Outline servers are not public distribution targets. Confidence: high Scope-risk: moderate Directive: Keep MCP publication behind an explicit allowlist and verify hidden plugin metadata before pushing. Tested: python scripts/validate_marketplace.py; temporary CODEX_HOME marketplace list/add/remove for mcp-playwright; hidden rg scan for internal MCP URLs and key markers. Not-tested: Live Playwright MCP browser tool invocation inside a fresh Codex session.
This commit is contained in:
@@ -255,6 +255,18 @@
|
|||||||
"authentication": "ON_INSTALL"
|
"authentication": "ON_INSTALL"
|
||||||
},
|
},
|
||||||
"category": "文档处理"
|
"category": "文档处理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mcp-playwright",
|
||||||
|
"source": {
|
||||||
|
"source": "local",
|
||||||
|
"path": "./plugins/codex/plugins/mcp-playwright"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"installation": "AVAILABLE",
|
||||||
|
"authentication": "ON_INSTALL"
|
||||||
|
},
|
||||||
|
"category": "MCP"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,31 @@
|
|||||||
# EAPIL Codex Plugin Marketplace
|
# EAPIL Codex 插件市场
|
||||||
|
|
||||||
This repository is a Codex plugin marketplace. Its primary purpose is to give AI agents a stable way to install EAPIL-maintained public skills and third-party Codex plugins.
|
这是 EAPIL 维护的 Codex 插件市场。首要用途是让 AI agent 能稳定安装公共 skills、MCP 插件和第三方 Codex plugin。
|
||||||
|
|
||||||
## For AI Agents
|
## 给 AI 的安装流程
|
||||||
|
|
||||||
When a user asks to install or use an EAPIL skill/plugin, do this first:
|
当用户要求安装或使用 EAPIL 插件时,优先执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main
|
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main
|
||||||
|
codex plugin marketplace upgrade eapil-skill-market
|
||||||
codex plugin list --marketplace eapil-skill-market
|
codex plugin list --marketplace eapil-skill-market
|
||||||
codex plugin add <plugin-name>@eapil-skill-market
|
codex plugin add <plugin-id>@eapil-skill-market
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the exact plugin id from the plugin table below. Do not guess translated names.
|
使用 `codex plugin list --marketplace eapil-skill-market` 输出里的精确 `plugin-id`。不要猜测中文名、翻译名或 display name。
|
||||||
|
|
||||||
After installation:
|
如果插件刚安装到当前 Codex 会话中,启动新的 Codex thread 或新的 Codex 调用后再依赖新增 skills/hooks/MCP/app 配置。
|
||||||
|
|
||||||
|
卸载插件:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin list --marketplace eapil-skill-market
|
codex plugin remove <plugin-id>@eapil-skill-market
|
||||||
```
|
```
|
||||||
|
|
||||||
If the plugin was just installed in the current Codex session, start a new Codex thread or new invocation before relying on newly installed skills being loaded into context.
|
## 本地调试安装
|
||||||
|
|
||||||
To remove a plugin:
|
从本仓库 checkout 调试:
|
||||||
|
|
||||||
```bash
|
|
||||||
codex plugin remove <plugin-name>@eapil-skill-market
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local Debug Install
|
|
||||||
|
|
||||||
From this repository checkout:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin marketplace add .
|
codex plugin marketplace add .
|
||||||
@@ -38,116 +33,105 @@ codex plugin list --marketplace eapil-skill-market
|
|||||||
codex plugin add docx@eapil-skill-market
|
codex plugin add docx@eapil-skill-market
|
||||||
```
|
```
|
||||||
|
|
||||||
For a temporary clean test:
|
临时干净环境验收:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tmp_home="$(mktemp -d)"
|
tmp_home="$(mktemp -d)"
|
||||||
CODEX_HOME="$tmp_home" codex plugin marketplace add /path/to/EapilSkillMarket
|
CODEX_HOME="$tmp_home" codex plugin marketplace add /path/to/EapilSkillMarket
|
||||||
CODEX_HOME="$tmp_home" codex plugin add <plugin-name>@eapil-skill-market
|
CODEX_HOME="$tmp_home" codex plugin list --marketplace eapil-skill-market
|
||||||
CODEX_HOME="$tmp_home" codex plugin remove <plugin-name>@eapil-skill-market
|
CODEX_HOME="$tmp_home" codex plugin add <plugin-id>@eapil-skill-market
|
||||||
|
CODEX_HOME="$tmp_home" codex plugin remove <plugin-id>@eapil-skill-market
|
||||||
CODEX_HOME="$tmp_home" codex plugin marketplace remove eapil-skill-market
|
CODEX_HOME="$tmp_home" codex plugin marketplace remove eapil-skill-market
|
||||||
rm -rf "$tmp_home"
|
rm -rf "$tmp_home"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sparse Checkout
|
## MCP 插件
|
||||||
|
|
||||||
Normal installation should leave sparse paths empty. If a constrained environment requires sparse checkout, include both marketplace metadata and plugin contents:
|
当前公开仓库只发布 `mcp-playwright`。KeyInfo 主项目里还维护了内部知识库、Firecrawl、Outline 等 MCP,但这些依赖内部地址、项目 Key、shared key 或用户私有 token,不能进入公开 Git 仓库。
|
||||||
|
|
||||||
```bash
|
`mcp-playwright` 是本地 MCP 插件:
|
||||||
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main --sparse .agents --sparse plugins/codex
|
|
||||||
|
- 插件 ID:`mcp-playwright`
|
||||||
|
- MCP server id:`playwright`
|
||||||
|
- 本地命令:`bunx @playwright/mcp@latest`
|
||||||
|
- 鉴权:无
|
||||||
|
|
||||||
|
安装后如需覆盖启用状态或审批策略,可在用户 Codex 配置里追加:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[plugins."mcp-playwright@eapil-skill-market".mcp_servers.playwright]
|
||||||
|
enabled = true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Current Plugins
|
后端自动同步公开 MCP 时只读取 `SKILL_MARKET_PUBLIC_MCP_IDS` allowlist,默认值是 `playwright`。除非确认目标 MCP 可公开、无内部 URL/密钥泄露风险,否则不要加入 allowlist。
|
||||||
|
|
||||||
| Plugin id | Display name | Category | Use when |
|
## 官方插件能力
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `docx` | docx | 文档处理 | Create, edit, review, or extract Word documents. |
|
|
||||||
| `eapil-gpt2-image` | eapil-gpt2-image | 设计 | Generate or edit images through EAPIL's OpenAI-compatible image API. |
|
|
||||||
| `find-skills` | find-skills | 工具 | Help users discover suitable installable skills. |
|
|
||||||
| `frontend-slides` | Frontend Slides | 文档处理 | Create animated fixed-stage HTML slides or convert PPT/PPTX to HTML slides. |
|
|
||||||
| `guizang-ppt-skill` | Guizang PPT Skill | 文档处理 | Create magazine-style or Swiss-style horizontal web PPTs. |
|
|
||||||
| `html-ppt` | HTML PPT Studio | 文档处理 | Create professional static HTML presentations with themes, layouts, and animations. |
|
|
||||||
| `karpathy-guidelines` | karpathy-guidelines | 开发工具 | Apply Andrej Karpathy-inspired AI coding and model-training guidance. |
|
|
||||||
| `literature-review` | literature-review | 文档处理 | Assist with literature review and academic paper drafting workflows. |
|
|
||||||
| `oh-my-codex` | oh-my-codex | 开发工具 | Add OMX workflows, plugin-scoped hooks, MCP config, and multi-agent orchestration for Codex CLI. |
|
|
||||||
| `paper2code` | paper2code | 文档处理 | Turn arXiv papers into concise referenced Python implementations. |
|
|
||||||
| `pdf` | pdf | 文档处理 | Extract, create, merge, split, fill, or analyze PDF files. |
|
|
||||||
| `postgres` | postgres | 数据分析 | Run read-only PostgreSQL schema inspection and SELECT analysis. |
|
|
||||||
| `ppt-master` | PPT Master | 文档处理 | Convert source documents into high-quality SVG pages and export PPTX decks. |
|
|
||||||
| `ppt-translator` | ppt-translator | 文档处理 | Translate PPT/PPTX files while preserving layout through a render-and-verify loop. |
|
|
||||||
| `pptx` | pptx | 文档处理 | Create, edit, analyze, or annotate PowerPoint presentations. |
|
|
||||||
| `shadcn` | shadcn/ui | 开发工具 | Work with shadcn/ui projects, components, registries, and presets. |
|
|
||||||
| `superpowers` | Superpowers | 开发工具 | Use structured planning, TDD, debugging, code review, and delivery workflows. |
|
|
||||||
| `threejs` | threejs | 设计 | Build Three.js scenes, materials, lighting, animation, postprocessing, and loaders. |
|
|
||||||
| `ui-ux-pro-max` | UI/UX Pro Max | 设计 | Use a UI/UX design knowledge base for style, typography, accessibility, charts, and frontend stack choices. |
|
|
||||||
| `xlsx` | xlsx | 文档处理 | Create, edit, analyze, recalculate, or visualize spreadsheets. |
|
|
||||||
| `patent-application-drafting-skill` | 专利申请撰写 | 文档处理 | Draft Chinese patent technical disclosures, claims, specifications, abstracts, and related Word documents. |
|
|
||||||
|
|
||||||
## Plugin Capabilities
|
本市场按 Codex 官方插件能力校验和发布,插件可以包含:
|
||||||
|
|
||||||
Most plugins expose one or more skills under `skills/`. Codex also supports richer plugin components, and this market may publish them when upstream plugins use them:
|
- `skills`:`./skills/` 下的一个或多个 `SKILL.md`。
|
||||||
|
- `mcpServers`:通常是 `./.mcp.json`,允许 MCP-only 插件。
|
||||||
|
- `apps`:通常是 `./.app.json`,用于 app connector metadata。
|
||||||
|
- `hooks`:插件作用域 Codex hooks,可为路径、路径数组、inline object 或 object 数组。
|
||||||
|
- `interface` / `assets`:`interface.logo`、`interface.composerIcon`、`interface.screenshots` 可引用插件内 `assets/` 文件或公开 URL。
|
||||||
|
|
||||||
- `hooks`: plugin-scoped Codex hooks, usually declared as `./hooks/hooks.json`.
|
市场校验器会检查 plugin id、manifest `name` 和目录名必须是 ASCII kebab-case;中文只能用于 `interface.displayName`、说明文案和分类等展示字段。
|
||||||
- `mcpServers`: MCP server metadata, usually declared as `./.mcp.json`.
|
|
||||||
- `apps`: app connector metadata, usually declared as `./.app.json`.
|
|
||||||
|
|
||||||
`oh-my-codex` currently uses all three. It requires the user's environment to have `oh-my-codex` / `omx` installed and set up for full runtime behavior. Codex may ask the user to trust plugin hooks before running them.
|
## 仓库结构
|
||||||
|
|
||||||
## Repository Layout
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.agents/plugins/marketplace.json
|
.agents/plugins/marketplace.json
|
||||||
plugins/codex/
|
plugins/codex/
|
||||||
.agents/plugins/marketplace.json
|
.agents/plugins/marketplace.json
|
||||||
plugins/<plugin-name>/
|
plugins/<plugin-id>/
|
||||||
.codex-plugin/plugin.json
|
.codex-plugin/plugin.json
|
||||||
skills/<skill-name>/SKILL.md
|
skills/<skill-name>/SKILL.md # 可选
|
||||||
hooks/ # optional
|
.mcp.json # 可选
|
||||||
.mcp.json # optional
|
.app.json # 可选
|
||||||
.app.json # optional
|
hooks/ # 可选
|
||||||
|
assets/ # 可选
|
||||||
config/external-sources.json
|
config/external-sources.json
|
||||||
scripts/validate_marketplace.py
|
scripts/validate_marketplace.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Maintenance Rules
|
## 维护规则
|
||||||
|
|
||||||
- This is a distribution repository. Keep the published Git history shallow and small.
|
- README 正文统一使用简体中文;插件 ID、命令、配置键和上游专有名称保留原文。
|
||||||
- Plugin ids, directory names, and skill `name` values use English kebab-case.
|
- 日常发布保留普通线性 Git 历史,使用普通 `git add -A`、`git commit`、`git push`,让 Codex marketplace 刷新获得增量传输收益。
|
||||||
- Preserve upstream display names such as `shadcn/ui`, `oh-my-codex`, and `Guizang PPT Skill`; do not translate proper names.
|
- 只有误同步大文件或用户明确要求历史清理时,才做一次性历史压缩。
|
||||||
- User-facing Chinese copy belongs in `description`, `shortDescription`, `longDescription`, `category`, and `defaultPrompt`.
|
- 每次新增、替换、重命名、移除 public skill/plugin,或新增 hooks/MCP/apps/assets 等能力后,都要同步更新本 README 和 `plugins/codex/README.md`。
|
||||||
- Update this README whenever a public skill/plugin is added, replaced, renamed, removed, or gains a new major capability such as hooks, MCP, or app metadata.
|
- 不要同步 README-only 预览 GIF/截图。skill/reference 真正需要的图片保留并压缩为 WebP。
|
||||||
- Do not sync README-only preview GIFs/screenshots into this repository. Skill/reference images needed by the skill should be compressed to WebP instead.
|
- 第三方插件本地化写入 `config/external-sources.json` 的 `manifestOverrides` 和 `skillDescriptions`,不要直接手改生成 manifest。
|
||||||
- Third-party plugin localization belongs in `config/external-sources.json` through `manifestOverrides` and `skillDescriptions`; do not hand-edit generated plugin manifests.
|
- KeyInfo 内部 MCP 默认不公开。公开 MCP 必须经过 `SKILL_MARKET_PUBLIC_MCP_IDS` allowlist,并确认无内部 URL、shared key、项目 key 或用户 token 泄露风险。
|
||||||
|
|
||||||
## Third-Party Sync
|
## 第三方同步
|
||||||
|
|
||||||
Third-party sources are tracked in `config/external-sources.json`.
|
第三方来源记录在 `config/external-sources.json`。
|
||||||
|
|
||||||
Supported adapters:
|
支持的适配器:
|
||||||
|
|
||||||
- `codex-plugin`: upstream is already a Codex plugin.
|
- `codex-plugin`:上游已经是 Codex plugin。
|
||||||
- `claude-skill`: upstream is a Claude-style skill that is converted into a Codex plugin.
|
- `claude-skill`:上游是 Claude-style skill,会转换为 Codex plugin。
|
||||||
|
|
||||||
Independent PowerShell sync:
|
PowerShell 独立同步:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
powershell -ExecutionPolicy Bypass -File .\scripts\sync_external_plugins.ps1
|
powershell -ExecutionPolicy Bypass -File .\scripts\sync_external_plugins.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
With proxy:
|
带代理:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
powershell -ExecutionPolicy Bypass -File .\scripts\sync_external_plugins.ps1 -Proxy http://127.0.0.1:2085
|
powershell -ExecutionPolicy Bypass -File .\scripts\sync_external_plugins.ps1 -Proxy http://127.0.0.1:2085
|
||||||
```
|
```
|
||||||
|
|
||||||
## Validation
|
## 发布前验证
|
||||||
|
|
||||||
Before publishing changes:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/validate_marketplace.py
|
python scripts/validate_marketplace.py
|
||||||
python /Users/eapil/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py plugins/codex/plugins/<plugin-name>
|
rg -n "<plugin-id>|<display-name>" README.md plugins/codex/README.md
|
||||||
rg -n "<plugin-name>|<display-name>" README.md plugins/codex/README.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For new plugins, also install from a temporary `CODEX_HOME` and run the smallest safe invocation that proves Codex can load the skill/plugin.
|
对包含 `skills` 的插件,可额外运行系统 `validate_plugin.py`。不要用旧 validator 降级官方能力;MCP-only、app-only 或 hooks-only 插件以本仓库 marketplace 校验和 Codex 实装验收为准。
|
||||||
|
|
||||||
|
对新增插件,还要使用临时 `CODEX_HOME` 安装,并执行最小安全调用,证明 Codex 能加载该插件。MCP-only 插件至少要验证 marketplace list/add 成功,并检查 `.mcp.json` 已进入插件缓存;真实 MCP tool 调用可能依赖本地命令、浏览器权限或 token。
|
||||||
|
|||||||
@@ -255,6 +255,18 @@
|
|||||||
"authentication": "ON_INSTALL"
|
"authentication": "ON_INSTALL"
|
||||||
},
|
},
|
||||||
"category": "文档处理"
|
"category": "文档处理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mcp-playwright",
|
||||||
|
"source": {
|
||||||
|
"source": "local",
|
||||||
|
"path": "./plugins/mcp-playwright"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"installation": "AVAILABLE",
|
||||||
|
"authentication": "ON_INSTALL"
|
||||||
|
},
|
||||||
|
"category": "MCP"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
# Codex Marketplace Sparse Entry
|
# Codex 插件入口
|
||||||
|
|
||||||
This directory is the sparse checkout entry for the EAPIL Codex Plugin Marketplace. AI agents should normally install the marketplace from the repository root URL:
|
这个目录是 EAPIL Codex 插件市场的 sparse checkout 入口。AI agent 通常应从仓库根 URL 安装市场:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main
|
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main
|
||||||
|
codex plugin marketplace upgrade eapil-skill-market
|
||||||
codex plugin list --marketplace eapil-skill-market
|
codex plugin list --marketplace eapil-skill-market
|
||||||
codex plugin add <plugin-name>@eapil-skill-market
|
codex plugin add <plugin-id>@eapil-skill-market
|
||||||
```
|
```
|
||||||
|
|
||||||
Use sparse checkout only when the environment needs a smaller checkout:
|
只有在受限环境需要更小 checkout 时才使用 sparse 参数:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main --sparse .agents --sparse plugins/codex
|
codex plugin marketplace add https://git.playones.com/arechen/EapilSkillMarket.git --ref main --sparse .agents --sparse plugins/codex
|
||||||
```
|
```
|
||||||
|
|
||||||
Local debug from the repository root:
|
从仓库根目录本地调试:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codex plugin marketplace add .
|
codex plugin marketplace add .
|
||||||
codex plugin add docx@eapil-skill-market
|
codex plugin add docx@eapil-skill-market
|
||||||
```
|
```
|
||||||
|
|
||||||
Each `plugins/<plugin-name>` directory is an installable Codex plugin. Plugins can include plain skills plus official Codex plugin components such as `hooks`, `.mcp.json`, and `.app.json`. See the root `README.md` for the full plugin table and maintenance rules.
|
每个 `plugins/<plugin-id>` 目录都是一个可安装 Codex plugin。插件可以包含官方支持的 `skills`、`mcpServers`、`apps`、`hooks`、`interface` 和 `assets`。当前公开 MCP 只包含 `mcp-playwright`;KeyInfo 内部 MCP 不发布到公开仓库。
|
||||||
|
|
||||||
|
完整插件清单、MCP 说明和维护规则见根目录 `README.md`。
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "mcp-playwright",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Playwright 是由 Microsoft 开发并维护的开源浏览器自动化框架,专为现代 Web 应用设计。它提供了一套统一的 API,支持 Chromium、Firefox 和 WebKit 三大主流浏览器引擎,适用于端到端测试、网页数据采集及自动化操作等场景。",
|
||||||
|
"author": {
|
||||||
|
"name": "EAPIL",
|
||||||
|
"url": "https://git.playones.com/arechen/EapilSkillMarket"
|
||||||
|
},
|
||||||
|
"homepage": "https://git.playones.com/arechen/EapilSkillMarket",
|
||||||
|
"repository": "https://git.playones.com/arechen/EapilSkillMarket",
|
||||||
|
"license": "Proprietary",
|
||||||
|
"keywords": [
|
||||||
|
"eapil",
|
||||||
|
"codex-mcp",
|
||||||
|
"mcp",
|
||||||
|
"playwright"
|
||||||
|
],
|
||||||
|
"mcpServers": "./.mcp.json",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "playwright浏览器自动化操作",
|
||||||
|
"shortDescription": "Playwright 是由 Microsoft 开发并维护的开源浏览器自动化框架,专为现代 Web 应用设计。它提供了一套统一的 API,支持 Chromium、Firefox 和 WebKit 三大主流浏览器引擎,适用于端到端测试、网页数据采集及自动化操作等场景。",
|
||||||
|
"longDescription": "Playwright 是由 Microsoft 开发并维护的开源浏览器自动化框架,专为现代 Web 应用设计。它提供了一套统一的 API,支持 Chromium、Firefox 和 WebKit 三大主流浏览器引擎,适用于端到端测试、网页数据采集及自动化操作等场景。",
|
||||||
|
"developerName": "EAPIL",
|
||||||
|
"category": "MCP",
|
||||||
|
"capabilities": [
|
||||||
|
"Interactive",
|
||||||
|
"Read"
|
||||||
|
],
|
||||||
|
"defaultPrompt": [
|
||||||
|
"使用 playwright浏览器自动化操作 MCP 查询或处理这个任务。"
|
||||||
|
],
|
||||||
|
"websiteURL": "https://git.playones.com/arechen/EapilSkillMarket",
|
||||||
|
"privacyPolicyURL": "https://git.playones.com/arechen/EapilSkillMarket",
|
||||||
|
"termsOfServiceURL": "https://git.playones.com/arechen/EapilSkillMarket",
|
||||||
|
"brandColor": "#0F766E",
|
||||||
|
"screenshots": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"playwright": {
|
||||||
|
"command": "bunx",
|
||||||
|
"args": [
|
||||||
|
"@playwright/mcp@latest"
|
||||||
|
],
|
||||||
|
"default_tools_approval_mode": "approve",
|
||||||
|
"tools": {
|
||||||
|
"browser_navigate": {
|
||||||
|
"approval_mode": "approve"
|
||||||
|
},
|
||||||
|
"browser_tabs": {
|
||||||
|
"approval_mode": "approve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"id": "playwright",
|
||||||
|
"name": "playwright浏览器自动化操作",
|
||||||
|
"version": "20260605",
|
||||||
|
"keySource": "none",
|
||||||
|
"syncedAt": "2026-06-12T09:39:35Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# playwright浏览器自动化操作
|
||||||
|
|
||||||
|
Codex MCP 插件 ID:`mcp-playwright`。
|
||||||
|
|
||||||
|
Playwright 是由 Microsoft 开发并维护的开源浏览器自动化框架,专为现代 Web 应用设计。它提供了一套统一的 API,支持 Chromium、Firefox 和 WebKit 三大主流浏览器引擎,适用于端到端测试、网页数据采集及自动化操作等场景。
|
||||||
|
|
||||||
|
## 运行前提
|
||||||
|
|
||||||
|
- 本地命令:`bunx @playwright/mcp@latest`。
|
||||||
|
- 安装插件不会预先安装本地命令依赖;请先确认命令在当前 Codex 环境可执行。
|
||||||
|
|
||||||
|
## Codex 配置
|
||||||
|
|
||||||
|
插件自带 `.mcp.json` 声明传输配置。安装后如需调整启用状态或工具审批策略,可在用户 Codex 配置里覆盖:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[plugins."mcp-playwright@eapil-skill-market".mcp_servers.playwright]
|
||||||
|
enabled = true
|
||||||
|
```
|
||||||
@@ -82,6 +82,39 @@ def validate_hooks_field(plugin_dir: Path, manifest_path: Path, hooks: object) -
|
|||||||
raise AssertionError(f"{manifest_path} hooks must be a path, path list, inline object, or inline object list")
|
raise AssertionError(f"{manifest_path} hooks must be a path, path list, inline object, or inline object list")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_skills_component(plugin_dir: Path, manifest_path: Path, skills_rel: object) -> None:
|
||||||
|
require(isinstance(skills_rel, str) and skills_rel.startswith("./"), f"{manifest_path} skills path must be relative ./...")
|
||||||
|
skills_dir = validate_manifest_path(plugin_dir, manifest_path, skills_rel, "skills")
|
||||||
|
require(skills_dir.exists(), f"{manifest_path} skills directory does not exist: {skills_dir}")
|
||||||
|
skill_dirs = [path for path in skills_dir.iterdir() if path.is_dir()]
|
||||||
|
require(bool(skill_dirs), f"{manifest_path} skills component must contain at least one skill directory")
|
||||||
|
for skill_dir in skill_dirs:
|
||||||
|
require((skill_dir / "SKILL.md").exists(), f"Missing SKILL.md in {skill_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_json_component(
|
||||||
|
plugin_dir: Path,
|
||||||
|
manifest_path: Path,
|
||||||
|
value: object,
|
||||||
|
field: str,
|
||||||
|
required_top_level: str,
|
||||||
|
) -> None:
|
||||||
|
require(isinstance(value, str), f"{manifest_path} {field} must be a path string")
|
||||||
|
target = validate_manifest_path(plugin_dir, manifest_path, value, field)
|
||||||
|
payload = load_json(target)
|
||||||
|
require(isinstance(payload, dict), f"{target} must be a JSON object")
|
||||||
|
require(isinstance(payload.get(required_top_level), dict), f"{target} missing object {required_top_level}")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_interface_asset(plugin_dir: Path, manifest_path: Path, value: object, field: str) -> None:
|
||||||
|
if value is None or value == "":
|
||||||
|
return
|
||||||
|
require(isinstance(value, str), f"{manifest_path} interface.{field} must be a string")
|
||||||
|
if value.startswith(("http://", "https://")):
|
||||||
|
return
|
||||||
|
validate_manifest_path(plugin_dir, manifest_path, value, f"interface.{field}")
|
||||||
|
|
||||||
|
|
||||||
def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
|
def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
|
||||||
manifest_path = plugin_dir / ".codex-plugin" / "plugin.json"
|
manifest_path = plugin_dir / ".codex-plugin" / "plugin.json"
|
||||||
require(manifest_path.exists(), f"Missing manifest: {manifest_path}")
|
require(manifest_path.exists(), f"Missing manifest: {manifest_path}")
|
||||||
@@ -91,26 +124,24 @@ def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
|
|||||||
require(PLUGIN_NAME_RE.match(expected_name) is not None, f"{manifest_path} plugin name must be ASCII kebab-case")
|
require(PLUGIN_NAME_RE.match(expected_name) is not None, f"{manifest_path} plugin name must be ASCII kebab-case")
|
||||||
require(SEMVER_RE.match(str(manifest.get("version", ""))) is not None, f"{manifest_path} version must be semver-like")
|
require(SEMVER_RE.match(str(manifest.get("version", ""))) is not None, f"{manifest_path} version must be semver-like")
|
||||||
require(bool(manifest.get("description")), f"{manifest_path} missing description")
|
require(bool(manifest.get("description")), f"{manifest_path} missing description")
|
||||||
if "hooks" in manifest:
|
|
||||||
validate_hooks_field(plugin_dir, manifest_path, manifest["hooks"])
|
|
||||||
|
|
||||||
author = manifest.get("author")
|
author = manifest.get("author")
|
||||||
require(isinstance(author, dict) and bool(author.get("name")), f"{manifest_path} missing author.name")
|
require(isinstance(author, dict) and bool(author.get("name")), f"{manifest_path} missing author.name")
|
||||||
|
|
||||||
skills_rel = manifest.get("skills")
|
declared_components = [
|
||||||
require(isinstance(skills_rel, str) and skills_rel.startswith("./"), f"{manifest_path} skills path must be relative ./...")
|
key
|
||||||
skills_dir = validate_manifest_path(plugin_dir, manifest_path, skills_rel, "skills")
|
for key in ["skills", "mcpServers", "apps", "hooks"]
|
||||||
require(skills_dir.exists(), f"{manifest_path} skills directory does not exist: {skills_dir}")
|
if key in manifest
|
||||||
skill_dirs = [path for path in skills_dir.iterdir() if path.is_dir()]
|
]
|
||||||
require(bool(skill_dirs), f"{manifest_path} must contain at least one skill directory")
|
require(bool(declared_components), f"{manifest_path} must declare at least one component: skills, mcpServers, apps, or hooks")
|
||||||
for skill_dir in skill_dirs:
|
|
||||||
require((skill_dir / "SKILL.md").exists(), f"Missing SKILL.md in {skill_dir}")
|
|
||||||
|
|
||||||
for optional_path_field in ["mcpServers", "apps"]:
|
if "skills" in manifest:
|
||||||
value = manifest.get(optional_path_field)
|
validate_skills_component(plugin_dir, manifest_path, manifest.get("skills"))
|
||||||
if value is not None:
|
if "mcpServers" in manifest:
|
||||||
require(isinstance(value, str), f"{manifest_path} {optional_path_field} must be a path string")
|
validate_json_component(plugin_dir, manifest_path, manifest.get("mcpServers"), "mcpServers", "mcpServers")
|
||||||
validate_manifest_path(plugin_dir, manifest_path, value, optional_path_field)
|
if "apps" in manifest:
|
||||||
|
validate_json_component(plugin_dir, manifest_path, manifest.get("apps"), "apps", "apps")
|
||||||
|
if "hooks" in manifest:
|
||||||
|
validate_hooks_field(plugin_dir, manifest_path, manifest["hooks"])
|
||||||
|
|
||||||
interface = manifest.get("interface")
|
interface = manifest.get("interface")
|
||||||
require(isinstance(interface, dict), f"{manifest_path} missing interface object")
|
require(isinstance(interface, dict), f"{manifest_path} missing interface object")
|
||||||
@@ -118,6 +149,13 @@ def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
|
|||||||
require(bool(interface.get(key)), f"{manifest_path} missing interface.{key}")
|
require(bool(interface.get(key)), f"{manifest_path} missing interface.{key}")
|
||||||
capabilities = interface.get("capabilities")
|
capabilities = interface.get("capabilities")
|
||||||
require(isinstance(capabilities, list), f"{manifest_path} interface.capabilities must be a list")
|
require(isinstance(capabilities, list), f"{manifest_path} interface.capabilities must be a list")
|
||||||
|
validate_interface_asset(plugin_dir, manifest_path, interface.get("logo"), "logo")
|
||||||
|
validate_interface_asset(plugin_dir, manifest_path, interface.get("composerIcon"), "composerIcon")
|
||||||
|
screenshots = interface.get("screenshots")
|
||||||
|
if screenshots is not None:
|
||||||
|
require(isinstance(screenshots, list), f"{manifest_path} interface.screenshots must be a list")
|
||||||
|
for index, screenshot in enumerate(screenshots):
|
||||||
|
validate_interface_asset(plugin_dir, manifest_path, screenshot, f"screenshots[{index}]")
|
||||||
|
|
||||||
|
|
||||||
def resolve_marketplace_plugin_path(marketplace_path: Path, source_path: str) -> Path:
|
def resolve_marketplace_plugin_path(marketplace_path: Path, source_path: str) -> Path:
|
||||||
|
|||||||
Reference in New Issue
Block a user