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 postgreslistavailableextensions' init(): tools.Register (internal/tools/tools.go:43) returned false because the global toolRegistry already has a factory registered for the same resourceType string ("postgres-list-available-extensions"). The registry is populated once per tool package via init(); a repeated key means two factories claim one YAML `type`, so the process panics immediately at init time.
Source
Thrown at internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions.go:44
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
const resourceType string = "postgres-list-available-extensions"
const listAvailableExtensionsQuery = `
SELECT
name,
default_version,
comment as description
FROM
pg_available_extensions
ORDER BY name;
`
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
- Search for the collision: rg '"postgres-list-available-extensions"' internal/ cmd/; change one `resourceType` constant to a unique string.
- If the duplicate is a real new tool, rename the constant and package to its own kebab-case type.
- Delete the leftover duplicate package and import.
- Confirm with `go build ./...` and postgres tool tests.
Example fix
// before (duplicate package) const resourceType string = "postgres-list-available-extensions" // after const resourceType string = "postgres-list-available-extensions-v2"
Defensive patterns
Strategy: validation
Validate before calling
// Pre-build uniqueness check:
// rg -F '"postgres-list-available-extensions"' internal/ cmd/ # expect 1 hit
// CI smoke test that links all tool packages and thus runs every init():
func TestRegistrationsUnique(t *testing.T) {
// must not panic with "tool type %q already registered"
} Type guard
func registrationIsUnique(root, resourceType string) bool {
return countLiteralOccurrences(root, "\""+resourceType+"\"") == 1
} Prevention
- Whenever duplicating a postgres tool file, rename the `resourceType` constant immediately.
- Enforce constant == directory name (kebab-case) in code review.
- Include a test that imports every tool package so duplicate init()s fail in CI.
- Check for existing registrations with a literal search before adding imports.
- Delete forked copies once they are no longer needed.
When it happens
Trigger: Linking code where two init() functions register "postgres-list-available-extensions" — normally a duplicated postgreslistavailableextensions.go whose `resourceType` constant was never changed, or the same constant declared in two packages both imported.
Common situations: Copy-pasting the file to create a variant extensions tool and renaming only the package/directory; resolving a merge by keeping both copies; importing both upstream and a locally forked version into a custom binary.
Related errors
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/8dba13857de28a15.
Report an issue: GitHub.