googleapis/mcp-toolbox · critical

tool type %q already registered

Error message

tool type %q already registered

What it means

This panic is thrown by the package-level init() of internal/tools/databaseinsights/getadvancedaggregatedwaiteventstats when tools.Register(resourceType, newConfig) returns false. Register returns false only if the tool type key "databaseinsights-get-advanced-aggregated-wait-event-stats" already exists in the global registry, meaning two registrations raced or the same package/type was registered twice. It is a deliberate fail-fast to prevent silent tool overwrites.

Source

Thrown at internal/tools/databaseinsights/getadvancedaggregatedwaiteventstats/getadvancedaggregatedwaiteventstats.go:35

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	yaml "github.com/goccy/go-yaml"
	"github.com/googleapis/mcp-toolbox/internal/sources"
	"github.com/googleapis/mcp-toolbox/internal/sources/databaseinsights"
	"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 = "databaseinsights-get-advanced-aggregated-wait-event-stats"

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 {
	FetchWaitEventStats(ctx context.Context, req *databaseinsights.FetchWaitEventStatsRequest) (*databaseinsights.FetchWaitEventStatsResponse, error)
}

type Config struct {
	tools.ConfigBase `yaml:",inline"`
	Type             string                 `yaml:"type" validate:"required"`

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Grep the repo for "databaseinsights-get-advanced-aggregated-wait-event-stats" and remove or rename the duplicate resourceType declaration.
  2. Ensure each tool package has a unique resourceType const (follow kebab-case <source>-<tool> convention).
  3. Run `go list -deps ./... | sort | uniq -d` / tidy go.mod so the package is only linked once.
  4. If a test re-triggers init, restructure so registration happens once per process (package init is idempotent per binary; duplicates come from duplicated consts).

Example fix

// before
const resourceType string = "databaseinsights-get-advanced-aggregated-wait-event-stats" // duplicated in another file
// after
const resourceType string = "databaseinsights-get-advanced-aggregated-wait-event-stats-v2" // or remove the duplicate declaration
Defensive patterns

Strategy: validation

Validate before calling

const want = "databaseinsights-get-advanced-aggregated-wait-event-stats"
if n := strings.Count(runtime_AllDecls(), want); n > 1 {
    panic(fmt.Sprintf("resourceType %s declared %d times", want, n))
}

Try / catch

// Go panics cannot be caught in normal code; recover only at a boundary:
func safeInit() {
    defer func() {
        if r := recover(); r != nil {
            log.Fatalf("tool registration failed: %v", r)
        }
    }()
    registerTool(resourceType, newConfig)
}

Prevention

When it happens

Trigger: Calling tools.Register twice with the same resourceType (duplicate const value across files/packages, a package linked twice via conflicting module versions, or a test/helper that re-runs registration code).

Common situations: Copy-pasting a tool package and forgetting to change the resourceType const; merging branches that both introduce the same tool type; import cycles or vendoring that pull two copies of the package into the build; test harnesses that initialize the registry more than once.

Related errors


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