Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var EnvironmentAddServiceTool = &Tool{ Definition: newEnvironmentTool( "environment_add_service", "Add a service to the environment (e.g. database, cache, etc.)", mcp.WithString("name", mcp.Description("The name of the service to start."), mcp.Required(), ), mcp.WithString("image", mcp.Description("The image of the service to start."), mcp.Required(), ), mcp.WithString("command", mcp.Description("The command to start the service. If not provided the image default command will be used."), ), mcp.WithArray("ports", mcp.Description("Ports to expose. For each port, returns the container_internal (for use by environments) and host_external (for use by the user) address."), mcp.Items(map[string]any{"type": "number"}), ), mcp.WithArray("envs", mcp.Description("The environment variables to set (e.g. `[\"FOO=bar\", \"BAZ=qux\"]`)."), mcp.Items(map[string]any{"type": "string"}), ), mcp.WithArray("secrets", mcp.Description(`Secret references in the format of "SECRET_NAME=schema://value Secrets will be available in the environment as environment variables ($SECRET_NAME). Supported schemas are: - file://PATH: local file path - env://NAME: environment variable - op://<vault-name>/<item-name>/[section-name/]<field-name>: 1Password secret `), mcp.Items(map[string]any{"type": "string"}), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } serviceName, err := request.RequireString("name") if err != nil { return nil, err } image, err := request.RequireString("image") if err != nil { return nil, err } command := request.GetString("command", "") ports := []int{} if portList, ok := request.GetArguments()["ports"].([]any); ok { for _, port := range portList { ports = append(ports, int(port.(float64))) } } envs := request.GetStringSlice("envs", []string{}) secrets := request.GetStringSlice("secrets", []string{}) service, err := env.AddService(ctx, request.GetString("explanation", ""), &environment.ServiceConfig{ Name: serviceName, Image: image, Command: command, ExposedPorts: ports, Env: envs, Secrets: secrets, }) if err != nil { return nil, fmt.Errorf("failed to add service: %w", err) } if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return nil, fmt.Errorf("failed to update env: %w", err) } output, err := json.Marshal(service) if err != nil { return nil, fmt.Errorf("failed to marshal service: %w", err) } return mcp.NewToolResultText(fmt.Sprintf("Service added and started successfully: %s", string(output))), nil }, }
View Source
var EnvironmentCheckpointTool = &Tool{ Definition: newEnvironmentTool( "environment_checkpoint", "Checkpoints an environment in its current state as a container.", mcp.WithString("destination", mcp.Description("Container image destination to checkpoint to (e.g. registry.com/user/image:tag"), mcp.Required(), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { _, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } destination, err := request.RequireString("destination") if err != nil { return nil, err } endpoint, err := env.Checkpoint(ctx, destination) if err != nil { return nil, fmt.Errorf("failed to checkpoint environment: %w", err) } return mcp.NewToolResultText(fmt.Sprintf("Checkpoint pushed to %q. You MUST use the full content addressed (@sha256:...) reference in `docker` commands. The entrypoint is set to `sh`, keep that in mind when giving commands to the container.", endpoint)), nil }, }
View Source
var EnvironmentConfigTool = &Tool{ Definition: newEnvironmentTool( "environment_config", "Make environment config changes such as base image and setup commands."+ "If the environment is missing any tools or instructions, you MUST call this function to update the environment."+ "You MUST update the environment with any useful tools. You will be resumed with no other context than the information provided here", mcp.WithObject("config", mcp.Required(), mcp.Properties(map[string]any{ "base_image": map[string]any{ "type": "string", "description": "Base image for the environment", }, "setup_commands": map[string]any{ "type": "array", "description": "Commands that should be executed on top of the base image to set up the environment. Similar to `RUN` instructions in Dockerfiles.", "items": map[string]any{"type": "string"}, }, "envs": map[string]any{ "type": "array", "description": "The environment variables to set (e.g. `[\"FOO=bar\", \"BAZ=qux\"]`).", "items": map[string]any{"type": "string"}, }, }), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } updatedConfig := env.State.Config.Copy() newConfig, ok := request.GetArguments()["config"].(map[string]any) if !ok { return nil, errors.New("invalid config") } if baseImage, ok := newConfig["base_image"].(string); ok { updatedConfig.BaseImage = baseImage } if setupCommands, ok := newConfig["setup_commands"].([]any); ok { updatedConfig.SetupCommands = make([]string, len(setupCommands)) for i, command := range setupCommands { updatedConfig.SetupCommands[i] = command.(string) } } if envs, ok := newConfig["envs"].([]any); ok { updatedConfig.Env = make([]string, len(envs)) for i, env := range envs { updatedConfig.Env[i] = env.(string) } } if err := env.UpdateConfig(ctx, updatedConfig); err != nil { return nil, fmt.Errorf("unable to update the environment: %w", err) } if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return nil, fmt.Errorf("failed to update repository: %w", err) } out, err := marshalEnvironment(env) if err != nil { return nil, fmt.Errorf("failed to marshal environment: %w", err) } message := fmt.Sprintf(`SUCCESS: Configuration successfully applied. Environment has been restarted, all previous commands have been lost. IMPORTANT: The configuration changes are LOCAL to this environment. TELL THE USER: To make these changes persistent, they will have to run "cu config import %s" %s `, env.ID, out) return mcp.NewToolResultText(message), nil }, }
View Source
var EnvironmentCreateTool = &Tool{ Definition: newRepositoryTool( "environment_create", `Creates a new development environment. The environment is the result of a the setups commands on top of the base image. Environment configuration is managed by the user via cu config commands.`, mcp.WithString("title", mcp.Description("Short description of the work that is happening in this environment."), mcp.Required(), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, err := openRepository(ctx, request) if err != nil { return nil, err } title, err := request.RequireString("title") if err != nil { return nil, err } dag, ok := ctx.Value(daggerClientKey{}).(*dagger.Client) if !ok { return nil, fmt.Errorf("dagger client not found in context") } env, err := repo.Create(ctx, dag, title, request.GetString("explanation", "")) if err != nil { return nil, fmt.Errorf("failed to create environment: %w", err) } out, err := marshalEnvironment(env) if err != nil { return nil, fmt.Errorf("failed to marshal environment: %w", err) } dirty, status, err := repo.IsDirty(ctx) if err != nil { return nil, fmt.Errorf("unable to check if environment is dirty: %w", err) } if !dirty { return mcp.NewToolResultText(out), nil } return mcp.NewToolResultText(fmt.Sprintf(`%s CRITICAL: You MUST inform the user that the repository %s has uncommitted changes that are NOT included in this environment. The environment was created from the last committed state only. Uncommitted changes detected: %s You MUST tell the user: To include these changes in the environment, they need to commit them first using git commands outside the environment.`, out, request.GetString("environment_source", ""), status)), nil }, }
View Source
var EnvironmentFileDeleteTool = &Tool{ Definition: newEnvironmentTool( "environment_file_delete", "Deletes a file at the specified path.", mcp.WithString("target_file", mcp.Description("Path of the file to delete, absolute or relative to the workdir."), mcp.Required(), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } targetFile, err := request.RequireString("target_file") if err != nil { return nil, err } if err := env.FileDelete(ctx, request.GetString("explanation", ""), targetFile); err != nil { return nil, fmt.Errorf("failed to delete file: %w", err) } if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return nil, fmt.Errorf("failed to update env: %w", err) } return mcp.NewToolResultText(fmt.Sprintf("file %s deleted successfully and committed to container-use/ remote", targetFile)), nil }, }
View Source
var EnvironmentFileListTool = &Tool{ Definition: newEnvironmentTool( "environment_file_list", "List the contents of a directory", mcp.WithString("path", mcp.Description("Path of the directory to list contents of, absolute or relative to the workdir"), mcp.Required(), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { _, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } path, err := request.RequireString("path") if err != nil { return nil, err } out, err := env.FileList(ctx, path) if err != nil { return nil, fmt.Errorf("failed to list directory: %w", err) } return mcp.NewToolResultText(out), nil }, }
View Source
var EnvironmentFileReadTool = &Tool{ Definition: newEnvironmentTool( "environment_file_read", "Read the contents of a file, specifying a line range or the entire file.", mcp.WithString("target_file", mcp.Description("Path of the file to read, absolute or relative to the workdir"), mcp.Required(), ), mcp.WithBoolean("should_read_entire_file", mcp.Description("Whether to read the entire file. Defaults to false."), ), mcp.WithNumber("start_line_one_indexed_inclusive", mcp.Description("The one-indexed line number to start reading from (inclusive)."), ), mcp.WithNumber("end_line_one_indexed_inclusive", mcp.Description("The one-indexed line number to end reading at (inclusive)."), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { _, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } targetFile, err := request.RequireString("target_file") if err != nil { return nil, err } shouldReadEntireFile := request.GetBool("should_read_entire_file", false) startLineOneIndexedInclusive := request.GetInt("start_line_one_indexed_inclusive", 0) endLineOneIndexedInclusive := request.GetInt("end_line_one_indexed_inclusive", 0) fileContents, err := env.FileRead(ctx, targetFile, shouldReadEntireFile, startLineOneIndexedInclusive, endLineOneIndexedInclusive) if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } return mcp.NewToolResultText(fileContents), nil }, }
View Source
var EnvironmentFileWriteTool = &Tool{ Definition: newEnvironmentTool( "environment_file_write", "Write the contents of a file.", mcp.WithString("target_file", mcp.Description("Path of the file to write, absolute or relative to the workdir."), mcp.Required(), ), mcp.WithString("contents", mcp.Description("Full text content of the file you want to write."), mcp.Required(), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } targetFile, err := request.RequireString("target_file") if err != nil { return nil, err } contents, err := request.RequireString("contents") if err != nil { return nil, err } if err := env.FileWrite(ctx, request.GetString("explanation", ""), targetFile, contents); err != nil { return nil, fmt.Errorf("failed to write file: %w", err) } if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return nil, fmt.Errorf("unable to update the environment: %w", err) } return mcp.NewToolResultText(fmt.Sprintf("file %s written successfully and committed to container-use/ remote", targetFile)), nil }, }
View Source
var EnvironmentListTool = &Tool{ Definition: newRepositoryTool( "environment_list", "List available environments", ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, err := openRepository(ctx, request) if err != nil { return nil, err } envInfos, err := repo.List(ctx) if err != nil { return nil, fmt.Errorf("invalid source: %w", err) } responses := make([]EnvironmentResponse, len(envInfos)) for i, envInfo := range envInfos { responses[i] = *environmentResponseFromEnvInfo(envInfo) } out, err := json.Marshal(responses) if err != nil { return nil, err } return mcp.NewToolResultText(string(out)), nil }, }
View Source
var EnvironmentOpenTool = &Tool{ Definition: newEnvironmentTool( "environment_open", "Opens an existing environment. Return format is same as environment_create.", ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { _, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } return EnvironmentToCallResult(env) }, }
View Source
var EnvironmentRunCmdTool = &Tool{ Definition: newEnvironmentTool( "environment_run_cmd", "Run a terminal command inside a NEW container within the environment.", mcp.WithString("command", mcp.Description("The terminal command to execute. If empty, the environment's default command is used."), ), mcp.WithString("shell", mcp.Description("The shell that will be interpreting this command (default: sh)"), ), mcp.WithBoolean("background", mcp.Description(`Run the command in the background Must ALWAYS be set for long running command (e.g. http server). Failure to do so will result in the tool being stuck, awaiting for the command to finish.`, ), ), mcp.WithBoolean("use_entrypoint", mcp.Description("Use the image entrypoint, if present, by prepending it to the args."), ), mcp.WithArray("ports", 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."), mcp.Items(map[string]any{"type": "number"}), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } command := request.GetString("command", "") shell := request.GetString("shell", "sh") updateRepo := func() error { if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return fmt.Errorf("failed to update repository: %w", err) } return nil } background := request.GetBool("background", false) if background { ports := []int{} if portList, ok := request.GetArguments()["ports"].([]any); ok { for _, port := range portList { ports = append(ports, int(port.(float64))) } } endpoints, runErr := env.RunBackground(ctx, command, shell, ports, request.GetBool("use_entrypoint", false)) if err := updateRepo(); err != nil { return nil, err } if runErr != nil { return nil, fmt.Errorf("failed to run command: %w", runErr) } out, err := json.Marshal(endpoints) if err != nil { return nil, err } return mcp.NewToolResultText(fmt.Sprintf(`Command started in the background in NEW container. Endpoints are %s To access from the user's machine: use host_external. To access from other commands in this environment: use environment_internal. Any changes to the container workdir (%s) WILL NOT be committed to container-use/%s Background commands are unaffected by filesystem and any other kind of changes. You need to start a new command for changes to take effect.`, string(out), env.State.Config.Workdir, env.ID)), nil } stdout, runErr := env.Run(ctx, command, shell, request.GetBool("use_entrypoint", false)) if err := updateRepo(); err != nil { return nil, err } if runErr != nil { return nil, fmt.Errorf("failed to run command: %w", runErr) } return mcp.NewToolResultText(fmt.Sprintf("%s\n\nAny changes to the container workdir (%s) have been committed and pushed to container-use/ remote", stdout, env.State.Config.Workdir)), nil }, }
View Source
var EnvironmentUpdateMetadataTool = &Tool{ Definition: newEnvironmentTool( "environment_update_metadata", "Update environment metadata such as title. This updates the descriptive information about what work is being done in the environment.", mcp.WithString("title", mcp.Description("Updated title describing the work being done in this environment."), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { repo, env, err := openEnvironment(ctx, request) if err != nil { return nil, err } if title := request.GetString("title", ""); title != "" { env.State.Title = title } if err := repo.Update(ctx, env, request.GetString("explanation", "")); err != nil { return nil, fmt.Errorf("unable to update the environment: %w", err) } out, err := marshalEnvironment(env) if err != nil { return nil, fmt.Errorf("failed to marshal environment: %w", err) } return mcp.NewToolResultText(fmt.Sprintf("Environment metadata updated successfully.\n%s", out)), nil }, }
Functions ¶
func EnvironmentInfoToCallResult ¶ added in v0.1.0
func EnvironmentInfoToCallResult(envInfo *environment.EnvironmentInfo) (*mcp.CallToolResult, error)
func EnvironmentToCallResult ¶
func EnvironmentToCallResult(env *environment.Environment) (*mcp.CallToolResult, error)
Types ¶
type EnvironmentResponse ¶
type EnvironmentResponse struct {
ID string `json:"id"`
Title string `json:"title"`
Config *environment.EnvironmentConfig `json:"config"`
RemoteRef string `json:"remote_ref"`
CheckoutCommand string `json:"checkout_command_to_share_with_user"`
LogCommand string `json:"log_command_to_share_with_user"`
DiffCommand string `json:"diff_command_to_share_with_user"`
Services []*environment.Service `json:"services,omitempty"`
}
Click to show internal directories.
Click to hide internal directories.