Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Configuration is layered, with precedence **flags > `TIGER_*` env vars > config
- **Pass what you read down the call chain.** Hand the `*config.Config` (plus client and project ID where needed) to the functions that need them rather than reloading. Don't pass the App into `internal/common` helpers — they take the specific values they use, which keeps them usable from both CLI and MCP.
- **The App owns flag precedence.** `config.Load` binds the flags listed in `flagBindings` (`internal/config/config.go`) against the flag set it's given, so a flag bound for one command never leaks into another, and a command that doesn't define a flag simply skips it.
- **Flags that override config values** must be added to `flagBindings` and declared *without* a bound variable (`cmd.Flags().String(...)`, or `Var(new(outputFlag), ...)` for validating flag types), so the only way to read the value is through the config. A variable in scope is an invitation to read the raw flag instead, silently bypassing the env var and config file. Flags that aren't config values stay plain local variables; one that only needs an env-var fallback can read the env var directly (see `--new-password` in `service_update_password.go`).
- **Internal config keys are private.** Keys in `privateDefaultValues` (`internal/config/config.go`) — the endpoint URLs — are settable like any other but hidden from `tiger config list`, wherever their value comes from, unless the hidden `--all` flag is given, and never offered by shell completion. Public keys go in `publicDefaultValues`.
- **Config-derived state lives on the `Config` too.** Credential storage is a set of methods on it (`cfg.StoreCredentials`, `cfg.GetStoredCredentials`, `cfg.RemoveCredentials`), keyed off `cfg.ConfigDir`, and `cfg.Set`/`Unset`/`Reset` write the config file and then reload the struct **in place** — which is how a command's own config change (e.g. `tiger config set analytics false`) becomes visible to the rest of its invocation. `Set` also returns the value as stored, which isn't always its argument (`read_only` normalizes its legacy spellings) — echo the return value to the user, not the input.
- **The active project lives in the stored credentials, not the config file.** Switching projects (`tiger project use`) therefore requires an OAuth login — an API key is scoped to one project — and any project change clears a stale default `service_id` (see `clearStaleDefaultService` in `project_helper.go`, which `auth login` calls too).
- **MCP reloads per request.** The analytics middleware in `internal/mcp/server.go` calls `s.app.Load(ctx)` on every request, so config changes and logins/logouts take effect on the next tool call without restarting the server. Tool handlers read that state and must not load it again.
Expand Down
3 changes: 3 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ There are a handful of configuration options and environment variables that are
- `console_url` (`TIGER_CONSOLE_URL`) - Tiger Cloud Console URL (default: https://console.cloud.tigerdata.com)
- `gateway_url` (`TIGER_GATEWAY_URL`) - Tiger Cloud Gateway URL (default: https://console.cloud.tigerdata.com/api)
- `docs_mcp_url` (`TIGER_DOCS_MCP_URL`) - Docs MCP server URL (default: https://mcp.tigerdata.com/docs)
- `releases_url` (`TIGER_RELEASES_URL`) - Release CDN used by `tiger upgrade` and the version check (default: https://cli.tigerdata.com)

These are internal options: `tiger config list` and shell completion leave them out. Pass the hidden `--all` (`-a`) flag to `tiger config list` to include them.

## Running Tests

Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/auth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func offerProdProtection(cmd *cobra.Command, cfg *config.Config) bool {
return true
}

stored, err := config.LoadForOutput(cfg.ConfigDir, false, true)
stored, err := config.LoadForOutput(cfg.ConfigDir, config.OutputOptions{NoDefaults: true})
if err != nil {
// Can't tell whether it was ever answered, so don't ask — but do print the
// bullet, which is the harmless half of the two.
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/completion_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func configKeyCompletion(cmd *cobra.Command, args []string, toComplete string) (
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return filterCompletionsByPrefix(config.ValidConfigOptions(), toComplete), cobra.ShellCompDirectiveNoFileComp
return filterCompletionsByPrefix(config.PublicConfigOptions(), toComplete), cobra.ShellCompDirectiveNoFileComp
}

// configKeyValueCompletion completes the <key> and <value> arguments of
Expand All @@ -172,7 +172,7 @@ func configKeyValueCompletion(cmd *cobra.Command, args []string, toComplete stri
switch len(args) {
case 0:
// Completing the key
return filterCompletionsByPrefix(config.ValidConfigOptions(), toComplete), cobra.ShellCompDirectiveNoFileComp
return filterCompletionsByPrefix(config.PublicConfigOptions(), toComplete), cobra.ShellCompDirectiveNoFileComp
case 1:
// Completing the value, based on the key already typed
return filterCompletionsByPrefix(config.ValidConfigOptionValues(args[0]), toComplete), cobra.ShellCompDirectiveNoFileComp
Expand Down
9 changes: 5 additions & 4 deletions internal/cmd/completion_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ func TestCompletion(t *testing.T) {
{
// Config keys come straight from the registry, so this list is the
// same one `tiger config list` renders (see TestConfigListCoversEveryKey).
name: "config set completes keys",
// Private keys are left out.
name: "config set completes public keys",
args: []string{"__complete", "config", "set", ""},
wantStdout: "analytics\napi_url\ncolor\nconsole_url\ndocs_mcp\ndocs_mcp_url\ngateway_url\nmcp_max_rows\noutput\npassword_storage\nread_only\nreleases_url\nservice_id\nversion_check\n" + noFileComp,
wantStdout: "analytics\ncolor\ndocs_mcp\nmcp_max_rows\noutput\npassword_storage\nread_only\nservice_id\nversion_check\n" + noFileComp,
wantStderr: directive,
checks: []checkFunc{checkNotLoaded},
},
Expand All @@ -178,9 +179,9 @@ func TestCompletion(t *testing.T) {
checks: []checkFunc{checkNotLoaded},
},
{
name: "config unset completes keys",
name: "config unset completes public keys",
args: []string{"__complete", "config", "unset", "docs"},
wantStdout: "docs_mcp\ndocs_mcp_url\n" + noFileComp,
wantStdout: "docs_mcp\n" + noFileComp,
wantStderr: directive,
checks: []checkFunc{checkNotLoaded},
},
Expand Down
9 changes: 8 additions & 1 deletion internal/cmd/config_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
func buildConfigListCmd(app *common.App) *cobra.Command {
var noDefaults bool
var withEnv bool
var all bool

cmd := &cobra.Command{
Use: "list",
Expand All @@ -29,7 +30,11 @@ func buildConfigListCmd(app *common.App) *cobra.Command {
// Values are re-read free of env and CLI flags (unless --with-env
// is given), so `config list -o json` reports the configured
// `output` value rather than the flag's.
cfgOut, err := config.LoadForOutput(cfg.ConfigDir, withEnv, noDefaults)
cfgOut, err := config.LoadForOutput(cfg.ConfigDir, config.OutputOptions{
WithEnv: withEnv,
NoDefaults: noDefaults,
All: all,
})
if err != nil {
return err
}
Expand All @@ -50,6 +55,8 @@ func buildConfigListCmd(app *common.App) *cobra.Command {
registerFlagCompletion(cmd, "output", outputCompletion())
cmd.Flags().BoolVar(&noDefaults, "no-defaults", false, "do not show default values for unset fields")
cmd.Flags().BoolVar(&withEnv, "with-env", false, "apply environment variable overrides")
cmd.Flags().BoolVarP(&all, "all", "a", false, "include internal config values")
markFlagHidden(cmd, "all")

return cmd
}
Expand Down
Loading
Loading