siyuan-note/siyuan · error
tool handler is not configured
Error message
tool handler is not configured
What it means
A registered MCP tool has neither ContextHandler nor Handler set, so when it is invoked the server records 'tool handler is not configured' as a tool error result. This is an internal registration defect: a tool was advertised without an implementation.
Solutions
- Assign either ContextHandler(ctx, arguments) or Handler(arguments) on the tool at registration time
- If the tool is intentionally unavailable, do not register it (or filter it from the tools list) instead of registering with nil handlers
- Add a registration-time assertion/log that flags tools with no handler
Example fix
// before
server.AddTool(&mcpsdk.Tool{Name: "export"}) // no handler
// after
server.AddTool(&mcpsdk.Tool{Name: "export", Handler: exportHandler}) Defensive patterns
Strategy: validation
Validate before calling
func validateTool(t *Tool) error {
if t.ContextHandler == nil && t.Handler == nil {
return fmt.Errorf("tool %q has no handler", t.Name)
}
return nil
} Try / catch
if result.IsError != nil && *result.IsError {
if strings.Contains(resultText(result), "tool handler is not configured") {
// fix tool registration on the server side
}
} Prevention
- Assert handler presence whenever a tool is registered
- Filter out tools whose handler is conditionally unavailable instead of registering nil
- Cover every registered tool with a smoke test invocation
When it happens
Trigger: A tools/call targets a tool whose struct instance has nil ContextHandler and nil Handler — typically a tool added to the registry without assigning its handler function, or one whose handler was conditionally omitted (e.g. capability disabled) but still registered.
Common situations: Plugin/extension code registering tools declaratively and forgetting the handler; refactoring that renamed a handler function leaving the field nil; building tools from config where a handler mapping is missing.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- invalid input schema
- invalid output schema
- root type must be "object"
- tool handler unavailable
- attr must be a string or null (got %T)
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/4e7d651d6f1139aa.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/server.go:302
if errors.Is(err, model.ErrEncryptedBoxNotUnlocked) {
return toolErrorResult("encrypted notebook is locked, please unlock it first"), nil
}
logging.LogWarnf("mcp: acquire encrypted notebook operations for tool [%s] failed: %v", name, err)
return toolErrorResult(err.Error()), nil
}
}
defer releaseBoxLeases()
var (
result tools.CallToolResult
err error
)
if tool.ContextHandler != nil {
result, err = tool.ContextHandler(ctx, arguments)
} else if tool.Handler != nil {
result, err = tool.Handler(arguments)
} else {
err = fmt.Errorf("tool handler is not configured")
}
if err != nil {
return toolErrorResult(err.Error()), nil
}
if err = validator.ValidateOutputContext(ctx, result); err != nil {
return toolErrorResult(fmt.Sprintf(
"invalid tool output after execution; execution result may have side effects and must not be retried automatically: %v",
err)), nil
}
content := make([]mcpsdk.Content, 0, len(result.Content))
for _, item := range result.Content {
converted, convertErr := convertContentItem(item)
if convertErr != nil {
return toolErrorResult(fmt.Sprintf(
"invalid tool content after execution; execution result may have side effects and must not be retried automatically: %v",
convertErr)), nil
}View on GitHub (pinned to 9f775e8a12)