MCPcopy
hub / github.com/docker/docker-agent / execute

Method execute

pkg/tools/builtin/shell/script_shell.go:209–285  ·  view source on GitHub ↗
(ctx context.Context, toolConfig *latest.ScriptShellToolConfig, toolCall tools.ToolCall)

Source from the content-addressed store, hash-verified

207}
208
209func (t *ScriptToolSet) execute(ctx context.Context, toolConfig *latest.ScriptShellToolConfig, toolCall tools.ToolCall) (*tools.ToolCallResult, error) {
210 var params map[string]any
211 if toolCall.Function.Arguments != "" {
212 if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &params); err != nil {
213 return nil, fmt.Errorf("invalid arguments: %w", err)
214 }
215 }
216
217 // working_dir accepts ~, $VAR, ${VAR} and ${env.VAR} like every other
218 // working_dir field (issue #2615).
219 workingDir := path.ExpandWorkingDir("script shell working_dir", toolConfig.WorkingDir)
220
221 // Stamp the script_shell call shape onto the active span. Cmd
222 // ships unconditionally for the same reason as shell.RunShell —
223 // see that comment for the redact-at-collector guidance.
224 if span := trace.SpanFromContext(ctx); span.IsRecording() {
225 span.SetAttributes(
226 attribute.String("cagent.tool.script_shell.tool_name", toolCall.Function.Name),
227 attribute.String("cagent.tool.script_shell.cmd", toolConfig.Cmd),
228 attribute.String("cagent.tool.script_shell.cwd", cmp.Or(workingDir, ".")),
229 )
230 }
231
232 shell, argsPrefix := shellpath.DetectShell()
233
234 cmd := exec.CommandContext(ctx, shell, append(argsPrefix, toolConfig.Cmd)...)
235 cmd.Dir = workingDir
236 // Per-call clone: appending onto t.env would mutate the shared
237 // backing array under concurrent calls. Expand nil to os.Environ()
238 // so a nil t.env still inherits the parent env (a non-nil empty
239 // slice would strip it).
240 base := t.env
241 if base == nil {
242 base = os.Environ()
243 }
244 envCopy := make([]string, len(base), len(base)+len(toolConfig.Env)+len(toolConfig.Args))
245 copy(envCopy, base)
246 // Per-tool env overrides the toolset-level env (exec.Cmd dedupes with
247 // last-wins). Only the plain ${env.X} form is expanded; $X and ${X}
248 // stay literal because env values may legitimately contain $ (issue
249 // #2615).
250 for _, key := range slices.Sorted(maps.Keys(toolConfig.Env)) {
251 envCopy = append(envCopy, key+"="+path.ExpandEnvRefs(toolConfig.Env[key]))
252 }
253 for key, value := range params {
254 if value == nil {
255 continue
256 }
257 // Only forward arguments declared in the tool's schema. The
258 // LLM may hallucinate extra keys (e.g. LD_PRELOAD, PATH);
259 // without this filter they would land verbatim in the
260 // spawned process's environment.
261 if _, declared := toolConfig.Args[key]; !declared {
262 continue
263 }
264 valueStr := fmt.Sprintf("%v", value)
265 // A NUL byte mid-string silently truncates env entries at the
266 // execve boundary; refuse rather than spawn a process with a

Callers 1

ToolsMethod · 0.95

Calls 11

ExpandWorkingDirFunction · 0.92
DetectShellFunction · 0.92
ExpandEnvRefsFunction · 0.92
ResultErrorFunction · 0.92
ResultSuccessFunction · 0.92
newCommandOutputFunction · 0.85
IsRecordingMethod · 0.65
RunMethod · 0.65
SetAttributesMethod · 0.45
StringMethod · 0.45
KeysMethod · 0.45

Tested by

no test coverage detected