Ctrl K
ComunidadGuías…

internal/mcptools/manage_branches.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package mcptools

import (
	"context"
	"errors"
	"log"
	"strconv"

	mcpprot "github.com/modelcontextprotocol/go-sdk/mcp"
)

var (
	branchCreateAccess = Access{Perm: "branch:create", Fallback: FallbackAdminOnly}
	branchDeleteAccess = Access{Perm: "branch:delete", Fallback: FallbackAdminOnly}
)

type CreateBranchInput struct {
	AppId  string `json:"appId" jsonschema:"the app id, as returned by get_apps"`
	Branch string `json:"branch" jsonschema:"the branch name to create"`
}

type CreateBranchOutput struct {
	BranchId string `json:"branchId"`
	Branch   string `json:"branch"`
}

func createBranchHandler(deps Deps) func(ctx context.Context, req *mcpprot.CallToolRequest, input CreateBranchInput) (*mcpprot.CallToolResult, CreateBranchOutput, error) {
	return func(ctx context.Context, req *mcpprot.CallToolRequest, input CreateBranchInput) (*mcpprot.CallToolResult, CreateBranchOutput, error) {
		ctx, principal, err := requireAppPermission(ctx, deps, req, input.AppId, branchCreateAccess)
		if err != nil {
			return nil, CreateBranchOutput{}, err
		}
		if input.Branch == "" {
			return nil, CreateBranchOutput{}, errors.New("branch is required")
		}
		branchId, err := deps.BranchWriter.CreateBranch(ctx, input.AppId, input.Branch)
		if err != nil {
			return nil, CreateBranchOutput{}, writeError(err, "create the branch", "mcp create_branch", principal, input.AppId)
		}
		// Ids travel as strings: an int64 as a JSON number corrupts past 2^53.
		return nil, CreateBranchOutput{BranchId: strconv.FormatInt(branchId, 10), Branch: input.Branch}, nil
	}
}

func registerCreateBranch(server *mcpprot.Server, deps Deps) {
	mcpprot.AddTool(server, &mcpprot.Tool{
		Name:        "create_branch",
		Description: "Create a branch on an app (appId and branch required). Requires the branch:create permission.",
		Annotations: &mcpprot.ToolAnnotations{
			Title:           "Create branch",
			DestructiveHint: boolPtr(false),
			IdempotentHint:  false,
		},
	}, createBranchHandler(deps))
}

type DeleteBranchInput struct {
	AppId  string `json:"appId" jsonschema:"the app id, as returned by get_apps"`
	Branch string `json:"branch" jsonschema:"the branch name to delete, as returned by get_branches"`
}

type DeleteBranchOutput struct {
	Deleted bool   `json:"deleted"`
	Branch  string `json:"branch"`
}

func deleteBranchHandler(deps Deps) func(ctx context.Context, req *mcpprot.CallToolRequest, input DeleteBranchInput) (*mcpprot.CallToolResult, DeleteBranchOutput, error) {
	return func(ctx context.Context, req *mcpprot.CallToolRequest, input DeleteBranchInput) (*mcpprot.CallToolResult, DeleteBranchOutput, error) {
		ctx, principal, err := requireAppPermission(ctx, deps, req, input.AppId, branchDeleteAccess)
		if err != nil {
			return nil, DeleteBranchOutput{}, err
		}
		if input.Branch == "" {
			return nil, DeleteBranchOutput{}, errors.New("branch is required; list the branches with get_branches")
		}
		// Protection, active channels and active rollouts are all refused by
		// the service and the repository; their messages name what to unblock.
		if err := deps.BranchWriter.DeleteBranch(ctx, input.Branch, input.AppId); err != nil {
			return nil, DeleteBranchOutput{}, writeError(err, "delete the branch", "mcp delete_branch", principal, input.AppId)
		}
		log.Printf("mcp delete_branch: user %s deleted branch %s of app %s", principal.UserId, input.Branch, input.AppId)
		return nil, DeleteBranchOutput{Deleted: true, Branch: input.Branch}, nil
	}
}

func registerDeleteBranch(server *mcpprot.Server, deps Deps) {
	mcpprot.AddTool(server, &mcpprot.Tool{
		Name:        "delete_branch",
		Description: "Delete a branch of an app (appId and branch required), along with the update files it holds. Refused when the branch is protected, mapped to a channel, or serving an active rollout. Requires the branch:delete permission.",
		Annotations: &mcpprot.ToolAnnotations{
			Title:           "Delete branch",
			DestructiveHint: boolPtr(true),
			IdempotentHint:  false,
		},
	}, deleteBranchHandler(deps))
}