Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 84 additions & 70 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,20 @@ var rootCmd = &cobra.Command{

client := NewGraphClient(version)

trueValue := true
falseValue := false

// Register Teams
s.AddTool(
mcp.NewTool(
"teams",
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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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)
Expand All @@ -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)
})
Expand All @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand Down
Loading