docs: mettre à jour bootstrap-cli et cli-helpers
All checks were successful
CI / test (push) Successful in 12s

Documenter BitwardenLoginHandler, StandardConfigTestHandler et le
comportement opt-in de ManifestCheck dans RunDoctor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thibaud-lclr 2026-05-13 10:55:52 +02:00
parent e6c372bffc
commit f8eb0d3449
2 changed files with 130 additions and 35 deletions

View file

@ -1,8 +1,27 @@
# Bootstrap CLI # 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 ```go
func main() { func main() {
@ -10,26 +29,22 @@ func main() {
BinaryName: "my-mcp", BinaryName: "my-mcp",
Description: "Client MCP", Description: "Client MCP",
Version: version, Version: version,
EnableDoctorAlias: true, // expose `doctor` comme alias de `config test` EnableDoctorAlias: true,
AliasDescriptions: map[string]string{
"doctor": "Diagnostiquer la configuration locale.",
},
Hooks: bootstrap.Hooks{ Hooks: bootstrap.Hooks{
Setup: func(ctx context.Context, inv bootstrap.Invocation) error { Setup: func(ctx context.Context, inv bootstrap.Invocation) error {
return runSetup(ctx, inv.Args) return runSetup(ctx, inv.Args)
}, },
Login: func(ctx context.Context, inv bootstrap.Invocation) error { Login: bootstrap.BitwardenLoginHandler("my-mcp"),
return runLogin(ctx, inv.Args)
},
MCP: func(ctx context.Context, inv bootstrap.Invocation) error { MCP: func(ctx context.Context, inv bootstrap.Invocation) error {
return runMCP(ctx, inv.Args) return runMCP(ctx, inv.Args)
}, },
ConfigShow: func(ctx context.Context, inv bootstrap.Invocation) error { ConfigShow: func(ctx context.Context, inv bootstrap.Invocation) error {
return runConfigShow(ctx, inv.Args) return runConfigShow(ctx, inv.Args)
}, },
ConfigTest: func(ctx context.Context, inv bootstrap.Invocation) error { ConfigTest: bootstrap.StandardConfigTestHandler(bootstrap.StandardConfigTestOptions{
return runConfigTest(ctx, inv.Args) OpenStore: openStore,
}, ConnectivityCheck: connectivityCheck,
}),
Update: func(ctx context.Context, inv bootstrap.Invocation) error { Update: func(ctx context.Context, inv bootstrap.Invocation) error {
return runUpdate(ctx, inv.Args) 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`). ### `BitwardenLoginHandler`
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`). ```go
Les alias déclarés (ou `EnableDoctorAlias`) sont affichés dans l'aide globale. bootstrap.BitwardenLoginHandler(binaryName string) bootstrap.Handler
Le flag global `--debug` est supporté et active le debug des appels Bitwarden ```
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`). (`MCP_FRAMEWORK_BITWARDEN_DEBUG=1`).

View file

@ -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. `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`. Le package fournit 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 : 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 ```go
report := cli.RunDoctor(context.Background(), cli.DoctorOptions{ report := cli.RunDoctor(context.Background(), cli.DoctorOptions{
@ -137,32 +140,19 @@ report := cli.RunDoctor(context.Background(), cli.DoctorOptions{
ServiceName: "my-mcp", 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 { ConnectivityCheck: func(context.Context) cli.DoctorResult {
if err := pingBackend(); err != nil { if err := pingBackend(); err != nil {
return cli.DoctorResult{ return cli.DoctorResult{
Name: "connectivity", Name: "connectivity",
Status: cli.DoctorStatusFail, Status: cli.DoctorStatusFail,
Summary: "backend is unreachable", Summary: "backend inaccessible",
Detail: err.Error(), Detail: err.Error(),
} }
} }
return cli.DoctorResult{ return cli.DoctorResult{
Name: "connectivity", Name: "connectivity",
Status: cli.DoctorStatusOK, 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. Quand `SecretBackendPolicy` vaut `bitwarden-cli`, le check Bitwarden est ajouté automatiquement.
Pour le désactiver explicitement : Pour le désactiver explicitement :