From 6735a6ab265bbf5727fd06f886185af3adffd46d Mon Sep 17 00:00:00 2001 From: ollie Date: Tue, 28 Jul 2026 23:21:42 +0200 Subject: [PATCH] add built-in tool_list and tool_load tools --- pkg/tools/execute/code.go | 47 ++++++++++++++++++++++++++++++++++++- pkg/tools/execute/server.go | 26 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/pkg/tools/execute/code.go b/pkg/tools/execute/code.go index a0dc432..4592e14 100644 --- a/pkg/tools/execute/code.go +++ b/pkg/tools/execute/code.go @@ -43,7 +43,7 @@ var languagePatterns = map[string][]*regexp.Regexp{ "": bashPatterns, } -// Dispatch routes a shell tool call. +// Dispatch routes tool calls to the appropriate handler. func (e *Server) Dispatch(ctx context.Context, name string, args json.RawMessage) (string, error) { if e.OnPreDispatch != nil { e.OnPreDispatch() @@ -52,6 +52,10 @@ func (e *Server) Dispatch(ctx context.Context, name string, args json.RawMessage switch name { case "shell": return dispatchShell(ctx, e, args) + case "tool_list": + return dispatchToolList(ctx, e, args) + case "tool_load": + return dispatchToolLoad(ctx, e, args) default: return "", fmt.Errorf("unknown execute tool: %s", name) } @@ -147,3 +151,44 @@ func dispatchShell(ctx context.Context, e *Server, args json.RawMessage) (string } return e.executeWithStdin(ctx, a.Cmd, "bash", timeout, sandboxName, false, "") } + +// dispatchToolList lists all available tools from the global registry. +func dispatchToolList(ctx context.Context, e *Server, args json.RawMessage) (string, error) { + if e.toolRegistry == nil { + return "", fmt.Errorf("tool_list: no registry available") + } + summaries := e.toolRegistry.Summaries() + if len(summaries) == 0 { + return "(no tools found)", nil + } + var out strings.Builder + for _, s := range summaries { + out.WriteString(s.Name) + if s.Description != "" { + out.WriteString(" — ") + out.WriteString(s.Description) + } + out.WriteString("\n") + } + return strings.TrimRight(out.String(), "\n"), nil +} + +// dispatchToolLoad loads a tool into the current session. +func dispatchToolLoad(ctx context.Context, e *Server, args json.RawMessage) (string, error) { + var a struct { + Name string `json:"name"` + } + if err := json.Unmarshal(args, &a); err != nil { + return "", fmt.Errorf("tool_load: bad args: %w", err) + } + if a.Name == "" { + return "", fmt.Errorf("tool_load: name is required") + } + if e.toolRegistry == nil || e.sessionID == "" { + return "", fmt.Errorf("tool_load: no session registry") + } + if err := e.toolRegistry.Load(e.sessionID, a.Name); err != nil { + return "", fmt.Errorf("tool_load: %w", err) + } + return fmt.Sprintf("loaded: %s", a.Name), nil +} diff --git a/pkg/tools/execute/server.go b/pkg/tools/execute/server.go index acd8ea1..12dc177 100644 --- a/pkg/tools/execute/server.go +++ b/pkg/tools/execute/server.go @@ -150,6 +150,32 @@ timeout applies to each call (default: 30s). A non-zero exit is an error.`, } }`), }, + { + Name: "tool_list", + Description: `List all available tools that can be loaded. + +Usage: {"name": "toolname"} — if name is provided, loads that tool. +Otherwise lists all tools with descriptions.`, + InputSchema: json.RawMessage(`{ + "type": "object", + "properties": {} + }`), + }, + { + Name: "tool_load", + Description: `Load a tool by name into the current session. + +Usage: {"name": "toolname"} + +After loading, the tool becomes a native callable function.`, + InputSchema: json.RawMessage(`{ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string", "description": "Tool name to load."} + } + }`), + }, } if e.toolRegistry != nil && e.sessionID != "" {