{"record":{"id":"a196d47a9e030cc6","repo":"siyuan-note/siyuan","slug":"register-mcp-tool-s-v","errorCode":null,"errorMessage":"register MCP tool [%s]: %v","messagePattern":"register MCP tool \\[(.+?)\\]: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/mcp/tools/register.go","lineNumber":161,"sourceCode":"func notifyRegistryObservers(name string, tool *Tool) {\n\tfor _, observer := range registryObservers {\n\t\tobserver(name, tool)\n\t}\n}\n\nfunc register(t *Tool) {\n\tif t.Source == \"\" {\n\t\tt.Source = \"native\"\n\t}\n\tif t.CapabilityID == \"\" {\n\t\tt.CapabilityID = BuildCapabilityID(\"native\", \"backend\", t.Name)\n\t}\n\tif t.Runtime == \"\" {\n\t\tt.Runtime = \"kernel\"\n\t}\n\tattachEncryptedBoxLeaseResolver(t)\n\tif err := SetTool(t.Name, t); err != nil {\n\t\tpanic(fmt.Sprintf(\"register MCP tool [%s]: %v\", t.Name, err))\n\t}\n}\n","sourceCodeStart":143,"sourceCodeEnd":164,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/tools/register.go#L143-L164","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nregister(&Tool{Name: \"filesystem_read\", ...})\nregister(&Tool{Name: \"filesystem_read\", ...}) // duplicate\n// after\nregister(&Tool{Name: \"filesystem_read\", ...})\nregister(&Tool{Name: \"filesystem_write\", ...}) // unique names","handlingStrategy":"try-catch","validationCode":"// developer check before startup: ensure unique tool names\nconst names = tools.map(t => t.name);\nconst dupes = names.filter((n, i) => names.indexOf(n) !== i);\nif (dupes.length) throw new Error('duplicate MCP tool names: ' + dupes.join(', '));","typeGuard":null,"tryCatchPattern":"// registration panics by design; guard the build/test pipeline instead\nfunc TestToolNamesUnique(t *testing.T) {\n  seen := map[string]bool{}\n  for _, tl := range allTools {\n    if seen[tl.Name] { t.Fatalf(\"duplicate tool name: %s\", tl.Name) }\n    seen[tl.Name] = true\n  }\n}","preventionTips":["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"],"tags":["mcp","startup","duplicate-registration","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}