fix(mcp): auto-register tools on startup and support cross-platform setup for all major AI IDEs

- Fix missing mcp_server.tools import on server initialization so mobile_run_task, mobile_manage_task, mobile_get_device_state, and mobile_inspect_trace are automatically registered.

- Inject PYTHONPATH in generated and installed MCP configurations to guarantee Python module resolution across Windows, macOS, and Linux regardless of IDE CWD.

- Expand artemis mcp --install and --generate-config to support VS Code, Cline, Roo Code, Claude Code CLI (~/.claude.json), Cursor, Windsurf, Antigravity, and OpenClaw.

- Enhance config file merging with robust exception handling and fallback initialization.
This commit is contained in:
somew1nd committed 2026-08-18 17:49:47 -07:00
1 parent fc6ddad655
commit 9e5aee85c8
12 files changed
+202 -67

No files matched your search

+9 -8
View File
@@ -94,7 +94,7 @@ git clone https://github.com/google/artemis.git && cd artemis
start.bat start.bat
``` ```
> 💡 **Tip**: Opens `http://localhost:8000` in your default browser with a device connection wizard, live screen mirroring, prompt sandbox, and execution replays. You can also run directly from CLI: `artemis run "Open Settings, find Battery and tell me current level" --profile flash`. > 💡 **Tip**: Opens `http://localhost:8000` in your default browser with a device connection wizard, live screen mirroring, prompt sandbox, and execution replays. You can also run directly from CLI: `uv run artemis run "Open Settings, find Battery and tell me current level" --profile flash`.
<a id="mcp-setup"></a> <a id="mcp-setup"></a>
<a id="mcp"></a> <a id="mcp"></a>
@@ -107,21 +107,22 @@ ARTEMIS includes a native **Model Context Protocol (MCP)** server. Connect your
### 1. One-Click Auto Install (Recommended) ### 1. One-Click Auto Install (Recommended)
Run the built-in installer to automatically configure or update your AI IDE (automatically resolves your virtual environment Python and repository path): Running `./start.sh` or `start.bat` will prompt you to configure MCP for detected IDEs (or you can install/update anytime later manually using the commands below):
```bash ```bash
# Auto-install for Antigravity / Jetski: # Auto-install for Antigravity / Jetski:
artemis mcp --install antigravity uv run artemis mcp --install antigravity
# Or install for all detected AI IDEs (Antigravity, Cursor, Claude Desktop/Code, OpenClaw): # Or install for all detected AI IDEs (Antigravity, Cursor, Claude Desktop/Code, OpenClaw):
artemis mcp --install all uv run artemis mcp --install all
``` ```
> 💡 **Tip**: You can also configure MCP interactively during first-time setup via `artemis init`. > 💡 **Tip**: You can also configure MCP interactively during first-time setup via `uv run artemis init`.
> **Pro Tip**: If you want to use the `artemis` command globally without `uv run` in any directory, run `uv tool install -e .` once in the project root.
### 2. Manual Configuration (Optional) ### 2. Manual Configuration (Optional)
If you prefer to configure manually, run `artemis mcp --generate-config antigravity` (or `all`) to output the exact JSON snippet, or paste the following into your IDE configuration (replace `/path/to/artemis` with your actual repo path and point `command` to your `.venv` Python executable): If you prefer to configure manually, run `uv run artemis mcp --generate-config antigravity` (or `all`) to output the exact JSON snippet, or paste the following into your IDE configuration (replace `/path/to/artemis` with your actual repo path and point `command` to your `.venv` Python executable):
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`): * **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
```json ```json
@@ -217,9 +218,9 @@ if __name__ == "__main__":
<sub>💡 <b>Console Overview</b>: <b>① View Switcher</b> (Home / Workspace) · <b>② Model & Replay</b> (Flash/Pro status & video replay) · <b>③ Live Agent Stream</b> (Action perception, target coordinates & structured results) · <b>④ Prompt Dock</b> (Natural language dispatch) · <b>⑤ Task Queue & Dashboard</b> (Lifecycle & history)</sub> <sub>💡 <b>Console Overview</b>: <b>① View Switcher</b> (Home / Workspace) · <b>② Model & Replay</b> (Flash/Pro status & video replay) · <b>③ Live Agent Stream</b> (Action perception, target coordinates & structured results) · <b>④ Prompt Dock</b> (Natural language dispatch) · <b>⑤ Task Queue & Dashboard</b> (Lifecycle & history)</sub>
</p> </p>
* 🖥️ **Web Visual Test Console (`artemis ui`)**: Real-time screen projection and interactive panel, supporting natural language test dispatch, live reasoning telemetry, action trajectories, and execution replay; * 🖥️ **Web Visual Test Console (`uv run artemis ui`)**: Real-time screen projection and interactive panel, supporting natural language test dispatch, live reasoning telemetry, action trajectories, and execution replay;
* 🔌 **Native MCP Protocol (IDE Collaboration)**: Operates as a standard MCP server seamlessly integrating with **Antigravity, Claude Code, Windsurf**, etc., directly driving real devices inside the IDE to verify bugs and run test cases; * 🔌 **Native MCP Protocol (IDE Collaboration)**: Operates as a standard MCP server seamlessly integrating with **Antigravity, Claude Code, Windsurf**, etc., directly driving real devices inside the IDE to verify bugs and run test cases;
* 💻 **Developer CLI (`artemis run`)**: Direct terminal execution for automated test cases, exploratory stability inspection, or AndroidWorld benchmarks with high-fidelity structured terminal output; * 💻 **Developer CLI (`uv run artemis run`)**: Direct terminal execution for automated test cases, exploratory stability inspection, or AndroidWorld benchmarks with high-fidelity structured terminal output;
* 🐍 **Python SDK**: Integrates as a standard Python library into existing automated testing frameworks (e.g., pytest) or CI/CD pipelines with strongly typed Pydantic structured outputs and assertion support. * 🐍 **Python SDK**: Integrates as a standard Python library into existing automated testing frameworks (e.g., pytest) or CI/CD pipelines with strongly typed Pydantic structured outputs and assertion support.
## 📊 Head-to-Head Comparison ## 📊 Head-to-Head Comparison
+9 -8
View File
@@ -96,7 +96,7 @@ git clone https://github.com/google/artemis.git && cd artemis
start.bat start.bat
``` ```
> 💡 **提示**:启动后将自动在默认浏览器中打开 Web 控制台(`http://localhost:8000`),提供设备连接向导、实时投屏、任务演练与状态回放面板。你也可以通过命令行直接运行:`artemis run "打开系统设置,找到电池选项并告诉我当前电量" --profile flash`。 > 💡 **提示**:启动后将自动在默认浏览器中打开 Web 控制台(`http://localhost:8000`),提供设备连接向导、实时投屏、任务演练与状态回放面板。你也可以通过命令行直接运行:`uv run artemis run "打开系统设置,找到电池选项并告诉我当前电量" --profile flash`。
<a id="mcp-setup"></a> <a id="mcp-setup"></a>
<a id="mcp"></a> <a id="mcp"></a>
@@ -109,21 +109,22 @@ ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下
### 1. 一键自动安装到 IDE(推荐) ### 1. 一键自动安装到 IDE(推荐)
运行内置安装命令,即可自动配置或更新你的 AI IDE(会自动解析并填写虚拟环境 Python 和项目的绝对路径): 运行 `./start.sh` 或 `start.bat` 启动脚本时会主动询问是否需要配置当前检测到的 AI IDE(支持跳过并在之后随时手动执行以下命令挂载):
```bash ```bash
# 一键安装到 Antigravity / Jetski: # 一键安装到 Antigravity / Jetski:
artemis mcp --install antigravity uv run artemis mcp --install antigravity
# 或一键安装到所有检测到的 AI IDE(Antigravity、Cursor、Claude Desktop/Code、OpenClaw): # 或一键安装到所有检测到的 AI IDE(Antigravity、Cursor、Claude Desktop/Code、OpenClaw):
artemis mcp --install all uv run artemis mcp --install all
``` ```
> 💡 **提示**:你也可以在首次运行 `artemis init` 配置向导时,交互式完成 IDE 的 MCP 自动挂载。 > 💡 **提示**:你也可以在首次运行 `uv run artemis init` 配置向导时,交互式完成 IDE 的 MCP 自动挂载。
> **进阶提示**:如果希望在任意目录下都不需要加 `uv run` 就能全局直接使用 `artemis` 命令,可在项目根目录下执行一次 `uv tool install -e .`。
### 2. 手动配置(可选) ### 2. 手动配置(可选)
如果你习惯手动复制配置,可运行 `artemis mcp --generate-config antigravity`(或 `all`)获取自动计算好路径的 JSON 代码段,也可以参考以下模板将 `command` 填写为项目下 `.venv` 虚拟环境中的 Python 绝对路径,并将 `/path/to/artemis` 替换为项目实际路径: 如果你习惯手动复制配置,可运行 `uv run artemis mcp --generate-config antigravity`(或 `all`)获取自动计算好路径的 JSON 代码段,也可以参考以下模板将 `command` 填写为项目下 `.venv` 虚拟环境中的 Python 绝对路径,并将 `/path/to/artemis` 替换为项目实际路径:
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`): * **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
```json ```json
@@ -219,9 +220,9 @@ if __name__ == "__main__":
<sub>💡 <b>控制台功能布局</b>:<b>① 顶栏视图切换</b>(主页与工作区) · <b>② 运行模式与录屏回放</b>(Flash/Pro 状态与视频回放) · <b>③ 实时感知推理流</b>(动作分解、点击坐标与结构化总结) · <b>④ 自然语言下发胶囊</b>(自然语言驱动真机) · <b>⑤ 任务队列看板</b>(状态流转与历史回溯)</sub> <sub>💡 <b>控制台功能布局</b>:<b>① 顶栏视图切换</b>(主页与工作区) · <b>② 运行模式与录屏回放</b>(Flash/Pro 状态与视频回放) · <b>③ 实时感知推理流</b>(动作分解、点击坐标与结构化总结) · <b>④ 自然语言下发胶囊</b>(自然语言驱动真机) · <b>⑤ 任务队列看板</b>(状态流转与历史回溯)</sub>
</p> </p>
* 🖥️ **Web 可视化测试控制台 (`artemis ui`)**:集成设备实时投屏与交互面板,支持通过自然语言下发测试用例,实时观测推理步骤、操作轨迹、截图留存与异常状态回放; * 🖥️ **Web 可视化测试控制台 (`uv run artemis ui`)**:集成设备实时投屏与交互面板,支持通过自然语言下发测试用例,实时观测推理步骤、操作轨迹、截图留存与异常状态回放;
* 🔌 **MCP 协议集成 (AI IDE 协同)**:作为标准 MCP 服务器无缝接入 **Antigravity、Claude Code、Windsurf** 等开发环境,在 IDE 中直接驱动真机完成自动化测试与 Bug 复现验证; * 🔌 **MCP 协议集成 (AI IDE 协同)**:作为标准 MCP 服务器无缝接入 **Antigravity、Claude Code、Windsurf** 等开发环境,在 IDE 中直接驱动真机完成自动化测试与 Bug 复现验证;
* 💻 **命令行工具 (`artemis run`)**:支持通过 CLI 直接执行自动化用例、稳定性巡检或 AndroidWorld 基准评测,提供高保真结构化终端输出; * 💻 **命令行工具 (`uv run artemis run`)**:支持通过 CLI 直接执行自动化用例、稳定性巡检或 AndroidWorld 基准评测,提供高保真结构化终端输出;
* 🐍 **Python SDK**:作为标准 Python 库无缝集成至现有自动化测试框架(如 pytest)或 CI/CD 流水线,提供基于 Pydantic 的强类型结构化结果与断言支持。 * 🐍 **Python SDK**:作为标准 Python 库无缝集成至现有自动化测试框架(如 pytest)或 CI/CD 流水线,提供基于 Pydantic 的强类型结构化结果与断言支持。
## 📊 方案横向对比 ## 📊 方案横向对比
+16 -8
View File
@@ -140,13 +140,21 @@ def init_command() -> None:
console.print(" [2] Cursor") console.print(" [2] Cursor")
console.print(" [3] Claude Desktop / Claude Code") console.print(" [3] Claude Desktop / Claude Code")
console.print(" [4] Windsurf") console.print(" [4] Windsurf")
console.print(" [5] All supported IDEs") console.print(" [5] VS Code / Cline / Roo Code")
console.print(" [6] Skip for now") console.print(" [6] All supported IDEs")
console.print(" [7] Skip for now")
mcp_choice = Prompt.ask( mcp_choice = Prompt.ask(
"Select IDE for MCP auto-install", choices=["1", "2", "3", "4", "5", "6"], default="1" "Select IDE for MCP auto-install", choices=["1", "2", "3", "4", "5", "6", "7"], default="1"
) )
if mcp_choice != "6": if mcp_choice != "7":
target_map = {"1": "antigravity", "2": "cursor", "3": "claude", "4": "windsurf", "5": "all"} target_map = {
"1": "antigravity",
"2": "cursor",
"3": "claude",
"4": "windsurf",
"5": "vscode",
"6": "all",
}
target = target_map.get(mcp_choice, "antigravity") target = target_map.get(mcp_choice, "antigravity")
try: try:
from artemis.interfaces.cli.commands.mcp import install_mcp_config from artemis.interfaces.cli.commands.mcp import install_mcp_config
@@ -167,11 +175,11 @@ def init_command() -> None:
f"[bold green]🎉 Setup completed successfully![/bold green]\n\n" f"[bold green]🎉 Setup completed successfully![/bold green]\n\n"
f"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\n" f"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\n"
"Try running your first task:\n" "Try running your first task:\n"
' [bold cyan]artemis run "Open YouTube and search for Lo-Fi Hip Hop"[/bold cyan]\n\n' ' [bold cyan]uv run artemis run "Open YouTube and search for Lo-Fi Hip Hop"[/bold cyan]\n\n'
"Or connect/update your AI IDE anytime with:\n" "Or connect/update your AI IDE anytime with:\n"
" [bold cyan]artemis mcp --install all[/bold cyan]\n\n" " [bold cyan]uv run artemis mcp --install all[/bold cyan]\n\n"
"Or run health diagnostics anytime with:\n" "Or run health diagnostics anytime with:\n"
" [bold cyan]artemis doctor[/bold cyan]", " [bold cyan]uv run artemis doctor[/bold cyan]",
title="☕ Ready to Go!", title="☕ Ready to Go!",
expand=False, expand=False,
) )
+87 -34
View File
@@ -21,6 +21,7 @@ import sys
from typing import Annotated from typing import Annotated
from mcp_server.base import mcp as agent_mcp from mcp_server.base import mcp as agent_mcp
import mcp_server.tools # noqa: F401
from mcp_server.utils import env_utils from mcp_server.utils import env_utils
from artemis.mcp.adb_server import mcp as adb_mcp from artemis.mcp.adb_server import mcp as adb_mcp
from artemis.utils.logger import get_logger from artemis.utils.logger import get_logger
@@ -32,6 +33,17 @@ logger = get_logger(__name__)
console = Console() console = Console()
def _get_vscode_user_dir() -> Path:
"""Returns the platform-specific VS Code user configuration directory."""
if sys.platform == "darwin":
return Path.home() / "Library" / "Application Support" / "Code" / "User"
elif sys.platform == "win32":
appdata = os.environ.get("APPDATA") or str(Path.home() / "AppData" / "Roaming")
return Path(appdata) / "Code" / "User"
else:
return Path.home() / ".config" / "Code" / "User"
def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict: def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict:
"""Generates MCP configuration dictionary for the specified client.""" """Generates MCP configuration dictionary for the specified client."""
config_body = { config_body = {
@@ -40,6 +52,7 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict
"cwd": project_root, "cwd": project_root,
"env": { "env": {
"PYTHONUNBUFFERED": "1", "PYTHONUNBUFFERED": "1",
"PYTHONPATH": project_root,
}, },
} }
@@ -57,38 +70,44 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict
} }
} }
} }
elif client in ("cursor", "windsurf"):
return {"mcpServers": {"artemis": config_body}}
elif client in ("claude", "claude_code", "claude_desktop"):
return {"mcpServers": {"artemis": config_body}}
elif client == "openclaw": elif client == "openclaw":
return {"plugins": {"artemis_mcp": {"enabled": True, "type": "mcp", "server": config_body}}} return {"plugins": {"artemis_mcp": {"enabled": True, "type": "mcp", "server": config_body}}}
else: # generic else: # cursor, windsurf, claude, vscode, cline, roo, generic
return {"mcpServers": {"artemis": config_body}} return {"mcpServers": {"artemis": config_body}}
def _merge_json_file(file_path: Path, server_name: str, server_config: dict, key_name: str = "mcpServers") -> bool: def _merge_json_file(file_path: Path, server_name: str, server_config: dict, key_name: str = "mcpServers") -> bool:
"""Merges server configuration into a target JSON file without overwriting existing servers.""" """Merges server configuration into a target JSON file without overwriting existing servers."""
file_path.parent.mkdir(parents=True, exist_ok=True) try:
data: dict = {} file_path.parent.mkdir(parents=True, exist_ok=True)
if file_path.exists() and file_path.stat().st_size > 0: data: dict = {}
try: if file_path.exists() and file_path.stat().st_size > 0:
data = json.loads(file_path.read_text(encoding="utf-8")) try:
except Exception: data = json.loads(file_path.read_text(encoding="utf-8"))
data = {} if not isinstance(data, dict):
data = {}
except Exception:
data = {}
if key_name not in data or not isinstance(data.get(key_name), dict): if key_name not in data or not isinstance(data.get(key_name), dict):
data[key_name] = {} data[key_name] = {}
data[key_name][server_name] = server_config data[key_name][server_name] = server_config
file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
return True return True
except Exception as e:
logger.warning(f"Could not update MCP config file {file_path}: {e}")
return False
def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[str]: def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[str]:
"""Auto-installs/merges ARTEMIS MCP configuration into IDE config files.""" """Auto-installs/merges ARTEMIS MCP configuration into IDE config files across any OS."""
installed_paths: list[str] = [] installed_paths: list[str] = []
targets = ["antigravity", "cursor", "claude", "windsurf", "openclaw"] if client == "all" else [client] targets = (
["antigravity", "cursor", "claude", "windsurf", "vscode", "cline", "roo", "openclaw"]
if client == "all"
else [client]
)
for target in targets: for target in targets:
snippet = _get_config_snippet(target, python_exe, project_root) snippet = _get_config_snippet(target, python_exe, project_root)
@@ -96,9 +115,10 @@ def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[
server_cfg = snippet["mcpServers"]["artemis"] server_cfg = snippet["mcpServers"]["artemis"]
jetski_path = Path.home() / ".gemini" / "jetski" / "mcp_config.json" jetski_path = Path.home() / ".gemini" / "jetski" / "mcp_config.json"
config_path = Path.home() / ".gemini" / "config" / "mcp_config.json" config_path = Path.home() / ".gemini" / "config" / "mcp_config.json"
_merge_json_file(jetski_path, "artemis", server_cfg) if _merge_json_file(jetski_path, "artemis", server_cfg):
_merge_json_file(config_path, "artemis", server_cfg) installed_paths.append(str(jetski_path))
installed_paths.extend([str(jetski_path), str(config_path)]) if _merge_json_file(config_path, "artemis", server_cfg):
installed_paths.append(str(config_path))
elif target in ("claude", "claude_code", "claude_desktop"): elif target in ("claude", "claude_code", "claude_desktop"):
server_cfg = snippet["mcpServers"]["artemis"] server_cfg = snippet["mcpServers"]["artemis"]
if sys.platform == "darwin": if sys.platform == "darwin":
@@ -108,23 +128,43 @@ def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[
claude_path = Path(appdata) / "Claude" / "claude_desktop_config.json" claude_path = Path(appdata) / "Claude" / "claude_desktop_config.json"
else: else:
claude_path = Path.home() / ".config" / "Claude" / "claude_desktop_config.json" claude_path = Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
_merge_json_file(claude_path, "artemis", server_cfg) if _merge_json_file(claude_path, "artemis", server_cfg):
installed_paths.append(str(claude_path)) installed_paths.append(str(claude_path))
# Also install to Claude Code CLI global config (~/.claude.json)
claude_code_path = Path.home() / ".claude.json"
if _merge_json_file(claude_code_path, "artemis", server_cfg):
installed_paths.append(str(claude_code_path))
elif target == "cursor": elif target == "cursor":
server_cfg = snippet["mcpServers"]["artemis"] server_cfg = snippet["mcpServers"]["artemis"]
cursor_path = Path.home() / ".cursor" / "mcp.json" cursor_path = Path.home() / ".cursor" / "mcp.json"
_merge_json_file(cursor_path, "artemis", server_cfg) if _merge_json_file(cursor_path, "artemis", server_cfg):
installed_paths.append(str(cursor_path)) installed_paths.append(str(cursor_path))
elif target == "windsurf": elif target == "windsurf":
server_cfg = snippet["mcpServers"]["artemis"] server_cfg = snippet["mcpServers"]["artemis"]
windsurf_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json" windsurf_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json"
_merge_json_file(windsurf_path, "artemis", server_cfg) if _merge_json_file(windsurf_path, "artemis", server_cfg):
installed_paths.append(str(windsurf_path)) installed_paths.append(str(windsurf_path))
elif target == "vscode":
server_cfg = snippet["mcpServers"]["artemis"]
vscode_path = _get_vscode_user_dir() / "mcp.json"
if _merge_json_file(vscode_path, "artemis", server_cfg):
installed_paths.append(str(vscode_path))
elif target == "cline":
server_cfg = snippet["mcpServers"]["artemis"]
cline_path = _get_vscode_user_dir() / "globalStorage" / "saoudrizwan.claude-dev" / "settings" / "cline_mcp_settings.json"
if _merge_json_file(cline_path, "artemis", server_cfg):
installed_paths.append(str(cline_path))
elif target in ("roo", "roo_code"):
server_cfg = snippet["mcpServers"]["artemis"]
roo_path = _get_vscode_user_dir() / "globalStorage" / "rooveterinaryinc.roo-cline" / "settings" / "cline_mcp_settings.json"
if _merge_json_file(roo_path, "artemis", server_cfg):
installed_paths.append(str(roo_path))
elif target == "openclaw": elif target == "openclaw":
plugin_cfg = snippet["plugins"]["artemis_mcp"] plugin_cfg = snippet["plugins"]["artemis_mcp"]
openclaw_path = Path.home() / ".openclaw" / "openclaw.json" openclaw_path = Path.home() / ".openclaw" / "openclaw.json"
_merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins") if _merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins"):
installed_paths.append(str(openclaw_path)) installed_paths.append(str(openclaw_path))
return installed_paths return installed_paths
@@ -165,7 +205,7 @@ def mcp_command(
typer.Option( typer.Option(
"--install", "--install",
"-i", "-i",
help="Auto-install and merge ARTEMIS MCP configuration into 'antigravity', 'claude', 'cursor', 'windsurf', 'openclaw', or 'all'.", help="Auto-install and merge ARTEMIS MCP configuration into 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.",
), ),
] = None, ] = None,
generate_config: Annotated[ generate_config: Annotated[
@@ -173,7 +213,7 @@ def mcp_command(
typer.Option( typer.Option(
"--generate-config", "--generate-config",
"-g", "-g",
help="Output ready-to-use MCP configuration JSON for 'antigravity', 'cursor', 'claude', 'windsurf', 'openclaw', or 'all'.", help="Output ready-to-use MCP configuration JSON for 'antigravity', 'cursor', 'claude', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.",
), ),
] = None, ] = None,
) -> None: ) -> None:
@@ -191,11 +231,15 @@ def mcp_command(
"claude_desktop", "claude_desktop",
"cursor", "cursor",
"windsurf", "windsurf",
"vscode",
"cline",
"roo",
"roo_code",
"openclaw", "openclaw",
"all", "all",
): ):
console.print( console.print(
f"[bold red]Unsupported install target: '{install_config}'. Use 'antigravity', 'claude', 'cursor', 'windsurf', 'openclaw', or 'all'.[/bold red]" f"[bold red]Unsupported install target: '{install_config}'. Use 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.[/bold red]"
) )
raise typer.Exit(1) raise typer.Exit(1)
installed_paths = install_mcp_config(client, python_exe, project_root) installed_paths = install_mcp_config(client, python_exe, project_root)
@@ -215,12 +259,21 @@ def mcp_command(
"cursor (.cursor/mcp.json)": _get_config_snippet( "cursor (.cursor/mcp.json)": _get_config_snippet(
"cursor", python_exe, project_root "cursor", python_exe, project_root
), ),
"claude (claude_desktop_config.json)": _get_config_snippet( "claude (claude_desktop_config.json & ~/.claude.json)": _get_config_snippet(
"claude", python_exe, project_root "claude", python_exe, project_root
), ),
"windsurf (~/.codeium/windsurf/mcp_config.json)": _get_config_snippet( "windsurf (~/.codeium/windsurf/mcp_config.json)": _get_config_snippet(
"windsurf", python_exe, project_root "windsurf", python_exe, project_root
), ),
"vscode (mcp.json)": _get_config_snippet(
"vscode", python_exe, project_root
),
"cline (cline_mcp_settings.json)": _get_config_snippet(
"cline", python_exe, project_root
),
"roo (cline_mcp_settings.json)": _get_config_snippet(
"roo", python_exe, project_root
),
"openclaw (openclaw.json)": _get_config_snippet( "openclaw (openclaw.json)": _get_config_snippet(
"openclaw", python_exe, project_root "openclaw", python_exe, project_root
), ),
+4 -4
View File
@@ -62,7 +62,7 @@ The included [`rules.md`](./rules.md) file contains the **Mobile Testing Mindset
python -m mcp_server python -m mcp_server
# Or via Artemis CLI # Or via Artemis CLI
artemis mcp uv run artemis mcp
``` ```
### IDE Configuration ### IDE Configuration
@@ -70,10 +70,10 @@ artemis mcp
#### One-Click Auto Install (Recommended) #### One-Click Auto Install (Recommended)
Run the automated installer to detect and configure your IDE (Antigravity, Cursor, Claude Code/Desktop, OpenClaw): Run the automated installer to detect and configure your IDE (Antigravity, Cursor, Claude Code/Desktop, OpenClaw):
```bash ```bash
artemis mcp --install all uv run artemis mcp --install all
# Or install specifically for Antigravity: # Or install specifically for Antigravity:
artemis mcp --install antigravity uv run artemis mcp --install antigravity
``` ```
#### Manual JSON Config Generation #### Manual JSON Config Generation
Run `artemis mcp --generate-config antigravity` (or `all`) to output ready-to-use configuration JSON with resolved `.venv` Python and project paths. Run `uv run artemis mcp --generate-config antigravity` (or `all`) to output ready-to-use configuration JSON with resolved `.venv` Python and project paths.
+1
View File
@@ -16,5 +16,6 @@
from mcp_server.base import mcp from mcp_server.base import mcp
from mcp_server.server import main from mcp_server.server import main
import mcp_server.tools # noqa: F401
__all__ = ["mcp", "main"] __all__ = ["mcp", "main"]
+10
View File
@@ -16,6 +16,13 @@
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
try:
from mcp.server.fastmcp.server import Settings as FastMCPSettings
FastMCPSettings.model_rebuild()
except Exception:
pass
# Define the shared FastMCP instance for external IDE and agent clients. # Define the shared FastMCP instance for external IDE and agent clients.
mcp = FastMCP( mcp = FastMCP(
"artemis", "artemis",
@@ -27,3 +34,6 @@ mcp = FastMCP(
"and mobile_inspect_trace to inspect detailed execution steps and visual action overlays." "and mobile_inspect_trace to inspect detailed execution steps and visual action overlays."
), ),
) )
# Import tools so they are registered with the mcp instance upon module load
import mcp_server.tools # noqa: E402, F401
+1
View File
@@ -55,6 +55,7 @@ except Exception:
from mcp_server.base import mcp from mcp_server.base import mcp
# 2. Import tools to register them # 2. Import tools to register them
import mcp_server.tools # noqa: F401
def main(transport: str = "stdio", host: str = "127.0.0.1", port: int = 8001): def main(transport: str = "stdio", host: str = "127.0.0.1", port: int = 8001):
+24 -2
View File
@@ -130,9 +130,31 @@ if ((-not (Test-Path $ShowcaseIndex)) -and (-not (Test-Path $ShowcaseIndexAlt1))
} }
} }
# 7. Launch unified Showcase UI & open browser # 7. Optionally configure MCP for detected AI IDEs
Write-Host ""
Write-Host " 🔌 Would you like to configure ARTEMIS MCP for your AI IDEs?" -ForegroundColor Cyan
Write-Host " (Supported: Antigravity, Cursor, Claude Code/Desktop, OpenClaw, Windsurf)"
$installMcp = "Y"
if ([Console]::IsInputRedirected -eq $false) {
$response = Read-Host " Install MCP configuration now? [Y/n]"
if ($response -ne "") {
$installMcp = $response
}
} else {
$installMcp = "N"
}
if ($installMcp -match "^[Yy]") {
Write-Host " ✔ Installing MCP server configuration..." -ForegroundColor Green
uv run artemis mcp --install all 2>$null | Out-Null
Write-Host " 💡 Tip: You can update or re-install anytime with: uv run artemis mcp --install all" -ForegroundColor Cyan
} else {
Write-Host " ⏭️ Skipped. You can install MCP anytime later with: uv run artemis mcp --install all" -ForegroundColor Yellow
}
Write-Host ""
# 8. Launch unified Showcase UI & open browser
Write-Host " 🚀 Launching Artemis Showcase UI & Admin Console..." -ForegroundColor Green Write-Host " 🚀 Launching Artemis Showcase UI & Admin Console..." -ForegroundColor Green
Write-Host " 💡 Tip: Connect to Antigravity / Claude Desktop anytime: artemis mcp --install all" -ForegroundColor Cyan
if ($NoOpen) { if ($NoOpen) {
uv run artemis ui --port $Port --no-open uv run artemis ui --port $Port --no-open
} else { } else {
+21 -2
View File
@@ -153,7 +153,26 @@ if [ ! -f "${SHOWCASE_INDEX}" ] && [ ! -f "${SHOWCASE_INDEX_ALT1}" ] && [ ! -f "
fi fi
fi fi
# 7. Launch unified Showcase UI & auto-open browser # 7. Optionally configure MCP for detected AI IDEs
echo ""
echo -e " ${CYAN}🔌 Would you like to configure ARTEMIS MCP for your AI IDEs?${NC}"
echo -e " (Supported: Antigravity, Cursor, Claude Code/Desktop, OpenClaw, Windsurf)"
if [ -t 0 ]; then
read -r -p " Install MCP configuration now? [Y/n]: " INSTALL_MCP
INSTALL_MCP=${INSTALL_MCP:-Y}
else
INSTALL_MCP="n"
fi
if [[ "${INSTALL_MCP}" =~ ^[Yy]$ ]]; then
echo -e " ${GREEN}✔ Installing MCP server configuration...${NC}"
uv run artemis mcp --install all >/dev/null 2>&1 || true
echo -e " ${CYAN}💡 Tip: You can update or re-install anytime with: ${BOLD}uv run artemis mcp --install all${NC}"
else
echo -e " ${YELLOW}⏭️ Skipped. You can install MCP anytime later with: ${BOLD}uv run artemis mcp --install all${NC}"
fi
echo ""
# 8. Launch unified Showcase UI & auto-open browser
echo -e " ${GREEN}🚀 Launching Artemis Showcase UI & Admin Console...${NC}" echo -e " ${GREEN}🚀 Launching Artemis Showcase UI & Admin Console...${NC}"
echo -e " ${CYAN}💡 Tip: Connect to Antigravity / Claude Desktop anytime: ${BOLD}artemis mcp --install all${NC}"
exec uv run artemis ui --open "$@" exec uv run artemis ui --open "$@"
+14
View File
@@ -111,3 +111,17 @@ async def test_mobile_inspect_trace_invalid_action():
res = await mobile_inspect_trace(action="invalid_action", trace_id="trace-123") res = await mobile_inspect_trace(action="invalid_action", trace_id="trace-123")
assert "error" in res assert "error" in res
assert "not supported" in res["message"] assert "not supported" in res["message"]
@pytest.mark.asyncio
async def test_mcp_server_auto_registers_tools():
"""Verify that importing mcp_server.base auto-registers all mobile tools without manual tools import."""
from mcp_server.base import mcp
tools = await mcp.list_tools()
tool_names = {t.name for t in tools}
assert {
"mobile_run_task",
"mobile_manage_task",
"mobile_get_device_state",
"mobile_inspect_trace",
}.issubset(tool_names)
+6 -1
View File
@@ -90,13 +90,16 @@ def test_cli_mcp_generate_config_antigravity():
def test_cli_mcp_generate_config_all(): def test_cli_mcp_generate_config_all():
"""Verify 'artemis mcp --generate-config all' includes antigravity, cursor, windsurf, and claude.""" """Verify 'artemis mcp --generate-config all' includes antigravity, cursor, windsurf, claude, vscode, cline, and roo."""
result = runner.invoke(app, ["mcp", "--generate-config", "all"]) result = runner.invoke(app, ["mcp", "--generate-config", "all"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "antigravity" in result.output assert "antigravity" in result.output
assert "cursor" in result.output assert "cursor" in result.output
assert "windsurf" in result.output assert "windsurf" in result.output
assert "claude" in result.output assert "claude" in result.output
assert "vscode" in result.output
assert "cline" in result.output
assert "roo" in result.output
def test_cli_mcp_install_antigravity(tmp_path, monkeypatch): def test_cli_mcp_install_antigravity(tmp_path, monkeypatch):
@@ -111,6 +114,7 @@ def test_cli_mcp_install_antigravity(tmp_path, monkeypatch):
data = json.loads(jetski_file.read_text()) data = json.loads(jetski_file.read_text())
assert "artemis" in data["mcpServers"] assert "artemis" in data["mcpServers"]
assert "mobile_run_task" in data["mcpServers"]["artemis"]["tools"] assert "mobile_run_task" in data["mcpServers"]["artemis"]["tools"]
assert "PYTHONPATH" in data["mcpServers"]["artemis"]["env"]
def test_cli_mcp_install_all(tmp_path, monkeypatch): def test_cli_mcp_install_all(tmp_path, monkeypatch):
@@ -122,5 +126,6 @@ def test_cli_mcp_install_all(tmp_path, monkeypatch):
assert (tmp_path / ".cursor" / "mcp.json").exists() assert (tmp_path / ".cursor" / "mcp.json").exists()
assert (tmp_path / ".codeium" / "windsurf" / "mcp_config.json").exists() assert (tmp_path / ".codeium" / "windsurf" / "mcp_config.json").exists()
assert (tmp_path / ".openclaw" / "openclaw.json").exists() assert (tmp_path / ".openclaw" / "openclaw.json").exists()
assert (tmp_path / ".claude.json").exists()