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 postgreslistinstalledextensions' init(): tools.Register (internal/tools/tools.go:43) returned false, meaning another factory is already registered under the same resourceType string ("postgres-list-installed-extensions") in the shared toolRegistry map. The registry intentionally refuses to overwrite existing keys; a duplicate `type` string makes config resolution ambiguous, so the process panics during package initialization before serving requests.

Source

Thrown at internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go:55

        pg_get_userbyid(e.extowner) AS owner,
        c.description AS description
    FROM
        pg_catalog.pg_extension e
    LEFT JOIN
        pg_catalog.pg_namespace n
    ON
        n.oid = e.extnamespace
    LEFT JOIN
        pg_catalog.pg_description c
    ON
        c.objoid = e.oid
        AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass
    ORDER BY 1;
`

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. Find the collision using rg '"postgres-list-installed-extensions"' internal/ cmd/ and change one `resourceType` constant.
  2. For a deliberate variant tool, assign a unique kebab-case type string (e.g. "postgres-list-installed-extensions-detailed") and matching package name.
  3. Remove the unintended duplicate package/import.
  4. Confirm with `go build ./...` and unit tests that the toolbox starts.

Example fix

// before (variant copy)
const resourceType string = "postgres-list-installed-extensions"
// after
const resourceType string = "postgres-list-installed-extensions-detailed"
Defensive patterns

Strategy: validation

Validate before calling

// Verify single registration before building:
//   rg -F '"postgres-list-installed-extensions"' internal/ cmd/  # expect 1 hit
// CI smoke test that initializes every tool package:
func TestAllToolInits(t *testing.T) {
	// must not panic: tool type "postgres-list-installed-extensions" already registered
}

Type guard

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

Prevention

When it happens

Trigger: Compiling a binary that imports two packages registering "postgres-list-installed-extensions" — usually a duplicated postgreslistinstalledextensions.go with an unchanged `resourceType` constant, or two files in the build declaring the identical constant.

Common situations: Copy-pasting the tool to create a variant (e.g. filtering by schema) and renaming only the package; a merge conflict resolved by keeping both copies; importing both the upstream package and an internal fork.

Related errors


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