diff --git a/README.md b/README.md index e9fd55c..7eec50e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ git clone https://github.com/google/artemis.git && cd artemis 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`. @@ -107,21 +107,22 @@ ARTEMIS includes a native **Model Context Protocol (MCP)** server. Connect your ### 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 # 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): -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) -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`): ```json @@ -217,9 +218,9 @@ if __name__ == "__main__": 💡 Console Overview: ① View Switcher (Home / Workspace) · ② Model & Replay (Flash/Pro status & video replay) · ③ Live Agent Stream (Action perception, target coordinates & structured results) · ④ Prompt Dock (Natural language dispatch) · ⑤ Task Queue & Dashboard (Lifecycle & history)
-* 🖥️ **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; -* 💻 **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. ## 📊 Head-to-Head Comparison diff --git a/README_CN.md b/README_CN.md index 28afb73..d5337ce 100644 --- a/README_CN.md +++ b/README_CN.md @@ -96,7 +96,7 @@ git clone https://github.com/google/artemis.git && cd artemis start.bat ``` -> 💡 **提示**:启动后将自动在默认浏览器中打开 Web 控制台(`http://localhost:8000`),提供设备连接向导、实时投屏、任务演练与状态回放面板。你也可以通过命令行直接运行:`artemis run "打开系统设置,找到电池选项并告诉我当前电量" --profile flash`。 +> 💡 **提示**:启动后将自动在默认浏览器中打开 Web 控制台(`http://localhost:8000`),提供设备连接向导、实时投屏、任务演练与状态回放面板。你也可以通过命令行直接运行:`uv run artemis run "打开系统设置,找到电池选项并告诉我当前电量" --profile flash`。 @@ -109,21 +109,22 @@ ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下 ### 1. 一键自动安装到 IDE(推荐) -运行内置安装命令,即可自动配置或更新你的 AI IDE(会自动解析并填写虚拟环境 Python 和项目的绝对路径): +运行 `./start.sh` 或 `start.bat` 启动脚本时会主动询问是否需要配置当前检测到的 AI IDE(支持跳过并在之后随时手动执行以下命令挂载): ```bash # 一键安装到 Antigravity / Jetski: -artemis mcp --install antigravity +uv run artemis mcp --install antigravity # 或一键安装到所有检测到的 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. 手动配置(可选) -如果你习惯手动复制配置,可运行 `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`): ```json @@ -219,9 +220,9 @@ if __name__ == "__main__": 💡 控制台功能布局:① 顶栏视图切换(主页与工作区) · ② 运行模式与录屏回放(Flash/Pro 状态与视频回放) · ③ 实时感知推理流(动作分解、点击坐标与结构化总结) · ④ 自然语言下发胶囊(自然语言驱动真机) · ⑤ 任务队列看板(状态流转与历史回溯) -* 🖥️ **Web 可视化测试控制台 (`artemis ui`)**:集成设备实时投屏与交互面板,支持通过自然语言下发测试用例,实时观测推理步骤、操作轨迹、截图留存与异常状态回放; +* 🖥️ **Web 可视化测试控制台 (`uv run artemis ui`)**:集成设备实时投屏与交互面板,支持通过自然语言下发测试用例,实时观测推理步骤、操作轨迹、截图留存与异常状态回放; * 🔌 **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 的强类型结构化结果与断言支持。 ## 📊 方案横向对比 diff --git a/artemis/interfaces/cli/commands/init.py b/artemis/interfaces/cli/commands/init.py index 0dd5575..c73b59d 100644 --- a/artemis/interfaces/cli/commands/init.py +++ b/artemis/interfaces/cli/commands/init.py @@ -140,13 +140,21 @@ def init_command() -> None: console.print(" [2] Cursor") console.print(" [3] Claude Desktop / Claude Code") console.print(" [4] Windsurf") - console.print(" [5] All supported IDEs") - console.print(" [6] Skip for now") + console.print(" [5] VS Code / Cline / Roo Code") + console.print(" [6] All supported IDEs") + console.print(" [7] Skip for now") 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": - target_map = {"1": "antigravity", "2": "cursor", "3": "claude", "4": "windsurf", "5": "all"} + if mcp_choice != "7": + target_map = { + "1": "antigravity", + "2": "cursor", + "3": "claude", + "4": "windsurf", + "5": "vscode", + "6": "all", + } target = target_map.get(mcp_choice, "antigravity") try: 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"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\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" - " [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" - " [bold cyan]artemis doctor[/bold cyan]", + " [bold cyan]uv run artemis doctor[/bold cyan]", title="☕ Ready to Go!", expand=False, ) diff --git a/artemis/interfaces/cli/commands/mcp.py b/artemis/interfaces/cli/commands/mcp.py index 2b380d7..56d3b66 100644 --- a/artemis/interfaces/cli/commands/mcp.py +++ b/artemis/interfaces/cli/commands/mcp.py @@ -21,6 +21,7 @@ import sys from typing import Annotated from mcp_server.base import mcp as agent_mcp +import mcp_server.tools # noqa: F401 from mcp_server.utils import env_utils from artemis.mcp.adb_server import mcp as adb_mcp from artemis.utils.logger import get_logger @@ -32,6 +33,17 @@ logger = get_logger(__name__) 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: """Generates MCP configuration dictionary for the specified client.""" config_body = { @@ -40,6 +52,7 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict "cwd": project_root, "env": { "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": 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}} 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.""" - file_path.parent.mkdir(parents=True, exist_ok=True) - data: dict = {} - if file_path.exists() and file_path.stat().st_size > 0: - try: - data = json.loads(file_path.read_text(encoding="utf-8")) - except Exception: - data = {} + try: + file_path.parent.mkdir(parents=True, exist_ok=True) + data: dict = {} + if file_path.exists() and file_path.stat().st_size > 0: + try: + data = json.loads(file_path.read_text(encoding="utf-8")) + if not isinstance(data, dict): + data = {} + except Exception: + data = {} - if key_name not in data or not isinstance(data.get(key_name), dict): - data[key_name] = {} + if key_name not in data or not isinstance(data.get(key_name), dict): + data[key_name] = {} - data[key_name][server_name] = server_config - file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") - return True + data[key_name][server_name] = server_config + file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") + 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]: - """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] = [] - 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: 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"] jetski_path = Path.home() / ".gemini" / "jetski" / "mcp_config.json" config_path = Path.home() / ".gemini" / "config" / "mcp_config.json" - _merge_json_file(jetski_path, "artemis", server_cfg) - _merge_json_file(config_path, "artemis", server_cfg) - installed_paths.extend([str(jetski_path), str(config_path)]) + if _merge_json_file(jetski_path, "artemis", server_cfg): + installed_paths.append(str(jetski_path)) + if _merge_json_file(config_path, "artemis", server_cfg): + installed_paths.append(str(config_path)) elif target in ("claude", "claude_code", "claude_desktop"): server_cfg = snippet["mcpServers"]["artemis"] 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" else: claude_path = Path.home() / ".config" / "Claude" / "claude_desktop_config.json" - _merge_json_file(claude_path, "artemis", server_cfg) - installed_paths.append(str(claude_path)) + if _merge_json_file(claude_path, "artemis", server_cfg): + 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": server_cfg = snippet["mcpServers"]["artemis"] cursor_path = Path.home() / ".cursor" / "mcp.json" - _merge_json_file(cursor_path, "artemis", server_cfg) - installed_paths.append(str(cursor_path)) + if _merge_json_file(cursor_path, "artemis", server_cfg): + installed_paths.append(str(cursor_path)) elif target == "windsurf": server_cfg = snippet["mcpServers"]["artemis"] windsurf_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json" - _merge_json_file(windsurf_path, "artemis", server_cfg) - installed_paths.append(str(windsurf_path)) + if _merge_json_file(windsurf_path, "artemis", server_cfg): + 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": plugin_cfg = snippet["plugins"]["artemis_mcp"] openclaw_path = Path.home() / ".openclaw" / "openclaw.json" - _merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins") - installed_paths.append(str(openclaw_path)) + if _merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins"): + installed_paths.append(str(openclaw_path)) return installed_paths @@ -165,7 +205,7 @@ def mcp_command( typer.Option( "--install", "-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, generate_config: Annotated[ @@ -173,7 +213,7 @@ def mcp_command( typer.Option( "--generate-config", "-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: @@ -191,11 +231,15 @@ def mcp_command( "claude_desktop", "cursor", "windsurf", + "vscode", + "cline", + "roo", + "roo_code", "openclaw", "all", ): 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) 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", 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 ), "windsurf (~/.codeium/windsurf/mcp_config.json)": _get_config_snippet( "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", python_exe, project_root ), diff --git a/mcp_server/README.md b/mcp_server/README.md index 1bf94e8..ed884f5 100644 --- a/mcp_server/README.md +++ b/mcp_server/README.md @@ -62,7 +62,7 @@ The included [`rules.md`](./rules.md) file contains the **Mobile Testing Mindset python -m mcp_server # Or via Artemis CLI -artemis mcp +uv run artemis mcp ``` ### IDE Configuration @@ -70,10 +70,10 @@ artemis mcp #### One-Click Auto Install (Recommended) Run the automated installer to detect and configure your IDE (Antigravity, Cursor, Claude Code/Desktop, OpenClaw): ```bash -artemis mcp --install all +uv run artemis mcp --install all # Or install specifically for Antigravity: -artemis mcp --install antigravity +uv run artemis mcp --install antigravity ``` #### 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. diff --git a/mcp_server/__init__.py b/mcp_server/__init__.py index 8d3d542..ef07041 100644 --- a/mcp_server/__init__.py +++ b/mcp_server/__init__.py @@ -16,5 +16,6 @@ from mcp_server.base import mcp from mcp_server.server import main +import mcp_server.tools # noqa: F401 __all__ = ["mcp", "main"] diff --git a/mcp_server/base.py b/mcp_server/base.py index 1c60ae0..485e2ba 100644 --- a/mcp_server/base.py +++ b/mcp_server/base.py @@ -16,6 +16,13 @@ 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. mcp = FastMCP( "artemis", @@ -27,3 +34,6 @@ mcp = FastMCP( "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 diff --git a/mcp_server/server.py b/mcp_server/server.py index a9b91a8..67037f0 100644 --- a/mcp_server/server.py +++ b/mcp_server/server.py @@ -55,6 +55,7 @@ except Exception: from mcp_server.base import mcp # 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): diff --git a/scripts/start.ps1 b/scripts/start.ps1 index 5d449f3..fced28c 100644 --- a/scripts/start.ps1 +++ b/scripts/start.ps1 @@ -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 " 💡 Tip: Connect to Antigravity / Claude Desktop anytime: artemis mcp --install all" -ForegroundColor Cyan if ($NoOpen) { uv run artemis ui --port $Port --no-open } else { diff --git a/start.sh b/start.sh index 5081e5d..e775656 100755 --- a/start.sh +++ b/start.sh @@ -153,7 +153,26 @@ if [ ! -f "${SHOWCASE_INDEX}" ] && [ ! -f "${SHOWCASE_INDEX_ALT1}" ] && [ ! -f " 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 " ${CYAN}💡 Tip: Connect to Antigravity / Claude Desktop anytime: ${BOLD}artemis mcp --install all${NC}" exec uv run artemis ui --open "$@" diff --git a/tests/unit/mcp/test_mcp_tools.py b/tests/unit/mcp/test_mcp_tools.py index 8532864..a73ee10 100644 --- a/tests/unit/mcp/test_mcp_tools.py +++ b/tests/unit/mcp/test_mcp_tools.py @@ -111,3 +111,17 @@ async def test_mobile_inspect_trace_invalid_action(): res = await mobile_inspect_trace(action="invalid_action", trace_id="trace-123") assert "error" in res 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) diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index c486ebe..16f4445 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -90,13 +90,16 @@ def test_cli_mcp_generate_config_antigravity(): 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"]) assert result.exit_code == 0 assert "antigravity" in result.output assert "cursor" in result.output assert "windsurf" 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): @@ -111,6 +114,7 @@ def test_cli_mcp_install_antigravity(tmp_path, monkeypatch): data = json.loads(jetski_file.read_text()) assert "artemis" in data["mcpServers"] 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): @@ -122,5 +126,6 @@ def test_cli_mcp_install_all(tmp_path, monkeypatch): assert (tmp_path / ".cursor" / "mcp.json").exists() assert (tmp_path / ".codeium" / "windsurf" / "mcp_config.json").exists() assert (tmp_path / ".openclaw" / "openclaw.json").exists() + assert (tmp_path / ".claude.json").exists()