diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index dfb42b28c..d6ff27548 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -1682,6 +1682,84 @@ func (r *Resolver) UnlinkControlSnapshotTool(ctx context.Context, req *mcp.CallT return nil, types.UnlinkControlSnapshotOutput{}, nil } +func (r *Resolver) ListRiskObligationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRiskObligationsInput) (*mcp.CallToolResult, types.ListRiskObligationsOutput, error) { + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskGet) + + prb := r.ProboService(ctx, input.RiskID) + + 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, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + obligationPage, err := prb.Obligations.ListForRiskID(ctx, input.RiskID, cursor, coredata.NewObligationFilter(nil)) + if err != nil { + return nil, types.ListRiskObligationsOutput{}, fmt.Errorf("failed to list risk obligations: %w", err) + } + + return nil, types.NewListRiskObligationsOutput(obligationPage), nil +} + +func (r *Resolver) LinkRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkRiskInput) (*mcp.CallToolResult, types.LinkRiskOutput, error) { + svc := r.ProboService(ctx, input.RiskID) + + switch input.ResourceID.EntityType() { + case coredata.DocumentEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskDocumentMappingCreate) + if _, _, err := svc.Risks.CreateDocumentMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.LinkRiskOutput{}, fmt.Errorf("failed to link risk to document: %w", err) + } + case coredata.MeasureEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskMeasureMappingCreate) + if _, _, err := svc.Risks.CreateMeasureMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.LinkRiskOutput{}, fmt.Errorf("failed to link risk to measure: %w", err) + } + case coredata.ObligationEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskObligationMappingCreate) + if _, _, err := svc.Risks.CreateObligationMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.LinkRiskOutput{}, fmt.Errorf("failed to link risk to obligation: %w", err) + } + default: + return nil, types.LinkRiskOutput{}, fmt.Errorf("unsupported resource type for risk linking: entity type %d", input.ResourceID.EntityType()) + } + + return nil, types.LinkRiskOutput{}, nil +} + +func (r *Resolver) UnlinkRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkRiskInput) (*mcp.CallToolResult, types.UnlinkRiskOutput, error) { + svc := r.ProboService(ctx, input.RiskID) + + switch input.ResourceID.EntityType() { + case coredata.DocumentEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskDocumentMappingDelete) + if _, _, err := svc.Risks.DeleteDocumentMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.UnlinkRiskOutput{}, fmt.Errorf("failed to unlink risk from document: %w", err) + } + case coredata.MeasureEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskMeasureMappingDelete) + if _, _, err := svc.Risks.DeleteMeasureMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.UnlinkRiskOutput{}, fmt.Errorf("failed to unlink risk from measure: %w", err) + } + case coredata.ObligationEntityType: + r.MustAuthorize(ctx, input.RiskID, probo.ActionRiskObligationMappingDelete) + if _, _, err := svc.Risks.DeleteObligationMapping(ctx, input.RiskID, input.ResourceID); err != nil { + return nil, types.UnlinkRiskOutput{}, fmt.Errorf("failed to unlink risk from obligation: %w", err) + } + default: + return nil, types.UnlinkRiskOutput{}, fmt.Errorf("unsupported resource type for risk unlinking: entity type %d", input.ResourceID.EntityType()) + } + + return nil, types.UnlinkRiskOutput{}, nil +} + func (r *Resolver) ListTasksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTasksInput) (*mcp.CallToolResult, types.ListTasksOutput, error) { r.MustAuthorize(ctx, input.OrganizationID, probo.ActionTaskList) @@ -2404,6 +2482,146 @@ func (r *Resolver) DeleteMeasureTool(ctx context.Context, req *mcp.CallToolReque }, nil } +func (r *Resolver) ListMeasureRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureRisksInput) (*mcp.CallToolResult, types.ListMeasureRisksOutput, error) { + r.MustAuthorize(ctx, input.MeasureID, probo.ActionMeasureGet) + + prb := r.ProboService(ctx, input.MeasureID) + + pageOrderBy := page.OrderBy[coredata.RiskOrderField]{ + Field: coredata.RiskOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.RiskOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + riskPage, err := prb.Risks.ListForMeasureID(ctx, input.MeasureID, cursor, coredata.NewRiskFilter(nil, nil)) + if err != nil { + return nil, types.ListMeasureRisksOutput{}, fmt.Errorf("failed to list measure risks: %w", err) + } + + return nil, types.NewListMeasureRisksOutput(riskPage), nil +} + +func (r *Resolver) ListMeasureControlsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureControlsInput) (*mcp.CallToolResult, types.ListMeasureControlsOutput, error) { + r.MustAuthorize(ctx, input.MeasureID, probo.ActionMeasureGet) + + prb := r.ProboService(ctx, input.MeasureID) + + pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ + Field: coredata.ControlOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.ControlOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + controlPage, err := prb.Controls.ListForMeasureID(ctx, input.MeasureID, cursor, coredata.NewControlFilter(nil)) + if err != nil { + return nil, types.ListMeasureControlsOutput{}, fmt.Errorf("failed to list measure controls: %w", err) + } + + return nil, types.NewListMeasureControlsOutput(controlPage), nil +} + +func (r *Resolver) ListMeasureTasksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureTasksInput) (*mcp.CallToolResult, types.ListMeasureTasksOutput, error) { + r.MustAuthorize(ctx, input.MeasureID, probo.ActionMeasureGet) + + prb := r.ProboService(ctx, input.MeasureID) + + pageOrderBy := page.OrderBy[coredata.TaskOrderField]{ + Field: coredata.TaskOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.TaskOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + taskPage, err := prb.Tasks.ListForMeasureID(ctx, input.MeasureID, cursor) + if err != nil { + return nil, types.ListMeasureTasksOutput{}, fmt.Errorf("failed to list measure tasks: %w", err) + } + + return nil, types.NewListMeasureTasksOutput(taskPage), nil +} + +func (r *Resolver) ListMeasureEvidencesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureEvidencesInput) (*mcp.CallToolResult, types.ListMeasureEvidencesOutput, error) { + r.MustAuthorize(ctx, input.MeasureID, probo.ActionMeasureGet) + + prb := r.ProboService(ctx, input.MeasureID) + + pageOrderBy := page.OrderBy[coredata.EvidenceOrderField]{ + Field: coredata.EvidenceOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + evidencePage, err := prb.Evidences.ListForMeasureID(ctx, input.MeasureID, cursor) + if err != nil { + return nil, types.ListMeasureEvidencesOutput{}, fmt.Errorf("failed to list measure evidences: %w", err) + } + + return nil, types.NewListMeasureEvidencesOutput(evidencePage), nil +} + +func (r *Resolver) LinkMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkMeasureInput) (*mcp.CallToolResult, types.LinkMeasureOutput, error) { + svc := r.ProboService(ctx, input.MeasureID) + + switch input.ResourceID.EntityType() { + case coredata.ControlEntityType: + r.MustAuthorize(ctx, input.MeasureID, probo.ActionControlMeasureMappingCreate) + if _, _, err := svc.Controls.CreateMeasureMapping(ctx, input.ResourceID, input.MeasureID); err != nil { + return nil, types.LinkMeasureOutput{}, fmt.Errorf("failed to link measure to control: %w", err) + } + case coredata.RiskEntityType: + r.MustAuthorize(ctx, input.MeasureID, probo.ActionRiskMeasureMappingCreate) + if _, _, err := svc.Risks.CreateMeasureMapping(ctx, input.ResourceID, input.MeasureID); err != nil { + return nil, types.LinkMeasureOutput{}, fmt.Errorf("failed to link measure to risk: %w", err) + } + default: + return nil, types.LinkMeasureOutput{}, fmt.Errorf("unsupported resource type for measure linking: entity type %d", input.ResourceID.EntityType()) + } + + return nil, types.LinkMeasureOutput{}, nil +} + +func (r *Resolver) UnlinkMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkMeasureInput) (*mcp.CallToolResult, types.UnlinkMeasureOutput, error) { + svc := r.ProboService(ctx, input.MeasureID) + + switch input.ResourceID.EntityType() { + case coredata.ControlEntityType: + r.MustAuthorize(ctx, input.MeasureID, probo.ActionControlMeasureMappingDelete) + if _, _, err := svc.Controls.DeleteMeasureMapping(ctx, input.ResourceID, input.MeasureID); err != nil { + return nil, types.UnlinkMeasureOutput{}, fmt.Errorf("failed to unlink measure from control: %w", err) + } + case coredata.RiskEntityType: + r.MustAuthorize(ctx, input.MeasureID, probo.ActionRiskMeasureMappingDelete) + if _, _, err := svc.Risks.DeleteMeasureMapping(ctx, input.ResourceID, input.MeasureID); err != nil { + return nil, types.UnlinkMeasureOutput{}, fmt.Errorf("failed to unlink measure from risk: %w", err) + } + default: + return nil, types.UnlinkMeasureOutput{}, fmt.Errorf("unsupported resource type for measure unlinking: entity type %d", input.ResourceID.EntityType()) + } + + return nil, types.UnlinkMeasureOutput{}, nil +} + func (r *Resolver) ListUsersTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListUsersInput) (*mcp.CallToolResult, types.ListUsersOutput, error) { r.MustAuthorize(ctx, input.OrganizationID, iam.ActionMembershipProfileList) diff --git a/pkg/server/api/mcp/v1/server/server.go b/pkg/server/api/mcp/v1/server/server.go index 25d8fcb31..d161dba2d 100644 --- a/pkg/server/api/mcp/v1/server/server.go +++ b/pkg/server/api/mcp/v1/server/server.go @@ -31,6 +31,12 @@ type ResolverInterface interface { AddMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddMeasureInput) (*mcp.CallToolResult, types.AddMeasureOutput, error) UpdateMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateMeasureInput) (*mcp.CallToolResult, types.UpdateMeasureOutput, error) DeleteMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteMeasureInput) (*mcp.CallToolResult, types.DeleteMeasureOutput, error) + ListMeasureRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureRisksInput) (*mcp.CallToolResult, types.ListMeasureRisksOutput, error) + ListMeasureControlsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureControlsInput) (*mcp.CallToolResult, types.ListMeasureControlsOutput, error) + ListMeasureTasksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureTasksInput) (*mcp.CallToolResult, types.ListMeasureTasksOutput, error) + ListMeasureEvidencesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasureEvidencesInput) (*mcp.CallToolResult, types.ListMeasureEvidencesOutput, error) + LinkMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.LinkMeasureInput) (*mcp.CallToolResult, types.LinkMeasureOutput, error) + UnlinkMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnlinkMeasureInput) (*mcp.CallToolResult, types.UnlinkMeasureOutput, error) ListFrameworksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListFrameworksInput) (*mcp.CallToolResult, types.ListFrameworksOutput, error) GetFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetFrameworkInput) (*mcp.CallToolResult, types.GetFrameworkOutput, error) AddFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddFrameworkInput) (*mcp.CallToolResult, types.AddFrameworkOutput, error) @@ -86,6 +92,9 @@ type ResolverInterface interface { 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) + 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) ListTasksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTasksInput) (*mcp.CallToolResult, types.ListTasksOutput, error) GetTaskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetTaskInput) (*mcp.CallToolResult, types.GetTaskOutput, error) AddTaskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddTaskInput) (*mcp.CallToolResult, types.AddTaskOutput, error) @@ -395,6 +404,82 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) { }, resolver.DeleteMeasureTool, ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listMeasureRisks", + Description: "List risks linked to a measure", + InputSchema: types.ListMeasureRisksToolInputSchema, + OutputSchema: types.ListMeasureRisksToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, + }, + resolver.ListMeasureRisksTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listMeasureControls", + Description: "List controls linked to a measure", + InputSchema: types.ListMeasureControlsToolInputSchema, + OutputSchema: types.ListMeasureControlsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, + }, + resolver.ListMeasureControlsTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listMeasureTasks", + Description: "List tasks linked to a measure", + InputSchema: types.ListMeasureTasksToolInputSchema, + OutputSchema: types.ListMeasureTasksToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, + }, + resolver.ListMeasureTasksTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listMeasureEvidences", + Description: "List evidences linked to a measure", + InputSchema: types.ListMeasureEvidencesToolInputSchema, + OutputSchema: types.ListMeasureEvidencesToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, + }, + resolver.ListMeasureEvidencesTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "linkMeasure", + Description: "Link a measure to a resource (control or risk). The resource type is determined from the resource_id GID.", + InputSchema: types.LinkMeasureToolInputSchema, + OutputSchema: types.LinkMeasureToolOutputSchema, + }, + resolver.LinkMeasureTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "unlinkMeasure", + Description: "Unlink a measure from a resource (control or risk). The resource type is determined from the resource_id GID.", + InputSchema: types.UnlinkMeasureToolInputSchema, + OutputSchema: types.UnlinkMeasureToolOutputSchema, + }, + resolver.UnlinkMeasureTool, + ) mcp.AddTool( server, &mcp.Tool{ @@ -1033,6 +1118,40 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) { }, resolver.UnlinkControlSnapshotTool, ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listRiskObligations", + Description: "List obligations linked to a risk", + InputSchema: types.ListRiskObligationsToolInputSchema, + OutputSchema: types.ListRiskObligationsToolOutputSchema, + Annotations: &mcp.ToolAnnotations{ + ReadOnlyHint: true, + IdempotentHint: true, + }, + }, + resolver.ListRiskObligationsTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "linkRisk", + Description: "Link a risk to a resource (document, measure, or obligation). The resource type is determined from the resource_id GID.", + InputSchema: types.LinkRiskToolInputSchema, + OutputSchema: types.LinkRiskToolOutputSchema, + }, + resolver.LinkRiskTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "unlinkRisk", + Description: "Unlink a risk from a resource (document, measure, or obligation). The resource type is determined from the resource_id GID.", + InputSchema: types.UnlinkRiskToolInputSchema, + OutputSchema: types.UnlinkRiskToolOutputSchema, + }, + resolver.UnlinkRiskTool, + ) mcp.AddTool( server, &mcp.Tool{ diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 55dc5b84e..e690af941 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -1271,6 +1271,213 @@ components: items: $ref: "#/components/schemas/Measure" + ListMeasureRisksInput: + type: object + required: + - measure_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/RiskOrderBy" + description: Risk order by + + ListMeasureRisksOutput: + type: object + required: + - risks + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + risks: + type: array + items: + $ref: "#/components/schemas/Risk" + + ListMeasureControlsInput: + type: object + required: + - measure_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/ControlOrderBy" + description: Control order by + + ListMeasureControlsOutput: + type: object + required: + - controls + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + controls: + type: array + items: + $ref: "#/components/schemas/Control" + + ListMeasureTasksInput: + type: object + required: + - measure_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + order_by: + $ref: "#/components/schemas/TaskOrderBy" + description: Task order by + + ListMeasureTasksOutput: + type: object + required: + - tasks + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + tasks: + type: array + items: + $ref: "#/components/schemas/Task" + + ListMeasureEvidencesInput: + type: object + required: + - measure_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + size: + type: integer + description: Page size + + ListMeasureEvidencesOutput: + type: object + required: + - evidences + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + evidences: + type: array + items: + $ref: "#/components/schemas/Evidence" + + Evidence: + type: object + required: + - id + - organization_id + - measure_id + - state + - reference_id + - type + - url + - created_at + - updated_at + properties: + id: + $ref: "#/components/schemas/GID" + organization_id: + $ref: "#/components/schemas/GID" + measure_id: + $ref: "#/components/schemas/GID" + task_id: + $ref: "#/components/schemas/GID" + description: Task ID + nullable: true + state: + type: string + enum: + - REQUESTED + - FULFILLED + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.EvidenceState + reference_id: + type: string + description: Reference ID + type: + type: string + enum: + - FILE + - LINK + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.EvidenceType + url: + type: string + description: URL + description: + type: string + description: Description + nullable: true + created_at: + type: string + format: date-time + description: Creation timestamp + updated_at: + type: string + format: date-time + description: Update timestamp + + LinkMeasureInput: + type: object + required: + - measure_id + - resource_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + resource_id: + $ref: "#/components/schemas/GID" + description: ID of the resource to link (control or risk) + + LinkMeasureOutput: + type: object + + UnlinkMeasureInput: + type: object + required: + - measure_id + - resource_id + properties: + measure_id: + $ref: "#/components/schemas/GID" + description: Measure ID + resource_id: + $ref: "#/components/schemas/GID" + description: ID of the resource to unlink (control or risk) + + UnlinkMeasureOutput: + type: object + GetMeasureInput: type: object required: @@ -4085,6 +4292,69 @@ components: UnlinkControlSnapshotOutput: type: object + ListRiskObligationsInput: + type: object + required: + - risk_id + properties: + risk_id: + $ref: "#/components/schemas/GID" + description: Risk 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 + + ListRiskObligationsOutput: + type: object + required: + - obligations + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + obligations: + type: array + items: + $ref: "#/components/schemas/Obligation" + + LinkRiskInput: + type: object + required: + - risk_id + - resource_id + properties: + risk_id: + $ref: "#/components/schemas/GID" + description: Risk ID + resource_id: + $ref: "#/components/schemas/GID" + description: ID of the resource to link (document, measure, or obligation) + + LinkRiskOutput: + type: object + + UnlinkRiskInput: + type: object + required: + - risk_id + - resource_id + properties: + risk_id: + $ref: "#/components/schemas/GID" + description: Risk ID + resource_id: + $ref: "#/components/schemas/GID" + description: ID of the resource to unlink (document, measure, or obligation) + + UnlinkRiskOutput: + type: object + TaskState: type: string enum: @@ -5949,6 +6219,58 @@ tools: $ref: "#/components/schemas/DeleteMeasureInput" outputSchema: $ref: "#/components/schemas/DeleteMeasureOutput" + - name: listMeasureRisks + description: List risks linked to a measure + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListMeasureRisksInput" + outputSchema: + $ref: "#/components/schemas/ListMeasureRisksOutput" + - name: listMeasureControls + description: List controls linked to a measure + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListMeasureControlsInput" + outputSchema: + $ref: "#/components/schemas/ListMeasureControlsOutput" + - name: listMeasureTasks + description: List tasks linked to a measure + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListMeasureTasksInput" + outputSchema: + $ref: "#/components/schemas/ListMeasureTasksOutput" + - name: listMeasureEvidences + description: List evidences linked to a measure + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListMeasureEvidencesInput" + outputSchema: + $ref: "#/components/schemas/ListMeasureEvidencesOutput" + - name: linkMeasure + description: Link a measure to a resource (control or risk). The resource type is determined from the resource_id GID. + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/LinkMeasureInput" + outputSchema: + $ref: "#/components/schemas/LinkMeasureOutput" + - name: unlinkMeasure + description: Unlink a measure from a resource (control or risk). The resource type is determined from the resource_id GID. + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/UnlinkMeasureInput" + outputSchema: + $ref: "#/components/schemas/UnlinkMeasureOutput" - name: listFrameworks description: List all frameworks for the organization hints: @@ -6411,6 +6733,31 @@ tools: $ref: "#/components/schemas/UnlinkControlSnapshotInput" outputSchema: $ref: "#/components/schemas/UnlinkControlSnapshotOutput" + - name: listRiskObligations + description: List obligations linked to a risk + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListRiskObligationsInput" + outputSchema: + $ref: "#/components/schemas/ListRiskObligationsOutput" + - name: linkRisk + description: Link a risk to a resource (document, measure, or obligation). The resource type is determined from the resource_id GID. + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/LinkRiskInput" + outputSchema: + $ref: "#/components/schemas/LinkRiskOutput" + - name: unlinkRisk + description: Unlink a risk from a resource (document, measure, or obligation). The resource type is determined from the resource_id GID. + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/UnlinkRiskInput" + outputSchema: + $ref: "#/components/schemas/UnlinkRiskOutput" - name: listTasks description: List all tasks for the organization or measure hints: diff --git a/pkg/server/api/mcp/v1/types/control.go b/pkg/server/api/mcp/v1/types/control.go index 063c10f64..104c83880 100644 --- a/pkg/server/api/mcp/v1/types/control.go +++ b/pkg/server/api/mcp/v1/types/control.go @@ -31,6 +31,24 @@ func NewControl(c *coredata.Control) *Control { } } +func NewListMeasureControlsOutput(controlPage *page.Page[*coredata.Control, coredata.ControlOrderField]) ListMeasureControlsOutput { + controls := make([]*Control, 0, len(controlPage.Data)) + for _, c := range controlPage.Data { + controls = append(controls, NewControl(c)) + } + + var nextCursor *page.CursorKey + if len(controlPage.Data) > 0 { + cursorKey := controlPage.Data[len(controlPage.Data)-1].CursorKey(controlPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListMeasureControlsOutput{ + NextCursor: nextCursor, + Controls: controls, + } +} + func NewListControlsOutput(controlPage *page.Page[*coredata.Control, coredata.ControlOrderField]) ListControlsOutput { controls := make([]*Control, 0, len(controlPage.Data)) for _, c := range controlPage.Data { diff --git a/pkg/server/api/mcp/v1/types/evidence.go b/pkg/server/api/mcp/v1/types/evidence.go new file mode 100644 index 000000000..3e1577b29 --- /dev/null +++ b/pkg/server/api/mcp/v1/types/evidence.go @@ -0,0 +1,54 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/page" +) + +func NewEvidence(e *coredata.Evidence) *Evidence { + return &Evidence{ + ID: e.ID, + OrganizationID: e.OrganizationID, + MeasureID: e.MeasureID, + TaskID: e.TaskID, + State: EvidenceState(e.State.String()), + ReferenceID: e.ReferenceID, + Type: EvidenceType(e.Type.String()), + URL: e.URL, + Description: e.Description, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + } +} + +func NewListMeasureEvidencesOutput(evidencePage *page.Page[*coredata.Evidence, coredata.EvidenceOrderField]) ListMeasureEvidencesOutput { + evidences := make([]*Evidence, 0, len(evidencePage.Data)) + for _, v := range evidencePage.Data { + evidences = append(evidences, NewEvidence(v)) + } + + var nextCursor *page.CursorKey + if len(evidencePage.Data) > 0 { + cursorKey := evidencePage.Data[len(evidencePage.Data)-1].CursorKey(evidencePage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListMeasureEvidencesOutput{ + NextCursor: nextCursor, + Evidences: evidences, + } +} diff --git a/pkg/server/api/mcp/v1/types/obligation.go b/pkg/server/api/mcp/v1/types/obligation.go index 8f7ec24af..15d548c43 100644 --- a/pkg/server/api/mcp/v1/types/obligation.go +++ b/pkg/server/api/mcp/v1/types/obligation.go @@ -56,3 +56,21 @@ func NewListObligationsOutput(obligationPage *page.Page[*coredata.Obligation, co Obligations: obligations, } } + +func NewListRiskObligationsOutput(obligationPage *page.Page[*coredata.Obligation, coredata.ObligationOrderField]) ListRiskObligationsOutput { + 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 ListRiskObligationsOutput{ + NextCursor: nextCursor, + Obligations: obligations, + } +} diff --git a/pkg/server/api/mcp/v1/types/risk.go b/pkg/server/api/mcp/v1/types/risk.go index 1444380d4..061e99033 100644 --- a/pkg/server/api/mcp/v1/types/risk.go +++ b/pkg/server/api/mcp/v1/types/risk.go @@ -41,6 +41,24 @@ func NewRisk(r *coredata.Risk) *Risk { } } +func NewListMeasureRisksOutput(riskPage *page.Page[*coredata.Risk, coredata.RiskOrderField]) ListMeasureRisksOutput { + risks := make([]*Risk, 0, len(riskPage.Data)) + for _, v := range riskPage.Data { + risks = append(risks, NewRisk(v)) + } + + var nextCursor *page.CursorKey + if len(riskPage.Data) > 0 { + cursorKey := riskPage.Data[len(riskPage.Data)-1].CursorKey(riskPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListMeasureRisksOutput{ + NextCursor: nextCursor, + Risks: risks, + } +} + func NewListRisksOutput(riskPage *page.Page[*coredata.Risk, coredata.RiskOrderField]) ListRisksOutput { risks := make([]*Risk, 0, len(riskPage.Data)) for _, v := range riskPage.Data { diff --git a/pkg/server/api/mcp/v1/types/task.go b/pkg/server/api/mcp/v1/types/task.go index 9fcde9af5..744458430 100644 --- a/pkg/server/api/mcp/v1/types/task.go +++ b/pkg/server/api/mcp/v1/types/task.go @@ -32,6 +32,24 @@ func NewTask(t *coredata.Task) *Task { } } +func NewListMeasureTasksOutput(taskPage *page.Page[*coredata.Task, coredata.TaskOrderField]) ListMeasureTasksOutput { + tasks := make([]*Task, 0, len(taskPage.Data)) + for _, v := range taskPage.Data { + tasks = append(tasks, NewTask(v)) + } + + var nextCursor *page.CursorKey + if len(taskPage.Data) > 0 { + cursorKey := taskPage.Data[len(taskPage.Data)-1].CursorKey(taskPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListMeasureTasksOutput{ + NextCursor: nextCursor, + Tasks: tasks, + } +} + func NewListTasksOutput(taskPage *page.Page[*coredata.Task, coredata.TaskOrderField]) ListTasksOutput { tasks := make([]*Task, 0, len(taskPage.Data)) for _, v := range taskPage.Data { diff --git a/pkg/server/api/mcp/v1/types/types.go b/pkg/server/api/mcp/v1/types/types.go index 63429a87d..4c92073af 100644 --- a/pkg/server/api/mcp/v1/types/types.go +++ b/pkg/server/api/mcp/v1/types/types.go @@ -139,6 +139,10 @@ var ( 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"}`) + 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"]}`) + LinkRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) ListApplicabilityStatementsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","CONTROL_SECTION_TITLE"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"},"state_of_applicability_id":{"type":"string","format":"string"}},"required":["state_of_applicability_id"]}`) ListApplicabilityStatementsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"applicability_statements":{"type":"array","items":{"type":"object","properties":{"applicability":{"type":"boolean","description":"Whether the control is applicable"},"control_id":{"type":"string","format":"string"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"id":{"type":"string","format":"string"},"justification":{"description":"Justification for the applicability decision","anyOf":[{"type":"string"},{"type":"null"}]},"organization_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"state_of_applicability_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","state_of_applicability_id","control_id","organization_id","applicability","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["applicability_statements"]}`) ListAssetsToolInputSchema = 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","AMOUNT"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) @@ -161,6 +165,14 @@ var ( ListDocumentsToolOutputSchema = 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"]}`) ListFrameworksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"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","description":"Page size"}},"required":["organization_id"]}`) ListFrameworksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"frameworks":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Framework description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Framework name"},"organization_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["frameworks"]}`) + ListMeasureControlsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"measure_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"]},"size":{"type":"integer","description":"Page size"}},"required":["measure_id"]}`) + ListMeasureControlsToolOutputSchema = 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"]}`) + ListMeasureEvidencesToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"measure_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["measure_id"]}`) + ListMeasureEvidencesToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"evidences":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":"string","description":"Description"},"id":{"type":"string","format":"string"},"measure_id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"reference_id":{"type":"string","description":"Reference ID"},"state":{"type":"string","enum":["REQUESTED","FULFILLED"]},"task_id":{"type":"string","format":"string"},"type":{"type":"string","enum":["FILE","LINK"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"},"url":{"type":"string","description":"URL"}},"required":["id","organization_id","measure_id","state","reference_id","type","url","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["evidences"]}`) + ListMeasureRisksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"measure_id":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","UPDATED_AT","NAME","CATEGORY","TREATMENT","INHERENT_RISK_SCORE","RESIDUAL_RISK_SCORE","OWNER_FULL_NAME"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["measure_id"]}`) + ListMeasureRisksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"risks":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"]}}},"required":["risks"]}`) + ListMeasureTasksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"measure_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"]},"size":{"type":"integer","description":"Page size"}},"required":["measure_id"]}`) + ListMeasureTasksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"tasks":{"type":"array","items":{"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":["tasks"]}`) ListMeasuresToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","NOT_APPLICABLE","IMPLEMENTED"]}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) ListMeasuresToolOutputSchema = 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"]}`) ListMeetingAttendeesToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"meeting_id":{"type":"string","format":"string"}},"required":["meeting_id"]}`) @@ -175,6 +187,8 @@ var ( ListOrganizationsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Organization description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Organization name"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","name","description","created_at","updated_at"]}}}}`) ListProcessingActivitiesToolInputSchema = 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","NAME"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) ListProcessingActivitiesToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"processing_activities":{"type":"array","items":{"type":"object","properties":{"consent_evidence_link":{"type":["string","null"],"description":"Consent evidence link"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_protection_impact_assessment_needed":{"type":"string","enum":["NEEDED","NOT_NEEDED"]},"data_protection_officer_id":{"description":"Data protection officer profile ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"data_subject_category":{"type":["string","null"],"description":"Data subject category"},"id":{"type":"string","format":"string"},"international_transfers":{"type":"boolean","description":"International transfers"},"last_review_date":{"type":["string","null"],"description":"Last review date","format":"date-time"},"lawful_basis":{"type":"string","enum":["LEGITIMATE_INTEREST","CONSENT","CONTRACTUAL_NECESSITY","LEGAL_OBLIGATION","VITAL_INTERESTS","PUBLIC_TASK"]},"location":{"type":["string","null"],"description":"Location"},"name":{"type":"string","description":"Name"},"next_review_date":{"type":["string","null"],"description":"Next review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"personal_data_category":{"type":["string","null"],"description":"Personal data category"},"purpose":{"type":["string","null"],"description":"Purpose"},"recipients":{"type":["string","null"],"description":"Recipients"},"retention_period":{"type":["string","null"],"description":"Retention period"},"role":{"type":"string","enum":["CONTROLLER","PROCESSOR"]},"security_measures":{"type":["string","null"],"description":"Security measures"},"special_or_criminal_data":{"type":"string","enum":["YES","NO","POSSIBLE"]},"transfer_impact_assessment_needed":{"type":"string","enum":["NEEDED","NOT_NEEDED"]},"transfer_safeguard":{"description":"Transfer safeguard","anyOf":[{"type":"string","enum":["STANDARD_CONTRACTUAL_CLAUSES","BINDING_CORPORATE_RULES","ADEQUACY_DECISION","DEROGATIONS","CODES_OF_CONDUCT","CERTIFICATION_MECHANISMS"]},{"type":"null"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","special_or_criminal_data","lawful_basis","international_transfers","data_protection_impact_assessment_needed","transfer_impact_assessment_needed","role","created_at","updated_at"]}}},"required":["processing_activities"]}`) + ListRiskObligationsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"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"]},"risk_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["risk_id"]}`) + ListRiskObligationsToolOutputSchema = 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"]}`) ListRisksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"},"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","UPDATED_AT","NAME","CATEGORY","TREATMENT","INHERENT_RISK_SCORE","RESIDUAL_RISK_SCORE","OWNER_FULL_NAME"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) ListRisksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"next_cursor":{"type":"string","format":"string"},"risks":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":["string","null"],"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"]}}},"required":["risks"]}`) ListSnapshotsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"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"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`) @@ -207,6 +221,10 @@ var ( 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"}`) + 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"]}`) + UnlinkRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object"}`) UpdateApplicabilityStatementToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"applicability":{"type":"boolean","description":"Whether the control is applicable"},"id":{"type":"string","format":"string"},"justification":{"description":"Justification for the applicability decision","anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","applicability"]}`) UpdateApplicabilityStatementToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"applicability_statement":{"type":"object","properties":{"applicability":{"type":"boolean","description":"Whether the control is applicable"},"control_id":{"type":"string","format":"string"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"id":{"type":"string","format":"string"},"justification":{"description":"Justification for the applicability decision","anyOf":[{"type":"string"},{"type":"null"}]},"organization_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"state_of_applicability_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","state_of_applicability_id","control_id","organization_id","applicability","created_at","updated_at"]}},"required":["applicability_statement"]}`) UpdateAssetToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"amount":{"type":"integer","description":"Asset amount"},"asset_type":{"type":"string","enum":["PHYSICAL","VIRTUAL"]},"data_types_stored":{"type":"string","description":"Data types stored"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Asset name"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"vendor_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Vendor IDs"}},"required":["id"]}`) @@ -353,6 +371,86 @@ func (e AddVendorInputCategory) MarshalJSON() ([]byte, error) { return json.Marshal(string(e)) } +// EvidenceState represents an enumeration +type EvidenceState string + +const ( + EvidenceStateREQUESTED EvidenceState = "REQUESTED" + EvidenceStateFULFILLED EvidenceState = "FULFILLED" +) + +// IsValid returns true if the EvidenceState value is valid +func (e EvidenceState) IsValid() bool { + switch e { + case EvidenceStateREQUESTED: + return true + case EvidenceStateFULFILLED: + return true + } + return false +} + +// UnmarshalJSON implements json.Unmarshaler +func (e *EvidenceState) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + *e = EvidenceState(s) + if !e.IsValid() { + return fmt.Errorf("invalid EvidenceState value: %q", s) + } + return nil +} + +// MarshalJSON implements json.Marshaler +func (e EvidenceState) MarshalJSON() ([]byte, error) { + if !e.IsValid() { + return nil, fmt.Errorf("invalid EvidenceState value: %q", string(e)) + } + return json.Marshal(string(e)) +} + +// EvidenceType represents an enumeration +type EvidenceType string + +const ( + EvidenceFILE EvidenceType = "FILE" + EvidenceLINK EvidenceType = "LINK" +) + +// IsValid returns true if the EvidenceType value is valid +func (e EvidenceType) IsValid() bool { + switch e { + case EvidenceFILE: + return true + case EvidenceLINK: + return true + } + return false +} + +// UnmarshalJSON implements json.Unmarshaler +func (e *EvidenceType) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + *e = EvidenceType(s) + if !e.IsValid() { + return fmt.Errorf("invalid EvidenceType value: %q", s) + } + return nil +} + +// MarshalJSON implements json.Marshaler +func (e EvidenceType) MarshalJSON() ([]byte, error) { + if !e.IsValid() { + return nil, fmt.Errorf("invalid EvidenceType value: %q", string(e)) + } + return json.Marshal(string(e)) +} + // Vendor category type UpdateVendorInputCategory string @@ -1512,6 +1610,27 @@ type DocumentVersionSignatureOrderBy struct { Field coredata.DocumentVersionSignatureOrderField `json:"field"` } +// Evidence represents the schema +type Evidence struct { + // Creation timestamp + CreatedAt time.Time `json:"created_at"` + // Description + Description *string `json:"description,omitempty"` + ID gid.GID `json:"id"` + MeasureID gid.GID `json:"measure_id"` + OrganizationID gid.GID `json:"organization_id"` + // Reference ID + ReferenceID string `json:"reference_id"` + State EvidenceState `json:"state"` + // Task ID + TaskID *gid.GID `json:"task_id,omitempty"` + Type EvidenceType `json:"type"` + // Update timestamp + UpdatedAt time.Time `json:"updated_at"` + // URL + URL string `json:"url"` +} + // ExportStateOfApplicabilityPDFInput represents the schema type ExportStateOfApplicabilityPDFInput struct { // State of applicability ID @@ -1863,6 +1982,30 @@ type LinkControlSnapshotInput struct { type LinkControlSnapshotOutput struct { } +// LinkMeasureInput represents the schema +type LinkMeasureInput struct { + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // ID of the resource to link (control or risk) + ResourceID gid.GID `json:"resource_id"` +} + +// LinkMeasureOutput represents the schema +type LinkMeasureOutput struct { +} + +// LinkRiskInput represents the schema +type LinkRiskInput struct { + // ID of the resource to link (document, measure, or obligation) + ResourceID gid.GID `json:"resource_id"` + // Risk ID + RiskID gid.GID `json:"risk_id"` +} + +// LinkRiskOutput represents the schema +type LinkRiskOutput struct { +} + // ListApplicabilityStatementsInput represents the schema type ListApplicabilityStatementsInput struct { // Page cursor @@ -2076,6 +2219,80 @@ type ListFrameworksOutput struct { NextCursor *page.CursorKey `json:"next_cursor,omitempty"` } +// ListMeasureControlsInput represents the schema +type ListMeasureControlsInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // Control order by + OrderBy *ControlOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListMeasureControlsOutput represents the schema +type ListMeasureControlsOutput struct { + Controls []*Control `json:"controls"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + +// ListMeasureEvidencesInput represents the schema +type ListMeasureEvidencesInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListMeasureEvidencesOutput represents the schema +type ListMeasureEvidencesOutput struct { + Evidences []*Evidence `json:"evidences"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + +// ListMeasureRisksInput represents the schema +type ListMeasureRisksInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // Risk order by + OrderBy *RiskOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListMeasureRisksOutput represents the schema +type ListMeasureRisksOutput struct { + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` + Risks []*Risk `json:"risks"` +} + +// ListMeasureTasksInput represents the schema +type ListMeasureTasksInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // Task order by + OrderBy *TaskOrderBy `json:"order_by,omitempty"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListMeasureTasksOutput represents the schema +type ListMeasureTasksOutput struct { + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` + Tasks []*Task `json:"tasks"` +} + // ListMeasuresInput represents the schema type ListMeasuresInput struct { // Page cursor @@ -2199,6 +2416,25 @@ type ListProcessingActivitiesOutput struct { ProcessingActivities []*ProcessingActivity `json:"processing_activities"` } +// ListRiskObligationsInput represents the schema +type ListRiskObligationsInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Obligation order by + OrderBy *ObligationOrderBy `json:"order_by,omitempty"` + // Risk ID + RiskID gid.GID `json:"risk_id"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListRiskObligationsOutput represents the schema +type ListRiskObligationsOutput struct { + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` + Obligations []*Obligation `json:"obligations"` +} + // ListRisksInput represents the schema type ListRisksInput struct { // Page cursor @@ -2853,6 +3089,30 @@ type UnlinkControlSnapshotInput struct { type UnlinkControlSnapshotOutput struct { } +// UnlinkMeasureInput represents the schema +type UnlinkMeasureInput struct { + // Measure ID + MeasureID gid.GID `json:"measure_id"` + // ID of the resource to unlink (control or risk) + ResourceID gid.GID `json:"resource_id"` +} + +// UnlinkMeasureOutput represents the schema +type UnlinkMeasureOutput struct { +} + +// UnlinkRiskInput represents the schema +type UnlinkRiskInput struct { + // ID of the resource to unlink (document, measure, or obligation) + ResourceID gid.GID `json:"resource_id"` + // Risk ID + RiskID gid.GID `json:"risk_id"` +} + +// UnlinkRiskOutput represents the schema +type UnlinkRiskOutput struct { +} + // UpdateApplicabilityStatementInput represents the schema type UpdateApplicabilityStatementInput struct { // Whether the control is applicable