Skip to content

Commit 362a24b

Browse files
committed
Default to string for script tool arguments
Signed-off-by: David Gageot <david.gageot@docker.com>
1 parent 07f6f6c commit 362a24b

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

pkg/tools/builtin/script_shell.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"maps"
89
"os"
910
"os/exec"
1011
"slices"
@@ -110,7 +111,7 @@ func (t *ScriptShellTool) Tools(context.Context) ([]tools.Tool, error) {
110111

111112
inputSchema, err := tools.SchemaToMap(map[string]any{
112113
"type": "object",
113-
"properties": cfg.Args,
114+
"properties": defaultPropertyTypes(cfg.Args, "string"),
114115
"required": cfg.Required,
115116
})
116117
if err != nil {
@@ -158,3 +159,20 @@ func (t *ScriptShellTool) execute(ctx context.Context, toolConfig *latest.Script
158159

159160
return tools.ResultSuccess(limitOutput(string(output))), nil
160161
}
162+
163+
// defaultPropertyTypes returns a copy of properties where any property
164+
// missing a "type" field gets the given default type.
165+
func defaultPropertyTypes(properties map[string]any, defaultType string) map[string]any {
166+
result := make(map[string]any, len(properties))
167+
for k, v := range properties {
168+
if prop, ok := v.(map[string]any); ok && prop["type"] == nil {
169+
propCopy := make(map[string]any, len(prop)+1)
170+
maps.Copy(propCopy, prop)
171+
propCopy["type"] = defaultType
172+
result[k] = propCopy
173+
continue
174+
}
175+
result[k] = v
176+
}
177+
return result
178+
}

pkg/tools/builtin/script_shell_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,38 @@ func TestNewScriptShellTool_MissingRequired(t *testing.T) {
115115
require.Nil(t, tool)
116116
require.ErrorContains(t, err, "tool 'docker_images' has required arg 'img' which is not defined in args")
117117
}
118+
119+
func TestNewScriptShellTool_ArgWithoutType(t *testing.T) {
120+
shellTools := map[string]latest.ScriptShellToolConfig{
121+
"greet": {
122+
Description: "Greet someone",
123+
Cmd: "echo Hello $name",
124+
Args: map[string]any{
125+
"name": map[string]any{
126+
"description": "Name to greet",
127+
},
128+
},
129+
Required: []string{"name"},
130+
},
131+
}
132+
133+
tool, err := NewScriptShellTool(shellTools, nil)
134+
require.NoError(t, err)
135+
136+
allTools, err := tool.Tools(t.Context())
137+
require.NoError(t, err)
138+
assert.Len(t, allTools, 1)
139+
140+
schema, err := json.Marshal(allTools[0].Parameters)
141+
require.NoError(t, err)
142+
assert.JSONEq(t, `{
143+
"type": "object",
144+
"properties": {
145+
"name": {
146+
"description": "Name to greet",
147+
"type": "string"
148+
}
149+
},
150+
"required": ["name"]
151+
}`, string(schema))
152+
}

0 commit comments

Comments
 (0)