mcp: extends flags support for existing tools (#2889)

Signed-off-by: kapil <kapilsareen584@gmail.com>
This commit is contained in:
Kapil Sareen 2025-06-27 09:07:06 +05:30 committed by GitHub
parent 9a71fa4725
commit a90f07fdfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 237 additions and 42 deletions

View File

@ -29,19 +29,25 @@ func NewServer() *MCPServer {
mcpServer.AddTool(
mcp.NewTool("create",
mcp.WithDescription("Creates a knative function in the current directory"),
mcp.WithString("name",
mcp.Required(),
mcp.Description("Name of the function to be created"),
),
mcp.WithString("language",
mcp.Required(),
mcp.Description("Language/Runtime of the function to be created"),
),
mcp.WithDescription("Creates a Knative function project in the current or specified directory"),
mcp.WithString("cwd",
mcp.Required(),
mcp.Description("Current working directory of the MCP client"),
),
mcp.WithString("name",
mcp.Required(),
mcp.Description("Name of the function to be created (used as subdirectory)"),
),
mcp.WithString("language",
mcp.Required(),
mcp.Description("Language runtime to use (e.g., node, go, python)"),
),
// Optional flags
mcp.WithString("template", mcp.Description("Function template (e.g., http, cloudevents)")),
mcp.WithString("repository", mcp.Description("URI to Git repo containing the template")),
mcp.WithBoolean("confirm", mcp.Description("Prompt to confirm options interactively")),
mcp.WithBoolean("verbose", mcp.Description("Print verbose logs")),
),
handleCreateTool,
)
@ -51,7 +57,7 @@ func NewServer() *MCPServer {
mcp.WithDescription("Deploys the function to the cluster"),
mcp.WithString("registry",
mcp.Required(),
mcp.Description("Name of the registry to be used to push the function image"),
mcp.Description("Registry to be used to push the function image"),
),
mcp.WithString("cwd",
mcp.Required(),
@ -61,17 +67,41 @@ func NewServer() *MCPServer {
mcp.Required(),
mcp.Description("Builder to be used to build the function image"),
),
mcp.WithBoolean("remote",
mcp.DefaultBool(false),
mcp.Description("If true, the function will be deployed remotely"),
),
// Optional flags
mcp.WithString("image", mcp.Description("Full image name (overrides registry)")),
mcp.WithString("namespace", mcp.Description("Namespace to deploy the function into")),
mcp.WithString("git-url", mcp.Description("Git URL containing the function source")),
mcp.WithString("git-branch", mcp.Description("Git branch for remote deployment")),
mcp.WithString("git-dir", mcp.Description("Directory inside the Git repository")),
mcp.WithString("builder-image", mcp.Description("Custom builder image")),
mcp.WithString("domain", mcp.Description("Domain for the function route")),
mcp.WithString("platform", mcp.Description("Target platform to build for (e.g., linux/amd64)")),
mcp.WithString("path", mcp.Description("Path to the function directory")),
mcp.WithString("build", mcp.Description(`Build control: "true", "false", or "auto"`)),
mcp.WithString("pvc-size", mcp.Description("Custom volume size for remote builds")),
mcp.WithString("service-account", mcp.Description("Kubernetes ServiceAccount to use")),
mcp.WithString("remote-storage-class", mcp.Description("Storage class for remote volume")),
mcp.WithBoolean("confirm", mcp.Description("Prompt for confirmation before deploying")),
mcp.WithBoolean("push", mcp.Description("Push image to registry before deployment")),
mcp.WithBoolean("verbose", mcp.Description("Print verbose logs")),
mcp.WithBoolean("registry-insecure", mcp.Description("Skip TLS verification for registry")),
mcp.WithBoolean("build-timestamp", mcp.Description("Use actual time in image metadata")),
mcp.WithBoolean("remote", mcp.Description("Trigger remote deployment")),
),
handleDeployTool,
)
mcpServer.AddTool(
mcp.NewTool("list",
mcp.WithDescription("Lists all the functions deployed in the cluster"),
mcp.WithDescription("Lists all deployed functions in the current or specified namespace"),
// Optional flags
mcp.WithBoolean("all-namespaces", mcp.Description("List functions in all namespaces (overrides --namespace)")),
mcp.WithString("namespace", mcp.Description("The namespace to list functions in (default is current/active)")),
mcp.WithString("output", mcp.Description("Output format: human, plain, json, xml, yaml")),
mcp.WithBoolean("verbose", mcp.Description("Enable verbose output")),
),
handleListTool,
)
@ -85,12 +115,24 @@ func NewServer() *MCPServer {
),
mcp.WithString("builder",
mcp.Required(),
mcp.Description("Builder to be used to build the function image"),
mcp.Description("Builder to be used to build the function image (pack, s2i, host)"),
),
mcp.WithString("registry",
mcp.Required(),
mcp.Description("Name of the registry to be used to push the function image"),
mcp.Description("Registry to be used to push the function image (e.g. ghcr.io/user)"),
),
// Optional flags
mcp.WithString("builder-image", mcp.Description("Custom builder image to use with buildpacks")),
mcp.WithString("image", mcp.Description("Full image name (overrides registry + function name)")),
mcp.WithString("path", mcp.Description("Path to the function directory (default is current dir)")),
mcp.WithString("platform", mcp.Description("Target platform, e.g. linux/amd64 (for s2i builds)")),
mcp.WithBoolean("confirm", mcp.Description("Prompt for confirmation before proceeding")),
mcp.WithBoolean("push", mcp.Description("Push image to registry after building")),
mcp.WithBoolean("verbose", mcp.Description("Enable verbose logging output")),
mcp.WithBoolean("registry-insecure", mcp.Description("Skip TLS verification for insecure registries")),
mcp.WithBoolean("build-timestamp", mcp.Description("Use actual time for image timestamp (buildpacks only)")),
),
handleBuildTool,
)
@ -102,6 +144,14 @@ func NewServer() *MCPServer {
mcp.Required(),
mcp.Description("Name of the function to be deleted"),
),
// Optional flags
mcp.WithString("namespace", mcp.Description("Namespace to delete from (default: current or active)")),
mcp.WithString("path", mcp.Description("Path to the function project (default is current directory)")),
mcp.WithString("all", mcp.Description(`Delete all related resources like Pipelines, Secrets ("true"/"false")`)),
mcp.WithBoolean("confirm", mcp.Description("Prompt to confirm before deletion")),
mcp.WithBoolean("verbose", mcp.Description("Enable verbose output")),
),
handleDeleteTool,
)
@ -185,12 +235,33 @@ func handleCreateTool(
return mcp.NewToolResultError(err.Error()), nil
}
cmd := exec.Command("func", "create", "-l", language, name)
cmd.Dir = cwd
out, err := cmd.Output()
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
args := []string{"create", "-l", language}
// Optional flags
if v := request.GetString("template", ""); v != "" {
args = append(args, "--template", v)
}
if v := request.GetString("repository", ""); v != "" {
args = append(args, "--repository", v)
}
if request.GetBool("confirm", false) {
args = append(args, "--confirm")
}
if request.GetBool("verbose", false) {
args = append(args, "--verbose")
}
// `name` is passed as a positional argument (directory to create in)
args = append(args, name)
cmd := exec.Command("func", args...)
cmd.Dir = cwd
out, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("func create failed: %s", out)), nil
}
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
return mcp.NewToolResultText(string(body)), nil
}
@ -211,20 +282,74 @@ func handleDeployTool(
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
remote, err := request.RequireBool("remote")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
args := []string{"deploy", "--builder", builder, "--registry", registry}
// Optional flags
if v := request.GetString("image", ""); v != "" {
args = append(args, "--image", v)
}
var cmd *exec.Cmd
if remote {
cmd = exec.Command("func", "deploy", "--registry", registry, "--builder", builder, "--remote")
} else {
cmd = exec.Command("func", "deploy", "--registry", registry, "--builder", builder)
if v := request.GetString("namespace", ""); v != "" {
args = append(args, "--namespace", v)
}
if v := request.GetString("git-url", ""); v != "" {
args = append(args, "--git-url", v)
}
if v := request.GetString("git-branch", ""); v != "" {
args = append(args, "--git-branch", v)
}
if v := request.GetString("git-dir", ""); v != "" {
args = append(args, "--git-dir", v)
}
if v := request.GetString("builder-image", ""); v != "" {
args = append(args, "--builder-image", v)
}
if v := request.GetString("domain", ""); v != "" {
args = append(args, "--domain", v)
}
if v := request.GetString("platform", ""); v != "" {
args = append(args, "--platform", v)
}
if v := request.GetString("path", ""); v != "" {
args = append(args, "--path", v)
}
if v := request.GetString("build", ""); v != "" {
args = append(args, "--build", v)
}
if v := request.GetString("pvc-size", ""); v != "" {
args = append(args, "--pvc-size", v)
}
if v := request.GetString("service-account", ""); v != "" {
args = append(args, "--service-account", v)
}
if v := request.GetString("remote-storage-class", ""); v != "" {
args = append(args, "--remote-storage-class", v)
}
if request.GetBool("confirm", false) {
args = append(args, "--confirm")
}
if request.GetBool("push", false) {
args = append(args, "--push")
}
if request.GetBool("verbose", false) {
args = append(args, "--verbose")
}
if request.GetBool("registry-insecure", false) {
args = append(args, "--registry-insecure")
}
if request.GetBool("build-timestamp", false) {
args = append(args, "--build-timestamp")
}
if request.GetBool("remote", false) {
args = append(args, "--remote")
}
cmd := exec.Command("func", args...)
cmd.Dir = cwd
out, err := cmd.Output()
out, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
return mcp.NewToolResultError(fmt.Sprintf("func deploy failed: %s", out)), nil
}
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
return mcp.NewToolResultText(string(body)), nil
@ -234,10 +359,26 @@ func handleListTool(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
cmd := exec.Command("func", "list")
out, err := cmd.Output()
args := []string{"list"}
// Optional flags
if request.GetBool("all-namespaces", false) {
args = append(args, "--all-namespaces")
}
if v := request.GetString("namespace", ""); v != "" {
args = append(args, "--namespace", v)
}
if v := request.GetString("output", ""); v != "" {
args = append(args, "--output", v)
}
if request.GetBool("verbose", false) {
args = append(args, "--verbose")
}
cmd := exec.Command("func", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
return mcp.NewToolResultError(fmt.Sprintf("func list failed: %s", out)), nil
}
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
return mcp.NewToolResultText(string(body)), nil
@ -260,11 +401,43 @@ func handleBuildTool(
return mcp.NewToolResultError(err.Error()), nil
}
cmd := exec.Command("func", "build", "--builder", builder, "--registry", registry)
args := []string{"build", "--builder", builder, "--registry", registry}
// Optional flags
if v := request.GetString("builder-image", ""); v != "" {
args = append(args, "--builder-image", v)
}
if v := request.GetString("image", ""); v != "" {
args = append(args, "--image", v)
}
if v := request.GetString("path", ""); v != "" {
args = append(args, "--path", v)
}
if v := request.GetString("platform", ""); v != "" {
args = append(args, "--platform", v)
}
if v := request.GetBool("confirm", false); v {
args = append(args, "--confirm")
}
if v := request.GetBool("push", false); v {
args = append(args, "--push")
}
if v := request.GetBool("verbose", false); v {
args = append(args, "--verbose")
}
if v := request.GetBool("registry-insecure", false); v {
args = append(args, "--registry-insecure")
}
if v := request.GetBool("build-timestamp", false); v {
args = append(args, "--build-timestamp")
}
cmd := exec.Command("func", args...)
cmd.Dir = cwd
out, err := cmd.Output()
out, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
return mcp.NewToolResultError(fmt.Sprintf("func build failed: %s", out)), nil
}
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
return mcp.NewToolResultText(string(body)), nil
@ -278,11 +451,33 @@ func handleDeleteTool(
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
cmd := exec.Command("func", "delete", name)
out, err := cmd.Output()
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
args := []string{"delete", name}
// Optional flags
if v := request.GetString("namespace", ""); v != "" {
args = append(args, "--namespace", v)
}
if v := request.GetString("path", ""); v != "" {
args = append(args, "--path", v)
}
if v := request.GetString("all", ""); v != "" {
args = append(args, "--all", v)
}
if request.GetBool("confirm", false) {
args = append(args, "--confirm")
}
if request.GetBool("verbose", false) {
args = append(args, "--verbose")
}
cmd := exec.Command("func", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("func delete failed: %s", out)), nil
}
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
return mcp.NewToolResultText(string(body)), nil
}