googleapis/mcp-toolbox · error

tool type %q already registered

Error message

tool type %q already registered

What it means

This panic comes from the package-level init() of the postgres-list-views tool package. The tools.Register() call maintains a global map of tool type strings to config constructors and returns false if the key already exists, i.e. "postgres-list-views" was registered twice. Since registration happens in init(), the panic aborts the binary at startup.

Source

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

            viewowner AS owner_name,
            definition
        FROM pg_views
    )
    SELECT *
    FROM list_views
    WHERE
        schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
        AND schema_name NOT LIKE 'pg_temp_%'
        AND ($1::text IS NULL OR view_name ILIKE '%' || $1::text || '%')
        AND ($2::text IS NULL OR schema_name ILIKE '%' || $2::text || '%')
    ORDER BY
        schema_name, view_name
    LIMIT COALESCE($3::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. Grep for "postgres-list-views" to find every registration and delete/rename the duplicate.
  2. Give a copied package a unique kebab-case resourceType.
  3. Remove duplicate import paths / vendor copies and rebuild.
  4. Check git diff for accidental duplicate init() or constant edits before building.

Example fix

// before
const resourceType string = "postgres-list-views" // registered twice

// after
// stock package keeps "postgres-list-views";
// new package:
const resourceType string = "postgres-list-materialized-views"
Defensive patterns

Strategy: validation

Validate before calling

// Startup validation in main() (for forks that control registration):
key := "postgres-list-views"
if _, exists := registeredKeys[key]; exists {
    return fmt.Errorf("refusing to start: %q registered twice", key)
}

Type guard

func isDuplicate(key string, seen map[string]bool) bool {
    if seen[key] { return true }
    seen[key] = true
    return false
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("tool registration panic (duplicate resourceType?): %v", r)
    }
}() // wrap the code path that first imports tool packages

Prevention

When it happens

Trigger: A build containing two registrations for "postgres-list-views": a duplicated postgreslistviews package under a different import path (vendor/fork), or a modified resourceType in another postgres tool package colliding with postgres-list-views (postgreslistviews.go:30).

Common situations: Template-copying postgreslistviews.go to create a new listing tool without changing resourceType; duplicate vendoring; merge conflict residue; custom prebuilt configs compiled into a binary that also links the stock package under a second path.

Related errors


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