siyuan-note/siyuan · critical
register MCP tool [%s]: %v
Error message
register MCP tool [%s]: %v
What it means
During package init, each built-in MCP tool is registered via register(); if SetTool rejects the tool (e.g. duplicate name, invalid definition), the kernel panics with 'register MCP tool [%s]: %v'. This is a fail-fast developer-facing invariant: tool registration happens once at startup, so any conflict indicates a programming or build-stamping error, not a runtime condition a user can recover from.
Source
Thrown at kernel/mcp/tools/register.go:161
func notifyRegistryObservers(name string, tool *Tool) {
for _, observer := range registryObservers {
observer(name, tool)
}
}
func register(t *Tool) {
if t.Source == "" {
t.Source = "native"
}
if t.CapabilityID == "" {
t.CapabilityID = BuildCapabilityID("native", "backend", t.Name)
}
if t.Runtime == "" {
t.Runtime = "kernel"
}
attachEncryptedBoxLeaseResolver(t)
if err := SetTool(t.Name, t); err != nil {
panic(fmt.Sprintf("register MCP tool [%s]: %v", t.Name, err))
}
}
View on GitHub (pinned to 8641553a1f)
Solutions
- Read the panic message to get the offending tool name, then search kernel/mcp/tools for duplicate Name values and remove or rename one
- Ensure each tool has a unique, stable name; registration order in init does not deduplicate
- Rebuild after cleaning stale generated registration code
- If this appears after a merge, diff the tool list against the pre-merge branch to find the duplicate
Example fix
// before
register(&Tool{Name: "filesystem_read", ...})
register(&Tool{Name: "filesystem_read", ...}) // duplicate
// after
register(&Tool{Name: "filesystem_read", ...})
register(&Tool{Name: "filesystem_write", ...}) // unique names Defensive patterns
Strategy: try-catch
Validate before calling
// developer check before startup: ensure unique tool names
const names = tools.map(t => t.name);
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
if (dupes.length) throw new Error('duplicate MCP tool names: ' + dupes.join(', ')); Try / catch
// registration panics by design; guard the build/test pipeline instead
func TestToolNamesUnique(t *testing.T) {
seen := map[string]bool{}
for _, tl := range allTools {
if seen[tl.Name] { t.Fatalf("duplicate tool name: %s", tl.Name) }
seen[tl.Name] = true
}
} Prevention
- Add a unit test asserting tool-name uniqueness across kernel/mcp/tools
- Keep one registration per tool file and grep for the name before adding a new tool
- Review tool renames in PRs for leftover duplicate registrations
When it happens
Trigger: Two tool definitions in kernel/mcp/tools share the same Name; SetTool returns an error for a malformed tool definition; a merge/patch introduces a duplicate init-time registration after a version change.
Common situations: A contributor adds a new tool whose name collides with an existing one; renaming a tool leaves an old registration in place; generated code registers the same tool twice across files.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- panic(err)
- tools/list returned an empty response
- tools/list repeated cursor %q
- tools/list exceeded %d pages
- unsupported server type: %s
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/a196d47a9e030cc6.
Report an issue: GitHub.