googleapis/mcp-toolbox · critical

tool type %q already registered

Error message

tool type %q already registered

What it means

This is a fail-fast panic raised during package initialization. The mysqlshowquerystats package registers its factory under resourceType "mysql-show-query-stats" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). When Register() returns false — the map already contains that type key — the init() panics so that duplicate tool-type claims fail the build/startup immediately instead of nondeterministically overriding a factory.

Source

Thrown at internal/tools/mysql/mysqlshowquerystats/mysqlshowquerystats.go:54

    count_star AS 'execution_count',
    ROUND(sum_timer_wait / 1000000000, 2) AS 'total_latency_ms',
    ROUND(avg_timer_wait / 1000000000, 2) AS 'average_latency_ms',
    ROUND(max_timer_wait / 1000000000, 2) AS 'max_latency_ms',
    sum_rows_sent AS 'total_rows_sent',
    sum_rows_examined AS 'total_rows_examined',
    sum_no_index_used AS 'full_table_scan_count',
    sum_no_good_index_used AS 'inefficient_index_used_count',
    last_seen AS 'last_executed'
FROM performance_schema.events_statements_summary_by_digest
WHERE schema_name NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
AND (schema_name = COALESCE(NULLIF(?, ''), NULLIF(DATABASE(), '')) OR COALESCE(NULLIF(?, ''), NULLIF(DATABASE(), '')) IS NULL)
ORDER BY sum_timer_wait DESC
LIMIT ?;
`

func init() {
	if !tools.Register(resourceType, newConfig) {
		panic(fmt.Sprintf("tool type %q already registered", resourceType))
	}
}

func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
	actual := Config{ConfigBase: tools.ConfigBase{Name: name}}
	if err := decoder.DecodeContext(ctx, &actual); err != nil {
		return nil, err
	}
	return actual, nil
}

type compatibleSource interface {
	MySQLPool() *sql.DB
	RunSQL(context.Context, string, []any) (any, error)
	MySQLDatabase() string
	PerformanceSchemaEnabled(context.Context) (bool, error)
}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. grep -rn 'mysql-show-query-stats' to identify both registering packages; assign the new tool a unique resourceType.
  2. Delete or unimport the obsolete duplicate package (check cmd/root.go imports).
  3. Deduplicate module copies (go mod tidy; remove duplicate replace directives).
  4. Verify with `go build ./...` and `go test ./internal/tools/mysql/...`.

Example fix

// before
const resourceType string = "mysql-show-query-stats" // in both original and copied package

// after
const resourceType string = "mysql-show-query-stats" // original only; copied tool uses "mysql-statement-digest-stats"
Defensive patterns

Strategy: validation

Validate before calling

// Verify uniqueness before building:
// grep -rn '"mysql-show-query-stats"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Exactly one match expected. Optional soft guard for custom tools:
func init() {
	if !tools.Register(resourceType, newConfig) {
		fmt.Printf("WARNING: %s already registered; skipping duplicate init\n", resourceType)
		return
	}
}

Type guard

func isRegistered(resourceType string) bool {
	_, exists := registrySnapshot[resourceType]
	return exists
}

Try / catch

// Recover at the process boundary:
func main() {
	defer func() {
		if r := recover(); r != nil {
			if s, ok := r.(string); ok && strings.Contains(s, "already registered") {
				log.Fatalf("duplicate tool registration: %s", s)
			}
			panic(r)
		}
	}()
	rootCmd.Execute()
}

Prevention

When it happens

Trigger: A second init() in the same binary calls tools.Register("mysql-show-query-stats", ...): a copy of this package kept after a refactor, the same package linked under two import paths (e.g. via a replace/fork), or a hand-written tool reusing this type string in its own registration.

Common situations: Template-copying another mysql performance_schema tool and leaving the const unchanged; merging branches that each import a variant of the tool; a monorepo tool override colliding with the stock toolbox tool.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/08a8deb179156473. Report an issue: GitHub.