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

  1. 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.
  2. 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").
  3. If the duplicate file is leftover dead code, delete the copied package and remove its import.
  4. 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 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


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/ef9df000f29d664b. Report an issue: GitHub.