docs: mettre à jour bootstrap-cli et cli-helpers
All checks were successful
CI / test (push) Successful in 12s
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:
parent
e6c372bffc
commit
f8eb0d3449
2 changed files with 130 additions and 35 deletions
|
|
@ -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`).
|
||||||
|
|
|
||||||
|
|
@ -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 :
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue