googleapis/mcp-toolbox · critical
tool type %q already registered
Error message
tool type %q already registered
What it means
This is a startup panic thrown by the tool package's init() when tools.Register (internal/tools/tools.go:43) finds that the global toolRegistry map already contains an entry for the tool type string. Each tool package registers a factory under a unique `resourceType` constant at process init time; a collision means two registrations for the same `type` string, so the toolbox refuses to guess which factory wins and crashes immediately. Because it fires in init(), the program never serves any requests.
Source
Thrown at internal/tools/oracle/oracleexecutesql/oracleexecutesql.go:22
import (
"context"
"database/sql"
"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"
)
const resourceType string = "oracle-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 {
OracleDB() *sql.DB
RunSQL(context.Context, string, []any, bool) (any, error)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`View on GitHub (pinned to 8cc6e09de2)
Solutions
- Grep the binary's imports for a second registration of "oracle-execute-sql" (rg 'oracle-execute-sql' internal/ cmd/) and change one of the `resourceType` constants to a unique kebab-case string.
- If the duplicate is an intentional variant tool, rename BOTH the `resourceType` constant and the package so the type string matches the new tool's kebab-case name (e.g. "oracle-execute-sql-v2").
- If the duplicate file is leftover dead code, delete the copied package and remove its import.
- Rebuild and run `go build ./...` plus unit tests to confirm only one init() registers the type.
Example fix
// before (copied package oracleexecutesqlvariant.go) const resourceType string = "oracle-execute-sql" // after const resourceType string = "oracle-execute-sql-variant"
Defensive patterns
Strategy: validation
Validate before calling
package main
import (
"fmt"
"os"
"github.com/googleapis/mcp-toolbox/internal/tools"
)
// Run before adding imports/registrations (e.g. in a test or build check):
func ensureTypeFree(resourceType string) error {
if resourceType == "" {
return fmt.Errorf("resourceType must be non-empty")
}
// Register in a scratch namespace is not exposed; instead verify uniqueness
// across the source tree before building:
// rg -F '"oracle-execute-sql"' internal/ cmd/ -> expect exactly 1 hit
if err := tools.Register("__probe__"+resourceType, nil); !err {
return fmt.Errorf("type %q collision pattern detected", resourceType)
}
return nil
}
func main() {
if err := ensureTypeFree("oracle-execute-sql"); err != nil {
os.Exit(1)
}
} Type guard
func isRegistered(resourceType string) bool {
return !tools.Register(resourceType+"__probe__", nil) // false => key was already taken
} Prevention
- When copying an existing tool file to create a new tool, always rename the `resourceType` constant in the same commit as the package rename.
- Follow the repo convention that `resourceType` equals the tool's kebab-case name and matches the directory name.
- Add a smoke test that imports all tool packages and asserts the binary initializes without panicking.
- Before building a custom binary, run `rg -F '"<your-type-string>"' internal/ cmd/` and confirm exactly one registration site.
- Never import both an upstream tool package and a local fork of it into the same binary.
When it happens
Trigger: Linking/importing the internal/tools/oracle/oracleexecutesql package (resourceType "oracle-execute-sql") together with another package whose init() calls tools.Register with the identical string — typically a copy-pasted copy of this file, a renamed package that kept the old constant, or two files in the same binary both declaring `const resourceType string = "oracle-execute-sql"`.
Common situations: A developer forked/copied oracleexecutesql.go into a new package to create a variant tool but only renamed the package/directory, not the `resourceType` constant, then imported both into cmd/ or a test binary; merge conflicts resolved by keeping two copies; or a refactoring PR duplicated a tool under a new directory without updating the registered type.
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/ef9df000f29d664b.
Report an issue: GitHub.