fix: scope MCP configuration to Codex and stabilize Windows launchers
This commit is contained in:
1 parent
18d17da966
commit
c96935b47b
13 files changed
+290
-1125
No files matched your search
+2
-2
@@ -356,10 +356,10 @@ if (-not (Test-Path -LiteralPath (Join-Path $RootDir '.env'))) {
|
||||
}
|
||||
}
|
||||
if (-not $SkipMcpSync) {
|
||||
Write-Host (Get-LaunchText ' [CHECK] Installing/updating MCP for detected AI clients...' ' [检查] 正在为检测到的 AI 客户端安装或更新 MCP...') -ForegroundColor Cyan
|
||||
Write-Host (Get-LaunchText ' [CHECK] Installing/updating Codex MCP...' ' [检查] 正在安装或更新 Codex MCP...') -ForegroundColor Cyan
|
||||
& $ArtemisPython (Join-Path $ScriptDir 'update_installation.py') mcp
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host (Get-LaunchText ' [ERROR] MCP synchronization failed. Fix the configuration above, or use -SkipMcpSync to run the web UI without MCP.' ' [错误] MCP 同步失败。请修复上方配置;仅需启动网页时可使用 -SkipMcpSync。') -ForegroundColor Red
|
||||
Write-Host (Get-LaunchText ' [ERROR] Codex MCP synchronization failed. Fix the configuration above, or use -SkipMcpSync to run the web UI without MCP.' ' [错误] Codex MCP 同步失败。请修复上方配置;仅需启动网页时可使用 -SkipMcpSync。') -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
|
||||
+62
-189
@@ -1,4 +1,4 @@
|
||||
"""Check an existing checkout and synchronize installed local MCP clients."""
|
||||
"""Check an existing checkout and synchronize the local Codex MCP configuration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -184,54 +184,6 @@ def _updated_entry(config: dict, template: dict, root: Path, names: tuple[str, .
|
||||
return updated
|
||||
|
||||
|
||||
def refresh_json(
|
||||
path: Path,
|
||||
keys: tuple[str, ...],
|
||||
root: Path,
|
||||
python: str,
|
||||
names: tuple[str, ...],
|
||||
template: dict | None = None,
|
||||
install_missing: bool = False,
|
||||
) -> bool | None:
|
||||
if not path.is_file() and not install_missing:
|
||||
return False
|
||||
raw = path.read_text(encoding="utf-8") if path.is_file() else "{}"
|
||||
data = json.loads(raw)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("MCP configuration must be a JSON object")
|
||||
parent = data
|
||||
for key in keys:
|
||||
child = parent.get(key)
|
||||
if child is None and install_missing:
|
||||
child = parent[key] = {}
|
||||
if not isinstance(child, dict):
|
||||
raise ValueError(f"Invalid MCP configuration section: {key}")
|
||||
parent = child
|
||||
config = parent.get("artemis")
|
||||
if config is None and install_missing:
|
||||
if template is None:
|
||||
raise ValueError("A standard MCP template is required for installation")
|
||||
updated = template
|
||||
elif isinstance(config, dict) and _is_standard_local_artemis(config):
|
||||
if template is None:
|
||||
template = {
|
||||
"command": python,
|
||||
"args": ["-m", "mcp_server"],
|
||||
"env": {"PYTHONPATH": str(root)},
|
||||
}
|
||||
updated = _updated_entry(config, template, root, names)
|
||||
elif config is not None and not isinstance(config, dict):
|
||||
raise ValueError("The Artemis MCP configuration is not an object")
|
||||
else:
|
||||
return None
|
||||
if updated == config:
|
||||
return False
|
||||
parent["artemis"] = updated
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
_atomic_write(path, json.dumps(data, ensure_ascii=False, indent=2) + "\n")
|
||||
return True
|
||||
|
||||
|
||||
def _toml_value(value: object) -> str:
|
||||
if isinstance(value, bool):
|
||||
return "true" if value else "false"
|
||||
@@ -363,152 +315,73 @@ def refresh_codex(
|
||||
|
||||
|
||||
def refresh_mcp(root: Path) -> int:
|
||||
"""Synchronize only Codex; never inspect or modify other clients."""
|
||||
sys.path.insert(0, str(root))
|
||||
from artemis.interfaces.cli.commands.mcp import (
|
||||
ARTEMIS_MCP_TOOLS,
|
||||
_get_config_snippet,
|
||||
_get_vscode_user_dir,
|
||||
)
|
||||
from artemis.interfaces.cli.commands.mcp import ARTEMIS_MCP_TOOLS, _get_config_snippet
|
||||
|
||||
home = Path.home()
|
||||
vscode = _get_vscode_user_dir()
|
||||
appdata = Path(os.environ.get("APPDATA", home / "AppData/Roaming"))
|
||||
cline_store = vscode / "globalStorage/saoudrizwan.claude-dev"
|
||||
roo_store = vscode / "globalStorage/rooveterinaryinc.roo-cline"
|
||||
paths = [
|
||||
(home / ".gemini/config/mcp_config.json", ("mcpServers",), "antigravity", home / ".gemini"),
|
||||
(home / ".gemini/jetski/mcp_config.json", ("mcpServers",), "jetski", home / ".gemini"),
|
||||
(
|
||||
home / ".gemini/antigravity/mcp_config.json",
|
||||
("mcpServers",),
|
||||
"antigravity",
|
||||
home / ".gemini",
|
||||
),
|
||||
(home / ".cursor/mcp.json", ("mcpServers",), "cursor", home / ".cursor"),
|
||||
(
|
||||
home / ".codeium/windsurf/mcp_config.json",
|
||||
("mcpServers",),
|
||||
"windsurf",
|
||||
home / ".codeium/windsurf",
|
||||
),
|
||||
(home / ".claude.json", ("mcpServers",), "claude", home / ".claude"),
|
||||
(
|
||||
appdata / "Claude/claude_desktop_config.json",
|
||||
("mcpServers",),
|
||||
"claude",
|
||||
appdata / "Claude",
|
||||
),
|
||||
(vscode / "mcp.json", ("servers",), "vscode", vscode),
|
||||
(home / ".copilot/mcp-config.json", ("servers",), "vscode", home / ".copilot"),
|
||||
(
|
||||
home / ".cline/data/settings/cline_mcp_settings.json",
|
||||
("mcpServers",),
|
||||
"cline",
|
||||
home / ".cline",
|
||||
),
|
||||
(home / ".cline/mcp.json", ("mcpServers",), "cline", home / ".cline"),
|
||||
(
|
||||
cline_store / "settings/cline_mcp_settings.json",
|
||||
("mcpServers",),
|
||||
"cline",
|
||||
cline_store,
|
||||
),
|
||||
(
|
||||
roo_store / "settings/mcp_settings.json",
|
||||
("mcpServers",),
|
||||
"roo",
|
||||
roo_store,
|
||||
),
|
||||
(
|
||||
roo_store / "settings/cline_mcp_settings.json",
|
||||
("mcpServers",),
|
||||
"roo",
|
||||
roo_store,
|
||||
),
|
||||
(root / ".roo/mcp.json", ("mcpServers",), "roo", root / ".roo"),
|
||||
(home / ".openclaw/openclaw.json", ("mcp", "servers"), "openclaw", home / ".openclaw"),
|
||||
]
|
||||
codex_home = Path(os.environ.get("CODEX_HOME", home / ".codex"))
|
||||
paths.append((codex_home / "config.toml", (), "codex", codex_home))
|
||||
installed = updated_count = ready = skipped = errors = 0
|
||||
for path, keys, client, marker in paths:
|
||||
if not path.is_file() and not marker.is_dir():
|
||||
continue
|
||||
existed = path.is_file()
|
||||
template = _get_config_snippet(client, sys.executable, str(root))
|
||||
for key in keys:
|
||||
template = template[key]
|
||||
template = template["artemis"] if keys else template["mcp_servers"]["artemis"]
|
||||
try:
|
||||
changed = (
|
||||
refresh_json(
|
||||
path,
|
||||
keys,
|
||||
root,
|
||||
sys.executable,
|
||||
ARTEMIS_MCP_TOOLS,
|
||||
template=template,
|
||||
install_missing=True,
|
||||
)
|
||||
if keys
|
||||
else refresh_codex(
|
||||
path,
|
||||
root,
|
||||
sys.executable,
|
||||
ARTEMIS_MCP_TOOLS,
|
||||
template=template,
|
||||
install_missing=True,
|
||||
)
|
||||
)
|
||||
if changed:
|
||||
print(
|
||||
launch_text(
|
||||
f"[{'UPDATED' if existed else 'INSTALLED'}] MCP configuration: {path}",
|
||||
f"[{'已更新' if existed else '已安装'}] MCP 配置:{path}",
|
||||
)
|
||||
)
|
||||
if existed:
|
||||
updated_count += 1
|
||||
else:
|
||||
installed += 1
|
||||
elif changed is None:
|
||||
print(
|
||||
launch_text(
|
||||
f"[SKIP] Custom Artemis MCP entry in {path}; use the manual installer for this client.",
|
||||
f"[跳过] {path} 使用自定义 ARTEMIS MCP 启动方式;请手动检查此客户端。",
|
||||
)
|
||||
)
|
||||
skipped += 1
|
||||
else:
|
||||
ready += 1
|
||||
except (OSError, ValueError, TypeError, AttributeError):
|
||||
print(
|
||||
launch_text(
|
||||
f"[ERROR] Could not safely synchronize {path}; original configuration preserved.",
|
||||
f"[错误] 无法安全同步 {path};原配置已保留。",
|
||||
)
|
||||
)
|
||||
errors += 1
|
||||
print(
|
||||
launch_text(
|
||||
f"[CHECK] MCP clients: installed {installed}, updated {updated_count}, already current {ready}, custom skipped {skipped}, errors {errors}.",
|
||||
f"[检查] MCP 客户端:新装 {installed} 个、更新 {updated_count} 个、已是最新 {ready} 个、自定义跳过 {skipped} 个、失败 {errors} 个。",
|
||||
)
|
||||
)
|
||||
print(
|
||||
launch_text(
|
||||
"[ACTION] Reload or restart your AI client to load the current MCP process and tool schemas.",
|
||||
"[操作] 请在 AI 客户端重新加载 MCP 或重启客户端,使新版进程和工具签名生效。",
|
||||
)
|
||||
)
|
||||
if not (installed or updated_count or ready or skipped or errors):
|
||||
codex_home = Path(os.environ.get("CODEX_HOME") or (Path.home() / ".codex"))
|
||||
path = codex_home / "config.toml"
|
||||
print(launch_text(f"[CHECK] Codex MCP configuration: {path}", f"[检查] Codex MCP 配置:{path}"))
|
||||
if not path.is_file() and not codex_home.is_dir():
|
||||
print(
|
||||
launch_text(
|
||||
"[TIP] No supported client detected; install one with: uv run artemis mcp --install <client>",
|
||||
"[提示] 未检测到受支持的客户端;可执行 uv run artemis mcp --install <client>。",
|
||||
"[SKIP] Codex configuration directory not found. Install with: uv run artemis mcp --install codex",
|
||||
"[跳过] 未发现 Codex 配置目录。可执行 uv run artemis mcp --install codex 安装。",
|
||||
)
|
||||
)
|
||||
return 1 if errors else 0
|
||||
return 0
|
||||
existed = path.is_file()
|
||||
template = _get_config_snippet("codex", sys.executable, str(root))["mcp_servers"]["artemis"]
|
||||
try:
|
||||
changed = refresh_codex(
|
||||
path, root, sys.executable, ARTEMIS_MCP_TOOLS, template=template, install_missing=True
|
||||
)
|
||||
except (OSError, ValueError, TypeError, AttributeError) as exc:
|
||||
if isinstance(exc, OSError):
|
||||
reason = launch_text(
|
||||
"Cannot read/write the configuration; check file permissions.",
|
||||
"无法读写配置,请检查文件权限。",
|
||||
)
|
||||
else:
|
||||
reason = launch_text(
|
||||
"Invalid TOML or Artemis configuration structure; check the file in Codex.",
|
||||
"TOML 格式或 ARTEMIS 配置结构无效,请在 Codex 中检查配置文件。",
|
||||
)
|
||||
print(
|
||||
launch_text(
|
||||
f"[ERROR] Cannot safely synchronize {path}; original preserved. {reason}",
|
||||
f"[错误] 无法安全同步 {path};原配置已保留。{reason}",
|
||||
)
|
||||
)
|
||||
return 1
|
||||
if changed:
|
||||
print(
|
||||
launch_text(
|
||||
f"[{'UPDATED' if existed else 'INSTALLED'}] Codex MCP: {path}",
|
||||
f"[{'已更新' if existed else '已安装'}] Codex MCP:{path}",
|
||||
)
|
||||
)
|
||||
elif changed is None:
|
||||
print(
|
||||
launch_text(
|
||||
"[SKIP] Custom Codex Artemis MCP command preserved; check it manually.",
|
||||
"[跳过] Codex 使用自定义 ARTEMIS MCP 启动方式,已保留,请手动检查。",
|
||||
)
|
||||
)
|
||||
return 0
|
||||
else:
|
||||
print(
|
||||
launch_text(
|
||||
"[OK] Codex Artemis MCP is already current.", "[完成] Codex ARTEMIS MCP 已是最新。"
|
||||
)
|
||||
)
|
||||
print(
|
||||
launch_text(
|
||||
"[ACTION] Reload MCP or restart Codex to activate the updated tools.",
|
||||
"[操作] 请在 Codex 中重新加载 MCP 或重启 Codex,使新版进程和工具签名生效。",
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
def frontend_fingerprint(root: Path) -> str:
|
||||
|
||||
Reference in new issue
Block a user