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-replication-stats tool package. tools.Register() registers the config constructor for "postgres-replication-stats" in a global registry and returns false if that key already exists, so init() panics on a duplicate. Because init runs at link/startup time, the whole toolbox binary fails immediately.

Source

Thrown at internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go:52

        pid,
        usename,
        application_name,
        backend_xmin,
        client_addr,
        state,
        sync_state,
        pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS sent_lag,
        pg_size_pretty(pg_wal_lsn_diff(sent_lsn, write_lsn)) AS write_lag,
        pg_size_pretty(pg_wal_lsn_diff(write_lsn, flush_lsn)) AS flush_lag,
        pg_size_pretty(pg_wal_lsn_diff(flush_lsn, replay_lsn)) AS replay_lag,
        pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS total_lag
    FROM
        pg_stat_replication;
`

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 repo for "postgres-replication-stats" and delete/rename the duplicate registration.
  2. Assign a unique resourceType to any copied package.
  3. Remove duplicate import paths or vendor entries and rebuild cleanly.
  4. Re-verify with a pristine checkout to rule out local edits as the cause.

Example fix

// before
const resourceType string = "postgres-replication-stats" // registered twice

// after
// stock package keeps "postgres-replication-stats";
// new package:
const resourceType string = "postgres-logical-replication-stats"
Defensive patterns

Strategy: validation

Validate before calling

// Static check you can run in CI before building:
// grep -r 'postgres-replication-stats' --include='*.go' | wc -l  -> must match expected occurrences (1 const + 1 panic format only)
// Or programmatically:
if n := countPackagesDeclaring("postgres-replication-stats"); n > 1 {
    return fmt.Errorf("%q declared in %d packages", "postgres-replication-stats", n)
}

Type guard

func assertSingleOwner(rt string, owners []string) bool {
    return len(owners) == 1
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("panic during tool registration (duplicate key?): %v", r)
    }
}() // place around the first server start in your launcher if registration is made lazy in a fork

Prevention

When it happens

Trigger: A build registering "postgres-replication-stats" twice: a duplicated postgresreplicationstats package under another import path, or someone changed another tool package's resourceType to collide with it (postgresreplicationstats.go:30).

Common situations: Cloning this package as a template for a new stats tool without renaming resourceType; vendor/fork duplication; merge conflict leftovers; a custom build importing both upstream and an internal patched copy.

Related errors


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