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 postgreslistinstalledextensions' init(): tools.Register (internal/tools/tools.go:43) returned false, meaning another factory is already registered under the same resourceType string ("postgres-list-installed-extensions") in the shared toolRegistry map. The registry intentionally refuses to overwrite existing keys; a duplicate `type` string makes config resolution ambiguous, so the process panics during package initialization before serving requests.
Source
Thrown at internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go:55
pg_get_userbyid(e.extowner) AS owner,
c.description AS description
FROM
pg_catalog.pg_extension e
LEFT JOIN
pg_catalog.pg_namespace n
ON
n.oid = e.extnamespace
LEFT JOIN
pg_catalog.pg_description c
ON
c.objoid = e.oid
AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass
ORDER BY 1;
`
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 using rg '"postgres-list-installed-extensions"' internal/ cmd/ and change one `resourceType` constant.
- For a deliberate variant tool, assign a unique kebab-case type string (e.g. "postgres-list-installed-extensions-detailed") and matching package name.
- Remove the unintended duplicate package/import.
- Confirm with `go build ./...` and unit tests that the toolbox starts.
Example fix
// before (variant copy) const resourceType string = "postgres-list-installed-extensions" // after const resourceType string = "postgres-list-installed-extensions-detailed"
Defensive patterns
Strategy: validation
Validate before calling
// Verify single registration before building:
// rg -F '"postgres-list-installed-extensions"' internal/ cmd/ # expect 1 hit
// CI smoke test that initializes every tool package:
func TestAllToolInits(t *testing.T) {
// must not panic: tool type "postgres-list-installed-extensions" already registered
} Type guard
func registrationIsSingular(root, resourceType string) bool {
return countLiteralMatches(root, "\""+resourceType+"\"") == 1
} Prevention
- When forking a tool into a new package, rename the `resourceType` constant in the same commit.
- Keep `resourceType` matching the tool's kebab-case name and directory.
- Add an import-all smoke test to CI so duplicate registrations are caught before release.
- Search the tree for the type string literal before importing any new tool package.
- Delete dead/forked duplicates promptly.
When it happens
Trigger: Compiling a binary that imports two packages registering "postgres-list-installed-extensions" — usually a duplicated postgreslistinstalledextensions.go with an unchanged `resourceType` constant, or two files in the build declaring the identical constant.
Common situations: Copy-pasting the tool to create a variant (e.g. filtering by schema) and renaming only the package; a merge conflict resolved by keeping both 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/53ccf41b502bd4a4.
Report an issue: GitHub.