diff --git a/examples/localcowork/scripts/smoke-test.sh b/examples/localcowork/scripts/smoke-test.sh index a50d101..9517cef 100755 --- a/examples/localcowork/scripts/smoke-test.sh +++ b/examples/localcowork/scripts/smoke-test.sh @@ -60,297 +60,312 @@ SAVE_RESULTS=false # ── Parse CLI Arguments ────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do - case $1 in - --contract) - RUN_HEALTH=false; RUN_TOOLS=false; shift ;; - --health) - RUN_CONTRACT=false; RUN_TOOLS=false; shift ;; - --tools) - RUN_CONTRACT=false; RUN_HEALTH=false; shift ;; - --server) - FILTER_SERVER="$2"; shift 2 ;; - --save) - SAVE_RESULTS=true; shift ;; - --help|-h) - head -26 "$0" | tail -18 - exit 0 - ;; - *) - echo "Unknown option: $1"; exit 1 ;; - esac + case $1 in + --contract) + RUN_HEALTH=false + RUN_TOOLS=false + shift + ;; + --health) + RUN_CONTRACT=false + RUN_TOOLS=false + shift + ;; + --tools) + RUN_CONTRACT=false + RUN_HEALTH=false + shift + ;; + --server) + FILTER_SERVER="$2" + shift 2 + ;; + --save) + SAVE_RESULTS=true + shift + ;; + --help | -h) + head -26 "$0" | tail -18 + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac done # ── Helper Functions ───────────────────────────────────────────────────── print_header() { - echo "" - echo -e "${BOLD}═══════════════════════════════════════════════════${NC}" - echo -e "${BOLD} $1${NC}" - echo -e "${BOLD}═══════════════════════════════════════════════════${NC}" - echo "" + echo "" + echo -e "${BOLD}═══════════════════════════════════════════════════${NC}" + echo -e "${BOLD} $1${NC}" + echo -e "${BOLD}═══════════════════════════════════════════════════${NC}" + echo "" } print_result() { - local status="$1" - local name="$2" - local detail="${3:-}" - ((TOTAL++)) - if [ "$status" = "PASS" ]; then - ((PASSED++)) - echo -e " ${GREEN}✅ PASS${NC} $name" - elif [ "$status" = "FAIL" ]; then - ((FAILED++)) - echo -e " ${RED}❌ FAIL${NC} $name" - if [ -n "$detail" ]; then - echo -e " ${RED}$detail${NC}" - fi - elif [ "$status" = "SKIP" ]; then - ((SKIPPED++)) - echo -e " ${YELLOW}⏭️ SKIP${NC} $name ${YELLOW}($detail)${NC}" - fi + local status="$1" + local name="$2" + local detail="${3:-}" + ((TOTAL++)) + if [ "$status" = "PASS" ]; then + ((PASSED++)) + echo -e " ${GREEN}✅ PASS${NC} $name" + elif [ "$status" = "FAIL" ]; then + ((FAILED++)) + echo -e " ${RED}❌ FAIL${NC} $name" + if [ -n "$detail" ]; then + echo -e " ${RED}$detail${NC}" + fi + elif [ "$status" = "SKIP" ]; then + ((SKIPPED++)) + echo -e " ${YELLOW}⏭️ SKIP${NC} $name ${YELLOW}($detail)${NC}" + fi } # List all implemented servers (those with at least an src/ directory and entry point) find_implemented_servers() { - local servers=() - for server_dir in "$MCP_DIR"/*/; do - local server_name - server_name=$(basename "$server_dir") - - # Skip _shared directory - [ "$server_name" = "_shared" ] && continue - - # Filter by server name if specified - if [ -n "$FILTER_SERVER" ] && [ "$server_name" != "$FILTER_SERVER" ]; then - continue - fi - - # Check for an entry point (TS or Python) - if [ -f "$server_dir/src/index.ts" ] || [ -f "$server_dir/src/__init__.py" ] || [ -f "$server_dir/src/main.py" ]; then - servers+=("$server_name") - fi - done - echo "${servers[@]}" + local servers=() + for server_dir in "$MCP_DIR"/*/; do + local server_name + server_name=$(basename "$server_dir") + + # Skip _shared directory + [ "$server_name" = "_shared" ] && continue + + # Filter by server name if specified + if [ -n "$FILTER_SERVER" ] && [ "$server_name" != "$FILTER_SERVER" ]; then + continue + fi + + # Check for an entry point (TS or Python) + if [ -f "$server_dir/src/index.ts" ] || [ -f "$server_dir/src/__init__.py" ] || [ -f "$server_dir/src/main.py" ]; then + servers+=("$server_name") + fi + done + echo "${servers[@]}" } # Detect server language detect_language() { - local server_name="$1" - local server_dir="$MCP_DIR/$server_name" - if [ -f "$server_dir/package.json" ] || [ -f "$server_dir/tsconfig.json" ]; then - echo "ts" - elif [ -f "$server_dir/pyproject.toml" ] || [ -f "$server_dir/requirements.txt" ] || [ -f "$server_dir/setup.py" ]; then - echo "py" - else - echo "unknown" - fi + local server_name="$1" + local server_dir="$MCP_DIR/$server_name" + if [ -f "$server_dir/package.json" ] || [ -f "$server_dir/tsconfig.json" ]; then + echo "ts" + elif [ -f "$server_dir/pyproject.toml" ] || [ -f "$server_dir/requirements.txt" ] || [ -f "$server_dir/setup.py" ]; then + echo "py" + else + echo "unknown" + fi } # Count tool files in a server count_tool_files() { - local server_name="$1" - local server_dir="$MCP_DIR/$server_name/src/tools" - if [ -d "$server_dir" ]; then - find "$server_dir" -type f \( -name '*.ts' -o -name '*.py' \) ! -name '__init__*' ! -name 'index.*' | wc -l | tr -d ' ' - else - echo "0" - fi + local server_name="$1" + local server_dir="$MCP_DIR/$server_name/src/tools" + if [ -d "$server_dir" ]; then + find "$server_dir" -type f \( -name '*.ts' -o -name '*.py' \) ! -name '__init__*' ! -name 'index.*' | wc -l | tr -d ' ' + else + echo "0" + fi } # ── Phase 1: Contract Tests ───────────────────────────────────────────── run_contract_tests() { - print_header "Phase 1: Contract Compliance" - echo -e " ${CYAN}Validating tool signatures against docs/mcp-tool-registry.yaml${NC}" - echo "" - - local servers - servers=$(find_implemented_servers) - - if [ -z "$servers" ]; then - print_result "SKIP" "Contract tests" "No implemented servers found" - return - fi - - # Check if registry exists - if [ ! -f "$REGISTRY" ]; then - print_result "FAIL" "Registry check" "docs/mcp-tool-registry.yaml not found" - return - fi - - # Run the contract validator if it exists - if [ -f "$SMOKE_DIR/contract-validator.ts" ]; then - local output - if output=$(cd "$PROJECT_ROOT" && npx tsx "$SMOKE_DIR/contract-validator.ts" $servers 2>&1); then - # Parse output — each line is "PASS|FAIL|SKIP server.tool detail" - while IFS= read -r line; do - if [[ "$line" =~ ^(PASS|FAIL|SKIP)[[:space:]]+(.*) ]]; then - local status="${BASH_REMATCH[1]}" - local rest="${BASH_REMATCH[2]}" - print_result "$status" "$rest" - fi - done <<< "$output" - else - print_result "FAIL" "Contract validator" "Script exited with error" - echo "$output" | head -5 | while IFS= read -r line; do echo " $line"; done - fi - else - # Fallback: basic structural checks - for server_name in $servers; do - local tool_count - tool_count=$(count_tool_files "$server_name") - if [ "$tool_count" -gt 0 ]; then - print_result "PASS" "$server_name — $tool_count tool file(s) present" - else - print_result "FAIL" "$server_name — no tool files in src/tools/" - fi - done - fi + print_header "Phase 1: Contract Compliance" + echo -e " ${CYAN}Validating tool signatures against docs/mcp-tool-registry.yaml${NC}" + echo "" + + local servers + servers=$(find_implemented_servers) + + if [ -z "$servers" ]; then + print_result "SKIP" "Contract tests" "No implemented servers found" + return + fi + + # Check if registry exists + if [ ! -f "$REGISTRY" ]; then + print_result "FAIL" "Registry check" "docs/mcp-tool-registry.yaml not found" + return + fi + + # Run the contract validator if it exists + if [ -f "$SMOKE_DIR/contract-validator.ts" ]; then + local output + if output=$(cd "$PROJECT_ROOT" && npx tsx "$SMOKE_DIR/contract-validator.ts" "$servers" 2>&1); then + # Parse output — each line is "PASS|FAIL|SKIP server.tool detail" + while IFS= read -r line; do + if [[ "$line" =~ ^(PASS|FAIL|SKIP)[[:space:]]+(.*) ]]; then + local status="${BASH_REMATCH[1]}" + local rest="${BASH_REMATCH[2]}" + print_result "$status" "$rest" + fi + done <<<"$output" + else + print_result "FAIL" "Contract validator" "Script exited with error" + echo "$output" | head -5 | while IFS= read -r line; do echo " $line"; done + fi + else + # Fallback: basic structural checks + for server_name in $servers; do + local tool_count + tool_count=$(count_tool_files "$server_name") + if [ "$tool_count" -gt 0 ]; then + print_result "PASS" "$server_name — $tool_count tool file(s) present" + else + print_result "FAIL" "$server_name — no tool files in src/tools/" + fi + done + fi } # ── Phase 2: Server Health ─────────────────────────────────────────────── run_health_tests() { - print_header "Phase 2: Server Health" - echo -e " ${CYAN}Checking each server can start and respond to initialize${NC}" - echo "" - - local servers - servers=$(find_implemented_servers) - - if [ -z "$servers" ]; then - print_result "SKIP" "Health tests" "No implemented servers found" - return - fi - - # Run the health checker if it exists - if [ -f "$SMOKE_DIR/server-health.ts" ]; then - local output - if output=$(cd "$PROJECT_ROOT" && npx tsx "$SMOKE_DIR/server-health.ts" $servers 2>&1); then - while IFS= read -r line; do - if [[ "$line" =~ ^(PASS|FAIL|SKIP)[[:space:]]+(.*) ]]; then - local status="${BASH_REMATCH[1]}" - local rest="${BASH_REMATCH[2]}" - print_result "$status" "$rest" - fi - done <<< "$output" - else - print_result "FAIL" "Health checker" "Script exited with error" - fi - else - # Fallback: check entry points exist - for server_name in $servers; do - local lang - lang=$(detect_language "$server_name") - if [ "$lang" = "ts" ] && [ -f "$MCP_DIR/$server_name/src/index.ts" ]; then - print_result "PASS" "$server_name — entry point exists (index.ts)" - elif [ "$lang" = "py" ] && { [ -f "$MCP_DIR/$server_name/src/__init__.py" ] || [ -f "$MCP_DIR/$server_name/src/main.py" ]; }; then - print_result "PASS" "$server_name — entry point exists (__init__.py)" - else - print_result "FAIL" "$server_name — no entry point found" - fi - done - fi + print_header "Phase 2: Server Health" + echo -e " ${CYAN}Checking each server can start and respond to initialize${NC}" + echo "" + + local servers + servers=$(find_implemented_servers) + + if [ -z "$servers" ]; then + print_result "SKIP" "Health tests" "No implemented servers found" + return + fi + + # Run the health checker if it exists + if [ -f "$SMOKE_DIR/server-health.ts" ]; then + local output + if output=$(cd "$PROJECT_ROOT" && npx tsx "$SMOKE_DIR/server-health.ts" "$servers" 2>&1); then + while IFS= read -r line; do + if [[ "$line" =~ ^(PASS|FAIL|SKIP)[[:space:]]+(.*) ]]; then + local status="${BASH_REMATCH[1]}" + local rest="${BASH_REMATCH[2]}" + print_result "$status" "$rest" + fi + done <<<"$output" + else + print_result "FAIL" "Health checker" "Script exited with error" + fi + else + # Fallback: check entry points exist + for server_name in $servers; do + local lang + lang=$(detect_language "$server_name") + if [ "$lang" = "ts" ] && [ -f "$MCP_DIR/$server_name/src/index.ts" ]; then + print_result "PASS" "$server_name — entry point exists (index.ts)" + elif [ "$lang" = "py" ] && { [ -f "$MCP_DIR/$server_name/src/__init__.py" ] || [ -f "$MCP_DIR/$server_name/src/main.py" ]; }; then + print_result "PASS" "$server_name — entry point exists (__init__.py)" + else + print_result "FAIL" "$server_name — no entry point found" + fi + done + fi } # ── Phase 3: Per-Tool Smoke Tests ─────────────────────────────────────── run_tool_smoke_tests() { - print_header "Phase 3: Per-Tool Smoke Tests" - echo -e " ${CYAN}Running *.smoke.test.ts and *_smoke_test.py files${NC}" - echo "" - - local ts_smoke_tests=() - local py_smoke_tests=() - local search_dir="$MCP_DIR" - - # If filtering by server, narrow the search - if [ -n "$FILTER_SERVER" ]; then - search_dir="$MCP_DIR/$FILTER_SERVER" - fi - - # Discover TypeScript smoke tests - while IFS= read -r -d '' file; do - ts_smoke_tests+=("$file") - done < <(find "$search_dir" -name '*.smoke.test.ts' -print0 2>/dev/null) - - # Discover Python smoke tests - while IFS= read -r -d '' file; do - py_smoke_tests+=("$file") - done < <(find "$search_dir" -name '*_smoke_test.py' -print0 2>/dev/null) - - # Also check tests/smoke/ for project-level smoke tests - while IFS= read -r -d '' file; do - ts_smoke_tests+=("$file") - done < <(find "$SMOKE_DIR" -name '*.smoke.test.ts' -print0 2>/dev/null) - - while IFS= read -r -d '' file; do - py_smoke_tests+=("$file") - done < <(find "$SMOKE_DIR" -name '*_smoke_test.py' -print0 2>/dev/null) - - local found_any=false - - # Run TypeScript smoke tests - if [ ${#ts_smoke_tests[@]} -gt 0 ]; then - found_any=true - echo -e " ${CYAN}TypeScript smoke tests: ${#ts_smoke_tests[@]} file(s)${NC}" - for test_file in "${ts_smoke_tests[@]}"; do - local relative - relative=$(realpath --relative-to="$PROJECT_ROOT" "$test_file") - if (cd "$PROJECT_ROOT" && npx vitest run "$relative" --reporter=dot 2>&1 | tail -1 | grep -q "passed"); then - print_result "PASS" "$relative" - else - print_result "FAIL" "$relative" "vitest failed — run directly for details" - fi - done - fi - - # Run Python smoke tests - if [ ${#py_smoke_tests[@]} -gt 0 ]; then - found_any=true - echo -e " ${CYAN}Python smoke tests: ${#py_smoke_tests[@]} file(s)${NC}" - for test_file in "${py_smoke_tests[@]}"; do - local relative - relative=$(realpath --relative-to="$PROJECT_ROOT" "$test_file") - if (cd "$PROJECT_ROOT" && python -m pytest "$relative" -x -q 2>&1 | tail -1 | grep -q "passed"); then - print_result "PASS" "$relative" - else - print_result "FAIL" "$relative" "pytest failed — run directly for details" - fi - done - fi - - if [ "$found_any" = false ]; then - print_result "SKIP" "Per-tool smoke tests" "No *.smoke.test.ts or *_smoke_test.py files found" - fi + print_header "Phase 3: Per-Tool Smoke Tests" + echo -e " ${CYAN}Running *.smoke.test.ts and *_smoke_test.py files${NC}" + echo "" + + local ts_smoke_tests=() + local py_smoke_tests=() + local search_dir="$MCP_DIR" + + # If filtering by server, narrow the search + if [ -n "$FILTER_SERVER" ]; then + search_dir="$MCP_DIR/$FILTER_SERVER" + fi + + # Discover TypeScript smoke tests + while IFS= read -r -d '' file; do + ts_smoke_tests+=("$file") + done < <(find "$search_dir" -name '*.smoke.test.ts' -print0 2>/dev/null) + + # Discover Python smoke tests + while IFS= read -r -d '' file; do + py_smoke_tests+=("$file") + done < <(find "$search_dir" -name '*_smoke_test.py' -print0 2>/dev/null) + + # Also check tests/smoke/ for project-level smoke tests + while IFS= read -r -d '' file; do + ts_smoke_tests+=("$file") + done < <(find "$SMOKE_DIR" -name '*.smoke.test.ts' -print0 2>/dev/null) + + while IFS= read -r -d '' file; do + py_smoke_tests+=("$file") + done < <(find "$SMOKE_DIR" -name '*_smoke_test.py' -print0 2>/dev/null) + + local found_any=false + + # Run TypeScript smoke tests + if [ ${#ts_smoke_tests[@]} -gt 0 ]; then + found_any=true + echo -e " ${CYAN}TypeScript smoke tests: ${#ts_smoke_tests[@]} file(s)${NC}" + for test_file in "${ts_smoke_tests[@]}"; do + local relative + relative=$(realpath --relative-to="$PROJECT_ROOT" "$test_file") + if (cd "$PROJECT_ROOT" && npx vitest run "$relative" --reporter=dot 2>&1 | tail -1 | grep -q "passed"); then + print_result "PASS" "$relative" + else + print_result "FAIL" "$relative" "vitest failed — run directly for details" + fi + done + fi + + # Run Python smoke tests + if [ ${#py_smoke_tests[@]} -gt 0 ]; then + found_any=true + echo -e " ${CYAN}Python smoke tests: ${#py_smoke_tests[@]} file(s)${NC}" + for test_file in "${py_smoke_tests[@]}"; do + local relative + relative=$(realpath --relative-to="$PROJECT_ROOT" "$test_file") + if (cd "$PROJECT_ROOT" && python -m pytest "$relative" -x -q 2>&1 | tail -1 | grep -q "passed"); then + print_result "PASS" "$relative" + else + print_result "FAIL" "$relative" "pytest failed — run directly for details" + fi + done + fi + + if [ "$found_any" = false ]; then + print_result "SKIP" "Per-tool smoke tests" "No *.smoke.test.ts or *_smoke_test.py files found" + fi } # ── Results Summary ────────────────────────────────────────────────────── print_summary() { - print_header "Smoke Test Summary" - - echo -e " Total: ${BOLD}$TOTAL${NC}" - echo -e " Passed: ${GREEN}$PASSED${NC}" - echo -e " Failed: ${RED}$FAILED${NC}" - echo -e " Skipped: ${YELLOW}$SKIPPED${NC}" - echo "" - - if [ "$FAILED" -eq 0 ] && [ "$TOTAL" -gt 0 ]; then - echo -e " ${GREEN}${BOLD}All smoke tests passed!${NC}" - elif [ "$TOTAL" -eq 0 ]; then - echo -e " ${YELLOW}${BOLD}No tests were run.${NC}" - else - echo -e " ${RED}${BOLD}$FAILED test(s) failed. Fix before pushing.${NC}" - fi - echo "" + print_header "Smoke Test Summary" + + echo -e " Total: ${BOLD}$TOTAL${NC}" + echo -e " Passed: ${GREEN}$PASSED${NC}" + echo -e " Failed: ${RED}$FAILED${NC}" + echo -e " Skipped: ${YELLOW}$SKIPPED${NC}" + echo "" + + if [ "$FAILED" -eq 0 ] && [ "$TOTAL" -gt 0 ]; then + echo -e " ${GREEN}${BOLD}All smoke tests passed!${NC}" + elif [ "$TOTAL" -eq 0 ]; then + echo -e " ${YELLOW}${BOLD}No tests were run.${NC}" + else + echo -e " ${RED}${BOLD}$FAILED test(s) failed. Fix before pushing.${NC}" + fi + echo "" } save_results() { - mkdir -p "$RESULTS_DIR" - local result_file="$RESULTS_DIR/$TIMESTAMP.json" + mkdir -p "$RESULTS_DIR" + local result_file="$RESULTS_DIR/$TIMESTAMP.json" - cat > "$result_file" <"$result_file" </dev/null || echo 'unknown')", @@ -362,8 +377,8 @@ save_results() { "success": $([ "$FAILED" -eq 0 ] && echo "true" || echo "false") } EOF - echo -e " Results saved to: ${CYAN}$result_file${NC}" - echo "" + echo -e " Results saved to: ${CYAN}$result_file${NC}" + echo "" } # ── Main ───────────────────────────────────────────────────────────────── @@ -372,7 +387,7 @@ print_header "LocalCowork Smoke Test Suite" echo -e " ${CYAN}Registry:${NC} $REGISTRY" echo -e " ${CYAN}Servers:${NC} $MCP_DIR" if [ -n "$FILTER_SERVER" ]; then - echo -e " ${CYAN}Filter:${NC} $FILTER_SERVER only" + echo -e " ${CYAN}Filter:${NC} $FILTER_SERVER only" fi [ "$RUN_CONTRACT" = true ] && run_contract_tests @@ -385,9 +400,9 @@ print_summary # Exit with appropriate code if [ "$FAILED" -gt 0 ]; then - exit 1 + exit 1 elif [ "$TOTAL" -eq 0 ]; then - exit 2 + exit 2 else - exit 0 + exit 0 fi diff --git a/examples/localcowork/scripts/validate-mcp-servers.sh b/examples/localcowork/scripts/validate-mcp-servers.sh index e5d1a37..48c8109 100755 --- a/examples/localcowork/scripts/validate-mcp-servers.sh +++ b/examples/localcowork/scripts/validate-mcp-servers.sh @@ -22,9 +22,9 @@ echo "════════════════════════ echo "" if [ ! -f "$REGISTRY" ]; then - echo -e "${RED}❌ Registry not found: $REGISTRY${NC}" - echo " Run from project root." - exit 1 + echo -e "${RED}❌ Registry not found: $REGISTRY${NC}" + echo " Run from project root." + exit 1 fi # TypeScript servers @@ -36,99 +36,98 @@ ALL_SERVERS="$TS_SERVERS $PY_SERVERS" # If a specific server was requested if [ $# -ge 1 ]; then - ALL_SERVERS="$1" + ALL_SERVERS="$1" fi total_tools=0 -implemented_tools=0 total_tests=0 servers_complete=0 servers_total=0 for server in $ALL_SERVERS; do - servers_total=$((servers_total + 1)) - server_dir="$MCP_DIR/$server" - echo -e "${BLUE}── $server ──${NC}" - - # Check if server directory exists - if [ ! -d "$server_dir" ]; then - echo -e " ${RED}❌ Directory not found: $server_dir${NC}" - continue - fi - - # Check for entry point - if [ -f "$server_dir/src/index.ts" ]; then - echo -e " ${GREEN}✅ Entry point: src/index.ts${NC}" - lang="ts" - elif [ -f "$server_dir/src/__init__.py" ]; then - echo -e " ${GREEN}✅ Entry point: src/__init__.py${NC}" - lang="py" - else - echo -e " ${YELLOW}⚠️ No entry point found (src/index.ts or src/__init__.py)${NC}" - lang="unknown" - fi - - # Count tool files - if [ "$lang" = "ts" ]; then - tool_files=$(find "$server_dir/src/tools" -name "*.ts" 2>/dev/null | wc -l | tr -d ' ') - test_files=$(find "$server_dir/tests" -name "*.test.ts" 2>/dev/null | wc -l | tr -d ' ') - elif [ "$lang" = "py" ]; then - tool_files=$(find "$server_dir/src/tools" -name "*.py" -not -name "__init__.py" 2>/dev/null | wc -l | tr -d ' ') - test_files=$(find "$server_dir/tests" -name "test_*.py" 2>/dev/null | wc -l | tr -d ' ') - else - tool_files=0 - test_files=0 - fi - - echo " Tools implemented: $tool_files" - echo " Test files: $test_files" - - total_tools=$((total_tools + tool_files)) - total_tests=$((total_tests + test_files)) - - # Run type check - if [ "$lang" = "ts" ] && [ -f "$server_dir/tsconfig.json" ]; then - if npx tsc --noEmit --project "$server_dir/tsconfig.json" 2>/dev/null; then - echo -e " ${GREEN}✅ Type check passes${NC}" - else - echo -e " ${RED}❌ Type check failed${NC}" - fi - elif [ "$lang" = "py" ]; then - if mypy --strict "$server_dir/src/" 2>/dev/null; then - echo -e " ${GREEN}✅ mypy passes${NC}" - else - echo -e " ${YELLOW}⚠️ mypy check skipped or failed${NC}" - fi - fi - - # Run tests - if [ "$lang" = "ts" ] && [ "$test_files" -gt 0 ]; then - if cd "$server_dir" && npx vitest run --reporter=dot 2>/dev/null; then - echo -e " ${GREEN}✅ Tests pass${NC}" - servers_complete=$((servers_complete + 1)) - else - echo -e " ${RED}❌ Tests failed${NC}" - fi - cd - > /dev/null - elif [ "$lang" = "py" ] && [ "$test_files" -gt 0 ]; then - if cd "$server_dir" && pytest tests/ --quiet 2>/dev/null; then - echo -e " ${GREEN}✅ Tests pass${NC}" - servers_complete=$((servers_complete + 1)) - else - echo -e " ${RED}❌ Tests failed${NC}" - fi - cd - > /dev/null - else - echo -e " ${YELLOW}⚠️ No tests to run${NC}" - fi - - # Check for TODO markers - todo_count=$(grep -r "TODO" "$server_dir/src/" 2>/dev/null | wc -l | tr -d ' ') - if [ "$todo_count" -gt 0 ]; then - echo -e " ${YELLOW}🔨 $todo_count TODO markers remaining${NC}" - fi - - echo "" + servers_total=$((servers_total + 1)) + server_dir="$MCP_DIR/$server" + echo -e "${BLUE}── $server ──${NC}" + + # Check if server directory exists + if [ ! -d "$server_dir" ]; then + echo -e " ${RED}❌ Directory not found: $server_dir${NC}" + continue + fi + + # Check for entry point + if [ -f "$server_dir/src/index.ts" ]; then + echo -e " ${GREEN}✅ Entry point: src/index.ts${NC}" + lang="ts" + elif [ -f "$server_dir/src/__init__.py" ]; then + echo -e " ${GREEN}✅ Entry point: src/__init__.py${NC}" + lang="py" + else + echo -e " ${YELLOW}⚠️ No entry point found (src/index.ts or src/__init__.py)${NC}" + lang="unknown" + fi + + # Count tool files + if [ "$lang" = "ts" ]; then + tool_files=$(find "$server_dir/src/tools" -name "*.ts" 2>/dev/null | wc -l | tr -d ' ') + test_files=$(find "$server_dir/tests" -name "*.test.ts" 2>/dev/null | wc -l | tr -d ' ') + elif [ "$lang" = "py" ]; then + tool_files=$(find "$server_dir/src/tools" -name "*.py" -not -name "__init__.py" 2>/dev/null | wc -l | tr -d ' ') + test_files=$(find "$server_dir/tests" -name "test_*.py" 2>/dev/null | wc -l | tr -d ' ') + else + tool_files=0 + test_files=0 + fi + + echo " Tools implemented: $tool_files" + echo " Test files: $test_files" + + total_tools=$((total_tools + tool_files)) + total_tests=$((total_tests + test_files)) + + # Run type check + if [ "$lang" = "ts" ] && [ -f "$server_dir/tsconfig.json" ]; then + if npx tsc --noEmit --project "$server_dir/tsconfig.json" 2>/dev/null; then + echo -e " ${GREEN}✅ Type check passes${NC}" + else + echo -e " ${RED}❌ Type check failed${NC}" + fi + elif [ "$lang" = "py" ]; then + if mypy --strict "$server_dir/src/" 2>/dev/null; then + echo -e " ${GREEN}✅ mypy passes${NC}" + else + echo -e " ${YELLOW}⚠️ mypy check skipped or failed${NC}" + fi + fi + + # Run tests + if [ "$lang" = "ts" ] && [ "$test_files" -gt 0 ]; then + if cd "$server_dir" && npx vitest run --reporter=dot 2>/dev/null; then + echo -e " ${GREEN}✅ Tests pass${NC}" + servers_complete=$((servers_complete + 1)) + else + echo -e " ${RED}❌ Tests failed${NC}" + fi + cd - >/dev/null + elif [ "$lang" = "py" ] && [ "$test_files" -gt 0 ]; then + if cd "$server_dir" && pytest tests/ --quiet 2>/dev/null; then + echo -e " ${GREEN}✅ Tests pass${NC}" + servers_complete=$((servers_complete + 1)) + else + echo -e " ${RED}❌ Tests failed${NC}" + fi + cd - >/dev/null + else + echo -e " ${YELLOW}⚠️ No tests to run${NC}" + fi + + # Check for TODO markers + todo_count=$(grep -r "TODO" "$server_dir/src/" 2>/dev/null | wc -l | tr -d ' ') + if [ "$todo_count" -gt 0 ]; then + echo -e " ${YELLOW}🔨 $todo_count TODO markers remaining${NC}" + fi + + echo "" done # ── Summary ──────────────────────────────────────────────────────────────── @@ -143,8 +142,8 @@ echo " Test files: $total_tests total" echo "" if [ "$servers_complete" -eq "$servers_total" ]; then - echo -e "${GREEN}✅ All servers passing!${NC}" + echo -e "${GREEN}✅ All servers passing!${NC}" else - remaining=$((servers_total - servers_complete)) - echo -e "${YELLOW}⚠️ $remaining servers need work${NC}" + remaining=$((servers_total - servers_complete)) + echo -e "${YELLOW}⚠️ $remaining servers need work${NC}" fi