-
Notifications
You must be signed in to change notification settings - Fork 8
Add Service Dependency Support #99
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 1 commit
4e62908
c81cb70
e483ef1
a6d7f04
9e2bb84
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 |
|---|---|---|
|
|
@@ -107,6 +107,14 @@ type serializedCampaign struct { | |
| Reminder *opslevel.CampaignReminder | ||
| } | ||
|
|
||
| type serializedDependency struct { | ||
| Id string | ||
| ServiceId string | ||
| Aliases []string | ||
| Locked bool | ||
| Notes string | ||
| } | ||
|
|
||
| // AccountMetadata represents the different types of account metadata that can be fetched | ||
| type AccountMetadata string | ||
|
|
||
|
|
@@ -768,6 +776,118 @@ For complete reference: | |
| return newToolResult(campaigns, err) | ||
| }) | ||
|
|
||
| // Register component dependencies tool | ||
| s.AddTool( | ||
| mcp.NewTool( | ||
| "componentDependencies", | ||
| mcp.WithDescription("Get all the services that a specific component depends on. Returns the dependency graph showing which services this component consumes or calls."), | ||
| mcp.WithString("serviceId", mcp.Required(), mcp.Description("The id of the service to fetch dependencies for.")), | ||
| mcp.WithString("search", mcp.Description("Optional search term to filter dependencies by name.")), | ||
| mcp.WithToolAnnotation(mcp.ToolAnnotation{ | ||
| Title: "Component Dependencies in OpsLevel", | ||
| 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 mcp.NewToolResultError("serviceId parameter is required"), nil | ||
| } | ||
|
|
||
| service := opslevel.Service{ | ||
| ServiceId: opslevel.ServiceId{ | ||
| Id: opslevel.ID(serviceId), | ||
| }, | ||
| } | ||
|
|
||
| search := req.GetString("search", "") | ||
| variables := &opslevel.PayloadVariables{ | ||
| "after": "", | ||
| "first": 100, | ||
| } | ||
| if search != "" { | ||
| (*variables)["search"] = search | ||
| } | ||
|
|
||
| resp, err := service.GetDependencies(client, variables) | ||
| if err != nil { | ||
| return mcp.NewToolResultErrorFromErr("failed to get dependencies", err), nil | ||
| } | ||
|
|
||
| var dependencies []serializedDependency | ||
| for _, edge := range resp.Edges { | ||
| dep := serializedDependency{ | ||
| Id: string(edge.Id), | ||
| ServiceId: string(edge.Node.Id), | ||
|
wesleyjellis marked this conversation as resolved.
Outdated
|
||
| Aliases: edge.Node.Aliases, | ||
| Locked: edge.Locked, | ||
| Notes: edge.Notes, | ||
|
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. These are kind of odd values to return. I think we should drop
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. even
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. Apparently that's just what's returned by dependencies.go. We get the information on the edge (like note, locked) and |
||
| } | ||
| dependencies = append(dependencies, dep) | ||
| } | ||
|
|
||
| return newToolResult(dependencies, nil) | ||
| }) | ||
|
|
||
| // Register component dependents tool | ||
| s.AddTool( | ||
| mcp.NewTool( | ||
| "componentDependents", | ||
| mcp.WithDescription("Get all the services that depend on a specific component. Returns the reverse dependency graph showing which services consume or call this component."), | ||
| mcp.WithString("serviceId", mcp.Required(), mcp.Description("The id of the service to fetch dependents for.")), | ||
| mcp.WithString("search", mcp.Description("Optional search term to filter dependents by name.")), | ||
| mcp.WithToolAnnotation(mcp.ToolAnnotation{ | ||
| Title: "Component Dependents in OpsLevel", | ||
| 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 mcp.NewToolResultError("serviceId parameter is required"), nil | ||
| } | ||
|
|
||
| service := opslevel.Service{ | ||
| ServiceId: opslevel.ServiceId{ | ||
| Id: opslevel.ID(serviceId), | ||
| }, | ||
| } | ||
|
|
||
| search := req.GetString("search", "") | ||
| variables := &opslevel.PayloadVariables{ | ||
| "after": "", | ||
| "first": 100, | ||
| } | ||
| if search != "" { | ||
| (*variables)["search"] = search | ||
| } | ||
|
|
||
| resp, err := service.GetDependents(client, variables) | ||
| if err != nil { | ||
| return mcp.NewToolResultErrorFromErr("failed to get dependents", err), nil | ||
| } | ||
|
|
||
| var dependents []serializedDependency | ||
| for _, edge := range resp.Edges { | ||
| dep := serializedDependency{ | ||
| Id: string(edge.Id), | ||
| ServiceId: string(edge.Node.Id), | ||
| Aliases: edge.Node.Aliases, | ||
| Locked: edge.Locked, | ||
| Notes: edge.Notes, | ||
| } | ||
| dependents = append(dependents, dep) | ||
| } | ||
|
|
||
| return newToolResult(dependents, nil) | ||
| }) | ||
|
|
||
| log.Info().Msg("Starting MCP server...") | ||
| if err := server.ServeStdio(s); err != nil { | ||
| if err == context.Canceled { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.