diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index 55b286906..b3f7124cc 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -1578,108 +1578,215 @@ func (r *Resolver) UpdateControlTool(ctx context.Context, req *mcp.CallToolReque }, nil } -func (r *Resolver) LinkControlMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlMeasureInput) (*mcp.CallToolResult, types.LinkControlMeasureOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlMeasureMappingCreate) - +func (r *Resolver) LinkControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlInput) (*mcp.CallToolResult, types.LinkControlOutput, error) { svc := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.CreateMeasureMapping(ctx, input.ControlID, input.MeasureID) - if err != nil { - return nil, types.LinkControlMeasureOutput{}, fmt.Errorf("failed to link control measure: %w", err) + switch input.ResourceID.EntityType() { + case coredata.MeasureEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlMeasureMappingCreate) + if _, _, err := svc.Controls.CreateMeasureMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.LinkControlOutput{}, fmt.Errorf("failed to link control to measure: %w", err) + } + case coredata.DocumentEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlDocumentMappingCreate) + if _, _, err := svc.Controls.CreateDocumentMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.LinkControlOutput{}, fmt.Errorf("failed to link control to document: %w", err) + } + case coredata.AuditEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlAuditMappingCreate) + if _, _, err := svc.Controls.CreateAuditMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.LinkControlOutput{}, fmt.Errorf("failed to link control to audit: %w", err) + } + case coredata.SnapshotEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlSnapshotMappingCreate) + if _, _, err := svc.Controls.CreateSnapshotMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.LinkControlOutput{}, fmt.Errorf("failed to link control to snapshot: %w", err) + } + case coredata.ObligationEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlObligationMappingCreate) + if _, _, err := svc.Controls.CreateObligationMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.LinkControlOutput{}, fmt.Errorf("failed to link control to obligation: %w", err) + } + default: + return nil, types.LinkControlOutput{}, fmt.Errorf("unsupported resource type for control linking: entity type %d", input.ResourceID.EntityType()) } - return nil, types.LinkControlMeasureOutput{}, nil + return nil, types.LinkControlOutput{}, nil } -func (r *Resolver) UnlinkControlMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlMeasureInput) (*mcp.CallToolResult, types.UnlinkControlMeasureOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlMeasureMappingDelete) - +func (r *Resolver) UnlinkControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlInput) (*mcp.CallToolResult, types.UnlinkControlOutput, error) { svc := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.DeleteMeasureMapping(ctx, input.ControlID, input.MeasureID) - if err != nil { - return nil, types.UnlinkControlMeasureOutput{}, fmt.Errorf("failed to unlink control measure: %w", err) + switch input.ResourceID.EntityType() { + case coredata.MeasureEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlMeasureMappingDelete) + if _, _, err := svc.Controls.DeleteMeasureMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.UnlinkControlOutput{}, fmt.Errorf("failed to unlink control from measure: %w", err) + } + case coredata.DocumentEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlDocumentMappingDelete) + if _, _, err := svc.Controls.DeleteDocumentMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.UnlinkControlOutput{}, fmt.Errorf("failed to unlink control from document: %w", err) + } + case coredata.AuditEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlAuditMappingDelete) + if _, _, err := svc.Controls.DeleteAuditMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.UnlinkControlOutput{}, fmt.Errorf("failed to unlink control from audit: %w", err) + } + case coredata.SnapshotEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlSnapshotMappingDelete) + if _, _, err := svc.Controls.DeleteSnapshotMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.UnlinkControlOutput{}, fmt.Errorf("failed to unlink control from snapshot: %w", err) + } + case coredata.ObligationEntityType: + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlObligationMappingDelete) + if _, _, err := svc.Controls.DeleteObligationMapping(ctx, input.ControlID, input.ResourceID); err != nil { + return nil, types.UnlinkControlOutput{}, fmt.Errorf("failed to unlink control from obligation: %w", err) + } + default: + return nil, types.UnlinkControlOutput{}, fmt.Errorf("unsupported resource type for control unlinking: entity type %d", input.ResourceID.EntityType()) } - return nil, types.UnlinkControlMeasureOutput{}, nil + return nil, types.UnlinkControlOutput{}, nil } -func (r *Resolver) LinkControlDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlDocumentInput) (*mcp.CallToolResult, types.LinkControlDocumentOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlDocumentMappingCreate) +func (r *Resolver) ListControlObligationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlObligationsInput) (*mcp.CallToolResult, types.ListControlObligationsOutput, error) { + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlGet) - svc := r.ProboService(ctx, input.ControlID) + prb := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.CreateDocumentMapping(ctx, input.ControlID, input.DocumentID) - if err != nil { - return nil, types.LinkControlDocumentOutput{}, fmt.Errorf("failed to link control document: %w", err) + pageOrderBy := page.OrderBy[coredata.ObligationOrderField]{ + Field: coredata.ObligationOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.ObligationOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } } - return nil, types.LinkControlDocumentOutput{}, nil + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + obligationPage, err := prb.Obligations.ListForControlID(ctx, input.ControlID, cursor, coredata.NewObligationFilter(nil)) + if err != nil { + return nil, types.ListControlObligationsOutput{}, fmt.Errorf("failed to list control obligations: %w", err) + } + + return nil, types.NewListControlObligationsOutput(obligationPage), nil } -func (r *Resolver) UnlinkControlDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlDocumentInput) (*mcp.CallToolResult, types.UnlinkControlDocumentOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlDocumentMappingDelete) +func (r *Resolver) ListControlMeasuresTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlMeasuresInput) (*mcp.CallToolResult, types.ListControlMeasuresOutput, error) { + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlGet) - svc := r.ProboService(ctx, input.ControlID) + prb := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.DeleteDocumentMapping(ctx, input.ControlID, input.DocumentID) - if err != nil { - return nil, types.UnlinkControlDocumentOutput{}, fmt.Errorf("failed to unlink control document: %w", err) + pageOrderBy := page.OrderBy[coredata.MeasureOrderField]{ + Field: coredata.MeasureOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.MeasureOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } } - return nil, types.UnlinkControlDocumentOutput{}, nil + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + measurePage, err := prb.Measures.ListForControlID(ctx, input.ControlID, cursor, coredata.NewMeasureFilter(nil, nil)) + if err != nil { + return nil, types.ListControlMeasuresOutput{}, fmt.Errorf("failed to list control measures: %w", err) + } + + return nil, types.NewListControlMeasuresOutput(measurePage), nil } -func (r *Resolver) LinkControlAuditTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlAuditInput) (*mcp.CallToolResult, types.LinkControlAuditOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlAuditMappingCreate) +func (r *Resolver) ListControlDocumentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlDocumentsInput) (*mcp.CallToolResult, types.ListControlDocumentsOutput, error) { + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlGet) - svc := r.ProboService(ctx, input.ControlID) + prb := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.CreateAuditMapping(ctx, input.ControlID, input.AuditID) - if err != nil { - return nil, types.LinkControlAuditOutput{}, fmt.Errorf("failed to link control audit: %w", err) + pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{ + Field: coredata.DocumentOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } } - return nil, types.LinkControlAuditOutput{}, nil + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + docPage, err := prb.Documents.ListForControlID(ctx, input.ControlID, cursor, coredata.NewDocumentFilter(nil)) + if err != nil { + return nil, types.ListControlDocumentsOutput{}, fmt.Errorf("failed to list control documents: %w", err) + } + + approverIDsMap := make(map[gid.GID][]gid.GID) + for _, d := range docPage.Data { + approverPage, err := prb.Documents.ListApprovers(ctx, d.ID, allApproversCursor()) + if err != nil { + return nil, types.ListControlDocumentsOutput{}, fmt.Errorf("failed to list document approvers: %w", err) + } + approverIDsMap[d.ID] = profileIDs(approverPage) + } + + return nil, types.NewListControlDocumentsOutput(docPage, approverIDsMap), nil } -func (r *Resolver) UnlinkControlAuditTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlAuditInput) (*mcp.CallToolResult, types.UnlinkControlAuditOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlAuditMappingDelete) +func (r *Resolver) ListControlAuditsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlAuditsInput) (*mcp.CallToolResult, types.ListControlAuditsOutput, error) { + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlGet) - svc := r.ProboService(ctx, input.ControlID) + prb := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.DeleteAuditMapping(ctx, input.ControlID, input.AuditID) - if err != nil { - return nil, types.UnlinkControlAuditOutput{}, fmt.Errorf("failed to unlink control audit: %w", err) + pageOrderBy := page.OrderBy[coredata.AuditOrderField]{ + Field: coredata.AuditOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.AuditOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } } - return nil, types.UnlinkControlAuditOutput{}, nil + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + auditPage, err := prb.Audits.ListForControlID(ctx, input.ControlID, cursor) + if err != nil { + return nil, types.ListControlAuditsOutput{}, fmt.Errorf("failed to list control audits: %w", err) + } + + return nil, types.NewListControlAuditsOutput(auditPage), nil } -func (r *Resolver) LinkControlSnapshotTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlSnapshotInput) (*mcp.CallToolResult, types.LinkControlSnapshotOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlSnapshotMappingCreate) +func (r *Resolver) ListControlSnapshotsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlSnapshotsInput) (*mcp.CallToolResult, types.ListControlSnapshotsOutput, error) { + r.MustAuthorize(ctx, input.ControlID, probo.ActionControlGet) - svc := r.ProboService(ctx, input.ControlID) + prb := r.ProboService(ctx, input.ControlID) - _, _, err := svc.Controls.CreateSnapshotMapping(ctx, input.ControlID, input.SnapshotID) - if err != nil { - return nil, types.LinkControlSnapshotOutput{}, fmt.Errorf("failed to link control snapshot: %w", err) + pageOrderBy := page.OrderBy[coredata.SnapshotOrderField]{ + Field: coredata.SnapshotOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.SnapshotOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } } - return nil, types.LinkControlSnapshotOutput{}, nil -} + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) -func (r *Resolver) UnlinkControlSnapshotTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlSnapshotInput) (*mcp.CallToolResult, types.UnlinkControlSnapshotOutput, error) { - r.MustAuthorize(ctx, input.ControlID, probo.ActionControlSnapshotMappingDelete) - - svc := r.ProboService(ctx, input.ControlID) - - _, _, err := svc.Controls.DeleteSnapshotMapping(ctx, input.ControlID, input.SnapshotID) + snapshotPage, err := prb.Snapshots.ListForControlID(ctx, input.ControlID, cursor) if err != nil { - return nil, types.UnlinkControlSnapshotOutput{}, fmt.Errorf("failed to unlink control snapshot: %w", err) + return nil, types.ListControlSnapshotsOutput{}, fmt.Errorf("failed to list control snapshots: %w", err) } - return nil, types.UnlinkControlSnapshotOutput{}, nil + return nil, types.NewListControlSnapshotsOutput(snapshotPage), nil } func (r *Resolver) ListRiskObligationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRiskObligationsInput) (*mcp.CallToolResult, types.ListRiskObligationsOutput, error) { diff --git a/pkg/server/api/mcp/v1/server/server.go b/pkg/server/api/mcp/v1/server/server.go index 0c25c4b9a..e85e54f0f 100644 --- a/pkg/server/api/mcp/v1/server/server.go +++ b/pkg/server/api/mcp/v1/server/server.go @@ -4,6 +4,7 @@ package server import ( "context" + "github.com/modelcontextprotocol/go-sdk/mcp" "go.probo.inc/probo/pkg/server/api/mcp/v1/types" ) @@ -86,14 +87,13 @@ type ResolverInterface interface { GetControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetControlInput) (*mcp.CallToolResult, types.GetControlOutput, error) AddControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddControlInput) (*mcp.CallToolResult, types.AddControlOutput, error) UpdateControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateControlInput) (*mcp.CallToolResult, types.UpdateControlOutput, error) - LinkControlMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlMeasureInput) (*mcp.CallToolResult, types.LinkControlMeasureOutput, error) - UnlinkControlMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlMeasureInput) (*mcp.CallToolResult, types.UnlinkControlMeasureOutput, error) - LinkControlDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlDocumentInput) (*mcp.CallToolResult, types.LinkControlDocumentOutput, error) - UnlinkControlDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlDocumentInput) (*mcp.CallToolResult, types.UnlinkControlDocumentOutput, error) - LinkControlAuditTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlAuditInput) (*mcp.CallToolResult, types.LinkControlAuditOutput, error) - UnlinkControlAuditTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlAuditInput) (*mcp.CallToolResult, types.UnlinkControlAuditOutput, error) - LinkControlSnapshotTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlSnapshotInput) (*mcp.CallToolResult, types.LinkControlSnapshotOutput, error) - UnlinkControlSnapshotTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlSnapshotInput) (*mcp.CallToolResult, types.UnlinkControlSnapshotOutput, error) + LinkControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkControlInput) (*mcp.CallToolResult, types.LinkControlOutput, error) + UnlinkControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkControlInput) (*mcp.CallToolResult, types.UnlinkControlOutput, error) + ListControlObligationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlObligationsInput) (*mcp.CallToolResult, types.ListControlObligationsOutput, error) + ListControlMeasuresTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlMeasuresInput) (*mcp.CallToolResult, types.ListControlMeasuresOutput, error) + ListControlDocumentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlDocumentsInput) (*mcp.CallToolResult, types.ListControlDocumentsOutput, error) + ListControlAuditsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlAuditsInput) (*mcp.CallToolResult, types.ListControlAuditsOutput, error) + ListControlSnapshotsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlSnapshotsInput) (*mcp.CallToolResult, types.ListControlSnapshotsOutput, error) ListRiskObligationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRiskObligationsInput) (*mcp.CallToolResult, types.ListRiskObligationsOutput, error) LinkRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkRiskInput) (*mcp.CallToolResult, types.LinkRiskOutput, error) UnlinkRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkRiskInput) (*mcp.CallToolResult, types.UnlinkRiskOutput, error) @@ -1067,82 +1067,92 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) { mcp.AddTool( server, &mcp.Tool{ - Name: "linkControlMeasure", - Description: "Link a measure to a control", - InputSchema: types.LinkControlMeasureToolInputSchema, - OutputSchema: types.LinkControlMeasureToolOutputSchema, + Name: "linkControl", + Description: "Link a resource to a control (measure, document, audit, snapshot, or obligation). The resource type is determined from the resource_id GID.", + InputSchema: types.LinkControlToolInputSchema, + OutputSchema: types.LinkControlToolOutputSchema, }, - resolver.LinkControlMeasureTool, + resolver.LinkControlTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "unlinkControlMeasure", - Description: "Unlink a measure from a control", - InputSchema: types.UnlinkControlMeasureToolInputSchema, - OutputSchema: types.UnlinkControlMeasureToolOutputSchema, + Name: "unlinkControl", + Description: "Unlink a resource from a control (measure, document, audit, snapshot, or obligation). The resource type is determined from the resource_id GID.", + InputSchema: types.UnlinkControlToolInputSchema, + OutputSchema: types.UnlinkControlToolOutputSchema, }, - resolver.UnlinkControlMeasureTool, + resolver.UnlinkControlTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "linkControlDocument", - Description: "Link a document to a control", - InputSchema: types.LinkControlDocumentToolInputSchema, - OutputSchema: types.LinkControlDocumentToolOutputSchema, + Name: "listControlObligations", + Description: "List obligations linked to a control", + InputSchema: types.ListControlObligationsToolInputSchema, + OutputSchema: types.ListControlObligationsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, }, - resolver.LinkControlDocumentTool, + resolver.ListControlObligationsTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "unlinkControlDocument", - Description: "Unlink a document from a control", - InputSchema: types.UnlinkControlDocumentToolInputSchema, - OutputSchema: types.UnlinkControlDocumentToolOutputSchema, + Name: "listControlMeasures", + Description: "List measures linked to a control", + InputSchema: types.ListControlMeasuresToolInputSchema, + OutputSchema: types.ListControlMeasuresToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, }, - resolver.UnlinkControlDocumentTool, + resolver.ListControlMeasuresTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "linkControlAudit", - Description: "Link an audit to a control", - InputSchema: types.LinkControlAuditToolInputSchema, - OutputSchema: types.LinkControlAuditToolOutputSchema, + Name: "listControlDocuments", + Description: "List documents linked to a control", + InputSchema: types.ListControlDocumentsToolInputSchema, + OutputSchema: types.ListControlDocumentsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, }, - resolver.LinkControlAuditTool, + resolver.ListControlDocumentsTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "unlinkControlAudit", - Description: "Unlink an audit from a control", - InputSchema: types.UnlinkControlAuditToolInputSchema, - OutputSchema: types.UnlinkControlAuditToolOutputSchema, + Name: "listControlAudits", + Description: "List audits linked to a control", + InputSchema: types.ListControlAuditsToolInputSchema, + OutputSchema: types.ListControlAuditsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, }, - resolver.UnlinkControlAuditTool, + resolver.ListControlAuditsTool, ) mcp.AddTool( server, &mcp.Tool{ - Name: "linkControlSnapshot", - Description: "Link a snapshot to a control", - InputSchema: types.LinkControlSnapshotToolInputSchema, - OutputSchema: types.LinkControlSnapshotToolOutputSchema, + Name: "listControlSnapshots", + Description: "List snapshots linked to a control", + InputSchema: types.ListControlSnapshotsToolInputSchema, + OutputSchema: types.ListControlSnapshotsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, }, - resolver.LinkControlSnapshotTool, - ) - mcp.AddTool( - server, - &mcp.Tool{ - Name: "unlinkControlSnapshot", - Description: "Unlink a snapshot from a control", - InputSchema: types.UnlinkControlSnapshotToolInputSchema, - OutputSchema: types.UnlinkControlSnapshotToolOutputSchema, - }, - resolver.UnlinkControlSnapshotTool, + resolver.ListControlSnapshotsTool, ) mcp.AddTool( server, diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 91416a9e3..f6269bced 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -4317,133 +4317,192 @@ components: control: $ref: "#/components/schemas/Control" - LinkControlMeasureInput: + LinkControlInput: type: object required: - control_id - - measure_id + - resource_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - measure_id: + resource_id: $ref: "#/components/schemas/GID" - description: Measure ID + description: ID of the resource to link (measure, document, audit, snapshot, or obligation) - LinkControlMeasureOutput: + LinkControlOutput: type: object - UnlinkControlMeasureInput: + UnlinkControlInput: type: object required: - control_id - - measure_id + - resource_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - measure_id: + resource_id: $ref: "#/components/schemas/GID" - description: Measure ID + description: ID of the resource to unlink (measure, document, audit, snapshot, or obligation) - UnlinkControlMeasureOutput: + UnlinkControlOutput: type: object - LinkControlDocumentInput: + ListControlObligationsInput: type: object required: - control_id - - document_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - document_id: - $ref: "#/components/schemas/GID" - description: Document ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/ObligationOrderBy" + description: Obligation order by - LinkControlDocumentOutput: + ListControlObligationsOutput: type: object + required: + - obligations + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + obligations: + type: array + items: + $ref: "#/components/schemas/Obligation" - UnlinkControlDocumentInput: + ListControlMeasuresInput: type: object required: - control_id - - document_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - document_id: - $ref: "#/components/schemas/GID" - description: Document ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/MeasureOrderBy" + description: Measure order by - UnlinkControlDocumentOutput: + ListControlMeasuresOutput: type: object + required: + - measures + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + measures: + type: array + items: + $ref: "#/components/schemas/Measure" - LinkControlAuditInput: + ListControlDocumentsInput: type: object required: - control_id - - audit_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - audit_id: - $ref: "#/components/schemas/GID" - description: Audit ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/DocumentOrderBy" + description: Document order by - LinkControlAuditOutput: + ListControlDocumentsOutput: type: object + required: + - documents + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + documents: + type: array + items: + $ref: "#/components/schemas/Document" - UnlinkControlAuditInput: + ListControlAuditsInput: type: object required: - control_id - - audit_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - audit_id: - $ref: "#/components/schemas/GID" - description: Audit ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/AuditOrderBy" + description: Audit order by - UnlinkControlAuditOutput: + ListControlAuditsOutput: type: object + required: + - audits + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + audits: + type: array + items: + $ref: "#/components/schemas/Audit" - LinkControlSnapshotInput: + ListControlSnapshotsInput: type: object required: - control_id - - snapshot_id properties: control_id: $ref: "#/components/schemas/GID" description: Control ID - snapshot_id: - $ref: "#/components/schemas/GID" - description: Snapshot ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/SnapshotOrderBy" + description: Snapshot order by - LinkControlSnapshotOutput: - type: object - - UnlinkControlSnapshotInput: + ListControlSnapshotsOutput: type: object required: - - control_id - - snapshot_id + - snapshots properties: - control_id: - $ref: "#/components/schemas/GID" - description: Control ID - snapshot_id: - $ref: "#/components/schemas/GID" - description: Snapshot ID - - UnlinkControlSnapshotOutput: - type: object + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + snapshots: + type: array + items: + $ref: "#/components/schemas/Snapshot" ListRiskObligationsInput: type: object @@ -6839,70 +6898,67 @@ tools: $ref: "#/components/schemas/UpdateControlInput" outputSchema: $ref: "#/components/schemas/UpdateControlOutput" - - name: linkControlMeasure - description: Link a measure to a control + - name: linkControl + description: Link a resource to a control (measure, document, audit, snapshot, or obligation). The resource type is determined from the resource_id GID. hints: readonly: false inputSchema: - $ref: "#/components/schemas/LinkControlMeasureInput" + $ref: "#/components/schemas/LinkControlInput" outputSchema: - $ref: "#/components/schemas/LinkControlMeasureOutput" - - name: unlinkControlMeasure - description: Unlink a measure from a control + $ref: "#/components/schemas/LinkControlOutput" + - name: unlinkControl + description: Unlink a resource from a control (measure, document, audit, snapshot, or obligation). The resource type is determined from the resource_id GID. hints: readonly: false inputSchema: - $ref: "#/components/schemas/UnlinkControlMeasureInput" + $ref: "#/components/schemas/UnlinkControlInput" outputSchema: - $ref: "#/components/schemas/UnlinkControlMeasureOutput" - - name: linkControlDocument - description: Link a document to a control + $ref: "#/components/schemas/UnlinkControlOutput" + - name: listControlObligations + description: List obligations linked to a control hints: - readonly: false + readonly: true + idempotent: true inputSchema: - $ref: "#/components/schemas/LinkControlDocumentInput" + $ref: "#/components/schemas/ListControlObligationsInput" outputSchema: - $ref: "#/components/schemas/LinkControlDocumentOutput" - - name: unlinkControlDocument - description: Unlink a document from a control + $ref: "#/components/schemas/ListControlObligationsOutput" + - name: listControlMeasures + description: List measures linked to a control hints: - readonly: false + readonly: true + idempotent: true inputSchema: - $ref: "#/components/schemas/UnlinkControlDocumentInput" + $ref: "#/components/schemas/ListControlMeasuresInput" outputSchema: - $ref: "#/components/schemas/UnlinkControlDocumentOutput" - - name: linkControlAudit - description: Link an audit to a control + $ref: "#/components/schemas/ListControlMeasuresOutput" + - name: listControlDocuments + description: List documents linked to a control hints: - readonly: false + readonly: true + idempotent: true inputSchema: - $ref: "#/components/schemas/LinkControlAuditInput" + $ref: "#/components/schemas/ListControlDocumentsInput" outputSchema: - $ref: "#/components/schemas/LinkControlAuditOutput" - - name: unlinkControlAudit - description: Unlink an audit from a control + $ref: "#/components/schemas/ListControlDocumentsOutput" + - name: listControlAudits + description: List audits linked to a control hints: - readonly: false + readonly: true + idempotent: true inputSchema: - $ref: "#/components/schemas/UnlinkControlAuditInput" + $ref: "#/components/schemas/ListControlAuditsInput" outputSchema: - $ref: "#/components/schemas/UnlinkControlAuditOutput" - - name: linkControlSnapshot - description: Link a snapshot to a control + $ref: "#/components/schemas/ListControlAuditsOutput" + - name: listControlSnapshots + description: List snapshots linked to a control hints: - readonly: false + readonly: true + idempotent: true inputSchema: - $ref: "#/components/schemas/LinkControlSnapshotInput" + $ref: "#/components/schemas/ListControlSnapshotsInput" outputSchema: - $ref: "#/components/schemas/LinkControlSnapshotOutput" - - name: unlinkControlSnapshot - description: Unlink a snapshot from a control - hints: - readonly: false - inputSchema: - $ref: "#/components/schemas/UnlinkControlSnapshotInput" - outputSchema: - $ref: "#/components/schemas/UnlinkControlSnapshotOutput" + $ref: "#/components/schemas/ListControlSnapshotsOutput" - name: listRiskObligations description: List obligations linked to a risk hints: diff --git a/pkg/server/api/mcp/v1/types/audit.go b/pkg/server/api/mcp/v1/types/audit.go index 27ea97fc7..813d7fdbb 100644 --- a/pkg/server/api/mcp/v1/types/audit.go +++ b/pkg/server/api/mcp/v1/types/audit.go @@ -34,6 +34,24 @@ func NewAudit(a *coredata.Audit) *Audit { } } +func NewListControlAuditsOutput(auditPage *page.Page[*coredata.Audit, coredata.AuditOrderField]) ListControlAuditsOutput { + audits := make([]*Audit, 0, len(auditPage.Data)) + for _, v := range auditPage.Data { + audits = append(audits, NewAudit(v)) + } + + var nextCursor *page.CursorKey + if len(auditPage.Data) > 0 { + cursorKey := auditPage.Data[len(auditPage.Data)-1].CursorKey(auditPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListControlAuditsOutput{ + NextCursor: nextCursor, + Audits: audits, + } +} + func NewListAuditsOutput(auditPage *page.Page[*coredata.Audit, coredata.AuditOrderField]) ListAuditsOutput { audits := make([]*Audit, 0, len(auditPage.Data)) for _, v := range auditPage.Data { diff --git a/pkg/server/api/mcp/v1/types/document.go b/pkg/server/api/mcp/v1/types/document.go index 1047f2f2f..5b048b1c3 100644 --- a/pkg/server/api/mcp/v1/types/document.go +++ b/pkg/server/api/mcp/v1/types/document.go @@ -35,6 +35,24 @@ func NewDocument(d *coredata.Document, approverIDs []gid.GID) *Document { } } +func NewListControlDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField], approverIDsMap map[gid.GID][]gid.GID) ListControlDocumentsOutput { + documents := make([]*Document, 0, len(documentPage.Data)) + for _, d := range documentPage.Data { + documents = append(documents, NewDocument(d, approverIDsMap[d.ID])) + } + + var nextCursor *page.CursorKey + if len(documentPage.Data) > 0 { + cursorKey := documentPage.Data[len(documentPage.Data)-1].CursorKey(documentPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListControlDocumentsOutput{ + NextCursor: nextCursor, + Documents: documents, + } +} + func NewListDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField], approverIDsMap map[gid.GID][]gid.GID) ListDocumentsOutput { documents := make([]*Document, 0, len(documentPage.Data)) for _, d := range documentPage.Data { diff --git a/pkg/server/api/mcp/v1/types/measure.go b/pkg/server/api/mcp/v1/types/measure.go index 123b65130..d96dc6cf2 100644 --- a/pkg/server/api/mcp/v1/types/measure.go +++ b/pkg/server/api/mcp/v1/types/measure.go @@ -31,6 +31,24 @@ func NewMeasure(m *coredata.Measure) *Measure { } } +func NewListControlMeasuresOutput(measurePage *page.Page[*coredata.Measure, coredata.MeasureOrderField]) ListControlMeasuresOutput { + measures := make([]*Measure, 0, len(measurePage.Data)) + for _, v := range measurePage.Data { + measures = append(measures, NewMeasure(v)) + } + + var nextCursor *page.CursorKey + if len(measurePage.Data) > 0 { + cursorKey := measurePage.Data[len(measurePage.Data)-1].CursorKey(measurePage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListControlMeasuresOutput{ + NextCursor: nextCursor, + Measures: measures, + } +} + func NewListMeasuresOutput(measurePage *page.Page[*coredata.Measure, coredata.MeasureOrderField]) ListMeasuresOutput { measures := make([]*Measure, 0, len(measurePage.Data)) for _, v := range measurePage.Data { diff --git a/pkg/server/api/mcp/v1/types/obligation.go b/pkg/server/api/mcp/v1/types/obligation.go index 15d548c43..fb601e100 100644 --- a/pkg/server/api/mcp/v1/types/obligation.go +++ b/pkg/server/api/mcp/v1/types/obligation.go @@ -57,6 +57,24 @@ func NewListObligationsOutput(obligationPage *page.Page[*coredata.Obligation, co } } +func NewListControlObligationsOutput(obligationPage *page.Page[*coredata.Obligation, coredata.ObligationOrderField]) ListControlObligationsOutput { + obligations := make([]*Obligation, 0, len(obligationPage.Data)) + for _, v := range obligationPage.Data { + obligations = append(obligations, NewObligation(v)) + } + + var nextCursor *page.CursorKey + if len(obligationPage.Data) > 0 { + cursorKey := obligationPage.Data[len(obligationPage.Data)-1].CursorKey(obligationPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListControlObligationsOutput{ + NextCursor: nextCursor, + Obligations: obligations, + } +} + func NewListRiskObligationsOutput(obligationPage *page.Page[*coredata.Obligation, coredata.ObligationOrderField]) ListRiskObligationsOutput { obligations := make([]*Obligation, 0, len(obligationPage.Data)) for _, v := range obligationPage.Data { diff --git a/pkg/server/api/mcp/v1/types/snapshot.go b/pkg/server/api/mcp/v1/types/snapshot.go index 43430dd03..f880dd7ba 100644 --- a/pkg/server/api/mcp/v1/types/snapshot.go +++ b/pkg/server/api/mcp/v1/types/snapshot.go @@ -29,6 +29,24 @@ func NewSnapshot(s *coredata.Snapshot) *Snapshot { } } +func NewListControlSnapshotsOutput(snapshotPage *page.Page[*coredata.Snapshot, coredata.SnapshotOrderField]) ListControlSnapshotsOutput { + snapshots := make([]*Snapshot, 0, len(snapshotPage.Data)) + for _, s := range snapshotPage.Data { + snapshots = append(snapshots, NewSnapshot(s)) + } + + var nextCursor *page.CursorKey + if len(snapshotPage.Data) > 0 { + cursorKey := snapshotPage.Data[len(snapshotPage.Data)-1].CursorKey(snapshotPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListControlSnapshotsOutput{ + NextCursor: nextCursor, + Snapshots: snapshots, + } +} + func NewListSnapshotsOutput(snapshotPage *page.Page[*coredata.Snapshot, coredata.SnapshotOrderField]) ListSnapshotsOutput { snapshots := make([]*Snapshot, 0, len(snapshotPage.Data)) for _, s := range snapshotPage.Data { diff --git a/pkg/server/api/mcp/v1/types/types.go b/pkg/server/api/mcp/v1/types/types.go index 5514e9f73..5bc88df70 100644 --- a/pkg/server/api/mcp/v1/types/types.go +++ b/pkg/server/api/mcp/v1/types/types.go @@ -133,14 +133,8 @@ var ( GetUserToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"user":{"type":"object","properties":{"additional_email_addresses":{"type":"array","items":{"type":"string","format":"string"},"description":"Additional email addresses"},"contract_end_date":{"type":["string","null"],"description":"Contract end date","format":"date-time"},"contract_start_date":{"type":["string","null"],"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"email_address":{"type":"string","format":"string"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"type":["string","null"],"description":"Position"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","full_name","email_address","additional_email_addresses","kind","created_at","updated_at"]}},"required":["user"]}`) InviteUserToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organization_id":{"type":"string","format":"string"},"profile_id":{"type":"string","format":"string"}},"required":["organization_id","profile_id"]}`) InviteUserToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"invitation_id":{"type":"string","format":"string"}},"required":["invitation_id"]}`) - LinkControlAuditToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audit_id":{"type":"string","format":"string"},"control_id":{"type":"string","format":"string"}},"required":["control_id","audit_id"]}`) - LinkControlAuditToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - LinkControlDocumentToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"document_id":{"type":"string","format":"string"}},"required":["control_id","document_id"]}`) - LinkControlDocumentToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - LinkControlMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"measure_id":{"type":"string","format":"string"}},"required":["control_id","measure_id"]}`) - LinkControlMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - LinkControlSnapshotToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"snapshot_id":{"type":"string","format":"string"}},"required":["control_id","snapshot_id"]}`) - LinkControlSnapshotToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) + LinkControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"resource_id":{"type":"string","format":"string"}},"required":["control_id","resource_id"]}`) + LinkControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) LinkMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"measure_id":{"type":"string","format":"string"},"resource_id":{"type":"string","format":"string"}},"required":["measure_id","resource_id"]}`) LinkMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) LinkRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"resource_id":{"type":"string","format":"string"},"risk_id":{"type":"string","format":"string"}},"required":["risk_id","resource_id"]}`) @@ -153,6 +147,16 @@ var ( ListAuditsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audits":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":["string","null"],"description":"Audit name"},"organization_id":{"type":"string","format":"string"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","COMPLETED","REJECTED","OUTDATED"]},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"},"valid_from":{"type":["string","null"],"description":"Valid from date","format":"date-time"},"valid_until":{"type":["string","null"],"description":"Valid until date","format":"date-time"}},"required":["id","organization_id","framework_id","state","trust_center_visibility","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["audits"]}`) ListContinualImprovementsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","REFERENCE_ID","TARGET_DATE","STATUS","PRIORITY"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) ListContinualImprovementsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"continual_improvements":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Description"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"reference_id":{"type":"string","description":"Reference ID"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"type":["string","null"],"description":"Source"},"source_id":{"type":["string","null"],"description":"Source ID"},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"target_date":{"type":["string","null"],"description":"Target date","format":"date-time"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","reference_id","owner_id","status","priority","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["continual_improvements"]}`) + ListControlAuditsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","VALID_FROM","VALID_UNTIL","STATE"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["control_id"]}`) + ListControlAuditsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audits":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":["string","null"],"description":"Audit name"},"organization_id":{"type":"string","format":"string"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","COMPLETED","REJECTED","OUTDATED"]},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"},"valid_from":{"type":["string","null"],"description":"Valid from date","format":"date-time"},"valid_until":{"type":["string","null"],"description":"Valid until date","format":"date-time"}},"required":["id","organization_id","framework_id","state","trust_center_visibility","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["audits"]}`) + ListControlDocumentsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","TITLE","DOCUMENT_TYPE"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["control_id"]}`) + ListControlDocumentsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"approver_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Approver IDs"},"classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"current_published_version":{"type":["integer","null"],"description":"Current published version number"},"document_type":{"type":"string","enum":["OTHER","ISMS","POLICY","PROCEDURE"]},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"title":{"type":"string","description":"Document title"},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","approver_ids","title","document_type","classification","trust_center_visibility","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["documents"]}`) + ListControlMeasuresToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["control_id"]}`) + ListControlMeasuresToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"measures":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","description":"Measure category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Measure description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Measure name"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","NOT_APPLICABLE","IMPLEMENTED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","category","name","state","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["measures"]}`) + ListControlObligationsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","LAST_REVIEW_DATE","DUE_DATE","STATUS"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["control_id"]}`) + ListControlObligationsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"obligations":{"type":"array","items":{"type":"object","properties":{"actions_to_be_implemented":{"type":["string","null"],"description":"Actions to be implemented"},"area":{"type":["string","null"],"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"type":["string","null"],"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"type":["string","null"],"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"type":["string","null"],"description":"Regulator"},"requirement":{"type":["string","null"],"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"type":["string","null"],"description":"Source"},"source_id":{"type":["string","null"],"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"type":{"type":"string","enum":["LEGAL","CONTRACTUAL"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","owner_id","status","type","created_at","updated_at"]}}},"required":["obligations"]}`) + ListControlSnapshotsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME","TYPE"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["control_id"]}`) + ListControlSnapshotsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"snapshots":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Snapshot description","anyOf":[{"type":"string","description":"Snapshot description"},{"type":"null","description":"No description"}]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Snapshot name"},"organization_id":{"type":"string","format":"string"},"type":{"type":"string","enum":["RISKS","VENDORS","ASSETS","DATA","NONCONFORMITIES","OBLIGATIONS","CONTINUAL_IMPROVEMENTS","PROCESSING_ACTIVITIES","STATES_OF_APPLICABILITY"]}},"required":["id","organization_id","name","type","created_at"]}}},"required":["snapshots"]}`) ListControlsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"}}},"framework_id":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","SECTION_TITLE"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) ListControlsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"controls":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Control description"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["controls"]}`) ListDataProtectionImpactAssessmentsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer"}},"required":["organization_id"]}`) @@ -217,14 +221,8 @@ var ( TakeSnapshotToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"snapshot":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Snapshot description","anyOf":[{"type":"string","description":"Snapshot description"},{"type":"null","description":"No description"}]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Snapshot name"},"organization_id":{"type":"string","format":"string"},"type":{"type":"string","enum":["RISKS","VENDORS","ASSETS","DATA","NONCONFORMITIES","OBLIGATIONS","CONTINUAL_IMPROVEMENTS","PROCESSING_ACTIVITIES","STATES_OF_APPLICABILITY"]}},"required":["id","organization_id","name","type","created_at"]}},"required":["snapshot"]}`) UnassignTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`) UnassignTaskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"task":{"type":"object","properties":{"assigned_to_id":{"description":"Assigned to person ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"Not assigned"}]},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"deadline":{"description":"Deadline","anyOf":[{"type":"string","description":"Deadline","format":"date-time"},{"type":"null","description":"No deadline"}]},"description":{"description":"Task description","anyOf":[{"type":"string","description":"Task description"},{"type":"null","description":"No description"}]},"id":{"type":"string","format":"string"},"measure_id":{"description":"Measure ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No measure"}]},"name":{"type":"string","description":"Task name"},"organization_id":{"type":"string","format":"string"},"state":{"type":"string","enum":["TODO","DONE"]},"time_estimate":{"description":"Time estimate","anyOf":[{"type":"string","description":"A duration"},{"type":"null","description":"No time estimate"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","state","created_at","updated_at"]}},"required":["task"]}`) - UnlinkControlAuditToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audit_id":{"type":"string","format":"string"},"control_id":{"type":"string","format":"string"}},"required":["control_id","audit_id"]}`) - UnlinkControlAuditToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - UnlinkControlDocumentToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"document_id":{"type":"string","format":"string"}},"required":["control_id","document_id"]}`) - UnlinkControlDocumentToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - UnlinkControlMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"measure_id":{"type":"string","format":"string"}},"required":["control_id","measure_id"]}`) - UnlinkControlMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) - UnlinkControlSnapshotToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"snapshot_id":{"type":"string","format":"string"}},"required":["control_id","snapshot_id"]}`) - UnlinkControlSnapshotToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) + UnlinkControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control_id":{"type":"string","format":"string"},"resource_id":{"type":"string","format":"string"}},"required":["control_id","resource_id"]}`) + UnlinkControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) UnlinkMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"measure_id":{"type":"string","format":"string"},"resource_id":{"type":"string","format":"string"}},"required":["measure_id","resource_id"]}`) UnlinkMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) UnlinkRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"resource_id":{"type":"string","format":"string"},"risk_id":{"type":"string","format":"string"}},"required":["risk_id","resource_id"]}`) @@ -1957,52 +1955,16 @@ type InviteUserOutput struct { InvitationID gid.GID `json:"invitation_id"` } -// LinkControlAuditInput represents the schema -type LinkControlAuditInput struct { - // Audit ID - AuditID gid.GID `json:"audit_id"` +// LinkControlInput represents the schema +type LinkControlInput struct { // Control ID ControlID gid.GID `json:"control_id"` + // ID of the resource to link (measure, document, audit, snapshot, or obligation) + ResourceID gid.GID `json:"resource_id"` } -// LinkControlAuditOutput represents the schema -type LinkControlAuditOutput struct { -} - -// LinkControlDocumentInput represents the schema -type LinkControlDocumentInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Document ID - DocumentID gid.GID `json:"document_id"` -} - -// LinkControlDocumentOutput represents the schema -type LinkControlDocumentOutput struct { -} - -// LinkControlMeasureInput represents the schema -type LinkControlMeasureInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Measure ID - MeasureID gid.GID `json:"measure_id"` -} - -// LinkControlMeasureOutput represents the schema -type LinkControlMeasureOutput struct { -} - -// LinkControlSnapshotInput represents the schema -type LinkControlSnapshotInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Snapshot ID - SnapshotID gid.GID `json:"snapshot_id"` -} - -// LinkControlSnapshotOutput represents the schema -type LinkControlSnapshotOutput struct { +// LinkControlOutput represents the schema +type LinkControlOutput struct { } // LinkMeasureInput represents the schema @@ -2107,6 +2069,101 @@ type ListContinualImprovementsOutput struct { NextCursor *page.CursorKey `json:"next_cursor,omitempty"` } +// ListControlAuditsInput represents the schema +type ListControlAuditsInput struct { + // Control ID + ControlID gid.GID `json:"control_id"` + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Audit order by + OrderBy *AuditOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListControlAuditsOutput represents the schema +type ListControlAuditsOutput struct { + Audits []*Audit `json:"audits"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + +// ListControlDocumentsInput represents the schema +type ListControlDocumentsInput struct { + // Control ID + ControlID gid.GID `json:"control_id"` + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Document order by + OrderBy *DocumentOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListControlDocumentsOutput represents the schema +type ListControlDocumentsOutput struct { + Documents []*Document `json:"documents"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + +// ListControlMeasuresInput represents the schema +type ListControlMeasuresInput struct { + // Control ID + ControlID gid.GID `json:"control_id"` + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Measure order by + OrderBy *MeasureOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListControlMeasuresOutput represents the schema +type ListControlMeasuresOutput struct { + Measures []*Measure `json:"measures"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + +// ListControlObligationsInput represents the schema +type ListControlObligationsInput struct { + // Control ID + ControlID gid.GID `json:"control_id"` + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Obligation order by + OrderBy *ObligationOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListControlObligationsOutput represents the schema +type ListControlObligationsOutput struct { + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` + Obligations []*Obligation `json:"obligations"` +} + +// ListControlSnapshotsInput represents the schema +type ListControlSnapshotsInput struct { + // Control ID + ControlID gid.GID `json:"control_id"` + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Snapshot order by + OrderBy *SnapshotOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListControlSnapshotsOutput represents the schema +type ListControlSnapshotsOutput struct { + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` + Snapshots []*Snapshot `json:"snapshots"` +} + // ListControlsInput represents the schema type ListControlsInput struct { // Page cursor @@ -3083,52 +3140,16 @@ type UnassignTaskOutput struct { Task *Task `json:"task"` } -// UnlinkControlAuditInput represents the schema -type UnlinkControlAuditInput struct { - // Audit ID - AuditID gid.GID `json:"audit_id"` +// UnlinkControlInput represents the schema +type UnlinkControlInput struct { // Control ID ControlID gid.GID `json:"control_id"` + // ID of the resource to unlink (measure, document, audit, snapshot, or obligation) + ResourceID gid.GID `json:"resource_id"` } -// UnlinkControlAuditOutput represents the schema -type UnlinkControlAuditOutput struct { -} - -// UnlinkControlDocumentInput represents the schema -type UnlinkControlDocumentInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Document ID - DocumentID gid.GID `json:"document_id"` -} - -// UnlinkControlDocumentOutput represents the schema -type UnlinkControlDocumentOutput struct { -} - -// UnlinkControlMeasureInput represents the schema -type UnlinkControlMeasureInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Measure ID - MeasureID gid.GID `json:"measure_id"` -} - -// UnlinkControlMeasureOutput represents the schema -type UnlinkControlMeasureOutput struct { -} - -// UnlinkControlSnapshotInput represents the schema -type UnlinkControlSnapshotInput struct { - // Control ID - ControlID gid.GID `json:"control_id"` - // Snapshot ID - SnapshotID gid.GID `json:"snapshot_id"` -} - -// UnlinkControlSnapshotOutput represents the schema -type UnlinkControlSnapshotOutput struct { +// UnlinkControlOutput represents the schema +type UnlinkControlOutput struct { } // UnlinkMeasureInput represents the schema