fix: prevent attach/detach-server from repeating server arg on tab
Added __mcpctl_needs_server_arg guard in fish and position check in bash so completions stop after one server name is selected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,8 @@ _mcpctl() {
|
|||||||
COMPREPLY=($(compgen -W "-i --input -p --password -c --conflict -h --help" -- "$cur"))
|
COMPREPLY=($(compgen -W "-i --input -p --password -c --conflict -h --help" -- "$cur"))
|
||||||
return ;;
|
return ;;
|
||||||
attach-server)
|
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
|
local proj names all_servers proj_servers
|
||||||
proj=$(_mcpctl_get_project_value)
|
proj=$(_mcpctl_get_project_value)
|
||||||
if [[ -n "$proj" ]]; then
|
if [[ -n "$proj" ]]; then
|
||||||
@@ -137,6 +139,8 @@ _mcpctl() {
|
|||||||
COMPREPLY=($(compgen -W "$names" -- "$cur"))
|
COMPREPLY=($(compgen -W "$names" -- "$cur"))
|
||||||
return ;;
|
return ;;
|
||||||
detach-server)
|
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
|
local proj names
|
||||||
proj=$(_mcpctl_get_project_value)
|
proj=$(_mcpctl_get_project_value)
|
||||||
if [[ -n "$proj" ]]; then
|
if [[ -n "$proj" ]]; then
|
||||||
|
|||||||
@@ -159,11 +159,31 @@ complete -c mcpctl -n "__fish_seen_subcommand_from edit; and __mcpctl_needs_reso
|
|||||||
# Resource names — after resource type is selected
|
# Resource names — after resource type is selected
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from get describe delete edit; and not __mcpctl_needs_resource_type" -a '(__mcpctl_resource_names)' -d 'Resource name'
|
complete -c mcpctl -n "__fish_seen_subcommand_from get describe delete edit; and not __mcpctl_needs_resource_type" -a '(__mcpctl_resource_names)' -d 'Resource name'
|
||||||
|
|
||||||
# attach-server: show servers NOT in the project
|
# Helper: check if attach-server/detach-server already has a server argument
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from attach-server" -a '(__mcpctl_available_servers)' -d 'Server'
|
function __mcpctl_needs_server_arg
|
||||||
|
set -l tokens (commandline -opc)
|
||||||
|
set -l found_cmd false
|
||||||
|
for tok in $tokens
|
||||||
|
if $found_cmd
|
||||||
|
if not string match -q -- '-*' $tok
|
||||||
|
return 1 # server arg already present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if contains -- $tok attach-server detach-server
|
||||||
|
set found_cmd true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if $found_cmd
|
||||||
|
return 0 # command found but no server arg yet
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
# detach-server: show servers IN the project
|
# attach-server: show servers NOT in the project (only if no server arg yet)
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from detach-server" -a '(__mcpctl_project_servers)' -d 'Server'
|
complete -c mcpctl -n "__fish_seen_subcommand_from attach-server; and __mcpctl_needs_server_arg" -a '(__mcpctl_available_servers)' -d 'Server'
|
||||||
|
|
||||||
|
# detach-server: show servers IN the project (only if no server arg yet)
|
||||||
|
complete -c mcpctl -n "__fish_seen_subcommand_from detach-server; and __mcpctl_needs_server_arg" -a '(__mcpctl_project_servers)' -d 'Server'
|
||||||
|
|
||||||
# get/describe options
|
# get/describe options
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from get" -s o -l output -d 'Output format' -xa 'table json yaml'
|
complete -c mcpctl -n "__fish_seen_subcommand_from get" -s o -l output -d 'Output format' -xa 'table json yaml'
|
||||||
|
|||||||
@@ -94,19 +94,20 @@ describe('fish completions', () => {
|
|||||||
expect(resourceNamesFn, 'must handle instances via server.name').toContain('.server.name');
|
expect(resourceNamesFn, 'must handle instances via server.name').toContain('.server.name');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('attach-server completes with available (unattached) servers', () => {
|
it('attach-server completes with available (unattached) servers and guards against repeat', () => {
|
||||||
// Find the line that provides argument completions AFTER attach-server is selected
|
|
||||||
const attachLine = fishFile.split('\n').find((l) =>
|
const attachLine = fishFile.split('\n').find((l) =>
|
||||||
l.startsWith('complete') && l.includes('__fish_seen_subcommand_from attach-server'));
|
l.startsWith('complete') && l.includes('__fish_seen_subcommand_from attach-server'));
|
||||||
expect(attachLine, 'attach-server argument completion must exist').toBeDefined();
|
expect(attachLine, 'attach-server argument completion must exist').toBeDefined();
|
||||||
expect(attachLine, 'attach-server must use __mcpctl_available_servers').toContain('__mcpctl_available_servers');
|
expect(attachLine, 'attach-server must use __mcpctl_available_servers').toContain('__mcpctl_available_servers');
|
||||||
|
expect(attachLine, 'attach-server must guard with __mcpctl_needs_server_arg').toContain('__mcpctl_needs_server_arg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detach-server completes with project servers', () => {
|
it('detach-server completes with project servers and guards against repeat', () => {
|
||||||
const detachLine = fishFile.split('\n').find((l) =>
|
const detachLine = fishFile.split('\n').find((l) =>
|
||||||
l.startsWith('complete') && l.includes('__fish_seen_subcommand_from detach-server'));
|
l.startsWith('complete') && l.includes('__fish_seen_subcommand_from detach-server'));
|
||||||
expect(detachLine, 'detach-server argument completion must exist').toBeDefined();
|
expect(detachLine, 'detach-server argument completion must exist').toBeDefined();
|
||||||
expect(detachLine, 'detach-server must use __mcpctl_project_servers').toContain('__mcpctl_project_servers');
|
expect(detachLine, 'detach-server must use __mcpctl_project_servers').toContain('__mcpctl_project_servers');
|
||||||
|
expect(detachLine, 'detach-server must guard with __mcpctl_needs_server_arg').toContain('__mcpctl_needs_server_arg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('non-project commands do not show with --project', () => {
|
it('non-project commands do not show with --project', () => {
|
||||||
@@ -142,16 +143,18 @@ describe('bash completions', () => {
|
|||||||
expect(bashFile).toMatch(/get\|describe\|delete\)[\s\S]*?_mcpctl_resource_names/);
|
expect(bashFile).toMatch(/get\|describe\|delete\)[\s\S]*?_mcpctl_resource_names/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('attach-server filters out already-attached servers using project context', () => {
|
it('attach-server filters out already-attached servers and guards against repeat', () => {
|
||||||
const attachBlock = bashFile.match(/attach-server\)[\s\S]*?return ;;/)?.[0] ?? '';
|
const attachBlock = bashFile.match(/attach-server\)[\s\S]*?return ;;/)?.[0] ?? '';
|
||||||
expect(attachBlock, 'attach-server must use _mcpctl_get_project_value').toContain('_mcpctl_get_project_value');
|
expect(attachBlock, 'attach-server must use _mcpctl_get_project_value').toContain('_mcpctl_get_project_value');
|
||||||
expect(attachBlock, 'attach-server must query project servers to exclude').toContain('--project');
|
expect(attachBlock, 'attach-server must query project servers to exclude').toContain('--project');
|
||||||
|
expect(attachBlock, 'attach-server must check position to prevent repeat').toContain('cword - subcmd_pos');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detach-server shows only project servers', () => {
|
it('detach-server shows only project servers and guards against repeat', () => {
|
||||||
const detachBlock = bashFile.match(/detach-server\)[\s\S]*?return ;;/)?.[0] ?? '';
|
const detachBlock = bashFile.match(/detach-server\)[\s\S]*?return ;;/)?.[0] ?? '';
|
||||||
expect(detachBlock, 'detach-server must use _mcpctl_get_project_value').toContain('_mcpctl_get_project_value');
|
expect(detachBlock, 'detach-server must use _mcpctl_get_project_value').toContain('_mcpctl_get_project_value');
|
||||||
expect(detachBlock, 'detach-server must query project servers').toContain('--project');
|
expect(detachBlock, 'detach-server must query project servers').toContain('--project');
|
||||||
|
expect(detachBlock, 'detach-server must check position to prevent repeat').toContain('cword - subcmd_pos');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('instances use server.name instead of name', () => {
|
it('instances use server.name instead of name', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user