From 58636d5ce9527367673521bda66304fd9d45d041 Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Tue, 10 Jun 2025 12:43:37 -0300 Subject: [PATCH] Upgrade mcp-go to v0.31.0 - Change tool annotation type from bool to *bool - Change the way we fetch arguments to new method --- src/cmd/root.go | 154 ++++++++++++++++++++++++++---------------------- src/go.mod | 2 +- src/go.sum | 6 +- 3 files changed, 87 insertions(+), 75 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index a3739dc..5b07970 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -122,6 +122,9 @@ var rootCmd = &cobra.Command{ client := NewGraphClient(version) + trueValue := true + falseValue := false + // Register Teams s.AddTool( mcp.NewTool( @@ -129,10 +132,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the team names, identifiers and metadata for the OpsLevel account. Teams are owners of other objects in OpsLevel. Only use this if you need to search all teams."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Teams in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -147,10 +150,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the user names, e-mail addresses and metadata for the OpsLevel account. Users are the people in OpsLevel. Only use this if you need to search all users."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Users in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -166,10 +169,10 @@ var rootCmd = &cobra.Command{ mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Actions in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -185,10 +188,10 @@ var rootCmd = &cobra.Command{ mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Filters in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -203,10 +206,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the components in the OpsLevel account. Components are objects in OpsLevel that represent things like apis, libraries, services, frontends, backends, etc. Use this tool to list what components are in the catalog, what team is the owner, what primary coding language is used, and what primary framework is used. It also includes its rubric level, corresponding to the maturity of the component; a higher index is better. A level is achieved by passing all checks tied to that same level. The Lifecycle field indicates the stage of the component (e.g., Alpha, Beta, GA, Decommissioned). The Tier field represents the importance and criticality of the component, with Tier 1 being the most critical (customer-facing with high impact) and Tier 4 being of least importance."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Components in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -238,10 +241,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the infrastructure in the OpsLevel account. Infrastructure are objects in OpsLevel that represent cloud provider resources like vpc, databases, caches, networks, vms, etc."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Infrastructure in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -270,10 +273,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the domains in the OpsLevel account. Domains are comprised of child Systems which contain Components. Used to represent large business units or verticals within OpsLevel."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Domains in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -288,10 +291,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the systems in the OpsLevel account. Systems are made up of Components that combine to form a unified whole or function. eg a 'Checkout' System that combines a cart and payment component."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Systems in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -308,15 +311,21 @@ var rootCmd = &cobra.Command{ mcp.WithString("identifier", mcp.Required(), mcp.Description("The ID or alias of the resource.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Resource Details in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { - resourceTypeString := req.Params.Arguments["resourceType"].(string) - identifier := req.Params.Arguments["identifier"].(string) + resourceTypeString, err := req.RequireString("resourceType") + if err != nil { + return nil, err + } + identifier, err := req.RequireString("identifier") + if err != nil { + return nil, err + } resourceType := opslevel.AliasOwnerTypeEnum(resourceTypeString) resp, err := client.GetAliasableResource(resourceType, identifier) switch v := resp.(type) { @@ -338,17 +347,14 @@ var rootCmd = &cobra.Command{ mcp.WithString("searchTerm", mcp.Description("To filter documents with.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Documents in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { - searchTerm := "" - if req.Params.Arguments["searchTerm"] != nil { - searchTerm = req.Params.Arguments["searchTerm"].(string) - } + searchTerm := req.GetString("searchTerm", "") variables := getListDocumentPayloadVariables(searchTerm) resp, err := client.ListDocuments(&variables) return newToolResult(resp.Nodes, err) @@ -361,14 +367,17 @@ var rootCmd = &cobra.Command{ mcp.WithString("id", mcp.Required(), mcp.Description("The id of the document to fetch.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Document in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { - id := req.Params.Arguments["id"].(string) + id, err := req.RequireString("id") + if err != nil { + return nil, err + } resp, err := client.GetDocument(opslevel.ID(id)) return newToolResult(resp, err) }) @@ -381,22 +390,23 @@ var rootCmd = &cobra.Command{ mcp.WithString("searchTerm", mcp.Description("To filter documents with.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Documents for Service in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + serviceId, err := req.RequireString("serviceId") + if err != nil { + return nil, err + } service := opslevel.Service{ ServiceId: opslevel.ServiceId{ - Id: opslevel.ID(req.Params.Arguments["serviceId"].(string)), + Id: opslevel.ID(serviceId), }, } - searchTerm := "" - if req.Params.Arguments["searchTerm"] != nil { - searchTerm = req.Params.Arguments["searchTerm"].(string) - } + searchTerm := req.GetString("searchTerm", "") variables := getListDocumentPayloadVariables(searchTerm) resp, err := service.GetDocuments(client, &variables) return newToolResult(resp, err) @@ -409,10 +419,10 @@ var rootCmd = &cobra.Command{ mcp.WithDescription("Get all the checks in the OpsLevel account. Checks provide a foundation for evaluating the maturity of software components, allowing for the definition and enforcement of criteria that ensure components are built and maintained according to best practices. Check priority is determined by level index, not level name—lower index means higher priority."), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Checks in OpsLevel", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -444,19 +454,23 @@ var rootCmd = &cobra.Command{ mcp.WithString("serviceId", mcp.Required(), mcp.Description("The id of the service to fetch.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: "Rubric of Checks for Component", - ReadOnlyHint: true, - DestructiveHint: false, - IdempotentHint: true, - OpenWorldHint: true, + ReadOnlyHint: &trueValue, + DestructiveHint: &falseValue, + IdempotentHint: &trueValue, + OpenWorldHint: &trueValue, }), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { - service, err := client.GetService(req.Params.Arguments["serviceId"].(string)) + serviceId, err := req.RequireString("serviceId") + if err != nil { + return nil, err + } + service, err := client.GetService(serviceId) if err != nil { return nil, err } if service.Id == "" { - return nil, fmt.Errorf("service with id %s not found", req.Params.Arguments["serviceId"].(string)) + return nil, fmt.Errorf("service with id %s not found", serviceId) } stats, err := service.GetServiceStats(client) diff --git a/src/go.mod b/src/go.mod index ebe7cf9..f3d84a1 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,7 +5,7 @@ go 1.24 toolchain go1.24.2 require ( - github.com/mark3labs/mcp-go v0.23.1 + github.com/mark3labs/mcp-go v0.31.0 github.com/opslevel/opslevel-go/v2025 v2025.5.28 github.com/relvacode/iso8601 v1.6.0 github.com/rs/zerolog v1.34.0 diff --git a/src/go.sum b/src/go.sum index 42b94d2..ff4ea7d 100644 --- a/src/go.sum +++ b/src/go.sum @@ -62,8 +62,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mark3labs/mcp-go v0.23.1 h1:RzTzZ5kJ+HxwnutKA4rll8N/pKV6Wh5dhCmiJUu5S9I= -github.com/mark3labs/mcp-go v0.23.1/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= +github.com/mark3labs/mcp-go v0.31.0 h1:4UxSV8aM770OPmTvaVe/b1rA2oZAjBMhGBfUgOGut+4= +github.com/mark3labs/mcp-go v0.31.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -79,8 +79,6 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/opslevel/moredefaults v0.0.0-20240529152742-17d1318a3c12 h1:OQZ3W8kbyCcdS8QUWFTnZd6xtdkfhdckc7Paro7nXio= github.com/opslevel/moredefaults v0.0.0-20240529152742-17d1318a3c12/go.mod h1:g2GSXVP6LO+5+AIsnMRPN+BeV86OXuFRTX7HXCDtYeI= -github.com/opslevel/opslevel-go/v2025 v2025.5.28 h1:LCkgLakF0MWIHMmgQHr3ldNjcO8CB4fLcs/LnRZXnHU= -github.com/opslevel/opslevel-go/v2025 v2025.5.28/go.mod h1:QiYGhFPwJj3V4efdiY3gqWlU0ZFMcJDiWPnQkPYKSYA= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=