Skip to content

Commit a90f07f

Browse files
authored
mcp: extends flags support for existing tools (knative#2889)
Signed-off-by: kapil <[email protected]>
1 parent 9a71fa4 commit a90f07f

File tree

1 file changed

+232
-37
lines changed

1 file changed

+232
-37
lines changed

pkg/mcp/mcp.go

Lines changed: 232 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,25 @@ func NewServer() *MCPServer {
2929

3030
mcpServer.AddTool(
3131
mcp.NewTool("create",
32-
mcp.WithDescription("Creates a knative function in the current directory"),
33-
mcp.WithString("name",
32+
mcp.WithDescription("Creates a Knative function project in the current or specified directory"),
33+
mcp.WithString("cwd",
3434
mcp.Required(),
35-
mcp.Description("Name of the function to be created"),
35+
mcp.Description("Current working directory of the MCP client"),
3636
),
37-
mcp.WithString("language",
37+
mcp.WithString("name",
3838
mcp.Required(),
39-
mcp.Description("Language/Runtime of the function to be created"),
39+
mcp.Description("Name of the function to be created (used as subdirectory)"),
4040
),
41-
mcp.WithString("cwd",
41+
mcp.WithString("language",
4242
mcp.Required(),
43-
mcp.Description("Current working directory of the MCP client"),
43+
mcp.Description("Language runtime to use (e.g., node, go, python)"),
4444
),
45+
46+
// Optional flags
47+
mcp.WithString("template", mcp.Description("Function template (e.g., http, cloudevents)")),
48+
mcp.WithString("repository", mcp.Description("URI to Git repo containing the template")),
49+
mcp.WithBoolean("confirm", mcp.Description("Prompt to confirm options interactively")),
50+
mcp.WithBoolean("verbose", mcp.Description("Print verbose logs")),
4551
),
4652
handleCreateTool,
4753
)
@@ -51,7 +57,7 @@ func NewServer() *MCPServer {
5157
mcp.WithDescription("Deploys the function to the cluster"),
5258
mcp.WithString("registry",
5359
mcp.Required(),
54-
mcp.Description("Name of the registry to be used to push the function image"),
60+
mcp.Description("Registry to be used to push the function image"),
5561
),
5662
mcp.WithString("cwd",
5763
mcp.Required(),
@@ -61,17 +67,41 @@ func NewServer() *MCPServer {
6167
mcp.Required(),
6268
mcp.Description("Builder to be used to build the function image"),
6369
),
64-
mcp.WithBoolean("remote",
65-
mcp.DefaultBool(false),
66-
mcp.Description("If true, the function will be deployed remotely"),
67-
),
70+
71+
// Optional flags
72+
mcp.WithString("image", mcp.Description("Full image name (overrides registry)")),
73+
mcp.WithString("namespace", mcp.Description("Namespace to deploy the function into")),
74+
mcp.WithString("git-url", mcp.Description("Git URL containing the function source")),
75+
mcp.WithString("git-branch", mcp.Description("Git branch for remote deployment")),
76+
mcp.WithString("git-dir", mcp.Description("Directory inside the Git repository")),
77+
mcp.WithString("builder-image", mcp.Description("Custom builder image")),
78+
mcp.WithString("domain", mcp.Description("Domain for the function route")),
79+
mcp.WithString("platform", mcp.Description("Target platform to build for (e.g., linux/amd64)")),
80+
mcp.WithString("path", mcp.Description("Path to the function directory")),
81+
mcp.WithString("build", mcp.Description(`Build control: "true", "false", or "auto"`)),
82+
mcp.WithString("pvc-size", mcp.Description("Custom volume size for remote builds")),
83+
mcp.WithString("service-account", mcp.Description("Kubernetes ServiceAccount to use")),
84+
mcp.WithString("remote-storage-class", mcp.Description("Storage class for remote volume")),
85+
86+
mcp.WithBoolean("confirm", mcp.Description("Prompt for confirmation before deploying")),
87+
mcp.WithBoolean("push", mcp.Description("Push image to registry before deployment")),
88+
mcp.WithBoolean("verbose", mcp.Description("Print verbose logs")),
89+
mcp.WithBoolean("registry-insecure", mcp.Description("Skip TLS verification for registry")),
90+
mcp.WithBoolean("build-timestamp", mcp.Description("Use actual time in image metadata")),
91+
mcp.WithBoolean("remote", mcp.Description("Trigger remote deployment")),
6892
),
6993
handleDeployTool,
7094
)
7195

7296
mcpServer.AddTool(
7397
mcp.NewTool("list",
74-
mcp.WithDescription("Lists all the functions deployed in the cluster"),
98+
mcp.WithDescription("Lists all deployed functions in the current or specified namespace"),
99+
100+
// Optional flags
101+
mcp.WithBoolean("all-namespaces", mcp.Description("List functions in all namespaces (overrides --namespace)")),
102+
mcp.WithString("namespace", mcp.Description("The namespace to list functions in (default is current/active)")),
103+
mcp.WithString("output", mcp.Description("Output format: human, plain, json, xml, yaml")),
104+
mcp.WithBoolean("verbose", mcp.Description("Enable verbose output")),
75105
),
76106
handleListTool,
77107
)
@@ -85,12 +115,24 @@ func NewServer() *MCPServer {
85115
),
86116
mcp.WithString("builder",
87117
mcp.Required(),
88-
mcp.Description("Builder to be used to build the function image"),
118+
mcp.Description("Builder to be used to build the function image (pack, s2i, host)"),
89119
),
90120
mcp.WithString("registry",
91121
mcp.Required(),
92-
mcp.Description("Name of the registry to be used to push the function image"),
122+
mcp.Description("Registry to be used to push the function image (e.g. ghcr.io/user)"),
93123
),
124+
125+
// Optional flags
126+
mcp.WithString("builder-image", mcp.Description("Custom builder image to use with buildpacks")),
127+
mcp.WithString("image", mcp.Description("Full image name (overrides registry + function name)")),
128+
mcp.WithString("path", mcp.Description("Path to the function directory (default is current dir)")),
129+
mcp.WithString("platform", mcp.Description("Target platform, e.g. linux/amd64 (for s2i builds)")),
130+
131+
mcp.WithBoolean("confirm", mcp.Description("Prompt for confirmation before proceeding")),
132+
mcp.WithBoolean("push", mcp.Description("Push image to registry after building")),
133+
mcp.WithBoolean("verbose", mcp.Description("Enable verbose logging output")),
134+
mcp.WithBoolean("registry-insecure", mcp.Description("Skip TLS verification for insecure registries")),
135+
mcp.WithBoolean("build-timestamp", mcp.Description("Use actual time for image timestamp (buildpacks only)")),
94136
),
95137
handleBuildTool,
96138
)
@@ -102,6 +144,14 @@ func NewServer() *MCPServer {
102144
mcp.Required(),
103145
mcp.Description("Name of the function to be deleted"),
104146
),
147+
148+
// Optional flags
149+
mcp.WithString("namespace", mcp.Description("Namespace to delete from (default: current or active)")),
150+
mcp.WithString("path", mcp.Description("Path to the function project (default is current directory)")),
151+
mcp.WithString("all", mcp.Description(`Delete all related resources like Pipelines, Secrets ("true"/"false")`)),
152+
153+
mcp.WithBoolean("confirm", mcp.Description("Prompt to confirm before deletion")),
154+
mcp.WithBoolean("verbose", mcp.Description("Enable verbose output")),
105155
),
106156
handleDeleteTool,
107157
)
@@ -185,12 +235,33 @@ func handleCreateTool(
185235
return mcp.NewToolResultError(err.Error()), nil
186236
}
187237

188-
cmd := exec.Command("func", "create", "-l", language, name)
238+
args := []string{"create", "-l", language}
239+
240+
// Optional flags
241+
if v := request.GetString("template", ""); v != "" {
242+
args = append(args, "--template", v)
243+
}
244+
if v := request.GetString("repository", ""); v != "" {
245+
args = append(args, "--repository", v)
246+
}
247+
if request.GetBool("confirm", false) {
248+
args = append(args, "--confirm")
249+
}
250+
if request.GetBool("verbose", false) {
251+
args = append(args, "--verbose")
252+
}
253+
254+
// `name` is passed as a positional argument (directory to create in)
255+
args = append(args, name)
256+
257+
cmd := exec.Command("func", args...)
189258
cmd.Dir = cwd
190-
out, err := cmd.Output()
259+
260+
out, err := cmd.CombinedOutput()
191261
if err != nil {
192-
return mcp.NewToolResultError(err.Error()), nil
262+
return mcp.NewToolResultError(fmt.Sprintf("func create failed: %s", out)), nil
193263
}
264+
194265
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
195266
return mcp.NewToolResultText(string(body)), nil
196267
}
@@ -211,20 +282,74 @@ func handleDeployTool(
211282
if err != nil {
212283
return mcp.NewToolResultError(err.Error()), nil
213284
}
214-
remote, err := request.RequireBool("remote")
215-
if err != nil {
216-
return mcp.NewToolResultError(err.Error()), nil
285+
286+
args := []string{"deploy", "--builder", builder, "--registry", registry}
287+
288+
// Optional flags
289+
if v := request.GetString("image", ""); v != "" {
290+
args = append(args, "--image", v)
291+
}
292+
if v := request.GetString("namespace", ""); v != "" {
293+
args = append(args, "--namespace", v)
294+
}
295+
if v := request.GetString("git-url", ""); v != "" {
296+
args = append(args, "--git-url", v)
297+
}
298+
if v := request.GetString("git-branch", ""); v != "" {
299+
args = append(args, "--git-branch", v)
300+
}
301+
if v := request.GetString("git-dir", ""); v != "" {
302+
args = append(args, "--git-dir", v)
303+
}
304+
if v := request.GetString("builder-image", ""); v != "" {
305+
args = append(args, "--builder-image", v)
306+
}
307+
if v := request.GetString("domain", ""); v != "" {
308+
args = append(args, "--domain", v)
309+
}
310+
if v := request.GetString("platform", ""); v != "" {
311+
args = append(args, "--platform", v)
312+
}
313+
if v := request.GetString("path", ""); v != "" {
314+
args = append(args, "--path", v)
315+
}
316+
if v := request.GetString("build", ""); v != "" {
317+
args = append(args, "--build", v)
318+
}
319+
if v := request.GetString("pvc-size", ""); v != "" {
320+
args = append(args, "--pvc-size", v)
217321
}
218-
var cmd *exec.Cmd
219-
if remote {
220-
cmd = exec.Command("func", "deploy", "--registry", registry, "--builder", builder, "--remote")
221-
} else {
222-
cmd = exec.Command("func", "deploy", "--registry", registry, "--builder", builder)
322+
if v := request.GetString("service-account", ""); v != "" {
323+
args = append(args, "--service-account", v)
223324
}
325+
if v := request.GetString("remote-storage-class", ""); v != "" {
326+
args = append(args, "--remote-storage-class", v)
327+
}
328+
329+
if request.GetBool("confirm", false) {
330+
args = append(args, "--confirm")
331+
}
332+
if request.GetBool("push", false) {
333+
args = append(args, "--push")
334+
}
335+
if request.GetBool("verbose", false) {
336+
args = append(args, "--verbose")
337+
}
338+
if request.GetBool("registry-insecure", false) {
339+
args = append(args, "--registry-insecure")
340+
}
341+
if request.GetBool("build-timestamp", false) {
342+
args = append(args, "--build-timestamp")
343+
}
344+
if request.GetBool("remote", false) {
345+
args = append(args, "--remote")
346+
}
347+
348+
cmd := exec.Command("func", args...)
224349
cmd.Dir = cwd
225-
out, err := cmd.Output()
350+
out, err := cmd.CombinedOutput()
226351
if err != nil {
227-
return mcp.NewToolResultError(err.Error()), nil
352+
return mcp.NewToolResultError(fmt.Sprintf("func deploy failed: %s", out)), nil
228353
}
229354
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
230355
return mcp.NewToolResultText(string(body)), nil
@@ -234,10 +359,26 @@ func handleListTool(
234359
ctx context.Context,
235360
request mcp.CallToolRequest,
236361
) (*mcp.CallToolResult, error) {
237-
cmd := exec.Command("func", "list")
238-
out, err := cmd.Output()
362+
args := []string{"list"}
363+
364+
// Optional flags
365+
if request.GetBool("all-namespaces", false) {
366+
args = append(args, "--all-namespaces")
367+
}
368+
if v := request.GetString("namespace", ""); v != "" {
369+
args = append(args, "--namespace", v)
370+
}
371+
if v := request.GetString("output", ""); v != "" {
372+
args = append(args, "--output", v)
373+
}
374+
if request.GetBool("verbose", false) {
375+
args = append(args, "--verbose")
376+
}
377+
378+
cmd := exec.Command("func", args...)
379+
out, err := cmd.CombinedOutput()
239380
if err != nil {
240-
return mcp.NewToolResultError(err.Error()), nil
381+
return mcp.NewToolResultError(fmt.Sprintf("func list failed: %s", out)), nil
241382
}
242383
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
243384
return mcp.NewToolResultText(string(body)), nil
@@ -260,11 +401,43 @@ func handleBuildTool(
260401
return mcp.NewToolResultError(err.Error()), nil
261402
}
262403

263-
cmd := exec.Command("func", "build", "--builder", builder, "--registry", registry)
404+
args := []string{"build", "--builder", builder, "--registry", registry}
405+
406+
// Optional flags
407+
if v := request.GetString("builder-image", ""); v != "" {
408+
args = append(args, "--builder-image", v)
409+
}
410+
if v := request.GetString("image", ""); v != "" {
411+
args = append(args, "--image", v)
412+
}
413+
if v := request.GetString("path", ""); v != "" {
414+
args = append(args, "--path", v)
415+
}
416+
if v := request.GetString("platform", ""); v != "" {
417+
args = append(args, "--platform", v)
418+
}
419+
420+
if v := request.GetBool("confirm", false); v {
421+
args = append(args, "--confirm")
422+
}
423+
if v := request.GetBool("push", false); v {
424+
args = append(args, "--push")
425+
}
426+
if v := request.GetBool("verbose", false); v {
427+
args = append(args, "--verbose")
428+
}
429+
if v := request.GetBool("registry-insecure", false); v {
430+
args = append(args, "--registry-insecure")
431+
}
432+
if v := request.GetBool("build-timestamp", false); v {
433+
args = append(args, "--build-timestamp")
434+
}
435+
436+
cmd := exec.Command("func", args...)
264437
cmd.Dir = cwd
265-
out, err := cmd.Output()
438+
out, err := cmd.CombinedOutput()
266439
if err != nil {
267-
return mcp.NewToolResultError(err.Error()), nil
440+
return mcp.NewToolResultError(fmt.Sprintf("func build failed: %s", out)), nil
268441
}
269442
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
270443
return mcp.NewToolResultText(string(body)), nil
@@ -278,11 +451,33 @@ func handleDeleteTool(
278451
if err != nil {
279452
return mcp.NewToolResultError(err.Error()), nil
280453
}
281-
cmd := exec.Command("func", "delete", name)
282-
out, err := cmd.Output()
454+
455+
args := []string{"delete", name}
456+
457+
// Optional flags
458+
if v := request.GetString("namespace", ""); v != "" {
459+
args = append(args, "--namespace", v)
460+
}
461+
if v := request.GetString("path", ""); v != "" {
462+
args = append(args, "--path", v)
463+
}
464+
if v := request.GetString("all", ""); v != "" {
465+
args = append(args, "--all", v)
466+
}
467+
468+
if request.GetBool("confirm", false) {
469+
args = append(args, "--confirm")
470+
}
471+
if request.GetBool("verbose", false) {
472+
args = append(args, "--verbose")
473+
}
474+
475+
cmd := exec.Command("func", args...)
476+
out, err := cmd.CombinedOutput()
283477
if err != nil {
284-
return mcp.NewToolResultError(err.Error()), nil
478+
return mcp.NewToolResultError(fmt.Sprintf("func delete failed: %s", out)), nil
285479
}
480+
286481
body := []byte(fmt.Sprintf(`{"result": "%s"}`, out))
287482
return mcp.NewToolResultText(string(body)), nil
288483
}

0 commit comments

Comments
 (0)