github/github-mcp-server · error

failed to unmarshal tools: %w

Error message

failed to unmarshal tools: %w

What it means

Returned by runListScopes when viper.UnmarshalKey("tools", &enabledTools) fails — the configured 'tools' value cannot be decoded into []string. Like the toolsets key, it only runs when viper.IsSet("tools") is true. Because tool names are later fed to inventory WithTools, a successful decode with wrong names will subsequently surface as ErrUnknownTools at Build(); this error is specifically a decode/type failure.

Source

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

	}
	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()

	// Build inventory using the same logic as the stdio server
	inventoryBuilder := github.NewInventory(t).
		WithReadOnly(readOnly)

	// Configure toolsets (same as stdio)
	if enabledToolsets != nil {
		inventoryBuilder = inventoryBuilder.WithToolsets(enabledToolsets)
	}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Write tools as a string list in the config: tools: ["issue", "issue_comment"]
  2. Drop the key to use defaults, or list toolsets instead if you meant whole toolsets
  3. Lint the config file for syntax and shape before running list-scopes
  4. Confirm tool names against current docs so the value survives the later WithTools validation

Example fix

# before (config.yml)
tools:
  - name: issue
    enabled: true

# after
tools: ["issue", "issue_comment"]
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the 'tools' config value is a plain string list before decoding
raw := viper.Get("tools")
if raw != nil {
    arr, ok := raw.([]any)
    if !ok {
        log.Fatalf("'tools' must be a list of tool names, got %T", raw)
    }
    for _, e := range arr {
        if _, ok := e.(string); !ok {
            log.Fatalf("'tools' entries must be strings, got %T", e)
        }
    }
}

Try / catch

var enabledTools []string
if viper.IsSet("tools") {
    if err := viper.UnmarshalKey("tools", &enabledTools); err != nil {
        log.Fatalf("invalid 'tools' config (must be a list of strings): %v", err)
    }
}

Prevention

When it happens

Trigger: Config sets tools to a non-list shape (map, number, nested object); an env/flag value that cannot be split/converted into a []string; malformed config syntax under the 'tools' key.

Common situations: Editing the config and writing 'tools: create_issue' (bare scalar in strict contexts) or a key/value block instead of a list; JSON configs using an object instead of an array; stale configs from older versions with a different schema.

Related errors


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