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. Each tool package in the toolbox registers its config factory under a unique `resourceType` string (here "dataplex-generate-data-insights") in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). Register() returns false when the type string is already present in the map, and the package's init() deliberately panics because a duplicate registration means two packages are competing for the same tool type key, which would silently break YAML config decoding.

Source

Thrown at internal/tools/dataplex/dataplexgeneratedatainsights/dataplexgeneratedatainsights.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/tools/dataplex/dataplexcommon"
	"github.com/googleapis/mcp-toolbox/internal/util"
	"github.com/googleapis/mcp-toolbox/internal/util/parameters"
)

const resourceType string = "dataplex-generate-data-insights"

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 {
	ProjectID() string
	GenerateDataInsights(ctx context.Context, location, resourcePath string, publish bool) (string, error)
}

type Config struct {
	tools.ConfigBase `yaml:",inline"`

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Search the repo for "dataplex-generate-data-insights" (grep -rn 'dataplex-generate-data-insights') and find the two packages registering it; give the new tool a unique resourceType (e.g. kebab-case tool name per conventions).
  2. Remove the stale/duplicate import or delete the obsolete tool package that no longer should be registered.
  3. If the collision is with a forked/renamed package, ensure only one copy of the tool package exists on the module path and go.mod doesn't vendor both old and new versions.
  4. Rebuild with `go build ./...` and run `go test ./internal/tools/...` to confirm the panic is gone.

Example fix

// before (copied tool, duplicate type)
const resourceType string = "dataplex-generate-data-insights"

// after (unique type for the new tool)
const resourceType string = "dataplex-generate-data-insights-report"
Defensive patterns

Strategy: validation

Validate before calling

// Before building, assert the type string is unique across the repo:
// grep -rn '"dataplex-generate-data-insights"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Expect exactly ONE registration. In Go you can also guard a custom tool's init():
func init() {
	if !tools.Register(resourceType, newConfig) {
		fmt.Printf("WARNING: %s already registered; skipping duplicate init\n", resourceType)
		return // or panic, matching project convention
	}
}

Type guard

func isRegistered(resourceType string) bool {
	_, exists := registrySnapshot[resourceType] // expose registry contents in a test helper
	return exists
}

Try / catch

// Go panics in init() cannot be recovered within init itself; recover at the top of main if you must:
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

When it happens

Trigger: Importing (directly or transitively) two packages whose init() functions call tools.Register() with the same resourceType constant "dataplex-generate-data-insights" — typically after a copy-pasted tool package kept the old const, or a rename/re-creation of the tool package left both old and new packages imported by cmd/root.go or a prebuilt-config registration list.

Common situations: A developer copies an existing tool directory (e.g. another dataplex tool) to start a new tool and forgets to change the resourceType constant; a merge/rebase re-adds an import that was removed; a fork adds a tool with a colliding type string; blank-importing two tool registration files that both claim the type.

Related errors


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