diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index df7057fa1a8..bc323d3f910 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -31,7 +31,6 @@ name: "Stale Repository Identifier" "on": schedule: - cron: "0 9 1 * *" - # Friendly format: every 12h (scattered) workflow_dispatch: inputs: organization: diff --git a/actions/setup/sh/check_mcp_servers.sh b/actions/setup/sh/check_mcp_servers.sh index 2eb35471dde..1b704e6dc77 100755 --- a/actions/setup/sh/check_mcp_servers.sh +++ b/actions/setup/sh/check_mcp_servers.sh @@ -19,8 +19,8 @@ set -e # GATEWAY_API_KEY : API key for gateway authentication # # Exit codes: -# 0 - All server checks completed (warnings logged for failures) -# 1 - Invalid arguments or configuration file issues +# 0 - All MCP server checks succeeded (some may be skipped if not HTTP-based) +# 1 - Invalid arguments, configuration file issues, or any MCP server failed to connect if [ "$#" -ne 3 ]; then echo "Usage: $0 GATEWAY_CONFIG_PATH GATEWAY_URL GATEWAY_API_KEY" >&2 @@ -135,9 +135,12 @@ while IFS= read -r SERVER_NAME; do AUTH_HEADER=$(echo "$SERVER_CONFIG" | jq -r '.headers.Authorization' 2>/dev/null) echo "Authentication: From gateway config (${AUTH_HEADER:0:20}...)" else - echo "WARNING: No Authorization header in gateway configuration for: $SERVER_NAME" + echo "ERROR: No Authorization header in gateway configuration for: $SERVER_NAME" echo "The gateway should have included authentication headers in its output." - echo "Skipping authentication check..." + echo "This indicates the gateway failed to properly configure the server." + SERVERS_FAILED=$((SERVERS_FAILED + 1)) + echo "" + continue fi echo "" diff --git a/actions/setup/sh/mcp_exit_code_logic_test.sh b/actions/setup/sh/mcp_exit_code_logic_test.sh new file mode 100755 index 00000000000..8253eb9aa50 --- /dev/null +++ b/actions/setup/sh/mcp_exit_code_logic_test.sh @@ -0,0 +1,154 @@ +#!/bin/bash +# Fast unit test for MCP gateway failure exit code logic +# Tests the final exit code determination without network timeouts + +set -e + +# Color codes +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 + +print_result() { + local test_name="$1" + local result="$2" + + TESTS_RUN=$((TESTS_RUN + 1)) + + if [ "$result" = "PASS" ]; then + echo -e "${GREEN}✓ PASS${NC}: $test_name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + else + echo -e "${RED}✗ FAIL${NC}: $test_name" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +echo "========================================" +echo "MCP Gateway Exit Code Logic Tests" +echo "========================================" +echo "" + +# Test 1: Simulate exit code logic with failures +echo "Test 1: Exit code logic with SERVERS_FAILED > 0" +SERVERS_FAILED=1 +SERVERS_SUCCEEDED=0 +if [ $SERVERS_FAILED -gt 0 ]; then + print_result "SERVERS_FAILED > 0 triggers exit 1 logic" "PASS" +elif [ $SERVERS_SUCCEEDED -eq 0 ]; then + print_result "Secondary condition not needed" "FAIL" +else + print_result "Should not reach success path" "FAIL" +fi +echo "" + +# Test 2: No successes should fail +echo "Test 2: Exit code logic with SERVERS_SUCCEEDED = 0" +SERVERS_FAILED=0 +SERVERS_SUCCEEDED=0 +if [ $SERVERS_FAILED -gt 0 ]; then + print_result "Should not trigger first condition" "FAIL" +elif [ $SERVERS_SUCCEEDED -eq 0 ]; then + print_result "SERVERS_SUCCEEDED = 0 triggers exit 1 logic" "PASS" +else + print_result "Should not reach success path" "FAIL" +fi +echo "" + +# Test 3: Success case +echo "Test 3: Exit code logic with successes and no failures" +SERVERS_FAILED=0 +SERVERS_SUCCEEDED=2 +if [ $SERVERS_FAILED -gt 0 ]; then + print_result "Should not trigger first condition" "FAIL" +elif [ $SERVERS_SUCCEEDED -eq 0 ]; then + print_result "Should not trigger second condition" "FAIL" +else + print_result "Success case triggers exit 0 logic" "PASS" +fi +echo "" + +# Test 4: Verify actual script exit codes with quick-fail configs +echo "Test 4: Actual script with invalid config (quick exit)" +tmpdir=$(mktemp -d) +# Invalid JSON - should fail immediately +echo "not json" > "$tmpdir/bad.json" +if bash actions/setup/sh/check_mcp_servers.sh "$tmpdir/bad.json" "http://localhost:8080" "key" >/dev/null 2>&1; then + print_result "Invalid JSON should fail" "FAIL" +else + exit_code=$? + if [ $exit_code -eq 1 ]; then + print_result "Invalid JSON returns exit code 1" "PASS" + else + echo " Expected 1, got $exit_code" + print_result "Invalid JSON exit code" "FAIL" + fi +fi +rm -rf "$tmpdir" +echo "" + +# Test 5: Empty servers (quick exit) +echo "Test 5: Actual script with empty servers (quick exit)" +tmpdir=$(mktemp -d) +cat > "$tmpdir/empty.json" <<'EOF' +{ + "mcpServers": {} +} +EOF +if bash actions/setup/sh/check_mcp_servers.sh "$tmpdir/empty.json" "http://localhost:8080" "key" >/dev/null 2>&1; then + print_result "Empty mcpServers returns exit code 0" "PASS" +else + echo " Empty servers should exit 0 (no servers to check)" + print_result "Empty mcpServers handling" "FAIL" +fi +rm -rf "$tmpdir" +echo "" + +# Test 6: Only stdio servers (quick exit - no HTTP servers to check) +echo "Test 6: Only stdio servers (should fail - no HTTP servers checked)" +tmpdir=$(mktemp -d) +cat > "$tmpdir/stdio.json" <<'EOF' +{ + "mcpServers": { + "stdio-server": { + "type": "stdio", + "command": "node", + "args": ["server.js"] + } + } +} +EOF +if bash actions/setup/sh/check_mcp_servers.sh "$tmpdir/stdio.json" "http://localhost:8080" "key" >/dev/null 2>&1; then + print_result "stdio-only should fail (no HTTP servers)" "FAIL" +else + exit_code=$? + if [ $exit_code -eq 1 ]; then + print_result "stdio-only returns exit code 1" "PASS" + else + echo " Expected 1, got $exit_code" + print_result "stdio-only exit code" "FAIL" + fi +fi +rm -rf "$tmpdir" +echo "" + +# Print summary +echo "========================================" +echo "Test Summary" +echo "========================================" +echo "Tests run: $TESTS_RUN" +echo "Tests passed: $TESTS_PASSED" +echo "Tests failed: $TESTS_FAILED" +echo "" + +if [ $TESTS_FAILED -gt 0 ]; then + echo -e "${RED}Some tests failed${NC}" + exit 1 +else + echo -e "${GREEN}All tests passed!${NC}" + exit 0 +fi diff --git a/actions/setup/sh/mcp_gateway_failure_handling_test.sh b/actions/setup/sh/mcp_gateway_failure_handling_test.sh new file mode 100755 index 00000000000..febe2393144 --- /dev/null +++ b/actions/setup/sh/mcp_gateway_failure_handling_test.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# Test script for verifying MCP gateway failure handling +# This test verifies that start_mcp_gateway.sh correctly terminates the gateway +# when check_mcp_servers.sh detects server connection failures + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CHECK_SCRIPT="$SCRIPT_DIR/check_mcp_servers.sh" + +# Color codes for output +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Test counters +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Print test result +print_result() { + local test_name="$1" + local result="$2" + + TESTS_RUN=$((TESTS_RUN + 1)) + + if [ "$result" = "PASS" ]; then + echo -e "${GREEN}✓ PASS${NC}: $test_name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + else + echo -e "${RED}✗ FAIL${NC}: $test_name" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +echo "========================================" +echo "MCP Gateway Failure Handling Tests" +echo "========================================" +echo "" + +# Test 1: Server with connection failure returns exit code 1 +test_server_connection_failure() { + echo "Test 1: Server connection failure returns exit code 1" + + local tmpdir=$(mktemp -d) + local config_file="$tmpdir/config.json" + + # Create config with HTTP server that cannot be reached + cat > "$config_file" <<'EOF' +{ + "mcpServers": { + "unreachable-server": { + "type": "http", + "url": "http://localhost:9999/mcp/unreachable", + "headers": { + "Authorization": "Bearer test-token" + } + } + } +} +EOF + + # Script should fail with exit code 1 (server cannot be connected) + # Use timeout to avoid long waits, but check the actual script exit code + timeout 20 bash "$CHECK_SCRIPT" "$config_file" "http://localhost:9999" "test-key" >/dev/null 2>&1 + local exit_code=$? + + # timeout returns 124 on timeout, but we want the script's exit code + # Script should fail before timeout with exit code 1 + if [ $exit_code -eq 1 ]; then + print_result "Connection failure returns exit code 1" "PASS" + elif [ $exit_code -eq 124 ]; then + echo " Test timed out - script may be hanging" + print_result "Connection failure test timed out" "FAIL" + else + echo " Expected exit code 1, got $exit_code" + print_result "Connection failure returns correct exit code" "FAIL" + fi + + rm -rf "$tmpdir" +} + +# Test 2: Multiple servers with at least one failure returns exit code 1 +test_multiple_servers_with_failure() { + echo "" + echo "Test 2: Multiple servers with at least one failure returns exit code 1" + + local tmpdir=$(mktemp -d) + local config_file="$tmpdir/config.json" + + # Create config with multiple HTTP servers, all unreachable + cat > "$config_file" <<'EOF' +{ + "mcpServers": { + "server1": { + "type": "http", + "url": "http://localhost:9998/mcp/server1", + "headers": { + "Authorization": "Bearer token1" + } + }, + "server2": { + "type": "http", + "url": "http://localhost:9997/mcp/server2", + "headers": { + "Authorization": "Bearer token2" + } + } + } +} +EOF + + # Script should fail because all servers are unreachable + # Use timeout to avoid long waits (multiple servers = more retry time) + timeout 30 bash "$CHECK_SCRIPT" "$config_file" "http://localhost:9998" "test-key" >/dev/null 2>&1 + local exit_code=$? + + if [ $exit_code -eq 1 ]; then + print_result "Multiple server failures return exit code 1" "PASS" + elif [ $exit_code -eq 124 ]; then + echo " Test timed out - script may be hanging" + print_result "Multiple server failures test timed out" "FAIL" + else + echo " Expected exit code 1, got $exit_code" + print_result "Multiple server failures return correct exit code" "FAIL" + fi + + rm -rf "$tmpdir" +} + +# Test 3: Verify exit code 1 for initialize request failure +test_initialize_failure() { + echo "" + echo "Test 3: Initialize request failure returns exit code 1" + + local tmpdir=$(mktemp -d) + local config_file="$tmpdir/config.json" + + # Create config with unreachable server + cat > "$config_file" <<'EOF' +{ + "mcpServers": { + "test-server": { + "type": "http", + "url": "http://localhost:8765/mcp/test", + "headers": { + "Authorization": "Bearer test-token" + } + } + } +} +EOF + + # Capture both exit code and output (with timeout) + output=$(timeout 20 bash "$CHECK_SCRIPT" "$config_file" "http://localhost:8765" "test-key" 2>&1) + exit_code=$? + + # Should fail with exit code 1 + if [ $exit_code -eq 1 ]; then + # Verify error message mentions failure + if echo "$output" | grep -q "failed"; then + print_result "Initialize failure detected and reported" "PASS" + else + echo " Exit code correct but error message not found" + print_result "Initialize failure error message" "FAIL" + fi + else + echo " Expected exit code 1, got $exit_code" + print_result "Initialize failure returns exit code 1" "FAIL" + fi + + rm -rf "$tmpdir" +} + +# Test 4: Verify no servers successfully checked returns exit code 1 +test_no_successful_checks() { + echo "" + echo "Test 4: No successful server checks returns exit code 1" + + local tmpdir=$(mktemp -d) + local config_file="$tmpdir/config.json" + + # Create config with only stdio servers (which get skipped) + cat > "$config_file" <<'EOF' +{ + "mcpServers": { + "stdio-server": { + "type": "stdio", + "command": "node", + "args": ["server.js"] + } + } +} +EOF + + # Should exit 1 because no HTTP servers were successfully checked + timeout 5 bash "$CHECK_SCRIPT" "$config_file" "http://localhost:8080" "test-key" >/dev/null 2>&1 + local exit_code=$? + + if [ $exit_code -eq 1 ]; then + print_result "No successful checks returns exit code 1" "PASS" + elif [ $exit_code -eq 124 ]; then + echo " Test timed out unexpectedly" + print_result "No successful checks test timed out" "FAIL" + else + echo " Expected exit code 1, got $exit_code" + print_result "No successful checks returns correct exit code" "FAIL" + fi + + rm -rf "$tmpdir" +} + +# Run all tests +test_server_connection_failure +test_multiple_servers_with_failure +test_initialize_failure +test_no_successful_checks + +# Print summary +echo "" +echo "========================================" +echo "Test Summary" +echo "========================================" +echo "Tests run: $TESTS_RUN" +echo "Tests passed: $TESTS_PASSED" +echo "Tests failed: $TESTS_FAILED" +echo "" + +if [ $TESTS_FAILED -gt 0 ]; then + echo -e "${RED}Some tests failed${NC}" + exit 1 +else + echo -e "${GREEN}All tests passed!${NC}" + exit 0 +fi diff --git a/actions/setup/sh/start_mcp_gateway.sh b/actions/setup/sh/start_mcp_gateway.sh index d65159e5850..91043877bde 100755 --- a/actions/setup/sh/start_mcp_gateway.sh +++ b/actions/setup/sh/start_mcp_gateway.sh @@ -235,6 +235,11 @@ if jq -e '.error' /tmp/gh-aw/mcp-config/gateway-output.json >/dev/null 2>&1; the exit 1 fi +# Log gateway version for debugging and troubleshooting +GATEWAY_VERSION=$(jq -r '.gateway.version // "unknown"' /tmp/gh-aw/mcp-config/gateway-output.json 2>/dev/null) +echo "Gateway version: $GATEWAY_VERSION" +echo "" + # Convert gateway output to agent-specific format echo "Converting gateway configuration to agent format..." export MCP_GATEWAY_OUTPUT=/tmp/gh-aw/mcp-config/gateway-output.json