Aizex Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Aizex Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
通过 Playwright 浏览器自动化控制 Aizex 号池(ChatGPT/Claude 共享账号平台), 以 MCP (Model Context Protocol) 协议暴露为工具,让 agent 能操控aizex界面获取高级模型的回复,也可以写脚本批量自动生成内容。
Mac (本机) VPS (远程)
┌─────────────────────┐ ┌──────────────┐
│ Chrome (非headless) │ │ nanobot │
│ ↑ │ │ ↑ │
│ Playwright 控制 │ │ MCP Client │
│ ↑ │ │ ↑ │
│ BrowserEngine │ SSH隧道 │ 127.0.0.1 │
│ ↑ │ ◄────────► │ :8000 │
│ AizexService │ └──────────────┘
│ ↑ │
│ FastMCP Server │
│ (port 8000) │
└─────────────────────┘src/browser/)非 headless 模式启动(headless 被 Cloudflare 拦截)。支持 adopt_context() 用于 session 恢复后接管新浏览器。
检测是否已登录、处理登录表单、等待跳转完成。支持 session 未过期时直接跳过登录。
包括发送消息、获取回复、检测生成状态、切换模型、管理对话列表、文件上传下载等。 所有操作通过 CSS 选择器(配置在 aizex.yaml)定位页面元素。
当 session 被踢(其他用户登录同一账号)时,打开新浏览器重新走 Authing → 选号池 → 进入聊天的完整流程。恢复后通过 engine.adopt_context() 替换主浏览器。
协调 engine、auth、actions,提供锁机制(run_locked)保证操作串行化。 实现 wait_and_get 的短锁轮询模式:短暂获取锁检查状态 → 释放锁等待 → 再次获取锁确认稳定。
在每次操作前后检查页面状态,检测异常(如被踢、登录过期、网络错误)。
src/mcp_server/)包含服务端防护:send_message 3 秒冷却期、浏览器单例(多 session 共享)。 使用统一的结果工厂模式(_build_success_response 和 _build_error_response)处理所有工具响应。 工具 docstring 使用中文,通过 tools/list 传递给 AI 客户端作为使用规则。
src/core/).env 加载配置。关键配置:browser_headless(默认 False)、poll_interval_ms、text_stability_window_ms。 遵循单一职责原则,只管理静态配置。
管理聊天URL和cookies的持久化,与Settings类分离,遵守单一职责原则。
error_fields_from_exception)。| 工具 | 用途 |
|---|---|
aizex_health | 检查服务状态(READY/SESSION_KICKED) |
aizex_new_chat | 新建对话 |
aizex_send_message | 发送消息(3秒冷却) |
aizex_wait_and_get | 等待回复完成并获取(默认超时900秒) |
aizex_get_last_response | 获取最后一条回复 |
aizex_is_generating | 检查是否正在生成 |
aizex_list_models | 列出可用模型(支持SVG检测当前选中模型) |
aizex_select_model | 切换模型 |
aizex_list_conversations | 列出对话列表 |
aizex_select_conversation | 切换对话 |
aizex_upload_file | 上传文件到当前对话 |
aizex_check_downloads | 检查当前页面可下载的文件 |
aizex_download_file | 下载文件(index=-1下载全部) |
aizex_refresh_page | 刷新当前页面(处理生成卡住或页面异常) |
aizex_recover_session | 恢复被踢的 session(弹窗操作) |
textbook/prepare.py:读取 config.yaml,生成 job.json 和课程提示词textbook/run.sh:一键运行脚本(自动 prepare + batch run)textbook/chapters/:参考教材内容目录textbook/runs/:运行配置目录textbook/output/:生成结果目录 # 一键运行教材生成
./textbook/run.sh textbook/runs/calculus_v1
# 续跑失败的任务
./textbook/run.sh textbook/runs/calculus_v1 resume
# 手动运行 prepare
python3 textbook/prepare.py textbook/runs/calculus_v1SessionStore类专门管理运行时状态.last_chat_url).last_cookies.json)_build_success_response和_build_error_response辅助函数is_current字段检测不准确,文本匹配容易出错await item.locator('svg').count() > 0检测_is_group_entry过滤aizex_refresh_page 用于处理页面状态异常wait_and_get 超时后需要重置页面状态aizex_check_downloads 支持扫描所有对话消息,而不仅仅是最后一条session_model 和 session_conversation_id 字段aizex_new_chat → aizex_select_model → aizex_send_messageinstance_id 隔离所有浏览器相关路径instance_id 和 pool_index 字段,支持环境变量配置AIZEX_PORT 环境变量配置aizex-multi.sh 脚本支持多实例生命周期管理 # 启动 2 个实例,分别进 0 号和 3 号池
./aizex-multi.sh start 2 0,3
# 查看状态
./aizex-multi.sh status
# 停止所有实例
./aizex-multi.sh stop*.memofun.net,不识别 *.aizex.net 域名Settings.ensure_directories() 非幂等,多次调用重复追加 -instance_id 后缀*.aizex.net 域名时被误判为错误页面,触发不必要的重新导航SessionStore._is_supported_chat_host() 同时支持 *.memofun.net 和 *.aizex.netSettings.ensure_directories() 使用 _ensure_instance_suffix() helper 确保路径只追加一次后缀ensure_ready_page() 中添加详细日志,便于问题诊断src/batch/engine.py):管理多个独立的 AizexService 实例src/batch/runner.py):读取 job 配置,分发任务到多个 slot 并行执行src/batch/history.py):JSON Lines 格式记录任务执行历史src/batch/__main__.py):支持 run、status、resume、create 命令 # 创建批处理任务配置
python -m src.batch create --name my_job --files file1.txt file2.txt --slots 3
# 运行批处理任务
python -m src.batch run job_my_job.json
# 查看任务状态
python -m src.batch status job_my_job.jsonadopt_context() 直接替换主浏览器引用,避免 WebSocket 断开。wait_and_get 不长期持有锁,用"短锁检查 → 释放 → 等待 → 再检查"的模式,避免阻塞其他操作。settings.update_base_url() 更新。./aizex.sh start/stop/restart/status/test/log/tunnel./aizex-multi.sh start/stop/status/loggit clone <repo-url> ~/aizex_mcp
cd ~/aizex_mcp
pip install -e .
playwright install chromiumcp .env.example .env
# 编辑 .env,填入 Aizex 账号密码和 URL关键配置项:
# Aizex URLs
AIZEX_BASE_URL=https://chat.aizex.com
AIZEX_CHAT_URL=/chat
AIZEX_LOGIN_URL=/login
# Authentication (required for login)
AIZEX_USERNAME=your_username_here
AIZEX_PASSWORD=your_password_here
# Browser settings (必须设置为 false)
AIZEX_BROWSER_HEADLESS=false
# 运行时状态存储
AIZEX_SESSION_STORE_DIR=./.sessionchmod +x aizex.sh
./aizex.sh start # 启动 MCP 服务 + SSH 隧道
./aizex.sh test # 端到端测试
./aizex.sh status # 查看状态make install # 安装依赖
make run # 启动 MCP 服务器(开发模式)在 nanobot 的 config.json 中添加:
{
"tools": {
"mcpServers": {
"aizex": {
"url": "http://127.0.0.1:8000/mcp"
}
}
}
}{ "mcpServers": { "aizex-mcp": { "command": "python3", "args": [ "run_stdio.py" ], "cwd": "/Users/csbot/aizex_mcp", "alwaysAllow": [ "aizex_health", "aizex_list_models", "aizex_list_conversations", "aizex_select_model", "aizex_new_chat", "aizex_send_message", "aizex_wait_and_get", "aizex_check_downloads", "aizex_upload_file", "aizex_get_last_response", "aizex_is_generating", "aizex_select_conversation", "aizex_download_file", "aizex_refresh_page", "aizex_recover_session" ], "timeout": 300 } } }
aizex_mcp/
├── src/
│ ├── browser/ # 浏览器自动化层
│ │ ├── actions.py # 原子操作(发送消息、切换模型等)
│ │ ├── service.py # 服务编排层
│ │ ├── engine.py # 浏览器引擎
│ │ ├── recovery.py # 会话恢复
│ │ ├── page_sentinel.py # 页面监控
│ │ └── ...
│ ├── mcp_server/ # MCP 服务层
│ │ ├── server.py # FastMCP 服务端
│ │ └── schemas.py # 数据模型
│ ├── core/ # 核心层
│ │ ├── settings.py # 配置管理
│ │ ├── session_store.py # 运行时状态管理
│ │ └── errors.py # 错误处理
│ └── batch/ # 批处理层(新增)
│ ├── __init__.py
│ ├── engine.py # SlotPool:管理多个 AizexService 实例
│ ├── runner.py # JobRunner:任务分发和执行
│ ├── history.py # 任务历史记录(JSON Lines 格式)
│ └── __main__.py # CLI 接口
├── textbook/ # 教材批量生成系统(新增)
│ ├── prepare.py # 配置解析和 job.json 生成
│ ├── run.sh # 一键运行脚本
│ ├── chapters/ # 参考教材内容
│ ├── runs/ # 运行配置
│ └── output/ # 生成结果
├── config/
│ └── selectors/
│ └── aizex.yaml # CSS 选择器配置
├── tests/ # 测试
├── .env.example # 环境变量示例
├── aizex.sh # 单实例控制脚本
├── aizex-multi.sh # 多实例控制脚本
├── job_example.json # 批处理任务配置示例
├── Makefile # 构建脚本
└── pyproject.toml # 项目配置# 运行所有测试
make test
# 运行特定测试
pytest tests/test_mcp_tools.py -xvs
# 测试模型列表功能
pytest tests/test_mcp_tools.py::test_aizex_list_models_tool -xvs# 代码格式化
make format
# 代码检查
make lint
# 类型检查
make type-checkAIZEX_BROWSER_HEADLESS=false./aizex.sh log.env 中的用户名密码aizex_recover_session./aizex.sh restart./aizex.sh log # 查看服务日志
tail -f aizex.log # 实时查看日志| 方法 | 参数 | 返回值 | 用途 |
|---|---|---|---|
startup() | 无 | 无 | 启动服务:启动浏览器 → 注入cookies → 导航 → 登录检查 → 备用recovery |
shutdown() | 无 | 无 | 关闭服务:停止浏览器引擎 |
ensure_ready_page() | 无 | Page | 确保页面就绪:检查URL → 检查登录 → 自动recovery |
health() | 无 | HealthResult | 健康检查:返回状态/URL/uptime |
new_chat() | 无 | ActionResult | 新建对话 |
list_models() | 无 | ModelListResult | 列出可用模型 |
select_model(model_name) | str | ActionResult | 选择模型 |
send_message(text) | str | ActionResult | 发送消息 |
is_generating(use_text_fallback) | bool | GeneratingResult | 检查是否正在生成 |
get_last_response() | 无 | ResponseResult | 获取最后一条回复 |
wait_and_get(timeout_sec) | int | ResponseResult | 等待生成完成并获取回复 |
list_conversations() | 无 | ConversationListResult | 列出对话列表 |
select_conversation(conversation_id, title) | str, str | ActionResult | 选择对话 |
upload_file(file_path) | str | UploadResult | 上传文件 |
check_downloads(scan_all) | bool | CheckDownloadsResult | 检查可下载文件 |
download_file(index) | int | DownloadActionResult | 下载指定文件 |
download_all() | 无 | DownloadActionResult | 下载所有文件 |
download_multiple(indices) | list | DownloadActionResult | 下载多个文件 |
refresh_page() | 无 | ActionResult | 刷新页面 |
recover_session(timeout_sec) | int | RecoveryResult | 恢复session |
check_credits(force_refresh) | bool | dict | 查询积分余额 |
startup() # 必须首先调用
↓
new_chat() → select_model() # 建议顺序(Claude对话禁止中途切模型)
↓
send_message() # 发送消息
↓
wait_and_get() # 等待回复(内部自动轮询)
↓
[可选] upload_file() / check_downloads() / download_file()
↓
shutdown() # 最后调用关键约束:
startup() → new_chat() → send_message() 是标准链路wait_and_get() 内部使用短锁轮询,不会阻塞其他操作的lock获取upload_file() 必须在 send_message() 之前调用recover_session() 成功后需重新 new_chat() 开始新对话startup() 从 .last_cookies.json 读取并注入到浏览器contextrecover_session() 完成 pool re-entry 后,从新context提取全量cookies并保存到 .last_cookies.json.last_cookies 或 .last_cookies.{instance_id}.json(多实例隔离)| 场景 | 原因 | 检测方式 |
|---|---|---|
| 其他用户登录同一号池 | Aizex号池session被踢 | PageSentinel检测"其他地方登录"等关键词 |
| Authing session过期 | Authing SSO登录态过期 | 登录检查失败 |
| 浏览器被强杀 | Ctrl+C或kill -9 | 重启后inject的cookies可能失效 |
| Cloudflare challenge | Cloudflare验证拦截 | 页面URL跳转到challenge页 |
recover_session() 的完整流程:
1. 启动临时非headless浏览器(独立profile: /tmp/aizex-recovery-profile-{instance_id})
2. 导航到 https://aizex.net/plusPool
3. 如果需要登录 → 点击"登录/注册" → Authing认证 → 验证pool可访问
4. 如果已有Authing session → 直接验证pool可访问
5. 切换到高级号池(如果有)
6. 采集积分信息(顺路)
7. 点击指定pool卡片(pool_index)
8. 等待新tab打开 → 提取新URL
9. 返回 RecoveryResult(包含 new_base_url, cookies, browser_context, credits)
10. 主服务调用 engine.adopt_context() 接管新浏览器
11. 更新 settings.base_url 和 session_store
12. 保存cookies到 .last_cookies.json注意:Recovery 使用独立的浏览器profile,不复用主浏览器的profile,避免SingletonLock冲突。
check_and_recover_response() 检查回复文本是否包含"其他地方登录"、"号池重进"等run_locked() 操作前,PageSentinel 检查页面是否有session kicked弹窗aizex_recover_session 工具系统不支持在运行时手动输入密码。认证凭据必须在 .env 中预先配置:
AIZEX_USERNAME=your_username
AIZEX_PASSWORD=your_password| 错误状态 | 触发条件 | 恢复方法 | 自动/手动 |
|---|---|---|---|
SESSION_KICKED | 响应含"其他地方登录"等关键词;PageSentinel检测到踢出弹窗 | 调用 aizex_recover_session | 手动(需用户确认) |
LOGIN_REQUIRED | Authing登录态过期;启动时检测未登录 | 调用 aizex_recover_session(recovery会走完整登录流程) | 手动 |
CLOUDFLARE_BLOCKED | PageSentinel检测到challenge页 | 调用 aizex_recover_session(非headless浏览器可绕过) | 手动 |
PAGE_NAVIGATION_LOST | 页面URL离开Aizex域名;about:blank | 调用 aizex_refresh_page 或等待自动recovery | 自动(run_locked内) |
SELECTOR_NOT_FOUND | 页面结构变化导致CSS选择器失效 | 调用 aizex_refresh_page;检查selectors配置 | 手动 |
SEND_COOLDOWN | 发送间隔小于3秒 | 等待3秒后重试 | 自动(服务端强制) |
TIMEOUT | wait_and_get 超时(默认900秒) | 调用 aizex_refresh_page;检查AI是否卡住 | 手动 |
ATTACHMENT_LIMIT | 附件数量超过上限 | 减少附件数量 | 手动 |
GENERATION_ERROR | AI生成过程中出错 | 调用 aizex_refresh_page;重新发送消息 | 手动 |
FILE_NOT_FOUND | 上传/下载文件路径不存在 | 检查文件路径 | 手动 |
SingletonLock | 多个浏览器实例共用同一profile | 使用不同的 instance_id 隔离 | 配置层面 |
在 run_locked() 内部,以下情况会自动触发 _auto_recover_in_place():
冷却限制:自动recovery有5分钟冷却期,防止频繁触发。
┌─────────────────────────────────────────────────────────────┐
│ MCP Server (单实例) │
│ instance_id="0" │
│ pool_index=0 │
│ profile: /srv/aizex/profile │
└─────────────────────────────────────────────────────────────┘
↓ 完全独立
┌─────────────────────────────────────────────────────────────┐
│ SlotPool (批处理) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Slot 0 │ │ Slot 1 │ │ Slot 2 │ │
│ │ instance_id │ │ instance_id │ │ instance_id │ │
│ │ = "batch_0" │ │ = "1" │ │ = "2" │ │
│ │ pool_index= │ │ pool_index= │ │ pool_index= │ │
│ │ 0 │ │ 1 │ │ 2 │ │
│ │ profile: │ │ profile: │ │ profile: │ │
│ │ /srv/aizex/ │ │ /srv/aizex/ │ │ /srv/aizex/ │ │
│ │ profile- │ │ profile-1 │ │ profile-2 │ │
│ │ batch_0 │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘| 资源 | MCP Server | SlotPool Slot 0 | SlotPool Slot 1 |
|---|---|---|---|
| instance_id | "0" | "batch_0" | "1" |
| Browser profile | /srv/aizex/profile | /srv/aizex/profile-batch_0 | /srv/aizex/profile-1 |
| Cookies | .last_cookies.json | .last_cookies.batch_0.json | .last_cookies.1.json |
| Chat URL | .last_chat_url | .last_chat_url.batch_0 | .last_chat_url.1 |
| Downloads | /srv/aizex/downloads | /srv/aizex/downloads-batch_0 | /srv/aizex/downloads-1 |
| Artifacts | /srv/aizex/artifacts | /srv/aizex/artifacts-batch_0 | /srv/aizex/artifacts-1 |
不会冲突,因为:
.env 配置(但通过环境变量可以覆盖)git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)MIT License
如有问题或建议,请通过项目 Issues 页面反馈。
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.