From b482b4883793d8d58d514438ab197762860559ea Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 15 Jun 2026 13:22:57 +0530 Subject: [PATCH 1/2] feat: poc - merge SVM operation in 1 tool --- descriptions/descriptions.go | 4 +- server/server.go | 8 ++- server/svm.go | 124 +++++++++++++---------------------- tool/tool.go | 12 ++-- 4 files changed, 56 insertions(+), 92 deletions(-) diff --git a/descriptions/descriptions.go b/descriptions/descriptions.go index 0cbf246..ea7ce99 100644 --- a/descriptions/descriptions.go +++ b/descriptions/descriptions.go @@ -157,9 +157,7 @@ const SearchOntapEndpoints = `Search the catalog by keyword across endpoint path const DescribeOntapEndpoint = `Get filterable query params for an endpoint. Call before ontap_get to learn valid filter names and which sub-objects need explicit fields (e.g. "space.*", "efficiency.*"). Pass cluster_name to automatically filter out fields and filters not available in that cluster's ONTAP version.` -const CreateSVM = `Create an SVM on a cluster by cluster name.` -const UpdateSVM = `Update an SVM on a cluster by cluster name.` -const DeleteSVM = `Delete an SVM on a cluster by cluster name.` +const SVMOperation = `Create, Update and Delete operations on SVM on a cluster by cluster name.` const DeleteSVMPeer = `Delete an SVM peer on a cluster by cluster name and local SVM name. The peer relationship UUID is looked up internally using the svm.name filter.` const OntapGet = `Execute a read-only GET against any ONTAP REST endpoint. diff --git a/server/server.go b/server/server.go index 4fbc36c..2ad0044 100644 --- a/server/server.go +++ b/server/server.go @@ -147,9 +147,7 @@ func (a *App) createMCPServer() *mcp.Server { addTool(a, server, "delete_nfs_export_policies_rules", descriptions.DeleteNFSExportPolicyRules, deleteAnnotation, a.DeleteNFSExportPoliciesRule) // operation on SVM object - addTool(a, server, "create_svm", descriptions.CreateSVM, createAnnotation, a.CreateSVM) - addTool(a, server, "update_svm", descriptions.UpdateSVM, updateAnnotation, a.UpdateSVM) - addTool(a, server, "delete_svm", descriptions.DeleteSVM, deleteAnnotation, a.DeleteSVM) + addTool(a, server, "svm_operation", descriptions.SVMOperation, operationAnnotation, a.SVMOperation) // operation on SVM peer object addTool(a, server, "delete_svm_peer", descriptions.DeleteSVMPeer, deleteAnnotation, a.DeleteSVMPeer) @@ -656,6 +654,10 @@ var ( DestructiveHint: new(true), IdempotentHint: true, } + operationAnnotation = mcp.ToolAnnotations{ + DestructiveHint: new(true), + IdempotentHint: true, + } ) func addTool[In, Out any](a *App, server *mcp.Server, name string, description string, annotations mcp.ToolAnnotations, handler mcp.ToolHandlerFor[In, Out]) { diff --git a/server/svm.go b/server/svm.go index be01736..8d79d16 100644 --- a/server/svm.go +++ b/server/svm.go @@ -9,105 +9,69 @@ import ( "github.com/netapp/ontap-mcp/tool" ) -func (a *App) CreateSVM(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.SVMCreate) (*mcp.CallToolResult, any, error) { +func (a *App) SVMOperation(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.SVMOperation) (*mcp.CallToolResult, any, error) { if !a.locks.TryLock(parameters.Cluster) { return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil } defer a.locks.Unlock(parameters.Cluster) - svmCreate, err := newCreateSVM(parameters) - if err != nil { - return nil, nil, err - } - - client, err := a.getClient(parameters.Cluster) - if err != nil { - return errorResult(err), nil, err - } - - err = client.CreateSVM(ctx, svmCreate) - if err != nil { - return errorResult(err), nil, err - } - - return &mcp.CallToolResult{ - Content: []mcp.Content{ - &mcp.TextContent{Text: "SVM created successfully"}, - }, - }, nil, nil -} - -func (a *App) UpdateSVM(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.SVM) (*mcp.CallToolResult, any, error) { - if !a.locks.TryLock(parameters.Cluster) { - return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil - } - defer a.locks.Unlock(parameters.Cluster) - - svmUpdate, err := newUpdateSVM(parameters) - if err != nil { - return nil, nil, err - } - client, err := a.getClient(parameters.Cluster) if err != nil { return errorResult(err), nil, err } - err = client.UpdateSVM(ctx, svmUpdate, parameters.Name) - if err != nil { - return errorResult(err), nil, err - } - - return &mcp.CallToolResult{ - Content: []mcp.Content{ - &mcp.TextContent{Text: "SVM updated successfully"}, - }, - }, nil, nil -} - -func (a *App) DeleteSVM(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.SVM) (*mcp.CallToolResult, any, error) { - if !a.locks.TryLock(parameters.Cluster) { - return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil - } - defer a.locks.Unlock(parameters.Cluster) - if parameters.Name == "" { return nil, nil, errors.New("SVM name is required") } - client, err := a.getClient(parameters.Cluster) - if err != nil { - return errorResult(err), nil, err + switch parameters.Type { + case "create": + svmCreate := ontap.SVMCreate{Name: parameters.Name} + err = client.CreateSVM(ctx, svmCreate) + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "SVM created successfully"}, + }, + }, nil, nil + case "update": + svmUpdate, err := updateSVMValidation(parameters.SVMUpdate) + if err != nil { + return nil, nil, err + } + + err = client.UpdateSVM(ctx, svmUpdate, parameters.Name) + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "SVM updated successfully"}, + }, + }, nil, nil + case "delete": + err = client.DeleteSVM(ctx, parameters.Name) + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "SVM deleted successfully"}, + }, + }, nil, nil + default: + return errorResult(errors.New("SVM operation type is not supported")), nil, nil } - - err = client.DeleteSVM(ctx, parameters.Name) - if err != nil { - return errorResult(err), nil, err - } - - return &mcp.CallToolResult{ - Content: []mcp.Content{ - &mcp.TextContent{Text: "SVM deleted successfully"}, - }, - }, nil, nil } -func newCreateSVM(in tool.SVMCreate) (ontap.SVMCreate, error) { - out := ontap.SVMCreate{} - if in.Name == "" { - return out, errors.New("SVM name is required") - } - out.Name = in.Name - return out, nil -} - -func newUpdateSVM(in tool.SVM) (ontap.SVM, error) { +func updateSVMValidation(in tool.SVMUpdate) (ontap.SVM, error) { out := ontap.SVM{} - if in.Name == "" { - return out, errors.New("SVM name is required") - } - hasUpdate := false if in.NewName != "" { out.Name = in.NewName diff --git a/tool/tool.go b/tool/tool.go index a5a14df..6644363 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -342,14 +342,14 @@ type DNSService struct { Servers []string `json:"servers" jsonschema:"list of DNS server IP addresses (e.g., [\"10.0.0.1\"])"` } -type SVMCreate struct { - Cluster string `json:"cluster_name" jsonschema:"cluster name"` - Name string `json:"svm_name" jsonschema:"SVM name"` +type SVMOperation struct { + Cluster string `json:"cluster_name" jsonschema:"cluster name"` + Type string `json:"type_operation" jsonschema:"SVM operation type (e.g., create, update, delete)"` + Name string `json:"svm_name" jsonschema:"SVM name"` + SVMUpdate SVMUpdate `json:"svm_update,omitzero" jsonschema:"update SVM operation"` } -type SVM struct { - Cluster string `json:"cluster_name" jsonschema:"cluster name"` - Name string `json:"svm_name" jsonschema:"SVM name"` +type SVMUpdate struct { NewName string `json:"new_name,omitzero" jsonschema:"new name of SVM"` State string `json:"state,omitzero" jsonschema:"state of SVM (e.g., starting, running, stopping, stopped, deleting, initializing)"` Comment string `json:"comment,omitzero" jsonschema:"comment"` From b8c61ad22fb458be2589bff7a4f6485be364d23d Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 15 Jun 2026 15:03:23 +0530 Subject: [PATCH 2/2] feat: handled copilot comments --- server/server.go | 1 - server/svm.go | 4 ++-- tool/tool.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server/server.go b/server/server.go index 2ad0044..d35045a 100644 --- a/server/server.go +++ b/server/server.go @@ -656,7 +656,6 @@ var ( } operationAnnotation = mcp.ToolAnnotations{ DestructiveHint: new(true), - IdempotentHint: true, } ) diff --git a/server/svm.go b/server/svm.go index 8d79d16..4d449e0 100644 --- a/server/svm.go +++ b/server/svm.go @@ -24,7 +24,7 @@ func (a *App) SVMOperation(ctx context.Context, _ *mcp.CallToolRequest, paramete return nil, nil, errors.New("SVM name is required") } - switch parameters.Type { + switch parameters.Operation { case "create": svmCreate := ontap.SVMCreate{Name: parameters.Name} err = client.CreateSVM(ctx, svmCreate) @@ -65,7 +65,7 @@ func (a *App) SVMOperation(ctx context.Context, _ *mcp.CallToolRequest, paramete }, }, nil, nil default: - return errorResult(errors.New("SVM operation type is not supported")), nil, nil + return errorResult(fmt.Errorf("unsupported type_operation %q; supported values: create, update, delete", parameters.Operation)), nil, nil } } diff --git a/tool/tool.go b/tool/tool.go index 6644363..03261f6 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -344,7 +344,7 @@ type DNSService struct { type SVMOperation struct { Cluster string `json:"cluster_name" jsonschema:"cluster name"` - Type string `json:"type_operation" jsonschema:"SVM operation type (e.g., create, update, delete)"` + Operation string `json:"operation" jsonschema:"SVM operation type (e.g., create, update, delete)"` Name string `json:"svm_name" jsonschema:"SVM name"` SVMUpdate SVMUpdate `json:"svm_update,omitzero" jsonschema:"update SVM operation"` }