googleapis/mcp-toolbox · critical

tool type %q already registered

Error message

tool type %q already registered

What it means

This is a startup panic from postgreslistindexes' init(): tools.Register (internal/tools/tools.go:43) returned false because the global toolRegistry map already contains a factory keyed by the package's resourceType string ("postgres-list-indexes"). Each tool type must be uniquely registered so YAML configs decode unambiguously; a collision is treated as a fatal programming error and panics at init time.

Source

Thrown at internal/tools/postgres/postgreslistindexes/postgreslistindexes.go:80

            AND s.schemaname NOT LIKE 'pg_temp_%'
    )
    SELECT *
    FROM IndexDetails
    WHERE
        ($1::text IS NULL OR schema_name LIKE '%' || $1 || '%')
        AND ($2::text IS NULL OR table_name LIKE '%' || $2 || '%')
        AND ($3::text IS NULL OR index_name LIKE '%' || $3 || '%')
        AND ($4::boolean IS NOT TRUE OR is_used IS FALSE)
    ORDER BY
        schema_name,
        table_name,
        index_name
    LIMIT COALESCE($5::int, 50);
`

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 {
	PostgresPool() *pgxpool.Pool
	RunSQL(context.Context, string, []any) (any, error)
}

type Config struct {
	tools.ConfigBase `yaml:",inline"`

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Locate the duplicate with rg '"postgres-list-indexes"' internal/ cmd/ and make one `resourceType` constant unique.
  2. If the copy is an intentional new tool, rename the constant and package to a distinct kebab-case type (e.g. "postgres-list-indexes-invalid").
  3. Delete the redundant copied package and its import.
  4. Run `go build ./...` and postgres tool tests to confirm startup.

Example fix

// before (cloned tool)
const resourceType string = "postgres-list-indexes"
// after
const resourceType string = "postgres-list-indexes-invalid"
Defensive patterns

Strategy: validation

Validate before calling

// Uniqueness check before build:
//   rg -F '"postgres-list-indexes"' internal/ cmd/  # expect exactly 1 hit
// CI smoke test linking every tool package:
func TestToolInitSmoke(t *testing.T) {
	// duplicate registrations panic here; test must pass
}

Type guard

func occursOnce(root, resourceType string) bool {
	return countLiterals(root, "\""+resourceType+"\"") == 1
}

Prevention

When it happens

Trigger: Linking a binary where two init() functions register "postgres-list-indexes" — most often a copy-pasted postgreslistindexes.go whose `resourceType` constant was left unchanged, or the same constant declared in two imported packages.

Common situations: Cloning the list-indexes tool for a schema-scoped or limited variant and renaming only the package/directory; a bad merge leaving duplicate packages; importing a forked copy alongside the original in a custom toolbox build.

Related errors


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