1616"""
1717
1818# Standard
19- from enum import Enum
2019from typing import Any , Dict , Optional
2120
2221# Third-Party
2322import typer
2423
2524# First-Party
2625from cforge .common import (
26+ CaseInsensitiveEnum ,
2727 AuthenticationError ,
2828 CLIError ,
2929 get_console ,
3434)
3535
3636
37- class _CaseInsensitiveEnum (str , Enum ):
38- """Enum that supports case-insensitive parsing for CLI options."""
39-
40- @classmethod
41- def _missing_ (cls , value : object ) -> Optional ["_CaseInsensitiveEnum" ]:
42- """Resolve unknown values by matching enum values case-insensitively.
43-
44- Typer converts CLI strings into Enum members. Implementing `_missing_`
45- allows `--mode EnFoRcE` to resolve to `PluginMode.ENFORCE`, while still
46- rejecting unknown values.
47- """
48- if not isinstance (value , str ):
49- return None
50- value_folded = value .casefold ()
51- for member in cls :
52- if member .value .casefold () == value_folded :
53- return member
54- return None
55-
56-
57- class PluginMode (_CaseInsensitiveEnum ):
37+ class PluginMode (CaseInsensitiveEnum ):
5838 """Valid plugin mode filters supported by the gateway admin API."""
5939
6040 ENFORCE = "enforce"
6141 PERMISSIVE = "permissive"
6242 DISABLED = "disabled"
6343
6444
65- def _handle_plugins_exception (exception : Exception ) -> None :
45+ def _parse_plugin_mode (mode : Optional [str ]) -> Optional [PluginMode ]:
46+ """Parse plugin mode with case-insensitive enum matching."""
47+ if mode is None :
48+ return None
49+ try :
50+ return PluginMode (mode )
51+ except ValueError as exc :
52+ choices = ", " .join (member .value for member in PluginMode )
53+ raise CLIError (f"Invalid value for '--mode': { mode !r} . Must be one of: { choices } ." ) from exc
54+
55+
56+ def _handle_plugins_exception (exception : Exception , operation : str , plugin_name : Optional [str ] = None ) -> None :
6657 """Provide plugin-specific hints and raise a CLI error."""
6758 console = get_console ()
6859
6960 if isinstance (exception , AuthenticationError ):
7061 console .print ("[yellow]Access denied. Requires admin.plugins permission.[/yellow]" )
71- elif isinstance (exception , CLIError ) and "(404)" in str (exception ):
72- console .print ("[yellow]Admin plugin API unavailable. Ensure MCPGATEWAY_ADMIN_API_ENABLED=true and gateway version supports /admin/plugins.[/yellow]" )
62+ elif isinstance (exception , CLIError ):
63+ error_str = str (exception )
64+ if "(404)" in error_str :
65+ error_str_folded = error_str .casefold ()
66+ if operation == "get" and "plugin" in error_str_folded and "not found" in error_str_folded :
67+ plugin_label = plugin_name or "requested plugin"
68+ console .print (f"[yellow]Plugin not found: { plugin_label } [/yellow]" )
69+ else :
70+ console .print ("[yellow]Admin plugin API unavailable. Ensure MCPGATEWAY_ADMIN_API_ENABLED=true and gateway version supports /admin/plugins.[/yellow]" )
7371
7472 handle_exception (exception )
7573
7674
7775def plugins_list (
7876 search : Optional [str ] = typer .Option (None , "--search" , help = "Search by plugin name, description, or author" ),
79- mode : Optional [PluginMode ] = typer .Option (None , "--mode" , help = "Filter by mode" ),
77+ mode : Optional [str ] = typer .Option (None , "--mode" , help = "Filter by mode" ),
8078 hook : Optional [str ] = typer .Option (None , "--hook" , help = "Filter by hook type" ),
8179 tag : Optional [str ] = typer .Option (None , "--tag" , help = "Filter by plugin tag" ),
8280 json_output : bool = typer .Option (False , "--json" , help = "Output as JSON" ),
@@ -88,8 +86,9 @@ def plugins_list(
8886 params : Dict [str , Any ] = {}
8987 if search :
9088 params ["search" ] = search
91- if mode :
92- params ["mode" ] = mode .value
89+ parsed_mode = _parse_plugin_mode (mode )
90+ if parsed_mode :
91+ params ["mode" ] = parsed_mode .value
9392 if hook :
9493 params ["hook" ] = hook
9594 if tag :
@@ -108,7 +107,7 @@ def plugins_list(
108107 console .print ("[yellow]No plugins found[/yellow]" )
109108
110109 except Exception as e :
111- _handle_plugins_exception (e )
110+ _handle_plugins_exception (e , operation = "list" )
112111
113112
114113def plugins_get (
@@ -120,7 +119,7 @@ def plugins_get(
120119 print_json (result , f"Plugin { name } " )
121120
122121 except Exception as e :
123- _handle_plugins_exception (e )
122+ _handle_plugins_exception (e , operation = "get" , plugin_name = name )
124123
125124
126125def plugins_stats () -> None :
@@ -130,4 +129,4 @@ def plugins_stats() -> None:
130129 print_json (result , "Plugin Statistics" )
131130
132131 except Exception as e :
133- _handle_plugins_exception (e )
132+ _handle_plugins_exception (e , operation = "stats" )
0 commit comments