Expose commitment CRUD on MCP, CLI, n8n

Sync GraphQL commitment group and item operations
to the remaining API surfaces so automation can
manage compliance portal commitments end to end.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-20 19:25:29 +02:00
parent e27a830d76
commit 2db37660d3
24 changed files with 3588 additions and 0 deletions

View File

@@ -7083,3 +7083,231 @@ func (r *Resolver) RemoveResourceAliasTool(ctx context.Context, req *mcp.CallToo
DeletedResourceID: input.ResourceID,
}, nil
}
// ListCommitmentGroupsTool handles the listCommitmentGroups tool
// List all commitment groups for a trust center
func (r *Resolver) ListCommitmentGroupsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCommitmentGroupsInput) (*mcp.CallToolResult, types.ListCommitmentGroupsOutput, error) {
scope, err := r.Authorize(ctx, input.TrustCenterID, probo.ActionCompliancePortalCommitmentGroupList)
if err != nil {
return nil, types.ListCommitmentGroupsOutput{}, err
}
prb := r.proboSvc
pageOrderBy := page.OrderBy[coredata.CompliancePortalCommitmentGroupOrderField]{
Field: coredata.CompliancePortalCommitmentGroupOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
if input.OrderBy != nil {
pageOrderBy = page.OrderBy[coredata.CompliancePortalCommitmentGroupOrderField]{
Field: input.OrderBy.Field,
Direction: input.OrderBy.Direction,
}
}
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
p, err := prb.CompliancePortalCommitmentGroups.ListForTrustCenterID(ctx, scope, input.TrustCenterID, cursor)
if err != nil {
return nil, types.ListCommitmentGroupsOutput{}, fmt.Errorf("cannot list commitment groups: %w", err)
}
return nil, types.NewListCommitmentGroupsOutput(p), nil
}
// AddCommitmentGroupTool handles the addCommitmentGroup tool
// Add a new commitment group to a trust center
func (r *Resolver) AddCommitmentGroupTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddCommitmentGroupInput) (*mcp.CallToolResult, types.AddCommitmentGroupOutput, error) {
scope, err := r.Authorize(ctx, input.TrustCenterID, probo.ActionCompliancePortalCommitmentGroupCreate)
if err != nil {
return nil, types.AddCommitmentGroupOutput{}, err
}
prb := r.proboSvc
group, err := prb.CompliancePortalCommitmentGroups.Create(
ctx, scope,
&probo.CreateCompliancePortalCommitmentGroupRequest{
TrustCenterID: input.TrustCenterID,
Title: input.Title,
Description: input.Description,
},
)
if err != nil {
return nil, types.AddCommitmentGroupOutput{}, fmt.Errorf("cannot add commitment group: %w", err)
}
return nil, types.AddCommitmentGroupOutput{CommitmentGroup: types.NewCommitmentGroup(group)}, nil
}
// UpdateCommitmentGroupTool handles the updateCommitmentGroup tool
// Update an existing commitment group
func (r *Resolver) UpdateCommitmentGroupTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCommitmentGroupInput) (*mcp.CallToolResult, types.UpdateCommitmentGroupOutput, error) {
scope, err := r.Authorize(ctx, input.ID, probo.ActionCompliancePortalCommitmentGroupUpdate)
if err != nil {
return nil, types.UpdateCommitmentGroupOutput{}, err
}
prb := r.proboSvc
updateReq := &probo.UpdateCompliancePortalCommitmentGroupRequest{
ID: input.ID,
}
if title := UnwrapOmittable(input.Title); title != nil {
updateReq.Title = *title
}
if description := UnwrapOmittable(input.Description); description != nil {
updateReq.Description = *description
}
if rank := UnwrapOmittable(input.Rank); rank != nil {
updateReq.Rank = *rank
}
group, err := prb.CompliancePortalCommitmentGroups.Update(ctx, scope, updateReq)
if err != nil {
return nil, types.UpdateCommitmentGroupOutput{}, fmt.Errorf("cannot update commitment group: %w", err)
}
return nil, types.UpdateCommitmentGroupOutput{CommitmentGroup: types.NewCommitmentGroup(group)}, nil
}
// DeleteCommitmentGroupTool handles the deleteCommitmentGroup tool
// Delete a commitment group
func (r *Resolver) DeleteCommitmentGroupTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCommitmentGroupInput) (*mcp.CallToolResult, types.DeleteCommitmentGroupOutput, error) {
scope, err := r.Authorize(ctx, input.ID, probo.ActionCompliancePortalCommitmentGroupDelete)
if err != nil {
return nil, types.DeleteCommitmentGroupOutput{}, err
}
prb := r.proboSvc
err = prb.CompliancePortalCommitmentGroups.Delete(ctx, scope, input.ID)
if err != nil {
return nil, types.DeleteCommitmentGroupOutput{}, fmt.Errorf("cannot delete commitment group: %w", err)
}
return nil, types.DeleteCommitmentGroupOutput{DeletedCommitmentGroupID: input.ID}, nil
}
// ListCommitmentsTool handles the listCommitments tool
// List all commitments in a commitment group
func (r *Resolver) ListCommitmentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCommitmentsInput) (*mcp.CallToolResult, types.ListCommitmentsOutput, error) {
scope, err := r.Authorize(ctx, input.GroupID, probo.ActionCompliancePortalCommitmentList)
if err != nil {
return nil, types.ListCommitmentsOutput{}, err
}
prb := r.proboSvc
pageOrderBy := page.OrderBy[coredata.CompliancePortalCommitmentOrderField]{
Field: coredata.CompliancePortalCommitmentOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
if input.OrderBy != nil {
pageOrderBy = page.OrderBy[coredata.CompliancePortalCommitmentOrderField]{
Field: input.OrderBy.Field,
Direction: input.OrderBy.Direction,
}
}
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
p, err := prb.CompliancePortalCommitments.ListForGroupID(ctx, scope, input.GroupID, cursor)
if err != nil {
return nil, types.ListCommitmentsOutput{}, fmt.Errorf("cannot list commitments: %w", err)
}
return nil, types.NewListCommitmentsOutput(p), nil
}
// AddCommitmentTool handles the addCommitment tool
// Add a new commitment to a commitment group
func (r *Resolver) AddCommitmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddCommitmentInput) (*mcp.CallToolResult, types.AddCommitmentOutput, error) {
scope, err := r.Authorize(ctx, input.GroupID, probo.ActionCompliancePortalCommitmentCreate)
if err != nil {
return nil, types.AddCommitmentOutput{}, err
}
prb := r.proboSvc
commitment, err := prb.CompliancePortalCommitments.Create(
ctx, scope,
&probo.CreateCompliancePortalCommitmentRequest{
GroupID: input.GroupID,
Icon: input.Icon,
Eyebrow: input.Eyebrow,
Title: input.Title,
Description: input.Description,
},
)
if err != nil {
return nil, types.AddCommitmentOutput{}, fmt.Errorf("cannot add commitment: %w", err)
}
return nil, types.AddCommitmentOutput{Commitment: types.NewCommitment(commitment)}, nil
}
// UpdateCommitmentTool handles the updateCommitment tool
// Update an existing commitment
func (r *Resolver) UpdateCommitmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCommitmentInput) (*mcp.CallToolResult, types.UpdateCommitmentOutput, error) {
scope, err := r.Authorize(ctx, input.ID, probo.ActionCompliancePortalCommitmentUpdate)
if err != nil {
return nil, types.UpdateCommitmentOutput{}, err
}
prb := r.proboSvc
updateReq := &probo.UpdateCompliancePortalCommitmentRequest{
ID: input.ID,
}
if icon := UnwrapOmittable(input.Icon); icon != nil {
updateReq.Icon = *icon
}
if eyebrow := UnwrapOmittable(input.Eyebrow); eyebrow != nil {
updateReq.Eyebrow = *eyebrow
}
if title := UnwrapOmittable(input.Title); title != nil {
updateReq.Title = *title
}
if description := UnwrapOmittable(input.Description); description != nil {
updateReq.Description = *description
}
if rank := UnwrapOmittable(input.Rank); rank != nil {
updateReq.Rank = *rank
}
commitment, err := prb.CompliancePortalCommitments.Update(ctx, scope, updateReq)
if err != nil {
return nil, types.UpdateCommitmentOutput{}, fmt.Errorf("cannot update commitment: %w", err)
}
return nil, types.UpdateCommitmentOutput{Commitment: types.NewCommitment(commitment)}, nil
}
// DeleteCommitmentTool handles the deleteCommitment tool
// Delete a commitment
func (r *Resolver) DeleteCommitmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCommitmentInput) (*mcp.CallToolResult, types.DeleteCommitmentOutput, error) {
scope, err := r.Authorize(ctx, input.ID, probo.ActionCompliancePortalCommitmentDelete)
if err != nil {
return nil, types.DeleteCommitmentOutput{}, err
}
prb := r.proboSvc
err = prb.CompliancePortalCommitments.Delete(ctx, scope, input.ID)
if err != nil {
return nil, types.DeleteCommitmentOutput{}, fmt.Errorf("cannot delete commitment: %w", err)
}
return nil, types.DeleteCommitmentOutput{DeletedCommitmentID: input.ID}, nil
}

View File

@@ -9140,6 +9140,366 @@ components:
$ref: "#/components/schemas/GID"
description: Deleted trust center reference ID
CompliancePortalCommitmentIcon:
type: string
enum:
- LOCK_KEY
- EYE_SLASH
- FINGERPRINT
- SHIELD_WARNING
- SHIELD_CHECK
- SIREN
- KEY
- LOCK
- CLOUD
- DATABASE
- GLOBE
- EYE
- USERS
- CERTIFICATE
- GAVEL
- HEARTBEAT
- BELL
- BUG
- CODE
- SERVER
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIcon
CompliancePortalCommitmentGroupOrderField:
type: string
enum:
- RANK
- CREATED_AT
- UPDATED_AT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentGroupOrderField
CompliancePortalCommitmentGroupOrderBy:
type: object
required:
- field
- direction
properties:
field:
$ref: "#/components/schemas/CompliancePortalCommitmentGroupOrderField"
direction:
$ref: "#/components/schemas/OrderDirection"
CompliancePortalCommitmentOrderField:
type: string
enum:
- RANK
- CREATED_AT
- UPDATED_AT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentOrderField
CompliancePortalCommitmentOrderBy:
type: object
required:
- field
- direction
properties:
field:
$ref: "#/components/schemas/CompliancePortalCommitmentOrderField"
direction:
$ref: "#/components/schemas/OrderDirection"
CommitmentGroup:
type: object
required:
- id
- title
- description
- rank
- created_at
- updated_at
properties:
id:
$ref: "#/components/schemas/GID"
title:
type: string
description:
type: string
rank:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
Commitment:
type: object
required:
- id
- group_id
- icon
- eyebrow
- title
- description
- rank
- created_at
- updated_at
properties:
id:
$ref: "#/components/schemas/GID"
group_id:
$ref: "#/components/schemas/GID"
icon:
$ref: "#/components/schemas/CompliancePortalCommitmentIcon"
eyebrow:
type: string
title:
type: string
description:
type: string
rank:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
ListCommitmentGroupsInput:
type: object
required:
- trust_center_id
properties:
trust_center_id:
$ref: "#/components/schemas/GID"
description: Trust center ID
order_by:
$ref: "#/components/schemas/CompliancePortalCommitmentGroupOrderBy"
description: Commitment group order by
size:
type: integer
description: Page size
cursor:
$ref: "#/components/schemas/CursorKey"
description: Page cursor
ListCommitmentGroupsOutput:
type: object
required:
- commitment_groups
properties:
next_cursor:
$ref: "#/components/schemas/CursorKey"
description: Next cursor
commitment_groups:
type: array
items:
$ref: "#/components/schemas/CommitmentGroup"
AddCommitmentGroupInput:
type: object
required:
- trust_center_id
- title
- description
properties:
trust_center_id:
$ref: "#/components/schemas/GID"
description: Trust center ID
title:
type: string
description: Group title
description:
type: string
description: Group description
AddCommitmentGroupOutput:
type: object
required:
- commitment_group
properties:
commitment_group:
$ref: "#/components/schemas/CommitmentGroup"
UpdateCommitmentGroupInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Commitment group ID
title:
type:
- string
- "null"
description: Group title
go.probo.inc/mcpgen/omittable: true
description:
type:
- string
- "null"
description: Group description
go.probo.inc/mcpgen/omittable: true
rank:
type:
- integer
- "null"
description: Group rank
go.probo.inc/mcpgen/omittable: true
UpdateCommitmentGroupOutput:
type: object
required:
- commitment_group
properties:
commitment_group:
$ref: "#/components/schemas/CommitmentGroup"
DeleteCommitmentGroupInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Commitment group ID
DeleteCommitmentGroupOutput:
type: object
required:
- deleted_commitment_group_id
properties:
deleted_commitment_group_id:
$ref: "#/components/schemas/GID"
description: Deleted commitment group ID
ListCommitmentsInput:
type: object
required:
- group_id
properties:
group_id:
$ref: "#/components/schemas/GID"
description: Commitment group ID
order_by:
$ref: "#/components/schemas/CompliancePortalCommitmentOrderBy"
description: Commitment order by
size:
type: integer
description: Page size
cursor:
$ref: "#/components/schemas/CursorKey"
description: Page cursor
ListCommitmentsOutput:
type: object
required:
- commitments
properties:
next_cursor:
$ref: "#/components/schemas/CursorKey"
description: Next cursor
commitments:
type: array
items:
$ref: "#/components/schemas/Commitment"
AddCommitmentInput:
type: object
required:
- group_id
- icon
- eyebrow
- title
- description
properties:
group_id:
$ref: "#/components/schemas/GID"
description: Commitment group ID
icon:
$ref: "#/components/schemas/CompliancePortalCommitmentIcon"
description: Commitment icon
eyebrow:
type: string
description: Commitment eyebrow
title:
type: string
description: Commitment title
description:
type: string
description: Commitment description
AddCommitmentOutput:
type: object
required:
- commitment
properties:
commitment:
$ref: "#/components/schemas/Commitment"
UpdateCommitmentInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Commitment ID
icon:
anyOf:
- $ref: "#/components/schemas/CompliancePortalCommitmentIcon"
- type: "null"
description: Commitment icon
go.probo.inc/mcpgen/omittable: true
eyebrow:
type:
- string
- "null"
description: Commitment eyebrow
go.probo.inc/mcpgen/omittable: true
title:
type:
- string
- "null"
description: Commitment title
go.probo.inc/mcpgen/omittable: true
description:
type:
- string
- "null"
description: Commitment description
go.probo.inc/mcpgen/omittable: true
rank:
type:
- integer
- "null"
description: Commitment rank
go.probo.inc/mcpgen/omittable: true
UpdateCommitmentOutput:
type: object
required:
- commitment
properties:
commitment:
$ref: "#/components/schemas/Commitment"
DeleteCommitmentInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Commitment ID
DeleteCommitmentOutput:
type: object
required:
- deleted_commitment_id
properties:
deleted_commitment_id:
$ref: "#/components/schemas/GID"
description: Deleted commitment ID
ResourceAlias:
type: object
required:
@@ -13656,6 +14016,74 @@ tools:
$ref: "#/components/schemas/DeleteTrustCenterReferenceInput"
outputSchema:
$ref: "#/components/schemas/DeleteTrustCenterReferenceOutput"
- name: listCommitmentGroups
description: List all commitment groups for a trust center
hints:
readonly: true
idempotent: true
inputSchema:
$ref: "#/components/schemas/ListCommitmentGroupsInput"
outputSchema:
$ref: "#/components/schemas/ListCommitmentGroupsOutput"
- name: addCommitmentGroup
description: Add a new commitment group to a trust center
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/AddCommitmentGroupInput"
outputSchema:
$ref: "#/components/schemas/AddCommitmentGroupOutput"
- name: updateCommitmentGroup
description: Update an existing commitment group
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/UpdateCommitmentGroupInput"
outputSchema:
$ref: "#/components/schemas/UpdateCommitmentGroupOutput"
- name: deleteCommitmentGroup
description: Delete a commitment group
hints:
readonly: false
destructive: true
inputSchema:
$ref: "#/components/schemas/DeleteCommitmentGroupInput"
outputSchema:
$ref: "#/components/schemas/DeleteCommitmentGroupOutput"
- name: listCommitments
description: List all commitments in a commitment group
hints:
readonly: true
idempotent: true
inputSchema:
$ref: "#/components/schemas/ListCommitmentsInput"
outputSchema:
$ref: "#/components/schemas/ListCommitmentsOutput"
- name: addCommitment
description: Add a new commitment to a commitment group
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/AddCommitmentInput"
outputSchema:
$ref: "#/components/schemas/AddCommitmentOutput"
- name: updateCommitment
description: Update an existing commitment
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/UpdateCommitmentInput"
outputSchema:
$ref: "#/components/schemas/UpdateCommitmentOutput"
- name: deleteCommitment
description: Delete a commitment
hints:
readonly: false
destructive: true
inputSchema:
$ref: "#/components/schemas/DeleteCommitmentInput"
outputSchema:
$ref: "#/components/schemas/DeleteCommitmentOutput"
- name: setResourceAlias
description: Set a resource alias for a resource
hints:

View File

@@ -0,0 +1,93 @@
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package types
import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
)
func NewCommitmentGroup(g *coredata.CompliancePortalCommitmentGroup) *CommitmentGroup {
return &CommitmentGroup{
ID: g.ID,
Title: g.Title,
Description: g.Description,
Rank: g.Rank,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
}
}
func NewListCommitmentGroupsOutput(
p *page.Page[*coredata.CompliancePortalCommitmentGroup, coredata.CompliancePortalCommitmentGroupOrderField],
) ListCommitmentGroupsOutput {
groups := make([]*CommitmentGroup, 0, len(p.Data))
for _, g := range p.Data {
groups = append(groups, NewCommitmentGroup(g))
}
var nextCursor *page.CursorKey
if len(p.Data) > 0 {
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
nextCursor = &cursorKey
}
return ListCommitmentGroupsOutput{
NextCursor: nextCursor,
CommitmentGroups: groups,
}
}
func NewCommitment(c *coredata.CompliancePortalCommitment) *Commitment {
return &Commitment{
ID: c.ID,
GroupID: c.GroupID,
Icon: c.Icon,
Eyebrow: c.Eyebrow,
Title: c.Title,
Description: c.Description,
Rank: c.Rank,
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
}
}
func NewListCommitmentsOutput(
p *page.Page[*coredata.CompliancePortalCommitment, coredata.CompliancePortalCommitmentOrderField],
) ListCommitmentsOutput {
commitments := make([]*Commitment, 0, len(p.Data))
for _, c := range p.Data {
commitments = append(commitments, NewCommitment(c))
}
var nextCursor *page.CursorKey
if len(p.Data) > 0 {
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
nextCursor = &cursorKey
}
return ListCommitmentsOutput{
NextCursor: nextCursor,
Commitments: commitments,
}
}