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 postgreslistindexes' init(): tools.Register (internal/tools/tools.go:43) returned false because the global toolRegistry map already contains a factory keyed by the package's resourceType string ("postgres-list-indexes"). Each tool type must be uniquely registered so YAML configs decode unambiguously; a collision is treated as a fatal programming error and panics at init time.
Source
Thrown at internal/tools/postgres/postgreslistindexes/postgreslistindexes.go:80
AND s.schemaname NOT LIKE 'pg_temp_%'
)
SELECT *
FROM IndexDetails
WHERE
($1::text IS NULL OR schema_name LIKE '%' || $1 || '%')
AND ($2::text IS NULL OR table_name LIKE '%' || $2 || '%')
AND ($3::text IS NULL OR index_name LIKE '%' || $3 || '%')
AND ($4::boolean IS NOT TRUE OR is_used IS FALSE)
ORDER BY
schema_name,
table_name,
index_name
LIMIT COALESCE($5::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
- Locate the duplicate with rg '"postgres-list-indexes"' internal/ cmd/ and make one `resourceType` constant unique.
- If the copy is an intentional new tool, rename the constant and package to a distinct kebab-case type (e.g. "postgres-list-indexes-invalid").
- Delete the redundant copied package and its import.
- Run `go build ./...` and postgres tool tests to confirm startup.
Example fix
// before (cloned tool) const resourceType string = "postgres-list-indexes" // after const resourceType string = "postgres-list-indexes-invalid"
Defensive patterns
Strategy: validation
Validate before calling
// Uniqueness check before build:
// rg -F '"postgres-list-indexes"' internal/ cmd/ # expect exactly 1 hit
// CI smoke test linking every tool package:
func TestToolInitSmoke(t *testing.T) {
// duplicate registrations panic here; test must pass
} Type guard
func occursOnce(root, resourceType string) bool {
return countLiterals(root, "\""+resourceType+"\"") == 1
} Prevention
- Rename `resourceType` whenever you clone a tool package; do it before any other edits.
- Keep the constant equal to the tool's kebab-case directory name.
- Maintain a test that imports all tool packages so duplicate init() panics surface in CI.
- Grep for the exact type literal before adding imports of new or forked packages.
- Avoid dual-importing upstream and forked versions of the same tool.
When it happens
Trigger: Linking a binary where two init() functions register "postgres-list-indexes" — most often a copy-pasted postgreslistindexes.go whose `resourceType` constant was left unchanged, or the same constant declared in two imported packages.
Common situations: Cloning the list-indexes tool for a schema-scoped or limited variant and renaming only the package/directory; a bad merge leaving duplicate packages; importing a forked copy alongside the original in a custom toolbox build.
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/852c740f84f97614.
Report an issue: GitHub.