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
58 changes: 58 additions & 0 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,56 @@ var rootCmd = &cobra.Command{
return newToolResult(resp, err)
})

// Register all documents, filtered by search term
s.AddTool(
mcp.NewTool("documents",
mcp.WithDescription("Get all the documents for the opslevel account. Documents are filterable by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
mcp.WithString("searchTerm", mcp.Description("To filter documents with.")),
),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
searchTerm := ""
if req.Params.Arguments["searchTerm"] != nil {
searchTerm = req.Params.Arguments["searchTerm"].(string)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I'm curious what type this is coming in as, that it needs casting to string?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably any and since searchTerm := "" is a string its needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, it's any.

}
variables := getListDocumentPayloadVariables(searchTerm)
resp, err := client.ListDocuments(&variables)
return newToolResult(resp.Nodes, err)
})

// Register document by id
s.AddTool(
mcp.NewTool("document",
mcp.WithDescription("Get document contents for the opslevel account, specified by id. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
mcp.WithString("id", mcp.Required(), mcp.Description("The id of the document to fetch.")),
),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
id := req.Params.Arguments["id"].(string)
resp, err := client.GetDocument(opslevel.ID(id))
return newToolResult(resp, err)
})

// Register all documents, filtered by service id and search term
s.AddTool(
mcp.NewTool("documentsOnService",
mcp.WithDescription("Get all documents on a specified service for the opslevel account, specified by service id and filtered by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
mcp.WithString("serviceId", mcp.Required(), mcp.Description("The id of the service which the documents are on.")),
mcp.WithString("searchTerm", mcp.Description("To filter documents with.")),
),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
service := opslevel.Service{
ServiceId: opslevel.ServiceId{
Id: opslevel.ID(req.Params.Arguments["serviceId"].(string)),
},
}
searchTerm := ""
if req.Params.Arguments["searchTerm"] != nil {
searchTerm = req.Params.Arguments["searchTerm"].(string)
}
variables := getListDocumentPayloadVariables(searchTerm)
resp, err := service.GetDocuments(client, &variables)
return newToolResult(resp, err)
})

log.Info().Msg("Starting MCP server...")
if err := server.ServeStdio(s); err != nil {
if err == context.Canceled {
Expand Down Expand Up @@ -228,3 +278,11 @@ func setupLogging() {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
}

func getListDocumentPayloadVariables(searchTerm string) opslevel.PayloadVariables {
return opslevel.PayloadVariables{
"searchTerm": searchTerm,
"after": "",
"first": 100,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to specify first and after here? opslevel-go should fill those in and auto-paginate for us, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears the pattern is only if you pass nil so since this is initialized it won't hit that logic. That would be a good improvement in opslevel-go though.

https://github.com/OpsLevel/opslevel-go/blob/main/document.go#L38

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put up an MR in opslevel-go to change this in documents (and the other spots where this pattern exists too)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being held up by some ci issues in opslevel go. IMO we should move forwards for now without this change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

}
}
2 changes: 1 addition & 1 deletion src/submodules/opslevel-go
Loading