github/github-mcp-server · error

failed to unmarshal toolsets: %w

Error message

failed to unmarshal toolsets: %w

What it means

Returned by runListScopes when viper.UnmarshalKey("toolsets", &enabledToolsets) fails — i.e. the configured 'toolsets' value (from config file, env var, or flag) cannot be decoded into []string. It executes only when viper.IsSet("toolsets") is true, so any value shaped wrong for a string slice (a map, a nested object, a scalar of unexpected type) triggers it. This runs during the list-scopes command, i.e. at CLI startup before any GitHub API traffic.

Source

Thrown at cmd/github-mcp-server/list_scopes.go:91

	_ = viper.BindPFlag("list-scopes-output", listScopesCmd.Flags().Lookup("output"))

	rootCmd.AddCommand(listScopesCmd)
}

// formatScopeDisplay formats a scope string for display, handling empty scopes.
func formatScopeDisplay(scope string) string {
	if scope == "" {
		return "(no scope required for public read access)"
	}
	return scope
}

func runListScopes() error {
	// Get toolsets configuration (same logic as stdio command)
	var enabledToolsets []string
	if viper.IsSet("toolsets") {
		if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
			return fmt.Errorf("failed to unmarshal toolsets: %w", err)
		}
	}
	// else: enabledToolsets stays nil, meaning "use defaults"

	// Get specific tools (similar to toolsets)
	var enabledTools []string
	if viper.IsSet("tools") {
		if err := viper.UnmarshalKey("tools", &enabledTools); err != nil {
			return fmt.Errorf("failed to unmarshal tools: %w", err)
		}
	}

	readOnly := viper.GetBool("read-only")
	outputFormat := viper.GetString("list-scopes-output")

	// Create translation helper
	t, _ := translations.TranslationHelper()

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Set toolsets as a plain list of strings in the config file: toolsets: ["repos", "issues"] (YAML) or ["repos","issues"] (JSON/TOML array)
  2. Remove the toolsets key entirely to fall back to defaults (enabledToolsets stays nil)
  3. Validate the config file syntax (yaml/json linter) before running the command
  4. Print the effective value with viper (viper.Get("toolsets")) in a scratch program to see the actual decoded type

Example fix

# before (config.yml)
toolsets:
  repos: enabled
  issues: enabled

# after
 toolsets: ["repos", "issues"]
Defensive patterns

Strategy: validation

Validate before calling

// Validate the configured shape before it reaches viper's decoder
raw := viper.Get("toolsets")
switch raw.(type) {
case []string, []any, nil: // ok
default:
    log.Fatalf("'toolsets' must be a list of strings, got %T", raw)
}
// stricter: ensure every element is a string
if arr, ok := raw.([]any); ok {
    for _, e := range arr {
        if _, ok := e.(string); !ok {
            log.Fatalf("toolsets must be a list of strings; got element %T", e)
        }
    }
}

Try / catch

var enabledToolsets []string
if viper.IsSet("toolsets") {
    if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
        // config shape problem: report and exit with a fix hint
        log.Fatalf("invalid 'toolsets' config (must be a list of strings): %v", err)
    }
}

Prevention

When it happens

Trigger: Config file sets toolsets to a non-list value (e.g. YAML 'toolsets: {repos: enabled}' or 'toolsets: 123'); an env var or flag delivering a type that viper's decoder (mapstructure) cannot convert to []string; malformed JSON/YAML/TOML structure for that key.

Common situations: Hand-edited github-mcp-server config where toolsets is written as a map or scalar instead of a list; mixing config formats (JSON toolsets string vs list); version upgrades that changed the expected shape; env var GITHUB_TOOLSETS with bracket/brace characters parsed as structure.

Related errors


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/99b38016db8cb631. Report an issue: GitHub.