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 postgresgetcolumncardinality's init(): tools.Register (internal/tools/tools.go:43) returned false because the tool registry map already contains a factory for the package's `resourceType` string ("postgres-get-column-cardinality"). Duplicate type strings make config decoding ambiguous, so the code panics at package-init time rather than allowing a silent overwrite.
Source
Thrown at internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go:53
ROUND(
CASE
WHEN s.n_distinct < 0 THEN ABS(s.n_distinct) * c.reltuples
ELSE s.n_distinct
END
) AS estimated_cardinality
FROM pg_stats s
JOIN pg_class c ON s.tablename = c.relname
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE s.schemaname = $1 -- Parameter for schema
AND s.tablename = $2 -- Parameter for table
AND n.nspname = $1 -- (same parameter as schema)
AND s.attname = COALESCE($3, s.attname)
ORDER BY estimated_cardinality DESC;
`
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
- Find the collision with rg '"postgres-get-column-cardinality"' internal/ cmd/ and change one `resourceType` constant.
- If the copy is a deliberate new tool, assign a distinct kebab-case type string and matching package name.
- Delete the redundant copy and its import if unintended.
- Run `go build ./...` and the postgres tool tests to verify.
Example fix
// before (duplicated tool) const resourceType string = "postgres-get-column-cardinality" // after const resourceType string = "postgres-get-column-cardinality-estimated"
Defensive patterns
Strategy: validation
Validate before calling
// Assert uniqueness before building:
// rg -F '"postgres-get-column-cardinality"' internal/ cmd/ # exactly 1 expected
// CI smoke test that forces init() of every tool package:
func TestInitNoPanics(t *testing.T) {
// must complete without "tool type already registered" panic
} Type guard
func isUniqueRegistration(sourceRoot, resourceType string) bool {
return countLiteralMatches(sourceRoot, "\""+resourceType+"\"") == 1
} Prevention
- Treat `resourceType` as part of the tool's identity: rename it whenever you fork the tool.
- Match the constant to the directory name; a mismatch in review is a red flag.
- Keep a CI test that imports all tool packages so duplicate init() registrations fail fast.
- Search the tree for the type literal before importing a new tool package.
- Avoid keeping both original and copied variants in the module.
When it happens
Trigger: Compiling a binary that pulls in two init() calls registering the same postgres tool type — usually a copied postgresgetcolumncardinality.go that kept the original `resourceType` constant, or two declarations of the same constant in the linked package set.
Common situations: Duplicating the file to create a customized cardinality query and renaming only the directory/package; a merge keeping old and new copies; importing both the upstream package and an internal fork.
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/99c46fd35815d89b.
Report an issue: GitHub.