Files
mcpctl/completions/mcpctl.bash
Michal 739e679d1a
Some checks failed
CI/CD / lint (pull_request) Successful in 1m7s
CI/CD / test (pull_request) Successful in 1m23s
CI/CD / typecheck (pull_request) Successful in 2m49s
CI/CD / smoke (pull_request) Failing after 1m54s
CI/CD / build (pull_request) Successful in 4m21s
CI/CD / publish (pull_request) Has been skipped
fix(pi): repair the /mcpctl menu, skills target, and typecheck the extension
The pi extension shipped in `src/pi-ext/` was covered by no tsconfig and no
eslint config, so nothing ever checked it against pi's API. Pointing tsc at
the published @earendil-works/pi-coding-agent types found the command surface
to be inert.

Fixes:

- `/mcpctl` did nothing. `ctx.ui.select` takes `string[]` and returns the
  chosen string; it was called with `{value,label}` objects, so the menu
  rendered five `[object Object]` rows and `choice === "status"` never
  matched any branch. Labels are now plain strings mapped back to actions.
- The headless branch returned a status string from a handler typed
  `Promise<void>`; pi drops it. Reports via notify instead.
- "Sync skills" omitted `--agent pi`, writing into ~/.claude/skills — in an
  integration whose stated purpose is to not depend on ~/.claude — and said
  so in its own success message. It also ran execSync with `stdio: "inherit"`,
  painting raw output over pi's TUI, and interpolated the project name into a
  shell string. Now execFile with `--agent pi` and captured output.
- Tool results typed `content[].type` as `string`; pi's AgentToolResult wants
  the `"text"` literal.
- `callTool` asserted `Promise<unknown>` to `ToolCallResult`.
- Sanitising MCP tool names to `[a-z0-9_]` can collide (`docs.search` vs
  `docs-search`). The colliding tool was silently never registered but still
  reported active, so its calls were forwarded to the first tool. Names are
  now disambiguated and tracked with the MCP tool they forward to.
- `registerWithPi` rewrote settings.json even when nothing changed. Since
  parsing strips `//` comments, a no-op run destroyed them.

Guards, so this class of bug can't return:

- `src/pi-ext/tsconfig.json` checks the extension against the real published
  pi types (dev dependency, not a shim — a shim drifting from the published
  API is the exact failure being guarded). Wired into `pnpm typecheck`.
- eslint now covers `src/pi-ext/*.ts` like every other source file.
- A test fails if the embedded copy in `config/pi-extension.ts` is stale;
  editing the sources without regenerating silently shipped old code.

Also: the branch added `config pi` without regenerating shell completions
(the committed-completions test was failing), and the doc advertised
`mcpctl pi sync-skills`, which does not exist. Both corrected, plus a note
on the session-token vs `mcpctl_pat_` bearer difference that would bite
against an authenticated `mcplocal serve`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017BMXdb2qZbPSh8Q7XpTyjB
2026-08-08 17:30:42 +01:00

516 lines
19 KiB
Bash

# mcpctl bash completions — auto-generated by scripts/generate-completions.ts
# DO NOT EDIT MANUALLY — run: pnpm completions:generate
_mcpctl() {
local cur prev words cword
_init_completion || return
local commands="status login logout config get describe delete logs create edit apply chat chat-llm patch passwd errors backup approve review skills console cache provider test migrate rotate"
local project_commands="get describe delete logs create edit attach-server detach-server favourites"
local global_opts="-v --version --daemon-url --direct -p --project -h --help"
local resources="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks all"
local resource_aliases="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks all server srv instance inst secret sec secretbackend sb llm agent personality template tpl project proj user group rbac-definition rbac-binding prompt promptrequest pr serverattachment sa proxymodel pm task tasks inference-task"
# Check if --project/-p was given
local has_project=false
local i
for ((i=1; i < cword; i++)); do
if [[ "${words[i]}" == "--project" || "${words[i]}" == "-p" ]]; then
has_project=true
break
fi
done
# Find the first subcommand
local subcmd=""
local subcmd_pos=0
for ((i=1; i < cword; i++)); do
if [[ "${words[i]}" == "--project" || "${words[i]}" == "--daemon-url" || "${words[i]}" == "-p" ]]; then
((i++))
continue
fi
if [[ "${words[i]}" != -* ]]; then
subcmd="${words[i]}"
subcmd_pos=$i
break
fi
done
# Find the resource type after resource commands
local resource_type=""
if [[ -n "$subcmd_pos" ]] && [[ $subcmd_pos -gt 0 ]]; then
for ((i=subcmd_pos+1; i < cword; i++)); do
if [[ "${words[i]}" != -* ]] && [[ " $resource_aliases " == *" ${words[i]} "* ]]; then
resource_type="${words[i]}"
break
fi
done
fi
# Helper: get --project/-p value
_mcpctl_get_project_value() {
local i
for ((i=1; i < cword; i++)); do
if [[ "${words[i]}" == "--project" || "${words[i]}" == "-p" ]] && (( i+1 < cword )); then
echo "${words[i+1]}"
return
fi
done
}
# Helper: fetch resource names
_mcpctl_resource_names() {
local rt="$1"
if [[ -n "$rt" ]]; then
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
}
# Helper: find sub-subcommand (for config/create)
_mcpctl_get_subcmd() {
local parent_pos="$1"
local i
for ((i=parent_pos+1; i < cword; i++)); do
if [[ "${words[i]}" != -* ]]; then
echo "${words[i]}"
return
fi
done
}
# If completing option values
if [[ "$prev" == "--project" || "$prev" == "-p" ]]; 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
case "$subcmd" in
status)
COMPREPLY=($(compgen -W "-o --output -h --help" -- "$cur"))
return ;;
login)
COMPREPLY=($(compgen -W "--mcpd-url -h --help" -- "$cur"))
return ;;
logout)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
return ;;
config)
local config_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$config_sub" ]]; then
COMPREPLY=($(compgen -W "view set path reset claude claude-generate pi prime-agent prime-agent-generate setup impersonate help" -- "$cur"))
else
case "$config_sub" in
view)
COMPREPLY=($(compgen -W "-o --output -h --help" -- "$cur"))
;;
set)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
path)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
reset)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
claude)
COMPREPLY=($(compgen -W "-p --project -o --output --inspect --stdout --skip-skills -h --help" -- "$cur"))
;;
claude-generate)
COMPREPLY=($(compgen -W "-p --project -o --output --inspect --stdout --skip-skills -h --help" -- "$cur"))
;;
pi)
COMPREPLY=($(compgen -W "-p --project --extension-dir --skip-skills --settings --pi-dir -h --help" -- "$cur"))
;;
prime-agent)
COMPREPLY=($(compgen -W "-p --project -o --output --gateway-url --token --skip-skills --skip-extension --skip-marker --dry-run -h --help" -- "$cur"))
;;
prime-agent-generate)
COMPREPLY=($(compgen -W "-p --project -o --output --gateway-url --token --skip-skills --skip-extension --skip-marker --dry-run -h --help" -- "$cur"))
;;
setup)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
impersonate)
COMPREPLY=($(compgen -W "--quit -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
get)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "$resources -o --output -p --project -A --all -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names -o --output -p --project -A --all -h --help" -- "$cur"))
fi
return ;;
describe)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "$resources -o --output --show-values -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names -o --output --show-values -h --help" -- "$cur"))
fi
return ;;
delete)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "$resources -p --project --agent -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names -p --project --agent -h --help" -- "$cur"))
fi
return ;;
logs)
if [[ $((cword - subcmd_pos)) -eq 1 ]]; then
local names
names=$(mcpctl get instances -o json 2>/dev/null | jq -r '.[][].server.name' 2>/dev/null)
COMPREPLY=($(compgen -W "$names -t --tail -i --instance -h --help" -- "$cur"))
else
COMPREPLY=($(compgen -W "-t --tail -i --instance -h --help" -- "$cur"))
fi
return ;;
create)
local create_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$create_sub" ]]; then
COMPREPLY=($(compgen -W "server secret llm agent secretbackend project user group rbac mcptoken prompt skill personality serverattachment promptrequest help" -- "$cur"))
else
case "$create_sub" in
server)
COMPREPLY=($(compgen -W "-d --description --package-name --runtime --docker-image --transport --repository-url --external-url --command --container-port --replicas --env --from-template --env-from-secret --force -h --help" -- "$cur"))
;;
secret)
COMPREPLY=($(compgen -W "--data --force -h --help" -- "$cur"))
;;
llm)
COMPREPLY=($(compgen -W "--type --model --url --tier --description --api-key-ref --extra --pool-name --visibility --force --skip-auth-check -h --help" -- "$cur"))
;;
agent)
COMPREPLY=($(compgen -W "--llm --project --description --system-prompt --system-prompt-file --proxy-model --default-temperature --default-top-p --default-top-k --default-max-tokens --default-seed --default-stop --default-extra --default-params-file --visibility --force -h --help" -- "$cur"))
;;
secretbackend)
COMPREPLY=($(compgen -W "--type --description --default --url --namespace --mount --path-prefix --auth --token-secret --role --auth-mount --sa-token-path --config --wizard --setup-token --policy-name --token-role --no-promote-default --force -h --help" -- "$cur"))
;;
project)
COMPREPLY=($(compgen -W "-d --description --proxy-model --prompt --llm --llm-model --gated --no-gated --server --favourite --favourite-index --no-favourite-index --max-favourites --force -h --help" -- "$cur"))
;;
user)
COMPREPLY=($(compgen -W "--password --name --force -h --help" -- "$cur"))
;;
group)
COMPREPLY=($(compgen -W "--description --member --force -h --help" -- "$cur"))
;;
rbac)
COMPREPLY=($(compgen -W "--subject --roleBindings --force -h --help" -- "$cur"))
;;
mcptoken)
COMPREPLY=($(compgen -W "-p --project --rbac --bind --ttl --description --force -h --help" -- "$cur"))
;;
prompt)
COMPREPLY=($(compgen -W "-p --project --agent --content --content-file --priority --link -h --help" -- "$cur"))
;;
skill)
COMPREPLY=($(compgen -W "-p --project --agent --content --content-file --description --priority --semver --metadata-file --files-dir -h --help" -- "$cur"))
;;
personality)
COMPREPLY=($(compgen -W "--agent --description --priority -h --help" -- "$cur"))
;;
serverattachment)
COMPREPLY=($(compgen -W "-p --project -h --help" -- "$cur"))
;;
promptrequest)
COMPREPLY=($(compgen -W "-p --project --content --content-file --priority -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
edit)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "servers secrets projects groups rbac prompts promptrequests personalities --bump --semver --note -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names --bump --semver --note -h --help" -- "$cur"))
fi
return ;;
apply)
COMPREPLY=($(compgen -f -W "-f --file --dry-run -h --help" -- "$cur"))
return ;;
chat)
if [[ $((cword - subcmd_pos)) -eq 1 ]]; then
local names
names=$(_mcpctl_resource_names "agents")
COMPREPLY=($(compgen -W "$names -p --project --allow-secrets -m --message --thread --system --system-file --system-append --personality --temperature --top-p --top-k --max-tokens --seed --stop --allow-tool --extra --no-stream -h --help" -- "$cur"))
else
COMPREPLY=($(compgen -W "-p --project --allow-secrets -m --message --thread --system --system-file --system-append --personality --temperature --top-p --top-k --max-tokens --seed --stop --allow-tool --extra --no-stream -h --help" -- "$cur"))
fi
return ;;
chat-llm)
if [[ $((cword - subcmd_pos)) -eq 1 ]]; then
local names
names=$(_mcpctl_resource_names "llms")
COMPREPLY=($(compgen -W "$names -m --message --system --temperature --max-tokens --no-stream --async -h --help" -- "$cur"))
else
COMPREPLY=($(compgen -W "-m --message --system --temperature --max-tokens --no-stream --async -h --help" -- "$cur"))
fi
return ;;
patch)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "$resources -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names -h --help" -- "$cur"))
fi
return ;;
passwd)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
return ;;
errors)
COMPREPLY=($(compgen -W "-n --limit -h --help" -- "$cur"))
return ;;
backup)
local backup_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$backup_sub" ]]; then
COMPREPLY=($(compgen -W "log restore help" -- "$cur"))
else
case "$backup_sub" in
log)
COMPREPLY=($(compgen -W "-n --limit -h --help" -- "$cur"))
;;
restore)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
attach-server)
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)
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 -h --help" -- "$cur"))
else
local names
names=$(_mcpctl_resource_names "$resource_type")
COMPREPLY=($(compgen -W "$names -h --help" -- "$cur"))
fi
return ;;
favourites)
local favourites_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$favourites_sub" ]]; then
COMPREPLY=($(compgen -W "suggest list help" -- "$cur"))
else
case "$favourites_sub" in
suggest)
COMPREPLY=($(compgen -W "--top --window -h --help" -- "$cur"))
;;
list)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
review)
local review_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$review_sub" ]]; then
COMPREPLY=($(compgen -W "pending next show approve reject diff help" -- "$cur"))
else
case "$review_sub" in
pending)
COMPREPLY=($(compgen -W "--type -h --help" -- "$cur"))
;;
next)
COMPREPLY=($(compgen -W "--type -h --help" -- "$cur"))
;;
show)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
approve)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
reject)
COMPREPLY=($(compgen -W "--reason -h --help" -- "$cur"))
;;
diff)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
skills)
local skills_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$skills_sub" ]]; then
COMPREPLY=($(compgen -W "sync help" -- "$cur"))
else
case "$skills_sub" in
sync)
COMPREPLY=($(compgen -W "-p --project --agent --dry-run --force --quiet --skip-postinstall --keep-orphans -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
mcp)
COMPREPLY=($(compgen -W "-p --project -h --help" -- "$cur"))
return ;;
console)
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 --stdin-mcp --audit -h --help" -- "$cur"))
else
COMPREPLY=($(compgen -W "--stdin-mcp --audit -h --help" -- "$cur"))
fi
return ;;
cache)
local cache_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$cache_sub" ]]; then
COMPREPLY=($(compgen -W "stats clear help" -- "$cur"))
else
case "$cache_sub" in
stats)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
clear)
COMPREPLY=($(compgen -W "--older-than -y --yes -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
provider)
local provider_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$provider_sub" ]]; then
COMPREPLY=($(compgen -W "status up down disable enable help" -- "$cur"))
else
case "$provider_sub" in
status)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
up)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
down)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
disable)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
enable)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
test)
local test_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$test_sub" ]]; then
COMPREPLY=($(compgen -W "mcp help" -- "$cur"))
else
case "$test_sub" in
mcp)
COMPREPLY=($(compgen -W "--token --tool --args --expect-tools --timeout -o --output --no-health -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
migrate)
local migrate_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$migrate_sub" ]]; then
COMPREPLY=($(compgen -W "secrets help" -- "$cur"))
else
case "$migrate_sub" in
secrets)
COMPREPLY=($(compgen -W "--from --to --names --keep-source --dry-run -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
rotate)
local rotate_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$rotate_sub" ]]; then
COMPREPLY=($(compgen -W "secretbackend help" -- "$cur"))
else
case "$rotate_sub" in
secretbackend)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
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