_mcpctl() { local cur prev words cword _init_completion || return local commands="status login logout config get describe delete logs create edit apply backup restore mcp console approve help" local project_commands="attach-server detach-server get describe delete logs create edit help" local global_opts="-v --version --daemon-url --direct --project -h --help" local resources="servers instances secrets templates projects users groups rbac prompts promptrequests" # Check if --project was given local has_project=false local i for ((i=1; i < cword; i++)); do if [[ "${words[i]}" == "--project" ]]; then has_project=true break fi done # Find the first subcommand (skip --project and its argument, skip flags) local subcmd="" local subcmd_pos=0 for ((i=1; i < cword; i++)); do if [[ "${words[i]}" == "--project" || "${words[i]}" == "--daemon-url" ]]; then ((i++)) # skip the argument continue fi if [[ "${words[i]}" != -* ]]; then subcmd="${words[i]}" subcmd_pos=$i break fi done # Find the resource type after get/describe/delete/edit local resource_type="" if [[ -n "$subcmd_pos" ]] && [[ $subcmd_pos -gt 0 ]]; then for ((i=subcmd_pos+1; i < cword; i++)); do if [[ "${words[i]}" != -* ]] && [[ " $resources " == *" ${words[i]} "* ]]; then resource_type="${words[i]}" break fi done fi # If completing the --project value if [[ "$prev" == "--project" ]]; then local names names=$(mcpctl get projects -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null) COMPREPLY=($(compgen -W "$names" -- "$cur")) return fi # Fetch resource names dynamically (jq extracts only top-level names) _mcpctl_resource_names() { local rt="$1" if [[ -n "$rt" ]]; then # Instances don't have a name field — use server.name instead if [[ "$rt" == "instances" ]]; then mcpctl get instances -o json 2>/dev/null | jq -r '.[][].server.name' 2>/dev/null else mcpctl get "$rt" -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null fi fi } # Get the --project value from the command line _mcpctl_get_project_value() { local i for ((i=1; i < cword; i++)); do if [[ "${words[i]}" == "--project" ]] && (( i+1 < cword )); then echo "${words[i+1]}" return fi done } case "$subcmd" in config) if [[ $((cword - subcmd_pos)) -eq 1 ]]; then COMPREPLY=($(compgen -W "view set path reset claude claude-generate setup impersonate help" -- "$cur")) fi return ;; status) COMPREPLY=($(compgen -W "-h --help" -- "$cur")) return ;; login) COMPREPLY=($(compgen -W "--url --email --password -h --help" -- "$cur")) return ;; logout) return ;; mcp) return ;; console) # First arg is project name if [[ $((cword - subcmd_pos)) -eq 1 ]]; then local names names=$(mcpctl get projects -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null) COMPREPLY=($(compgen -W "$names" -- "$cur")) fi return ;; get|describe|delete) if [[ -z "$resource_type" ]]; then COMPREPLY=($(compgen -W "$resources" -- "$cur")) else local names names=$(_mcpctl_resource_names "$resource_type") COMPREPLY=($(compgen -W "$names -o --output -h --help" -- "$cur")) fi return ;; edit) if [[ -z "$resource_type" ]]; then COMPREPLY=($(compgen -W "servers projects" -- "$cur")) else local names names=$(_mcpctl_resource_names "$resource_type") COMPREPLY=($(compgen -W "$names -h --help" -- "$cur")) fi return ;; logs) COMPREPLY=($(compgen -W "--tail --since -f --follow -h --help" -- "$cur")) return ;; create) if [[ $((cword - subcmd_pos)) -eq 1 ]]; then COMPREPLY=($(compgen -W "server secret project user group rbac prompt promptrequest help" -- "$cur")) fi return ;; apply) COMPREPLY=($(compgen -f -- "$cur")) return ;; backup) COMPREPLY=($(compgen -W "-o --output -p --password -h --help" -- "$cur")) return ;; restore) COMPREPLY=($(compgen -W "-i --input -p --password -c --conflict -h --help" -- "$cur")) return ;; attach-server) # Only complete if no server arg given yet (first arg after subcmd) if [[ $((cword - subcmd_pos)) -ne 1 ]]; then return; fi local proj names all_servers proj_servers proj=$(_mcpctl_get_project_value) if [[ -n "$proj" ]]; then all_servers=$(mcpctl get servers -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null) proj_servers=$(mcpctl --project "$proj" get servers -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null) names=$(comm -23 <(echo "$all_servers" | sort) <(echo "$proj_servers" | sort)) else names=$(_mcpctl_resource_names "servers") fi COMPREPLY=($(compgen -W "$names" -- "$cur")) return ;; detach-server) # Only complete if no server arg given yet (first arg after subcmd) if [[ $((cword - subcmd_pos)) -ne 1 ]]; then return; fi local proj names proj=$(_mcpctl_get_project_value) if [[ -n "$proj" ]]; then names=$(mcpctl --project "$proj" get servers -o json 2>/dev/null | jq -r '.[][].name' 2>/dev/null) fi COMPREPLY=($(compgen -W "$names" -- "$cur")) return ;; approve) if [[ -z "$resource_type" ]]; then COMPREPLY=($(compgen -W "promptrequest" -- "$cur")) else local names names=$(_mcpctl_resource_names "$resource_type") COMPREPLY=($(compgen -W "$names" -- "$cur")) fi return ;; help) COMPREPLY=($(compgen -W "$commands" -- "$cur")) return ;; esac # No subcommand yet — offer commands based on context if [[ -z "$subcmd" ]]; then if $has_project; then COMPREPLY=($(compgen -W "$project_commands $global_opts" -- "$cur")) else COMPREPLY=($(compgen -W "$commands $global_opts" -- "$cur")) fi fi } complete -F _mcpctl mcpctl