Skip to content

Conversation

@andrewstillv15
Copy link
Contributor

@andrewstillv15 andrewstillv15 commented Apr 23, 2025

Resolves #

Problem

The MCP server wasn't able to handle questions about retrieving documents.

Solution

This MR provides three tools to empower users to query for their documents within opslevel. They may query for their documents in bulk unconstrained, within the context of a service (both with filters by doc name) & may query for a document individually to get the contents.

There seems to be a generic problem with response length + claude that has now appeared due to the number of documents that you can fetch. I'll address this in a separate MR.

Checklist

  • I have run this code, and it appears to resolve the stated issue.
  • This PR has no user interface changes or has already received approval from product management to change the interface.
  • Make a changie entry that explains the customer facing outcome of this change.

@andrewstillv15 andrewstillv15 marked this pull request as ready for review April 23, 2025 21:35
@andrewstillv15 andrewstillv15 changed the title get document working, initial run at list docs Add tool support for document queries Apr 23, 2025
Copy link
Contributor

@rocktavious rocktavious left a comment

Choose a reason for hiding this comment

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

LGTM but i'd like @eapache-opslevel opinion too.

src/cmd/root.go Outdated

// Register all documents, filtered by search term
s.AddTool(
mcp.NewTool("listDocuments",
Copy link
Contributor

Choose a reason for hiding this comment

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

we don't have verbs in the other commands, so for consistency this should be documents

src/cmd/root.go Outdated
// Register all documents, filtered by search term
s.AddTool(
mcp.NewTool("listDocuments",
mcp.WithDescription("Get all the documents for the opslevel account. Documents are filterable by search term"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we describe what kind of things might be in documents?

Something like

"Get all the documents for the documents for the opslevel account. Documents could be things like run books, getting started guides and other forms of documentation."

Choose a reason for hiding this comment

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

If you do this copy-enriching here, similar verbage should happen on the other descriptions too.

src/cmd/root.go Outdated
// 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 documentatin, readmes, or other forms of documentation."),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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 documentatin, readmes, or other forms of documentation."),
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."),

src/cmd/root.go Outdated
// 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 documentatin, readmes, or other forms of documentation."),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
mcp.WithDescription("Get document contents for the opslevel account, specified by id. Documents could be things like runbooks, integration documentation, api documentatin, readmes, or other forms of documentation."),
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."),

src/cmd/root.go Outdated
// 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 documentatin, readmes, or other forms of documentation."),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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 documentatin, readmes, or other forms of documentation."),
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."),

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 := 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

src/cmd/root.go Outdated
}

func getListDocumentPayloadVariables(searchTerm string) opslevel.PayloadVariables {
variables := opslevel.PayloadVariables{
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you can just return the literal, you don't need to assign it to a variable and return the variable

@andrewstillv15 andrewstillv15 merged commit e471c9e into main Apr 24, 2025
1 check passed
@andrewstillv15 andrewstillv15 deleted the andrew/add-docs-tools branch April 24, 2025 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants