diff --git a/docs/bootstrap-cli.md b/docs/bootstrap-cli.md index 403de43..2b21dc9 100644 --- a/docs/bootstrap-cli.md +++ b/docs/bootstrap-cli.md @@ -1,8 +1,27 @@ # Bootstrap CLI -Le package `bootstrap` reste optionnel : une application peut l'utiliser pour uniformiser le parsing CLI et l'aide, sans imposer de runtime monolithique. +Le package `bootstrap` fournit un point d'entrée CLI uniforme pour les binaires +MCP. Il gère le parsing des arguments, l'aide, les alias et le routage vers les +hooks fournis par l'application. -Exemple minimal : +## Commandes disponibles + +| Commande | Description | +|---|---| +| `setup` | Initialiser ou mettre à jour la configuration locale | +| `login` | Authentifier et déverrouiller Bitwarden pour persister `BW_SESSION` | +| `mcp` | Démarrer le serveur MCP | +| `config show` | Afficher la configuration résolue et la provenance des valeurs | +| `config test` | Vérifier la configuration et la connectivité | +| `config delete` | Supprimer un profil local | +| `update` | Auto-update du binaire | +| `version` | Afficher la version | + +Les commandes sans hook correspondant sont automatiquement masquées de l'aide et +retournent une erreur `ErrUnknownCommand`. Exception : `version` affiche +`Options.Version` si fourni, sans hook. + +## Utilisation ```go func main() { @@ -10,26 +29,22 @@ func main() { BinaryName: "my-mcp", Description: "Client MCP", Version: version, - EnableDoctorAlias: true, // expose `doctor` comme alias de `config test` - AliasDescriptions: map[string]string{ - "doctor": "Diagnostiquer la configuration locale.", - }, + EnableDoctorAlias: true, Hooks: bootstrap.Hooks{ Setup: func(ctx context.Context, inv bootstrap.Invocation) error { return runSetup(ctx, inv.Args) }, - Login: func(ctx context.Context, inv bootstrap.Invocation) error { - return runLogin(ctx, inv.Args) - }, + Login: bootstrap.BitwardenLoginHandler("my-mcp"), MCP: func(ctx context.Context, inv bootstrap.Invocation) error { return runMCP(ctx, inv.Args) }, ConfigShow: func(ctx context.Context, inv bootstrap.Invocation) error { return runConfigShow(ctx, inv.Args) }, - ConfigTest: func(ctx context.Context, inv bootstrap.Invocation) error { - return runConfigTest(ctx, inv.Args) - }, + ConfigTest: bootstrap.StandardConfigTestHandler(bootstrap.StandardConfigTestOptions{ + OpenStore: openStore, + ConnectivityCheck: connectivityCheck, + }), Update: func(ctx context.Context, inv bootstrap.Invocation) error { return runUpdate(ctx, inv.Args) }, @@ -41,11 +56,97 @@ func main() { } ``` -Si `Hooks.Version` n'est pas fourni, la sous-commande `version` affiche automatiquement `Options.Version`. +## Handlers fournis -Sans arguments, `bootstrap.Run` affiche l'aide globale (pas de lancement implicite de `mcp`). -La commande `login` est optionnelle et peut être branchée pour gérer un unlock Bitwarden interactif. -La commande `config` impose une sous-commande (`show`, `test`, et optionnellement `delete`). -Les alias déclarés (ou `EnableDoctorAlias`) sont affichés dans l'aide globale. -Le flag global `--debug` est supporté et active le debug des appels Bitwarden +### `BitwardenLoginHandler` + +```go +bootstrap.BitwardenLoginHandler(binaryName string) bootstrap.Handler +``` + +Handler prêt à l'emploi pour la commande `login` des MCPs qui utilisent le +backend Bitwarden CLI. Il lance le flux interactif `bw unlock --raw`, persiste +`BW_SESSION` dans un fichier `0600` sous le répertoire de config utilisateur, et +confirme le résultat. + +À n'utiliser que si le MCP déclare `secret_store.backend_policy = "bitwarden-cli"` +dans son manifest. Pour les autres backends (`env-only`, `keyring-any`), ne pas +définir de hook `Login` : la commande est automatiquement masquée. + +```go +Hooks: bootstrap.Hooks{ + Login: bootstrap.BitwardenLoginHandler(mcpgen.BinaryName), +} +``` + +### `StandardConfigTestHandler` + +```go +bootstrap.StandardConfigTestHandler(opts bootstrap.StandardConfigTestOptions) bootstrap.Handler +``` + +Handler pour `config test` qui exécute un ensemble de checks standards et affiche +un rapport formaté. Aucun champ n'est obligatoire. + +```go +type StandardConfigTestOptions struct { + ConfigCheck cli.DoctorCheck // cli.NewConfigCheck(store) + OpenStore func() (secretstore.Store, error) // check disponibilité secret store + ConnectivityCheck cli.DoctorCheck // check applicatif (HTTP, IMAP…) + ExtraChecks []cli.DoctorCheck +} +``` + +Checks inclus automatiquement selon les champs fournis : + +| Champ | Check résultant | +|---|---| +| `ConfigCheck` | Fichier de configuration lisible | +| `OpenStore` | Secret store disponible | +| `ConnectivityCheck` | Connectivité applicative | +| `ExtraChecks` | Checks supplémentaires | + +Le `ManifestCheck` n'est pas inclus : le manifest est un artefact de build, pas +une contrainte runtime. + +```go +ConfigTest: bootstrap.StandardConfigTestHandler(bootstrap.StandardConfigTestOptions{ + ConfigCheck: cli.NewConfigCheck(frameworkconfig.NewStore[ProfileConfig]("my-mcp")), + OpenStore: openSecretStore, + ConnectivityCheck: func(ctx context.Context) cli.DoctorResult { + if err := pingBackend(ctx); err != nil { + return cli.DoctorResult{ + Name: "connectivity", + Status: cli.DoctorStatusFail, + Summary: "backend inaccessible", + Detail: err.Error(), + } + } + return cli.DoctorResult{ + Name: "connectivity", + Status: cli.DoctorStatusOK, + Summary: "backend accessible", + } + }, +}), +``` + +Pour un config test applicatif spécifique (appels API, messages ✓/✗), implémenter +un hook `ConfigTest` custom. + +## Options + +| Champ | Description | +|---|---| +| `BinaryName` | Nom du binaire, utilisé dans l'aide et les messages | +| `Description` | Description affichée dans l'aide globale | +| `Version` | Version affichée par `version` sans hook | +| `Args` | Arguments CLI (défaut : `os.Args[1:]`) | +| `Stdin/Stdout/Stderr` | I/O (défaut : `os.Stdin/Stdout/Stderr`) | +| `Aliases` | Alias de commandes | +| `AliasDescriptions` | Descriptions des alias dans l'aide | +| `EnableDoctorAlias` | Active `doctor` comme alias de `config test` | +| `DisabledCommands` | Commandes à masquer explicitement | + +Le flag global `--debug` active le debug des appels Bitwarden (`MCP_FRAMEWORK_BITWARDEN_DEBUG=1`). diff --git a/docs/cli-helpers.md b/docs/cli-helpers.md index 7f59080..fbb5ef8 100644 --- a/docs/cli-helpers.md +++ b/docs/cli-helpers.md @@ -126,8 +126,11 @@ if err := cli.RenderResolutionProvenance(os.Stdout, resolution); err != nil { `ResolveOptions.Order` permet de changer la priorité globale si nécessaire, et `FieldSpec.Sources` permet de définir un ordre spécifique pour un champ. -Le package fournit aussi un socle réutilisable pour une commande `doctor`. -L'application cliente choisit comment exposer la commande (`flag`, `cobra`, etc.), mais peut réutiliser les checks communs et ajouter ses propres hooks : +Le package fournit un socle réutilisable pour une commande `doctor`. +Pour les cas standards (config, secret store, connectivité), préférer +`bootstrap.StandardConfigTestHandler` qui câble `RunDoctor` sans boilerplate. + +Pour un contrôle fin ou un config test impératif, utiliser `RunDoctor` directement : ```go report := cli.RunDoctor(context.Background(), cli.DoctorOptions{ @@ -137,32 +140,19 @@ report := cli.RunDoctor(context.Background(), cli.DoctorOptions{ ServiceName: "my-mcp", }) }), - SecretBackendPolicy: secretstore.BackendBitwardenCLI, - BitwardenOptions: cli.BitwardenDoctorOptions{ - LookupEnv: os.LookupEnv, - }, - RequiredSecrets: []cli.DoctorSecret{ - {Name: "api-token", Label: "API token"}, - }, - SecretStoreFactory: func() (secretstore.Store, error) { - return secretstore.OpenFromManifest(secretstore.OpenFromManifestOptions{ - ServiceName: "my-mcp", - }) - }, - ManifestDir: ".", ConnectivityCheck: func(context.Context) cli.DoctorResult { if err := pingBackend(); err != nil { return cli.DoctorResult{ Name: "connectivity", Status: cli.DoctorStatusFail, - Summary: "backend is unreachable", + Summary: "backend inaccessible", Detail: err.Error(), } } return cli.DoctorResult{ Name: "connectivity", Status: cli.DoctorStatusOK, - Summary: "backend is reachable", + Summary: "backend accessible", } }, }) @@ -176,6 +166,10 @@ if report.HasFailures() { } ``` +`ManifestDir` est optionnel. Quand il est fourni, `RunDoctor` inclut un +`ManifestCheck` qui vérifie la présence et la validité de `mcp.toml` dans ce +répertoire. Ne l'inclure que si ce check est pertinent pour l'application. + Quand `SecretBackendPolicy` vaut `bitwarden-cli`, le check Bitwarden est ajouté automatiquement. Pour le désactiver explicitement :