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 tool package registers its config factory under the unique `resourceType` string "mysql-list-tables-missing-unique-indexes" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). When Register() returns false because another init() already claimed that exact type string, this init() panics instead of silently overwriting, preventing ambiguous YAML `kind` resolution at startup.
Source
Thrown at internal/tools/mysql/mysqllisttablesmissinguniqueindexes/mysqllisttablesmissinguniqueindexes.go:57
information_schema.table_constraints tco
ON
tab.table_schema = tco.table_schema
AND tab.table_name = tco.table_name
AND tco.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
WHERE
tco.constraint_type IS NULL
AND tab.table_schema NOT IN('mysql', 'information_schema', 'performance_schema', 'sys')
AND tab.table_type = 'BASE TABLE'
AND (COALESCE(?, '') = '' OR tab.table_schema = ?)
ORDER BY
tab.table_schema,
tab.table_name
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)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`View on GitHub (pinned to 8cc6e09de2)
Solutions
- grep -rn 'mysql-list-tables-missing-unique-indexes' to locate both registrations; make the new/renamed tool use a distinct resourceType constant.
- Remove the duplicate import (usually in cmd/root.go) or delete the obsolete tool package.
- Check go.mod/replace directives for duplicate module copies that would double-register the package.
- Run `go build ./...` and `go test ./internal/tools/mysql/...` to verify.
Example fix
// before copy of mysqllisttablesmissinguniqueindexes kept: const resourceType string = "mysql-list-tables-missing-unique-indexes" // after const resourceType string = "mysql-list-tables-missing-unique-indexes" // only in the original package; new tool gets its own unique type
Defensive patterns
Strategy: validation
Validate before calling
// Before building, assert uniqueness of the type string:
// grep -rn '"mysql-list-tables-missing-unique-indexes"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Expect exactly one registration. Guard custom tool init():
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
// Panics in init() cannot be recovered in init; recover in main if needed:
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 templating a new MySQL tool from an existing one, update resourceType first and run goimports/gofmt to keep names consistent.
- Add a uniqueness test over the tool registry that fails CI on any duplicate type string.
- Delete old tool package directories rather than leaving them unimported in the tree.
- Keep go.mod free of duplicate replace directives that can link two copies of the same package.
- grep the type string before every commit that touches tool registration.
When it happens
Trigger: Two init() functions call tools.Register("mysql-list-tables-missing-unique-indexes", ...) in the same binary — e.g. a copied/renamed MySQL tool package still using this constant, or the same package being linked twice under different import paths.
Common situations: Copy-pasting a nearby mysql tool directory (mysqllisttablestats, mysqllisttables, etc.) as a template and forgetting to update resourceType at line 30; a merge that re-imports an old package into cmd/root.go; vendoring two versions of the toolbox module so the package initializes twice.
Related errors
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/519132f24a5df7d0.
Report an issue: GitHub.