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 oceanbasesql package registers its config factory under resourceType "oceanbase-sql" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). A false return means the type string was already claimed by another package's init(), so this init() panics — the registry must keep a one-to-one mapping between YAML `kind` values and config factories for DecodeConfig to work.

Source

Thrown at internal/tools/oceanbase/oceanbasesql/oceanbasesql.go:34

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 = "oceanbase-sql"

func init() {
	if !tools.Register(resourceType, newConfig) {
		panic(fmt.Sprintf("tool type %q already registered", resourceType))
	}
}

type compatibleSource interface {
	OceanBasePool() *sql.DB
	RunSQL(context.Context, string, []any) (any, error)
}

type Config struct {
	tools.ConfigBase   `yaml:",inline"`
	Type               string                 `yaml:"type" validate:"required"`
	Source             string                 `yaml:"source" validate:"required"`
	Statement          string                 `yaml:"statement" validate:"required"`
	Parameters         parameters.Parameters  `yaml:"parameters"`
	TemplateParameters parameters.Parameters  `yaml:"templateParameters"`
	Annotations        *tools.ToolAnnotations `yaml:"annotations,omitempty"`
}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. grep -rn '"oceanbase-sql"' to find every tools.Register call with this type; keep exactly one.
  2. Rename the resourceType in the new/renamed tool package to a unique kebab-case value.
  3. Remove duplicate imports and deduplicate module copies (go mod tidy, audit replace/vendor).
  4. Run `go build ./...` and `go test ./internal/tools/oceanbase/oceanbasesql/...` to confirm the fix.

Example fix

// before (forked copy)
const resourceType string = "oceanbase-sql"

// after
const resourceType string = "oceanbase-sql-tenant"
Defensive patterns

Strategy: validation

Validate before calling

// Verify uniqueness before building:
// grep -rn '"oceanbase-sql"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Exactly one match expected. Soft guard for forked 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

When it happens

Trigger: Two init() functions register "oceanbase-sql": a duplicated/renamed copy of oceanbasesql still using the constant, the package linked under two import paths, or a hand-added tool reusing the built-in type string.

Common situations: Copying oceanbasesql (or mysqlsql) as a template and forgetting the const; merge conflicts that retain both old and new package directories; vendoring/forking the module so the package initializes twice.

Related errors


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