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
263 changes: 134 additions & 129 deletions src/cmd/mcp.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package cmd

import (
"context"
"encoding/json"

mcp_golang "github.com/metoro-io/mcp-golang"
"github.com/metoro-io/mcp-golang/transport/stdio"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

Expand All @@ -25,152 +27,155 @@ var mcpCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
done := make(chan struct{})

// transport := http.NewHTTPTransport("/mcp")
// transport.WithAddr(":8080")
// server := mcp_golang.NewServer(transport)
server := mcp_golang.NewServer(stdio.NewStdioServerTransport())
s := server.NewMCPServer(
"OpsLevel",
"1.0.0",
)

// Register Teams
if err := server.RegisterTool("teams", "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.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListTeams(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
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.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListTeams(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Users
if err := server.RegisterTool("users", "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.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListUsers(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("users", 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.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListUsers(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Actions
if err := server.RegisterTool("actions", "Get all the information about actions the user can run in the opslevel account", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListTriggerDefinitions(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("actions", mcp.WithDescription("Get all the information about actions the user can run in the opslevel account")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListTriggerDefinitions(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Filters
if err := server.RegisterTool("filters", "Get all the rubric filter names and which predicates they have for the opslevel account", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListFilters(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("filters", mcp.WithDescription("Get all the rubric filter names and which predicates they have for the opslevel account")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListFilters(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Components
if err := server.RegisterTool("components", "Get all the components in the opslevel account. Components are objects in opslevel that represent things like apis, libraries, services, frontends, backends, etc.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListServices(nil)
if err != nil {
return nil, err
}
var components []LightweightComponent
for _, node := range resp.Nodes {
components = append(components, LightweightComponent{
Id: string(node.Id),
Name: node.Name,
Owner: node.Owner.Alias,
URL: node.HtmlURL,
})
}
data, err := json.Marshal(components)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("components", 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.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListServices(nil)
if err != nil {
return nil, err
}
var components []LightweightComponent
for _, node := range resp.Nodes {
components = append(components, LightweightComponent{
Id: string(node.Id),
Name: node.Name,
Owner: node.Owner.Alias,
URL: node.HtmlURL,
})
}
data, err := json.Marshal(components)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Infra
if err := server.RegisterTool("infrastructure", "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.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListInfrastructure(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("infrastructure", 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.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListInfrastructure(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Domains
if err := server.RegisterTool("domains", "Get all the domains in the opslevel account. Domains are objects in opslevel that represent a top-level abstraction used to organize and categorize software systems.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListDomains(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}
s.AddTool(
mcp.NewTool("domains", mcp.WithDescription("Get all the domains in the opslevel account. Domains are objects in opslevel that represent a top-level abstraction used to organize and categorize software systems.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListDomains(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

// Register Systems
if err := server.RegisterTool("systems", "Get all the systems in the opslevel account. Systems are objects in opslevel that represent a grouping of services or components that act together to serve a business function or process.", func(args NullArguments) (*mcp_golang.ToolResponse, error) {
client := getClientGQL()
resp, err := client.ListSystems(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(data))), nil
}); err != nil {
panic(err)
}

if err := server.Serve(); err != nil {
s.AddTool(
mcp.NewTool("systems", mcp.WithDescription("Get all the systems in the opslevel account. Systems are objects in opslevel that represent a grouping of services or components that act together to serve a business function or process.")),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := getClientGQL()
resp, err := client.ListSystems(nil)
if err != nil {
return nil, err
}
data, err := json.Marshal(resp.Nodes)
if err != nil {
return nil, err
}
return mcp.NewToolResultText(string(data)), nil
})

log.Info().Msg("Starting MCP server...")
if err := server.ServeStdio(s); err != nil {
panic(err)
}
<-done

return nil
},
}
Expand Down
12 changes: 2 additions & 10 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/gosimple/slug v1.15.0
github.com/itchyny/gojq v0.12.17
github.com/manifoldco/promptui v0.9.0
github.com/metoro-io/mcp-golang v0.9.0
github.com/mark3labs/mcp-go v0.21.0
github.com/mitchellh/mapstructure v1.5.0
github.com/open-policy-agent/opa v0.67.1
github.com/opslevel/opslevel-go/v2024 v2024.12.24
Expand All @@ -32,9 +32,7 @@ require (
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
Expand All @@ -61,12 +59,10 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hasura/go-graphql-client v0.13.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.12.0 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
Expand All @@ -88,15 +84,11 @@ require (
github.com/spf13/cast v1.7.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/sdk v1.29.0 // indirect
Expand Down
Loading
Loading