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 postgreslistactivequeries' init(): tools.Register (internal/tools/tools.go:43) returned false, meaning another package already registered a factory under the same resourceType string ("postgres-list-active-queries") in the shared toolRegistry map. Since tool YAML configs resolve by `type` string, a duplicate would be ambiguous, so the binary panics during init before serving.

Source

Thrown at internal/tools/postgres/postgreslistactivequeries/postgreslistactivequeries.go:58

        wait_event_type,
        wait_event,
        backend_start,
        xact_start,
        query_start,
        now() - query_start AS query_duration,
        query
    FROM pg_stat_activity
    WHERE state = 'active'
        AND backend_type = 'client backend'
        AND ($1::INTERVAL IS NULL OR now() - query_start >= $1::INTERVAL)
        AND ($2::text IS NULL OR application_name NOT IN (SELECT trim(app) FROM unnest(string_to_array($2, ',')) AS app))
    ORDER BY query_duration DESC
    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. Locate the second registration via rg '"postgres-list-active-queries"' internal/ cmd/ and make one `resourceType` unique.
  2. For an intentional variant, rename the constant (and package) to a new kebab-case type such as "postgres-list-active-queries-extended".
  3. Remove the duplicate package/import if it is dead code.
  4. Verify startup with `go build ./...` and unit tests.

Example fix

// before (copied variant)
const resourceType string = "postgres-list-active-queries"
// after
const resourceType string = "postgres-list-active-queries-extended"
Defensive patterns

Strategy: validation

Validate before calling

// Uniqueness check before build/CI:
//   rg -F '"postgres-list-active-queries"' internal/ cmd/  # expect exactly 1 hit
// Smoke test linking all tool packages:
func TestToolRegistryInit(t *testing.T) {
	// panics on duplicate registration; must pass
}

Type guard

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

Prevention

When it happens

Trigger: Importing a second package whose init() registers "postgres-list-active-queries" — typically a copy-pasted postgreslistactivequeries.go with an unchanged `resourceType` constant — or two files in the build declaring the same constant.

Common situations: Cloning the tool to add filters or a different LIMIT default while renaming only the package; a merge resolution that kept duplicate directories; importing a forked/vendored variant 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/df9dbbfaa49b9aa9. Report an issue: GitHub.