Skip to main content
Glama

Genkit MCP

Official
by firebase
killports13.9 kB
#!/usr/bin/env bash # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # # Kills processes listening on specified TCP ports or port ranges. # Works on macOS and Linux. # # Usage: # killports [<options>] <port_or_range> [<port_or_range>...] # # Options: # -f, --force: Use SIGKILL (kill -9) instead of the default SIGTERM. # -y, --yes: Automatically answer yes to confirmation prompts. # # Arguments: # <port_or_range>: A single TCP port number or a range specified as start..end (inclusive). # # Examples: # killports 8000 8080 # Kills processes on ports 8000 and 8080 with SIGTERM after confirmation # killports 9000..9010 # Kills processes on ports 9000 through 9010 with SIGTERM after confirmation # killports -y 3100..3102 3400 # Kills processes on 3100-3102 and 3400 with SIGTERM, skipping confirmation # killports --force 8000 # Force kills process on port 8000 with SIGKILL, skipping confirmation # killports -y --force 8080 9090 # Force kills processes on 8080 and 9090 with SIGKILL, skipping confirmation # # To test the script, run the following commands to start test servers: # python3 -m http.server 3100 & # python3 -m http.server 3101 & # python3 -m http.server 3102 & # python3 -m http.server 3400 & # # Then run the script with the following command to kill the processes: # killports 3100..3102 3400 # # To force kill the processes, run the following command: # killports --force 3100..3102 3400 # # To skip confirmation, run the following command: # killports -y 3100..3102 3400 # # To force kill and skip confirmation, run the following command: # killports --force -y 3100..3102 3400 #set -x set -euo pipefail DEFAULT_SIGNAL="TERM" # Default signal (TERM allows graceful shutdown) FORCE_SIGNAL="KILL" # Force signal (KILL terminates immediately) # Populated by killports::parse_args declare -A TARGET_PORTS_MAP=() # Associative array for unique target ports FORCE_KILL=0 # 1 if --force is used, 0 otherwise SKIP_CONFIRMATION=0 # 1 if -y/--yes is used, 0 otherwise KILL_SIGNAL=$DEFAULT_SIGNAL # Signal to use (TERM or KILL) # Populated by killports::kill_processes KILLED_COUNT=0 # Number of processes successfully signaled FAILED_COUNT=0 # Number of processes failed to signal # Print usage instructions and exit. killports::usage() { local script_name script_name=$(basename "$0") echo "Usage: $script_name [<options>] <port_or_range> [<port_or_range>...]" >&2 echo "" >&2 echo "Options:" >&2 echo " -f, --force: Use SIGKILL (kill -9) instead of the default SIGTERM." >&2 echo " -y, --yes: Automatically answer yes to confirmation prompts." >&2 echo "" >&2 echo "Arguments:" >&2 echo " <port_or_range>: A single TCP port number or a range specified as start..end (inclusive)." >&2 echo "" >&2 echo "Examples:" >&2 echo " $script_name 8000 8080 # Kills processes on ports 8000 and 8080 with SIGTERM after confirmation" >&2 echo " $script_name 9000..9010 # Kills processes on ports 9000 through 9010 with SIGTERM after confirmation" >&2 echo " $script_name -y 3100..3102 3400 # Kills processes on 3100-3102 and 3400 with SIGTERM, skipping confirmation" >&2 echo " $script_name --force 8000 # Force kills process on port 8000 with SIGKILL, skipping confirmation" >&2 echo " $script_name -y --force 8080 9090 # Force kills processes on 8080 and 9090 with SIGKILL, skipping confirmation" >&2 exit 1 } # Check if a command exists. killports::command_exists() { command -v "$1" >/dev/null 2>&1 } # Parse command line arguments. # Populates global variables: TARGET_PORTS_MAP, FORCE_KILL, SKIP_CONFIRMATION, KILL_SIGNAL. killports::parse_args() { local args_remaining=() local arg port start_port end_port # Separate flags from port/range arguments while [[ $# -gt 0 ]]; do case "$1" in -f | --force) FORCE_KILL=1 KILL_SIGNAL=$FORCE_SIGNAL shift ;; -y | --yes) SKIP_CONFIRMATION=1 shift ;; --) # End of options marker shift args_remaining+=("$@") # Add all remaining arguments break # Stop processing options ;; -*) echo "Error: Unknown option: $1" >&2 killports::usage ;; *) # Not an option, assume it's a port/range args_remaining+=("$1") shift # consume the argument ;; esac done # Validation after Parsing Flags. if [[ ${#args_remaining[@]} -eq 0 ]]; then echo "Error: No ports or ranges specified." >&2 killports::usage fi if [[ $FORCE_KILL -eq 1 ]]; then echo "Using force kill (SIGKILL)." fi if [[ $SKIP_CONFIRMATION -eq 1 && $FORCE_KILL -eq 0 ]]; then echo "Skipping confirmation prompt." fi # Process Port/Range Arguments. for arg in "${args_remaining[@]}"; do if [[ $arg =~ ^([0-9]+)\.\.([0-9]+)$ ]]; then # Range a..b start_port="${BASH_REMATCH[1]}" end_port="${BASH_REMATCH[2]}" if ! [[ $start_port =~ ^[0-9]+$ && $end_port =~ ^[0-9]+$ && $start_port -ge 0 && $start_port -le 65535 && $end_port -ge 0 && $end_port -le 65535 ]]; then echo "Error: Invalid port numbers in range '$arg'. Ports must be between 0 and 65535." >&2 killports::usage fi if ((start_port > end_port)); then echo "Error: Start port ($start_port) cannot be greater than end port ($end_port) in range '$arg'." >&2 killports::usage fi echo "Adding ports from range $start_port to $end_port." for ((port = start_port; port <= end_port; port++)); do TARGET_PORTS_MAP[$port]=1 done elif [[ $arg =~ ^[0-9]+$ ]]; then # Single port port="$arg" if ! [[ $port =~ ^[0-9]+$ && $port -ge 0 && $port -le 65535 ]]; then echo "Error: Invalid port number '$port'. Port must be between 0 and 65535." >&2 killports::usage fi echo "Adding port $port." TARGET_PORTS_MAP[$port]=1 else echo "Error: Invalid argument '$arg'. Must be a port number (0-65535) or a range (e.g., 8000..8010)." >&2 killports::usage fi done # This check should technically be covered by the args_remaining check above, # but keeping it as a safeguard in case of future logic changes. if [[ ${#TARGET_PORTS_MAP[@]} -eq 0 ]]; then echo "Error: No valid ports or ranges were ultimately processed." >&2 killports::usage fi } # Check for required dependencies. killports::check_dependencies() { if ! killports::command_exists lsof; then echo "Error: 'lsof' command not found. Please install it." >&2 echo " On Debian/Ubuntu: sudo apt-get update && sudo apt-get install lsof" >&2 echo " On Fedora/CentOS/RHEL: sudo yum install lsof" >&2 echo " On macOS: 'lsof' is usually pre-installed." >&2 exit 1 fi } # Find processes listening on specified ports. # # Args: # $1: Comma-separated string of sorted target ports. # Outputs: # Prints process info (PID and Command) to stdout. killports::find_processes() { local awk_ports_list=$1 local process_info # Note: On some systems, lsof might need root privileges to see all processes. process_info=$(lsof -i TCP -sTCP:LISTEN -P -n 2>/dev/null | awk -v ports="$awk_ports_list" ' BEGIN { split(ports, port_arr, ","); for (i in port_arr) { target_ports[port_arr[i]] = 1; } # Skip header line explicitly if present (safer than tail) header_skipped = 0 } # Check if line looks like a header and skip NR == 1 && $1 == "COMMAND" && $2 == "PID" { next } { # Extract port from NAME field (column 9) # Matches *:port, 127.0.0.1:port, [::1]:port etc. if (match($9, /[:][0-9]+$/)) { port = substr($9, RSTART + 1) # Extract port number after ":" if (port in target_ports) { # Print PID (col 2) and Command (col 1) print $2 " " $1; } } }' | sort -k1,1n -u) echo "$process_info" } # Confirm and kill processes. # # Args: # $1: Process info string (multiline, PID COMMAND per line). # $2: Kill signal (e.g., TERM, KILL). # $3: Force kill flag (1 for force, 0 for confirmation). # $4: Skip confirmation flag (1 for yes, 0 otherwise). # Populates global variables: KILLED_COUNT, FAILED_COUNT. killports::kill_processes() { local process_info="$1" local signal_to_use="$2" local force_flag="$3" local skip_confirm_flag="$4" local pid cmd_rest confirm local line_count # Count lines/processes found. line_count=$(echo "$process_info" | wc -l) if [[ -z $process_info ]]; then # Should not happen if called after checking process_info, but defensive check. echo "Warning: kill_processes called with no PIDs found." >&2 return fi echo "" echo "Found the following $line_count process(es)/PID(s) listening on the specified ports:" echo "--------------------------------------------------" echo "PID COMMAND" echo "$process_info" echo "--------------------------------------------------" echo "" # Ask for confirmation unless force killing or skipping confirmation. if [[ $force_flag -eq 0 && $skip_confirm_flag -eq 0 ]]; then # Use line_count instead of array length. read -p "Terminate these $line_count process(es) with signal $signal_to_use? [y/N]: " confirm # Convert confirmation to lowercase. confirm=${confirm,,} if [[ $confirm != "y" && $confirm != "yes" ]]; then echo "Aborted by user." exit 0 fi elif [[ $force_flag -eq 1 ]]; then echo "Force killing specified. Skipping confirmation." # No need for an explicit message if only -y was used, parse_args already printed it. fi echo "Attempting to terminate PIDs with signal $signal_to_use..." KILLED_COUNT=0 # Reset counts for this run FAILED_COUNT=0 # Temporarily disable exit-on-error for the loop, as read can exit non-zero at EOF. set +e # Temporarily disable exit on error for the loop. while IFS=' ' read -r pid cmd_rest || [[ -n $pid ]]; do # Handle potential missing newline at EOF. if [[ -z $pid ]]; then continue; fi # Skip empty lines if any. # cmd_rest will contain the command name and any subsequent parts. echo -n "Killing PID $pid ($cmd_rest)... " # Execute kill command. if kill "-${signal_to_use}" "$pid" 2>/dev/null; then echo "Success." ((KILLED_COUNT++)) else # Check if the process still exists before declaring failure. if ps -p "$pid" >/dev/null; then echo "Failed. (Process might require higher privileges)." ((FAILED_COUNT++)) else echo "Failed. (Process likely already exited)." fi fi done <<<"$process_info" set -e # Re-enable exit on error. # Print Summary. killports::print_summary "$KILL_SIGNAL" "$KILLED_COUNT" "$FAILED_COUNT" # Exit Status. # Exit with 0 if all targeted processes were signaled successfully or didn't need signaling. # Exit with 1 if some processes failed to be signaled (might need sudo). if ((FAILED_COUNT > 0)); then exit 1 else exit 0 fi } # Print the final summary. # # Args: # $1: Kill signal used (e.g., TERM, KILL). # $2: Number of processes killed. # $3: Number of processes failed. killports::print_summary() { local signal_used="$1" local killed_num="$2" local failed_num="$3" echo "" echo "Summary:" echo " Successfully sent signal $signal_used to $killed_num process(es)." if ((failed_num > 0)); then echo " Failed to send signal $signal_used to $failed_num process(es)." >&2 echo " (You might need to run this script with 'sudo' for some processes)." >&2 fi } # Run the main function. # # Args: # $@: The command line arguments. main() { # Parse Arguments. # This populates TARGET_PORTS_MAP, FORCE_KILL, SKIP_CONFIRMATION, KILL_SIGNAL. killports::parse_args "$@" # Check Dependencies. killports::check_dependencies # Prepare Port List for Display and Filtering. local target_ports_sorted=() local ports_str="" # Get the unique sorted list of ports as a comma-separated list. mapfile -t target_ports_sorted < <(echo "${!TARGET_PORTS_MAP[@]}" | tr ' ' '\n' | sort -n) ports_str=$( IFS=, echo "${target_ports_sorted[*]}" ) echo "Searching for processes listening on TCP ports: $ports_str ..." # Find Processes. local process_info process_info=$(killports::find_processes "$ports_str") if [[ -z $process_info ]]; then echo "No processes found listening on the specified ports ($ports_str)." exit 0 fi # Kill Processes (includes confirmation logic). # This populates KILLED_COUNT, FAILED_COUNT. killports::kill_processes "$process_info" "$KILL_SIGNAL" "$FORCE_KILL" "$SKIP_CONFIRMATION" # Print Summary killports::print_summary "$KILL_SIGNAL" "$KILLED_COUNT" "$FAILED_COUNT" # Exit Status # - Exit with 0 if all targeted processes were signaled successfully or didn't # need signaling. # - Exit with 1 if some processes failed to be signaled (might need sudo). if ((FAILED_COUNT > 0)); then exit 1 else exit 0 fi } # Run the main function main "$@"

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/firebase/genkit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server