-
Notifications
You must be signed in to change notification settings - Fork 13
Add sosreport MCP tools #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| package sosreport | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "regexp" | ||
| "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/sosreport/types" | ||
| ) | ||
|
|
||
| const ( | ||
| // defaultResultLimit is the default maximum number of lines/results to return | ||
| defaultResultLimit = 100 | ||
| ) | ||
|
|
||
| // getCommandOutput reads a command output file by filepath from manifest | ||
| func getCommandOutput(sosreportPath, relativeFilepath, pattern string, maxLines int) (string, error) { | ||
| if err := validateSosreportPath(sosreportPath); err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| if err := validateRelativePath(relativeFilepath); err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| fullPath := filepath.Join(sosreportPath, relativeFilepath) | ||
| if _, err := os.Stat(fullPath); os.IsNotExist(err) { | ||
| return "", fmt.Errorf("command output file not found: %s", relativeFilepath) | ||
| } | ||
|
|
||
| file, err := os.Open(fullPath) | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to open file: %w", err) | ||
| } | ||
| defer file.Close() | ||
|
|
||
| var searchPattern *regexp.Regexp | ||
| if pattern != "" { | ||
| searchPattern, err = regexp.Compile(pattern) | ||
| if err != nil { | ||
| return "", fmt.Errorf("invalid pattern: %w", err) | ||
| } | ||
| } | ||
|
|
||
| if maxLines <= 0 { | ||
| maxLines = defaultResultLimit | ||
| } | ||
|
|
||
| output, err := readWithLimit(file, searchPattern, maxLines) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| if output == "" && pattern != "" { | ||
| return fmt.Sprintf("No lines matching pattern %q found\n", pattern), nil | ||
| } | ||
|
|
||
| return output, nil | ||
| } | ||
|
|
||
| // listPlugins returns a list of enabled plugins with their command counts | ||
| func listPlugins(sosreportPath string) (types.ListPluginsResult, error) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice plugin aggregation for the collected logs from sos report's manifest.json file !
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see a rason to do that as they are already there as part of sos_commands: |
||
| manifest, err := loadManifest(sosreportPath) | ||
| if err != nil { | ||
| return types.ListPluginsResult{}, err | ||
| } | ||
|
|
||
| var result types.ListPluginsResult | ||
| totalCommands := 0 | ||
|
|
||
| // Only show enabled plugins | ||
| for pluginName, plugin := range manifest.Components.Report.Plugins { | ||
| commandCount := len(plugin.Commands) | ||
| totalCommands += commandCount | ||
|
|
||
| result.Plugins = append(result.Plugins, types.PluginSummary{ | ||
| Name: pluginName, | ||
| CommandCount: commandCount, | ||
| }) | ||
| } | ||
|
|
||
| result.TotalCommands = totalCommands | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just for my understanding: what is the reason for exposing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it hurts, it can help the model to narrow down the search criteria if there are too many results. |
||
| return result, nil | ||
| } | ||
|
|
||
| // listCommands returns all commands for a specific plugin | ||
| func listCommands(sosreportPath, pluginName string) (types.ListCommandsResult, error) { | ||
| manifest, err := loadManifest(sosreportPath) | ||
| if err != nil { | ||
| return types.ListCommandsResult{}, err | ||
| } | ||
|
|
||
| plugin, exists := manifest.Components.Report.Plugins[pluginName] | ||
| if !exists { | ||
| return types.ListCommandsResult{}, fmt.Errorf("plugin %q not found in manifest", pluginName) | ||
| } | ||
|
|
||
| result := types.ListCommandsResult{ | ||
| Plugin: pluginName, | ||
| CommandCount: len(plugin.Commands), | ||
| } | ||
|
|
||
| for _, cmd := range plugin.Commands { | ||
| result.Commands = append(result.Commands, types.CommandSummary{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. every sosreport tools return the output with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great observation!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right, it's not specific to sosreport. It should be mcp-sdk designation^^ |
||
| Exec: cmd.Exec, | ||
| Filepath: cmd.Filepath, | ||
| }) | ||
| } | ||
|
|
||
| return result, nil | ||
| } | ||
|
|
||
| // searchCommands searches for commands matching a pattern across all plugins | ||
| func searchCommands(sosreportPath, pattern string, maxResults int) (types.SearchCommandsResult, error) { | ||
| manifest, err := loadManifest(sosreportPath) | ||
| if err != nil { | ||
| return types.SearchCommandsResult{}, err | ||
| } | ||
|
|
||
| searchPattern, err := regexp.Compile(pattern) | ||
| if err != nil { | ||
| return types.SearchCommandsResult{}, fmt.Errorf("invalid search pattern: %w", err) | ||
| } | ||
|
|
||
| var result types.SearchCommandsResult | ||
| if maxResults <= 0 { | ||
| maxResults = defaultResultLimit | ||
| } | ||
|
|
||
| for pluginName, plugin := range manifest.Components.Report.Plugins { | ||
| for _, cmd := range plugin.Commands { | ||
| if searchPattern.MatchString(cmd.Exec) || searchPattern.MatchString(cmd.Filepath) { | ||
| result.Matches = append(result.Matches, types.CommandMatch{ | ||
| Plugin: pluginName, | ||
| Exec: cmd.Exec, | ||
| Filepath: cmd.Filepath, | ||
| }) | ||
|
|
||
| if len(result.Matches) >= maxResults { | ||
| result.Total = len(result.Matches) | ||
| return result, nil | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| result.Total = len(result.Matches) | ||
| return result, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relative path should be validated so that it doesn't contain patterns like
../,../../, etc. for traversing file path outside of the sosreport path.