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-triggers tool package. tools.Register() is a global registry keyed by tool type string and returns false when the key "postgres-list-triggers" is already present. Go invokes init() at startup, so a duplicate registration of this key crashes the process before any configuration is loaded.

Source

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

        JOIN pg_namespace n
            ON c.relnamespace = n.oid
        LEFT JOIN pg_proc p
            ON t.tgfoid = p.oid
        WHERE NOT t.tgisinternal
    )
    SELECT *
    FROM trigger_list
    WHERE
        ($1::text IS NULL OR trigger_name LIKE '%' || $1::text || '%')
        AND ($2::text IS NULL OR schema_name LIKE '%' || $2::text || '%')
        AND ($3::text IS NULL OR table_name LIKE '%' || $3::text || '%')
    ORDER BY schema_name, table_name, trigger_name
    LIMIT COALESCE($4::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 the repository for "postgres-list-triggers" and remove or rename the package/constant registering it a second time.
  2. If the package was copied as a template, set a unique resourceType in the copy.
  3. Deduplicate import paths (go.mod replace directives, vendor/) and rebuild.
  4. Verify with a fresh clone that the panic is not caused by local uncommitted edits.

Example fix

// before
const resourceType string = "postgres-list-triggers" // in a copied package too

// after
// original package keeps "postgres-list-triggers";
// the new package uses:
const resourceType string = "postgres-list-scheduled-triggers"
Defensive patterns

Strategy: validation

Validate before calling

// Before building/starting, verify the key appears in exactly one package:
// go run: iterate all packages' resourceType constants and error on duplicates.
const want = "postgres-list-triggers"
if registrations[want] > 1 { // registrations collected by your registry scan
    panic("postgres-list-triggers registered more than once")
}

Type guard

func isRegisteredOnce(key string, registry map[string]int) bool {
    return registry[key] == 1
}

Try / catch

func start() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("startup: %v", r)
        }
    }()
    _ = tools.Register // force init of tool packages behind an explicit call in your fork
    return nil
}

Prevention

When it happens

Trigger: Importing a build where two packages call tools.Register with the key "postgres-list-triggers": a duplicated/copied postgreslisttriggers package under a different import path, or a local edit changing some package's resourceType to collide with postgres-list-triggers (postgreslisttriggers.go:30).

Common situations: Copy-pasting postgreslisttriggers.go into a new tool package without changing resourceType; vendoring or forking that exposes the same package twice; merge conflicts leaving a stale duplicate init(); experimental tool packages accidentally registered under an existing key.

Related errors


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