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 postgresexecutesql's init(): tools.Register (internal/tools/tools.go:43) returned false because the type string "postgres-execute-sql" was already used to register a ToolConfigFactory in the global toolRegistry. The registry forbids overwriting so that a YAML tool config's `type` unambiguously resolves to one factory; on collision the process panics during init, before main().
Source
Thrown at internal/tools/postgres/postgresexecutesql/postgresexecutesql.go:34
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/mcp-toolbox/internal/sources"
"github.com/googleapis/mcp-toolbox/internal/tools"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
const resourceType string = "postgres-execute-sql"
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
- rg '"postgres-execute-sql"' internal/ cmd/ to locate the second registration and make one `resourceType` unique.
- For an intentional variant tool, rename the constant and package to a new kebab-case type such as "postgres-execute-sql-readonly".
- Remove the unintended duplicate package/import.
- Confirm with `go build ./...` and unit tests that the server starts.
Example fix
// before (variant copy) const resourceType string = "postgres-execute-sql" // after const resourceType string = "postgres-execute-sql-readonly"
Defensive patterns
Strategy: validation
Validate before calling
// Before building a custom toolbox binary, verify the type string appears exactly once:
// rg -F '"postgres-execute-sql"' internal/ cmd/ # expect 1 hit in postgresexecutesql.go
// And keep a CI test that links all tool packages:
func TestAllToolsRegister(t *testing.T) {
// importing internal/tools/... packages must not panic
} Type guard
func registrationCollides(resourceType string) bool {
probe := resourceType + "__collision_probe__"
ok := tools.Register(probe, nil)
_ = ok
// search source for the real type string; >1 hit means a future collision
return countSourceOccurrences(resourceType) > 1
} Prevention
- Never copy a tool package without renaming its `resourceType` constant in the same change.
- Enforce that `resourceType` matches the package directory (kebab-case tool name per AGENTS.md).
- Add a link-all-packages smoke test so this panic fires in CI rather than at startup in production.
- Grep the type string before adding imports of new or forked tool packages.
- Remove superseded tool copies instead of keeping both versions importable.
When it happens
Trigger: Building a binary that imports two packages registering "postgres-execute-sql" — typically a duplicated postgresexecutesql.go whose `resourceType` constant was unchanged, or the same constant declared twice within reachable code.
Common situations: Copy-pasting the execute-sql tool to make a restricted variant (e.g. read-only) and renaming only the package/directory; merge conflicts resolved with both copies present; accidental import of a forked vendored copy alongside the original.
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/509fcca9d5053517.
Report an issue: GitHub.