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 neo4jcypher package registers its factory under resourceType "neo4j-cypher" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). A false return from Register() means the type string is already in the registry map, so init() panics rather than allowing a silent overwrite that would make YAML configs with `kind: neo4j-cypher` decode to the wrong tool.
Source
Thrown at internal/tools/neo4j/neo4jcypher/neo4jcypher.go:34
import (
"context"
"fmt"
"net/http"
"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"
)
const resourceType string = "neo4j-cypher"
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 {
Neo4jDatabase() string // kept to ensure neo4j source
RunQuery(context.Context, string, map[string]any, bool, bool) (any, error)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`View on GitHub (pinned to 8cc6e09de2)
Solutions
- grep -rn '"neo4j-cypher"' to locate both registrations; give the duplicate a unique resourceType.
- Remove the stale import or delete the obsolete package copy.
- Deduplicate module copies (go mod tidy, fix replace directives).
- Verify with `go build ./...` and `go test ./internal/tools/neo4j/...`.
Example fix
// before const resourceType string = "neo4j-cypher" // in both original and copied package // after const resourceType string = "neo4j-cypher" // original only; new tool uses "neo4j-read-cypher"
Defensive patterns
Strategy: validation
Validate before calling
// Verify uniqueness before building:
// grep -rn '"neo4j-cypher"' --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
- Rename resourceType, package, and directory together when templating a new Neo4j tool.
- Add a CI test asserting every resourceType string is registered at most once.
- Remove obsolete Neo4j tool packages instead of keeping unimported copies in the tree.
- Run go mod tidy after dependency changes to avoid dual module copies.
- grep the type string before committing any change to tool registration files.
When it happens
Trigger: Two init() functions register "neo4j-cypher": a duplicated/renamed copy of this package still using the constant, the package linked under two import paths (module replace/fork), or a new tool reusing the built-in type string.
Common situations: Copying neo4jcypher as a starting point for a variant Cypher tool and forgetting the const; a merge reintroducing an old package import; vendoring two versions of the toolbox 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/0e7a50554afa44ed.
Report an issue: GitHub.