googleapis/mcp-toolbox · critical
tool type %q already registered
Error message
tool type %q already registered
What it means
This is a fail-fast panic raised during package initialization. The neo4jschema package registers its factory under resourceType "neo4j-schema" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). Register() returns false when another init() already stored that type key, and init() deliberately panics (as its comment notes, this is the application's tool registry) because two factories for one `kind` would make YAML tool config decoding nondeterministic.
Source
Thrown at internal/tools/neo4j/neo4jschema/neo4jschema.go:41
"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/tools/neo4j/neo4jschema/cache"
"github.com/googleapis/mcp-toolbox/internal/tools/neo4j/neo4jschema/helpers"
"github.com/googleapis/mcp-toolbox/internal/tools/neo4j/neo4jschema/types"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
)
// type defines the unique identifier for this tool.
const resourceType string = "neo4j-schema"
// init registers the tool with the application's tool registry when the package is initialized.
func init() {
if !tools.Register(resourceType, newConfig) {
panic(fmt.Sprintf("tool type %q already registered", resourceType))
}
}
// newConfig decodes a YAML configuration into a Config struct.
// This function is called by the tool registry to create a new configuration object.
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
}
// compatibleSource defines the interface a data source must implement to be used by this tool.
// It ensures that the source can provide a Neo4j driver and database name.
type compatibleSource interface {
Neo4jDriver() neo4j.Driver
Neo4jDatabase() stringView on GitHub (pinned to 8cc6e09de2)
Solutions
- grep -rn '"neo4j-schema"' to find all registering packages; keep one and give the other a unique resourceType.
- Delete the obsolete duplicate package or remove its import from cmd/root.go.
- Deduplicate module versions (go mod tidy; review replace/vendor).
- Verify with `go build ./...` and `go test ./internal/tools/neo4j/neo4jschema/...`.
Example fix
// before const resourceType string = "neo4j-schema" // duplicated // after const resourceType string = "neo4j-schema" // original only; new tool uses "neo4j-schema-deep"
Defensive patterns
Strategy: validation
Validate before calling
// Verify uniqueness before building:
// grep -rn '"neo4j-schema"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Exactly one match expected. Soft guard for custom tools:
func init() {
if !tools.Register(resourceType, newConfig) {
fmt.Printf("WARNING: %s already registered; skipping duplicate init\n", resourceType)
return
}
} Type guard
func isRegistered(resourceType string) bool {
_, exists := registrySnapshot[resourceType]
return exists
} Try / catch
// Recover at the process boundary:
func main() {
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "already registered") {
log.Fatalf("duplicate tool registration: %s", s)
}
panic(r)
}
}()
rootCmd.Execute()
} Prevention
- Give every new Neo4j tool a unique kebab-case resourceType per project naming conventions.
- Add a registry-uniqueness test to internal/tools to fail CI on duplicate type strings.
- Resolve merge conflicts by deleting (not keeping) superseded tool packages.
- Keep a single module copy in go.mod; run go mod tidy after updates.
- grep the type string before committing registration changes.
When it happens
Trigger: Two packages register "neo4j-schema": a duplicated copy of neo4jschema still carrying the const, the package linked twice via different module paths, or a new schema-inspection tool reusing the built-in type string.
Common situations: Copying the schema tool as a template for a new introspection tool without renaming the type; a merge conflict resolution that kept both old and new package directories; vendoring duplicates of the module.
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/e5e08d5e318c6e33.
Report an issue: GitHub.