(singleTenant bool)
| 519 | } |
| 520 | |
| 521 | func createEnvironmentRunCmdTool(singleTenant bool) *Tool { |
| 522 | return &Tool{ |
| 523 | Definition: newEnvironmentTool( |
| 524 | envToolOptions{ |
| 525 | name: "environment_run_cmd", |
| 526 | description: "Run a terminal command inside a NEW container within the environment.", |
| 527 | useCurrentEnvironment: singleTenant, |
| 528 | }, |
| 529 | mcp.WithString("command", |
| 530 | mcp.Description("The terminal command to execute. If empty, the environment's default command is used."), |
| 531 | ), |
| 532 | mcp.WithString("shell", |
| 533 | mcp.Description("The shell that will be interpreting this command (default: sh)"), |
| 534 | ), |
| 535 | mcp.WithBoolean("background", |
| 536 | mcp.Description(`Run the command in the background |
| 537 | Must ALWAYS be set for long running command (e.g. http server). |
| 538 | Failure to do so will result in the tool being stuck, awaiting for the command to finish.`, |
| 539 | ), |
| 540 | ), |
| 541 | mcp.WithBoolean("use_entrypoint", |
| 542 | mcp.Description("Use the image entrypoint, if present, by prepending it to the args."), |
| 543 | ), |
| 544 | mcp.WithArray("ports", |
| 545 | mcp.Description("Ports to expose. Only works with background environments. For each port, returns the environment_internal (for use inside environments) and host_external (for use by the user) addresses."), |
| 546 | mcp.Items(map[string]any{"type": "number"}), |
| 547 | ), |
| 548 | ), |
| 549 | Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
| 550 | repo, env, err := openEnvironment(ctx, request) |
| 551 | if err != nil { |
| 552 | return nil, err |
| 553 | } |
| 554 | |
| 555 | command := request.GetString("command", "") |
| 556 | shell := request.GetString("shell", "sh") |
| 557 | |
| 558 | updateRepo := func() error { |
| 559 | if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { |
| 560 | return fmt.Errorf("failed to update repository: %w", err) |
| 561 | } |
| 562 | return nil |
| 563 | } |
| 564 | |
| 565 | background := request.GetBool("background", false) |
| 566 | if background { |
| 567 | ports := []int{} |
| 568 | if portList, ok := request.GetArguments()["ports"].([]any); ok { |
| 569 | for _, port := range portList { |
| 570 | ports = append(ports, int(port.(float64))) |
| 571 | } |
| 572 | } |
| 573 | endpoints, runErr := env.RunBackground(ctx, command, shell, ports, request.GetBool("use_entrypoint", false)) |
| 574 | // We want to update the repository even if the command failed. |
| 575 | if err := updateRepo(); err != nil { |
| 576 | return nil, err |
| 577 | } |
| 578 | if runErr != nil { |
no test coverage detected