From 8241cf7bb1cefc6c10748dd5dfd83e966083e77b Mon Sep 17 00:00:00 2001 From: Ziyang Guo <121015044+RerankerGuo@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:56:17 +0800 Subject: [PATCH 1/2] fix(manager): reject MCP options during runtime switches --- .../agent/skills/worker-management/SKILL.md | 4 +- .../scripts/update-worker-config.sh | 8 +- manager/tests/test-update-worker-runtime.sh | 114 ++++++++++++++++++ 3 files changed, 121 insertions(+), 5 deletions(-) create mode 100755 manager/tests/test-update-worker-runtime.sh diff --git a/manager/agent/skills/worker-management/SKILL.md b/manager/agent/skills/worker-management/SKILL.md index b6997612e..5997263b2 100644 --- a/manager/agent/skills/worker-management/SKILL.md +++ b/manager/agent/skills/worker-management/SKILL.md @@ -87,7 +87,7 @@ To migrate a Worker between runtimes (e.g. openclaw → copaw, copaw → hermes) bash /opt/hiclaw/agent/skills/worker-management/scripts/update-worker-config.sh \ --name \ --runtime \ - [--model ] [--skills s1,s2] [--mcp-servers s1,s2] + [--model ] [--skills s1,s2] ``` What happens behind the scenes: @@ -98,5 +98,5 @@ What happens behind the scenes: Constraints: -- `--package-dir` and `--channel-policy` cannot be combined with `--runtime` — apply those separately after the runtime switch settles +- `--package-dir`, `--channel-policy`, and `--mcp-servers` cannot be combined with `--runtime` — apply those separately after the runtime switch settles - The wrapper preserves Matrix account/room/credentials/MinIO data but loses container-local ephemeral state — see the runtime gotcha above diff --git a/manager/agent/skills/worker-management/scripts/update-worker-config.sh b/manager/agent/skills/worker-management/scripts/update-worker-config.sh index 84aa156fb..cdf735a0c 100644 --- a/manager/agent/skills/worker-management/scripts/update-worker-config.sh +++ b/manager/agent/skills/worker-management/scripts/update-worker-config.sh @@ -17,7 +17,7 @@ # # Usage: # update-worker-config.sh --name [--model ] [--skills s1,s2] [--mcp-servers s1,s2] [--package-dir ] -# update-worker-config.sh --name --runtime [--model ] [--skills s1,s2] [--mcp-servers s1,s2] +# update-worker-config.sh --name --runtime [--model ] [--skills s1,s2] # # Prerequisites: # - Worker must already exist (created via create-worker.sh) @@ -65,7 +65,7 @@ done if [ -z "${WORKER_NAME}" ]; then echo "Usage: update-worker-config.sh --name [--model ] [--skills s1,s2] [--mcp-servers s1,s2] [--package-dir ]" - echo " update-worker-config.sh --name --runtime [--model ] [--skills s1,s2] [--mcp-servers s1,s2]" + echo " update-worker-config.sh --name --runtime [--model ] [--skills s1,s2]" exit 1 fi @@ -92,6 +92,9 @@ if [ -n "${RUNTIME}" ]; then if [ -n "${CHANNEL_POLICY_JSON}" ]; then _fail "--channel-policy cannot be combined with --runtime. Apply the channel-policy separately (without --runtime) after the runtime switch settles." fi + if [ -n "${MCP_SERVERS}" ]; then + _fail "--mcp-servers cannot be combined with --runtime. Reauthorize MCP servers separately (without --runtime) after the runtime switch settles." + fi log "=== Switching runtime for Worker: ${WORKER_NAME} -> ${RUNTIME} ===" log " WARNING: existing container will be destroyed and recreated." @@ -101,7 +104,6 @@ if [ -n "${RUNTIME}" ]; then CLI_ARGS=(update worker --name "${WORKER_NAME}" --runtime "${RUNTIME}") [ -n "${MODEL_ID}" ] && CLI_ARGS+=(--model "${MODEL_ID}") [ -n "${WORKER_SKILLS}" ] && CLI_ARGS+=(--skills "${WORKER_SKILLS}") - [ -n "${MCP_SERVERS}" ] && CLI_ARGS+=(--mcp-servers "${MCP_SERVERS}") log "Step 1: Calling: hiclaw ${CLI_ARGS[*]}" if ! CLI_OUT=$(hiclaw "${CLI_ARGS[@]}" 2>&1); then diff --git a/manager/tests/test-update-worker-runtime.sh b/manager/tests/test-update-worker-runtime.sh new file mode 100755 index 000000000..84ee8de9a --- /dev/null +++ b/manager/tests/test-update-worker-runtime.sh @@ -0,0 +1,114 @@ +#!/bin/bash +# Regression tests for runtime-switch arguments in update-worker-config.sh. + +set -uo pipefail + +PASS=0 +FAIL=0 +TMPDIR_ROOT=$(mktemp -d) +trap 'rm -rf "${TMPDIR_ROOT}"' EXIT + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +UPDATE_SCRIPT="${PROJECT_ROOT}/manager/agent/skills/worker-management/scripts/update-worker-config.sh" +TEST_SCRIPT="${TMPDIR_ROOT}/update-worker-config.sh" +MOCK_BIN="${TMPDIR_ROOT}/bin" +HICLAW_LOG="${TMPDIR_ROOT}/hiclaw.log" + +pass() { + echo " PASS: $1" + PASS=$((PASS + 1)) +} + +fail() { + echo " FAIL: $1" + echo " expected: $2" + echo " got: $3" + FAIL=$((FAIL + 1)) +} + +assert_eq() { + local description="$1" expected="$2" actual="$3" + if [ "${expected}" = "${actual}" ]; then + pass "${description}" + else + fail "${description}" "${expected}" "${actual}" + fi +} + +assert_contains() { + local description="$1" needle="$2" actual="$3" + if printf '%s\n' "${actual}" | grep -qF -- "${needle}"; then + pass "${description}" + else + fail "${description}" "contains ${needle}" "${actual}" + fi +} + +# The production source only exports shared environment helpers. Runtime mode +# does not need them, so replace that source line in the isolated test copy. +sed 's|^source /opt/hiclaw/scripts/lib/hiclaw-env.sh$|: # environment supplied by test|' \ + "${UPDATE_SCRIPT}" > "${TEST_SCRIPT}" +chmod +x "${TEST_SCRIPT}" + +mkdir -p "${MOCK_BIN}" +cat > "${MOCK_BIN}/hiclaw" <<'EOF' +#!/bin/sh +case "$1 $2" in + "update worker") + printf '%s\n' "$@" > "${TEST_HICLAW_LOG:?}" + echo "worker updated" + ;; + "get workers") + printf '%s\n' '{"workers":[{"name":"alice","phase":"Running","runtime":"hermes"}]}' + ;; + *) + echo "unexpected hiclaw command: $*" >&2 + exit 2 + ;; +esac +EOF +chmod +x "${MOCK_BIN}/hiclaw" + +echo "=== Runtime switch rejects the unsupported MCP combination ===" +if REJECT_OUTPUT=$(PATH="${MOCK_BIN}:${PATH}" \ + TEST_HICLAW_LOG="${HICLAW_LOG}" \ + bash "${TEST_SCRIPT}" --name alice --runtime hermes --mcp-servers github 2>&1); then + fail "runtime and MCP options are rejected before the CLI call" \ + "non-zero exit" "exit 0" +else + assert_contains "rejection explains the incompatible options" \ + "--mcp-servers cannot be combined with --runtime" "${REJECT_OUTPUT}" +fi + +if [ -e "${HICLAW_LOG}" ]; then + fail "rejected options do not invoke hiclaw" "no command log" "$(cat "${HICLAW_LOG}")" +else + pass "rejected options do not invoke hiclaw" +fi + +echo "=== Runtime switch forwards supported update flags unchanged ===" +RUNTIME_OUTPUT=$(PATH="${MOCK_BIN}:${PATH}" \ + TEST_HICLAW_LOG="${HICLAW_LOG}" \ + bash "${TEST_SCRIPT}" \ + --name alice \ + --runtime hermes \ + --model qwen3.6-plus \ + --skills code-review) + +EXPECTED_ARGS=$(printf '%s\n' \ + update worker \ + --name alice \ + --runtime hermes \ + --model qwen3.6-plus \ + --skills code-review) +assert_eq "supported runtime arguments match hiclaw update worker" \ + "${EXPECTED_ARGS}" "$(cat "${HICLAW_LOG}")" +assert_contains "runtime switch still reports completion" \ + '"status": "runtime_switched"' "${RUNTIME_OUTPUT}" + +echo "" +echo "Results: ${PASS} passed, ${FAIL} failed" +if [ "${FAIL}" -gt 0 ]; then + exit 1 +fi From cc4e8814b9b88119f12f81a7bca1531270ee523f Mon Sep 17 00:00:00 2001 From: Ziyang Guo <121015044+RerankerGuo@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:56:57 +0800 Subject: [PATCH 2/2] chore(changelog): record runtime MCP contract fix --- changelog/current.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog/current.md b/changelog/current.md index f3ae6b4eb..350d153fa 100644 --- a/changelog/current.md +++ b/changelog/current.md @@ -4,6 +4,7 @@ Record image-affecting changes to `manager/`, `worker/`, `copaw/`, `openclaw-bas --- +- fix(manager): reject unsupported MCP overrides during Worker runtime switches and document the separate authorization step ([8241cf7](https://github.com/agentscope-ai/AgentTeams/commit/8241cf7bb1cefc6c10748dd5dfd83e966083e77b)). - feat(qwenpaw): add the QwenPaw worker runtime Python package baseline with runtime config sync, storage sync, heartbeat reporting, Matrix channel overlay, and focused unit tests. - fix(controller): surface Kubernetes Pod container failures in Worker backend status and status API responses. - feat(controller): expose low-cardinality AgentTeams controller metrics and optional Helm ServiceMonitor.