diff --git a/scripts/test-n8n-integration.sh b/scripts/test-n8n-integration.sh index 16e75a4..3f42861 100755 --- a/scripts/test-n8n-integration.sh +++ b/scripts/test-n8n-integration.sh @@ -9,6 +9,7 @@ echo "๐Ÿš€ Starting n8n integration test environment..." GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' +BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration @@ -19,6 +20,117 @@ AUTH_TOKEN="test-token-for-n8n-testing-minimum-32-chars" # n8n data directory for persistence N8N_DATA_DIR="$HOME/.n8n-mcp-test" +# Function to detect OS +detect_os() { + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if [ -f /etc/os-release ]; then + . /etc/os-release + echo "$ID" + else + echo "linux" + fi + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo "macos" + elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then + echo "windows" + else + echo "unknown" + fi +} + +# Function to check if Docker is installed +check_docker() { + if command -v docker &> /dev/null; then + echo -e "${GREEN}โœ… Docker is installed${NC}" + # Check if Docker daemon is running + if ! docker info &> /dev/null; then + echo -e "${YELLOW}โš ๏ธ Docker is installed but not running${NC}" + echo -e "${YELLOW}Please start Docker and run this script again${NC}" + exit 1 + fi + return 0 + else + return 1 + fi +} + +# Function to install Docker based on OS +install_docker() { + local os=$(detect_os) + echo -e "${YELLOW}๐Ÿ“ฆ Docker is not installed. Attempting to install...${NC}" + + case $os in + "ubuntu"|"debian") + echo -e "${BLUE}Installing Docker on Ubuntu/Debian...${NC}" + echo "This requires sudo privileges." + sudo apt-get update + sudo apt-get install -y ca-certificates curl gnupg + sudo install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg + sudo chmod a+r /etc/apt/keyrings/docker.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + sudo usermod -aG docker $USER + echo -e "${GREEN}โœ… Docker installed successfully${NC}" + echo -e "${YELLOW}โš ๏ธ Please log out and back in for group changes to take effect${NC}" + ;; + "fedora"|"rhel"|"centos") + echo -e "${BLUE}Installing Docker on Fedora/RHEL/CentOS...${NC}" + echo "This requires sudo privileges." + sudo dnf -y install dnf-plugins-core + sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + sudo systemctl start docker + sudo systemctl enable docker + sudo usermod -aG docker $USER + echo -e "${GREEN}โœ… Docker installed successfully${NC}" + echo -e "${YELLOW}โš ๏ธ Please log out and back in for group changes to take effect${NC}" + ;; + "macos") + echo -e "${BLUE}Installing Docker on macOS...${NC}" + if command -v brew &> /dev/null; then + echo "Installing Docker Desktop via Homebrew..." + brew install --cask docker + echo -e "${GREEN}โœ… Docker Desktop installed${NC}" + echo -e "${YELLOW}โš ๏ธ Please start Docker Desktop from Applications${NC}" + else + echo -e "${RED}โŒ Homebrew not found${NC}" + echo "Please install Docker Desktop manually from:" + echo "https://www.docker.com/products/docker-desktop/" + fi + ;; + "windows") + echo -e "${RED}โŒ Windows detected${NC}" + echo "Please install Docker Desktop manually from:" + echo "https://www.docker.com/products/docker-desktop/" + ;; + *) + echo -e "${RED}โŒ Unknown operating system: $os${NC}" + echo "Please install Docker manually from https://docs.docker.com/get-docker/" + ;; + esac + + # If we installed Docker on Linux, we need to restart for group changes + if [[ "$os" == "ubuntu" ]] || [[ "$os" == "debian" ]] || [[ "$os" == "fedora" ]] || [[ "$os" == "rhel" ]] || [[ "$os" == "centos" ]]; then + echo -e "${YELLOW}Please run 'newgrp docker' or log out and back in, then run this script again${NC}" + exit 0 + fi + + exit 1 +} + +# Check for Docker +if ! check_docker; then + install_docker +fi + +# Check for jq (optional but recommended) +if ! command -v jq &> /dev/null; then + echo -e "${YELLOW}โš ๏ธ jq is not installed (optional)${NC}" + echo -e "${YELLOW} Install it for pretty JSON output in tests${NC}" +fi + # Function to cleanup on exit cleanup() { echo -e "\n${YELLOW}๐Ÿงน Cleaning up...${NC}" @@ -87,12 +199,50 @@ for i in {1..30}; do sleep 1 done +# Guide user to get API key +echo -e "\n${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" +echo -e "${YELLOW}๐Ÿ”‘ n8n API Key Setup${NC}" +echo -e "${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" +echo -e "\nTo enable n8n management tools, you need to create an API key:" +echo -e "\n${GREEN}Steps:${NC}" +echo -e " 1. Open n8n in your browser: ${BLUE}http://localhost:${N8N_PORT}${NC}" +echo -e " 2. Click on your user menu (top right)" +echo -e " 3. Go to 'Settings'" +echo -e " 4. Navigate to 'API'" +echo -e " 5. Click 'Create API Key'" +echo -e " 6. Give it a name (e.g., 'n8n-mcp')" +echo -e " 7. Copy the generated API key" +echo -e "\n${YELLOW}Note: If this is your first time, you'll need to create an account first.${NC}" +echo -e "${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" + +# Wait for API key input +echo -e "\n${YELLOW}Please paste your n8n API key here (or press Enter to skip):${NC}" +read -r N8N_API_KEY + +# Check if API key was provided +if [ -z "$N8N_API_KEY" ]; then + echo -e "${YELLOW}โš ๏ธ No API key provided. n8n management tools will not be available.${NC}" + echo -e "${YELLOW} You can still use documentation and search tools.${NC}" + N8N_API_KEY="" + N8N_API_URL="" +else + echo -e "${GREEN}โœ… API key received${NC}" + # Set the API URL for localhost access (MCP server runs on host, not in Docker) + N8N_API_URL="http://localhost:${N8N_PORT}/api/v1" +fi + # Start MCP server echo -e "\n${GREEN}๐Ÿš€ Starting MCP server in n8n mode...${NC}" +if [ -n "$N8N_API_KEY" ]; then + echo -e "${YELLOW} With n8n management tools enabled${NC}" +fi + N8N_MODE=true \ MCP_MODE=http \ AUTH_TOKEN="${AUTH_TOKEN}" \ PORT=${MCP_PORT} \ +N8N_API_KEY="${N8N_API_KEY}" \ +N8N_API_URL="${N8N_API_URL}" \ node dist/mcp/index.js > /tmp/mcp-server.log 2>&1 & MCP_PID=$! @@ -137,6 +287,30 @@ curl -s -X POST http://localhost:${MCP_PORT}/mcp \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}},"id":1}' \ | jq '.' || echo "(Install jq for pretty JSON output)" +# Test available tools +echo -e "\n${YELLOW}๐Ÿงช Checking available MCP tools...${NC}" +if [ -n "$N8N_API_KEY" ]; then + echo -e "${GREEN}โœ… n8n Management Tools Available:${NC}" + echo " โ€ข n8n_list_workflows - List all workflows" + echo " โ€ข n8n_get_workflow - Get workflow details" + echo " โ€ข n8n_create_workflow - Create new workflows" + echo " โ€ข n8n_update_workflow - Update existing workflows" + echo " โ€ข n8n_delete_workflow - Delete workflows" + echo " โ€ข n8n_trigger_webhook_workflow - Trigger webhook workflows" + echo " โ€ข n8n_list_executions - List workflow executions" + echo " โ€ข And more..." +else + echo -e "${YELLOW}โš ๏ธ n8n Management Tools NOT Available${NC}" + echo " To enable, restart with an n8n API key" +fi + +echo -e "\n${GREEN}โœ… Documentation Tools Always Available:${NC}" +echo " โ€ข list_nodes - List available n8n nodes" +echo " โ€ข search_nodes - Search for specific nodes" +echo " โ€ข get_node_info - Get detailed node information" +echo " โ€ข validate_node_operation - Validate node configurations" +echo " โ€ข And many more..." + echo -e "\n${GREEN}โœ… Setup complete!${NC}" echo -e "\n๐Ÿ“ Next steps:" echo -e " 1. Open n8n at http://localhost:${N8N_PORT}" @@ -145,7 +319,7 @@ echo -e " 3. Add MCP Client Tool node" echo -e " 4. Configure it with:" echo -e " โ€ข Transport: HTTP" echo -e " โ€ข URL: http://host.docker.internal:${MCP_PORT}/mcp" -echo -e " โ€ข Auth: Bearer ${AUTH_TOKEN}" +echo -e " โ€ข Auth Token: ${BLUE}${AUTH_TOKEN}${NC}" echo -e "\n${YELLOW}Press Ctrl+C to stop both services${NC}" echo -e "\n${YELLOW}๐Ÿ“‹ To monitor MCP logs: tail -f /tmp/mcp-server.log${NC}" echo -e "${YELLOW}๐Ÿ“‹ To monitor n8n logs: docker logs -f n8n-test${NC}" diff --git a/tests/unit/http-server-session-management.test.ts b/tests/unit/http-server-session-management.test.ts index b0bc85d..22d627d 100644 --- a/tests/unit/http-server-session-management.test.ts +++ b/tests/unit/http-server-session-management.test.ts @@ -518,12 +518,18 @@ describe('HTTP Server Session Management', () => { }); it('should allow default token when NODE_ENV is not set', () => { - delete process.env.NODE_ENV; + const originalNodeEnv = process.env.NODE_ENV; + delete (process.env as any).NODE_ENV; process.env.AUTH_TOKEN = 'REPLACE_THIS_AUTH_TOKEN_32_CHARS_MIN_abcdefgh'; expect(() => { new SingleSessionHTTPServer(); }).not.toThrow(); + + // Restore original value + if (originalNodeEnv !== undefined) { + process.env.NODE_ENV = originalNodeEnv; + } }); }); @@ -1037,11 +1043,12 @@ describe('HTTP Server Session Management', () => { expect(sessionInfo.sessions).toHaveProperty('sessionIds'); expect(typeof sessionInfo.active).toBe('boolean'); - expect(typeof sessionInfo.sessions.total).toBe('number'); - expect(typeof sessionInfo.sessions.active).toBe('number'); - expect(typeof sessionInfo.sessions.expired).toBe('number'); - expect(sessionInfo.sessions.max).toBe(100); - expect(Array.isArray(sessionInfo.sessions.sessionIds)).toBe(true); + expect(sessionInfo.sessions).toBeDefined(); + expect(typeof sessionInfo.sessions!.total).toBe('number'); + expect(typeof sessionInfo.sessions!.active).toBe('number'); + expect(typeof sessionInfo.sessions!.expired).toBe('number'); + expect(sessionInfo.sessions!.max).toBe(100); + expect(Array.isArray(sessionInfo.sessions!.sessionIds)).toBe(true); }); it('should show legacy SSE session when present', async () => {