Files
KNEL-AIMiddleware/scripts/validate-all.sh
Charles N Wyble ea3b0907ae feat(scripts): add comprehensive validation script for all MCP/LSP servers
Created scripts/validate-all.sh to validate ALL servers with actual
protocol handshake messages:

Coverage:
- 29 MCP servers (Design, Infrastructure, Content, Communication,
  Analytics, Productivity, Reverse Engineering)
- 4 LSP servers (bash, docker, marksman, terraform-ls)

Features:
- Sends actual MCP initialize handshake to each container
- Sends actual LSP initialize handshake to LSP containers
- Categorizes results: PASSED, FAILED, SKIPPED, NEEDS_ENV,
  NEEDS_SERVICE, TRANSPORT_MISMATCH
- Provides detailed summary with server names and versions
- Color-coded output for easy scanning

Usage:
  ./scripts/validate-all.sh           # Test all servers
  ./scripts/validate-all.sh mcp       # MCP servers only
  ./scripts/validate-all.sh lsp       # LSP servers only
  ./scripts/validate-all.sh <service> # Specific service

This script implements the SDLC requirement for protocol validation.
Every server must pass build + start + protocol handshake to be
considered "working".
2026-02-20 09:24:59 -05:00

349 lines
13 KiB
Bash
Executable File

#!/bin/bash
# Comprehensive MCP/LSP Server Validation Script
# Tests all servers with actual protocol messages
#
# Usage:
# ./scripts/validate-all.sh # Test all servers
# ./scripts/validate-all.sh mcp # Test only MCP servers
# ./scripts/validate-all.sh lsp # Test only LSP servers
# ./scripts/validate-all.sh <service> # Test specific service
set -e
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Protocol messages
MCP_INIT='{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"protocolVersion":"2024-11-05","clientInfo":{"name":"validate-all","version":"1.0.0"}},"id":1}'
LSP_INIT='{"jsonrpc":"2.0","method":"initialize","params":{"processId":null,"capabilities":{},"rootUri":null,"workspaceFolders":null},"id":1}'
# Counters
PASSED=0
FAILED=0
SKIPPED=0
NEEDS_ENV=0
NEEDS_SERVICE=0
TRANSPORT_MISMATCH=0
# Results arrays
declare -a RESULTS_PASS
declare -a RESULTS_FAIL
declare -a RESULTS_SKIP
declare -a RESULTS_ENV
declare -a RESULTS_SERVICE
declare -a RESULTS_TRANSPORT
# MCP Servers configuration (container_name, timeout, env_vars...)
declare -A MCP_SERVERS
MCP_SERVERS=(
# Design & Engineering
["kneldevstack-aimiddleware-blender-mcp"]="10"
["kneldevstack-aimiddleware-freecad-mcp"]="10"
["kneldevstack-aimiddleware-gimp-mcp"]="10"
["kneldevstack-aimiddleware-kicad-mcp"]="10 KICAD_HOST=host.docker.internal KICAD_PORT=5555"
# Hosting & Infrastructure
["kneldevstack-aimiddleware-kubernetes-mcp"]="10"
["kneldevstack-aimiddleware-docker-mcp"]="10"
["kneldevstack-aimiddleware-proxmox-mcp"]="10 PROXMOX_HOST=https://proxmox.example.com PROXMOX_USER=root@pam PROXMOX_TOKEN=dummy PROXMOX_NODE=pve"
["kneldevstack-aimiddleware-terraform-mcp"]="10"
["kneldevstack-aimiddleware-cloudron-mcp"]="10 CLOUDRON_URL=https://cloudron.example.com CLOUDRON_TOKEN=dummy"
# Development Tools
["kneldevstack-aimiddleware-context7-mcp"]="10 UPSTASH_REDIS_REST_URL=https://dummy.upstash.io UPSTASH_REDIS_REST_TOKEN=dummy"
# Content Management
["kneldevstack-aimiddleware-nextcloud-mcp"]="10 NEXTCLOUD_HOST=https://nextcloud.example.com NEXTCLOUD_USERNAME=dummy NEXTCLOUD_PASSWORD=dummy"
["kneldevstack-aimiddleware-ghost-mcp"]="10 GHOST_API_URL=https://ghost.example.com GHOST_ADMIN_API_KEY=012345678901234567890123:0123456789012345678901234567890123456789012345678901234567890123"
["kneldevstack-aimiddleware-docspace-mcp"]="10 DOCSPACE_HOST=https://docspace.example.com DOCSPACE_TOKEN=dummy"
["kneldevstack-aimiddleware-wordpress-mcp"]="10 WORDPRESS_URL=https://wordpress.example.com WORDPRESS_USERNAME=dummy WORDPRESS_APPLICATION_PASSWORD=dummy"
# Communication & Collaboration
["kneldevstack-aimiddleware-discourse-mcp"]="10 DISCOURSE_URL=https://discourse.example.com DISCOURSE_API_KEY=dummy DISCOURSE_API_USERNAME=dummy"
["kneldevstack-aimiddleware-imap-mcp"]="10 IMAP_HOST=imap.example.com IMAP_PORT=993 IMAP_USERNAME=dummy IMAP_PASSWORD=dummy"
["kneldevstack-aimiddleware-postizz-mcp"]="10 POSTIZ_API_KEY=dummy POSTIZ_URL=https://postiz.example.com"
# Analytics & Security
["kneldevstack-aimiddleware-matomo-mcp"]="10 MATOMO_URL=https://matomo.example.com MATOMO_TOKEN=dummy"
["kneldevstack-aimiddleware-bitwarden-mcp"]="15 BITWARDEN_CLIENT_ID=dummy BITWARDEN_CLIENT_SECRET=dummy BITWARDEN_PASSWORD=dummy BITWARDEN_SERVER_URL=https://vault.bitwarden.com"
# Productivity & Automation
["kneldevstack-aimiddleware-audiobook-mcp"]="10"
["kneldevstack-aimiddleware-snipeit-mcp"]="10 SNIPEIT_URL=https://snipeit.example.com SNIPEIT_TOKEN=dummy"
["kneldevstack-aimiddleware-mcp-redmine"]="10 REDMINE_URL=https://redmine.example.com REDMINE_API_KEY=dummy"
["kneldevstack-aimiddleware-mcp-ansible"]="10"
["kneldevstack-aimiddleware-elasticsearch-mcp"]="10 ELASTICSEARCH_URL=http://localhost:9200 ELASTICSEARCH_USERNAME=dummy ELASTICSEARCH_PASSWORD=dummy"
["kneldevstack-aimiddleware-drawio-mcp"]="10"
# Additional Tools
["kneldevstack-aimiddleware-penpot-mcp"]="10 PENPOT_URL=https://design.penpot.app PENPOT_TOKEN=dummy"
["kneldevstack-aimiddleware-webserial-mcp"]="10 WEBSOCKET_URL=ws://host.docker.internal:3000"
# Reverse Engineering
["kneldevstack-aimiddleware-ghidra-mcp"]="15"
["kneldevstack-aimiddleware-reverse-engineering-assistant"]="15"
)
# LSP Servers configuration
declare -A LSP_SERVERS
LSP_SERVERS=(
["kneldevstack-aimiddleware-bash-language-server"]="10"
["kneldevstack-aimiddleware-docker-language-server"]="10"
["kneldevstack-aimiddleware-marksman"]="10"
["kneldevstack-aimiddleware-terraform-ls"]="10"
)
# Test MCP server
test_mcp() {
local container=$1
local timeout=$2
shift 2
local env_vars=("$@")
# Check if image exists
if ! docker image inspect "$container" &>/dev/null; then
echo -e "${YELLOW}SKIP${NC} $container: Image not built"
SKIPPED=$((SKIPPED + 1))
RESULTS_SKIP+=("$container: image not built")
return 2
fi
# Build env args
local env_args=""
for env_var in "${env_vars[@]}"; do
env_args="$env_args -e $env_var"
done
# Run test
local result
result=$( (echo "$MCP_INIT" | timeout "$timeout" docker run --rm -i $env_args "$container" 2>&1) || true )
# Check for valid MCP response
if echo "$result" | grep -q '"result".*"serverInfo"'; then
local server_name version
server_name=$(echo "$result" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4)
version=$(echo "$result" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
echo -e "${GREEN}PASS${NC} $container: $server_name v${version:-unknown}"
PASSED=$((PASSED + 1))
RESULTS_PASS+=("$container: $server_name v${version:-unknown}")
return 0
fi
# Check for specific failure modes
if echo "$result" | grep -qi "environment variable\|missing.*env\|required.*variable"; then
echo -e "${YELLOW}ENV${NC} $container: Needs environment variables"
NEEDS_ENV=$((NEEDS_ENV + 1))
RESULTS_ENV+=("$container")
return 3
fi
if echo "$result" | grep -qi "connection refused\|not connected\|ECONNREFUSED\|ETIMEDOUT\|network unreachable"; then
echo -e "${YELLOW}SVC${NC} $container: Needs backend service"
NEEDS_SERVICE=$((NEEDS_SERVICE + 1))
RESULTS_SERVICE+=("$container")
return 4
fi
if echo "$result" | grep -qi "http\|websocket\|ws://\|transport"; then
echo -e "${BLUE}TRN${NC} $container: Transport mismatch (not stdio)"
TRANSPORT_MISMATCH=$((TRANSPORT_MISMATCH + 1))
RESULTS_TRANSPORT+=("$container")
return 5
fi
# Generic failure
echo -e "${RED}FAIL${NC} $container: No valid response"
FAILED=$((FAILED + 1))
RESULTS_FAIL+=("$container: $(echo "$result" | head -1 | cut -c1-60)")
return 1
}
# Test LSP server
test_lsp() {
local container=$1
local timeout=$2
# Check if image exists
if ! docker image inspect "$container" &>/dev/null; then
echo -e "${YELLOW}SKIP${NC} $container: Image not built"
SKIPPED=$((SKIPPED + 1))
RESULTS_SKIP+=("$container: image not built")
return 2
fi
# Run test
local result
result=$( (echo "$LSP_INIT" | timeout "$timeout" docker run --rm -i "$container" 2>&1) || true )
# Check for valid LSP response
if echo "$result" | grep -q '"result".*"capabilities"'; then
echo -e "${GREEN}PASS${NC} $container: LSP initialized"
PASSED=$((PASSED + 1))
RESULTS_PASS+=("$container: LSP initialized")
return 0
fi
# Generic failure
echo -e "${RED}FAIL${NC} $container: No valid response"
FAILED=$((FAILED + 1))
RESULTS_FAIL+=("$container: $(echo "$result" | head -1 | cut -c1-60)")
return 1
}
# Print summary
print_summary() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}VALIDATION SUMMARY${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo -e "${GREEN}PASSED ($PASSED):${NC}"
if [ ${#RESULTS_PASS[@]} -gt 0 ]; then
for r in "${RESULTS_PASS[@]}"; do
echo "$r"
done
else
echo " (none)"
fi
echo ""
if [ ${#RESULTS_ENV[@]} -gt 0 ]; then
echo -e "${YELLOW}NEEDS ENV VARS ($NEEDS_ENV):${NC}"
for r in "${RESULTS_ENV[@]}"; do
echo "$r"
done
echo ""
fi
if [ ${#RESULTS_SERVICE[@]} -gt 0 ]; then
echo -e "${YELLOW}NEEDS BACKEND SERVICE ($NEEDS_SERVICE):${NC}"
for r in "${RESULTS_SERVICE[@]}"; do
echo "$r"
done
echo ""
fi
if [ ${#RESULTS_TRANSPORT[@]} -gt 0 ]; then
echo -e "${BLUE}TRANSPORT MISMATCH ($TRANSPORT_MISMATCH):${NC}"
for r in "${RESULTS_TRANSPORT[@]}"; do
echo "$r"
done
echo ""
fi
if [ ${#RESULTS_SKIP[@]} -gt 0 ]; then
echo -e "${YELLOW}SKIPPED ($SKIPPED):${NC}"
for r in "${RESULTS_SKIP[@]}"; do
echo " - $r"
done
echo ""
fi
if [ ${#RESULTS_FAIL[@]} -gt 0 ]; then
echo -e "${RED}FAILED ($FAILED):${NC}"
for r in "${RESULTS_FAIL[@]}"; do
echo "$r"
done
echo ""
fi
echo -e "${BLUE}========================================${NC}"
echo -e "Total: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}, ${YELLOW}$SKIPPED skipped${NC}"
echo -e " ${YELLOW}$NEEDS_ENV need env${NC}, ${YELLOW}$NEEDS_SERVICE need service${NC}, ${BLUE}$TRANSPORT_MISMATCH transport mismatch${NC}"
echo -e "${BLUE}========================================${NC}"
# Return non-zero if any hard failures
[ $FAILED -eq 0 ]
}
# Main
main() {
local mode="${1:-all}"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}KNEL-AIMiddleware Validation${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
case "$mode" in
mcp)
echo -e "${BLUE}Testing MCP Servers...${NC}"
echo ""
for container in "${!MCP_SERVERS[@]}"; do
config="${MCP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
env_vars=("${parts[@]:1}")
test_mcp "$container" "$timeout" "${env_vars[@]}" || true
done
;;
lsp)
echo -e "${BLUE}Testing LSP Servers...${NC}"
echo ""
for container in "${!LSP_SERVERS[@]}"; do
config="${LSP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
test_lsp "$container" "$timeout" || true
done
;;
all)
echo -e "${BLUE}Testing MCP Servers...${NC}"
echo ""
for container in "${!MCP_SERVERS[@]}"; do
config="${MCP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
env_vars=("${parts[@]:1}")
test_mcp "$container" "$timeout" "${env_vars[@]}" || true
done
echo ""
echo -e "${BLUE}Testing LSP Servers...${NC}"
echo ""
for container in "${!LSP_SERVERS[@]}"; do
config="${LSP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
test_lsp "$container" "$timeout" || true
done
;;
*)
# Test specific service
if [[ -v MCP_SERVERS["kneldevstack-aimiddleware-$mode"] ]]; then
container="kneldevstack-aimiddleware-$mode"
config="${MCP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
env_vars=("${parts[@]:1}")
test_mcp "$container" "$timeout" "${env_vars[@]}"
elif [[ -v LSP_SERVERS["kneldevstack-aimiddleware-$mode"] ]]; then
container="kneldevstack-aimiddleware-$mode"
config="${LSP_SERVERS[$container]}"
read -ra parts <<< "$config"
timeout="${parts[0]}"
test_lsp "$container" "$timeout"
else
echo -e "${RED}Unknown service: $mode${NC}"
echo "Available MCP services: ${!MCP_SERVERS[@]#kneldevstack-aimiddleware-}"
echo "Available LSP services: ${!LSP_SERVERS[@]#kneldevstack-aimiddleware-}"
exit 1
fi
;;
esac
if [ "$mode" != "all" ] && [ "$mode" != "mcp" ] && [ "$mode" != "lsp" ]; then
# Single service test, no summary
exit $?
fi
print_summary
}
main "$@"