diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index c02f46fb8..34804c71b 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -292,3 +292,93 @@ func (r *Resolver) UpdateRiskTool(ctx context.Context, req *mcp.CallToolRequest, Risk: types.NewRisk(risk), }, nil } + +func (r *Resolver) ListMeasuresTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasuresInput) (*mcp.CallToolResult, types.ListMeasuresOutput, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionListMeasures) + + prb := r.ProboService(ctx, input.OrganizationID) + + 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, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + var measureFilter = coredata.NewMeasureFilter(nil, nil) + if input.Filter != nil { + measureFilter = coredata.NewMeasureFilter(input.Filter.Query, input.Filter.State) + } + + page, err := prb.Measures.ListForOrganizationID(ctx, input.OrganizationID, cursor, measureFilter) + if err != nil { + panic(fmt.Errorf("cannot list organization measures: %w", err)) + } + + return nil, types.NewListMeasuresOutput(page), nil +} +func (r *Resolver) GetMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetMeasureInput) (*mcp.CallToolResult, types.GetMeasureOutput, error) { + r.MustBeAuthorized(ctx, input.ID, authz.ActionGet) + + prb := r.ProboService(ctx, input.ID) + + measure, err := prb.Measures.Get(ctx, input.ID) + if err != nil { + return nil, types.GetMeasureOutput{}, fmt.Errorf("failed to get measure: %w", err) + } + + return nil, types.GetMeasureOutput{ + Measure: types.NewMeasure(measure), + }, nil +} +func (r *Resolver) AddMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddMeasureInput) (*mcp.CallToolResult, types.AddMeasureOutput, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateMeasure) + + svc := r.ProboService(ctx, input.OrganizationID) + + measure, err := svc.Measures.Create( + ctx, + probo.CreateMeasureRequest{ + OrganizationID: input.OrganizationID, + Name: input.Name, + Description: input.Description, + Category: input.Category, + }, + ) + if err != nil { + return nil, types.AddMeasureOutput{}, fmt.Errorf("failed to create measure: %w", err) + } + + return nil, types.AddMeasureOutput{ + Measure: types.NewMeasure(measure), + }, nil +} +func (r *Resolver) UpdateMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateMeasureInput) (*mcp.CallToolResult, types.UpdateMeasureOutput, error) { + r.MustBeAuthorized(ctx, input.ID, authz.ActionUpdateMeasure) + + svc := r.ProboService(ctx, input.ID) + + measure, err := svc.Measures.Update( + ctx, + probo.UpdateMeasureRequest{ + ID: input.ID, + Name: input.Name, + Description: UnwrapOmittable(input.Description), + Category: input.Category, + State: input.State, + }, + ) + if err != nil { + return nil, types.UpdateMeasureOutput{}, fmt.Errorf("failed to update measure: %w", err) + } + + return nil, types.UpdateMeasureOutput{ + Measure: types.NewMeasure(measure), + }, nil +} diff --git a/pkg/server/api/mcp/v1/server/server.go b/pkg/server/api/mcp/v1/server/server.go index 869948834..b7e237b67 100644 --- a/pkg/server/api/mcp/v1/server/server.go +++ b/pkg/server/api/mcp/v1/server/server.go @@ -21,6 +21,10 @@ type ResolverInterface interface { GetRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetRiskInput) (*mcp.CallToolResult, types.GetRiskOutput, error) AddRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddRiskInput) (*mcp.CallToolResult, types.AddRiskOutput, error) UpdateRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateRiskInput) (*mcp.CallToolResult, types.UpdateRiskOutput, error) + ListMeasuresTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMeasuresInput) (*mcp.CallToolResult, types.ListMeasuresOutput, error) + GetMeasureTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetMeasureInput) (*mcp.CallToolResult, types.GetMeasureOutput, error) + 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) } // New creates a new MCP server instance with all handlers registered. @@ -150,4 +154,44 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) { }, resolver.UpdateRiskTool, ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listMeasures", + Description: "List all measures for the organization", + InputSchema: types.ListMeasuresToolInputSchema, + OutputSchema: types.ListMeasuresToolOutputSchema, + }, + resolver.ListMeasuresTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "getMeasure", + Description: "Get a measure by ID", + InputSchema: types.GetMeasureToolInputSchema, + OutputSchema: types.GetMeasureToolOutputSchema, + }, + resolver.GetMeasureTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "addMeasure", + Description: "Add a new measure to the organization", + InputSchema: types.AddMeasureToolInputSchema, + OutputSchema: types.AddMeasureToolOutputSchema, + }, + resolver.AddMeasureTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "updateMeasure", + Description: "Update an existing measure", + InputSchema: types.UpdateMeasureToolInputSchema, + OutputSchema: types.UpdateMeasureToolOutputSchema, + }, + resolver.UpdateMeasureTool, + ) } diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 0111fa28b..437f52d9f 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -671,6 +671,186 @@ components: risk: $ref: "#/components/schemas/Risk" + MeasureState: + type: string + enum: + - NOT_STARTED + - IN_PROGRESS + - NOT_APPLICABLE + - IMPLEMENTED + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.MeasureState + + MeasureOrderField: + type: string + enum: + - CREATED_AT + - NAME + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.MeasureOrderField + + MeasureOrderBy: + type: object + required: + - field + - direction + properties: + field: + $ref: "#/components/schemas/MeasureOrderField" + description: Measure order field + direction: + $ref: "#/components/schemas/OrderDirection" + description: Measure order direction + + Measure: + type: object + required: + - id + - category + - name + - state + - created_at + - updated_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Measure ID + category: + type: string + description: Measure category + name: + type: string + description: Measure name + description: + type: + - string + - "null" + description: Measure description + state: + $ref: "#/components/schemas/MeasureState" + description: Measure state + created_at: + type: string + format: date-time + description: Creation timestamp + updated_at: + type: string + format: date-time + description: Update timestamp + + ListMeasuresInput: + type: object + required: + - organization_id + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + order_by: + $ref: "#/components/schemas/MeasureOrderBy" + description: Measure order by + size: + type: integer + description: Page size + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + filter: + type: object + properties: + query: + type: string + description: Search query + state: + $ref: "#/components/schemas/MeasureState" + description: Measure state filter + + ListMeasuresOutput: + type: object + required: + - measures + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + measures: + type: array + items: + $ref: "#/components/schemas/Measure" + + GetMeasureInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Measure ID + + GetMeasureOutput: + type: object + required: + - measure + properties: + measure: + $ref: "#/components/schemas/Measure" + + AddMeasureInput: + type: object + required: + - organization_id + - name + - category + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + name: + type: string + description: Measure name + description: + type: string + description: Measure description + category: + type: string + description: Measure category + + AddMeasureOutput: + type: object + required: + - measure + properties: + measure: + $ref: "#/components/schemas/Measure" + + UpdateMeasureInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Measure ID + name: + type: string + description: Measure name + description: + type: ["string", "null"] + description: Measure description + go.probo.inc/mcpgen/omittable: true + category: + type: string + description: Measure category + state: + $ref: "#/components/schemas/MeasureState" + description: Measure state + + UpdateMeasureOutput: + type: object + required: + - measure + properties: + measure: + $ref: "#/components/schemas/Measure" + tools: - name: listOrganizations description: List all organizations the user has access to @@ -749,3 +929,31 @@ tools: $ref: "#/components/schemas/UpdateRiskInput" outputSchema: $ref: "#/components/schemas/UpdateRiskOutput" + - name: listMeasures + description: List all measures for the organization + readonly: true + inputSchema: + $ref: "#/components/schemas/ListMeasuresInput" + outputSchema: + $ref: "#/components/schemas/ListMeasuresOutput" + - name: getMeasure + description: Get a measure by ID + readonly: true + inputSchema: + $ref: "#/components/schemas/GetMeasureInput" + outputSchema: + $ref: "#/components/schemas/GetMeasureOutput" + - name: addMeasure + description: Add a new measure to the organization + readonly: false + inputSchema: + $ref: "#/components/schemas/AddMeasureInput" + outputSchema: + $ref: "#/components/schemas/AddMeasureOutput" + - name: updateMeasure + description: Update an existing measure + readonly: false + inputSchema: + $ref: "#/components/schemas/UpdateMeasureInput" + outputSchema: + $ref: "#/components/schemas/UpdateMeasureOutput" diff --git a/pkg/server/api/mcp/v1/types/measure.go b/pkg/server/api/mcp/v1/types/measure.go new file mode 100644 index 000000000..123b65130 --- /dev/null +++ b/pkg/server/api/mcp/v1/types/measure.go @@ -0,0 +1,50 @@ +// 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 NewMeasure(m *coredata.Measure) *Measure { + return &Measure{ + ID: m.ID, + Category: m.Category, + Name: m.Name, + Description: m.Description, + State: m.State, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +func NewListMeasuresOutput(measurePage *page.Page[*coredata.Measure, coredata.MeasureOrderField]) ListMeasuresOutput { + 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 ListMeasuresOutput{ + NextCursor: nextCursor, + Measures: measures, + } +} diff --git a/pkg/server/api/mcp/v1/types/types.go b/pkg/server/api/mcp/v1/types/types.go index 63f12128f..a9a009f50 100644 --- a/pkg/server/api/mcp/v1/types/types.go +++ b/pkg/server/api/mcp/v1/types/types.go @@ -12,16 +12,22 @@ import ( // Tool input schemas var ( + AddMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name","category"],"properties":{"category":{"type":"string","description":"Measure category"},"description":{"type":"string","description":"Measure description"},"name":{"type":"string","description":"Measure name"},"organization_id":{"type":"string","format":"string"}}}`) + AddMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["measure"],"properties":{"measure":{"type":"object","required":["id","category","name","state","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Measure category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) AddPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","full_name","primary_email_address","kind"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"type":"string","description":"Contract end date","format":"date-time"},"contract_start_date":{"type":"string","description":"Contract start date","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"type":"string","description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"}}}`) AddPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"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":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name","category","treatment","inherent_likelihood","inherent_impact"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"type":"string","description":"Risk description"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`) AddRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","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"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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":{"description":"Owner ID"},"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"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddVendorToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"type":"string","description":"Vendor description"},"name":{"type":"string","description":"Vendor name"},"organization_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}`) AddVendorToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["vendor"],"properties":{"vendor":{"type":"object","required":["id","name","organization_id","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Vendor description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Vendor name"},"organization_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) + GetMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) + GetMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["measure"],"properties":{"measure":{"type":"object","required":["id","category","name","state","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Measure category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) GetPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"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":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) GetRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","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"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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":{"description":"Owner ID"},"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"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) + ListMeasuresToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"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","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["asc","desc"]},"field":{"type":"string","enum":["CREATED_AT","NAME"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) + ListMeasuresToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["measures"],"properties":{"measures":{"type":"array","items":{"type":"object","required":["id","category","name","state","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Measure category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}},"next_cursor":{"type":"string","format":"string"}}}`) ListOrganizationsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organization_id":{"type":"string","format":"string"}}}`) ListOrganizationsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","required":["id","name","description","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Organization description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Organization name"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) ListPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"exclude_contract_ended":{"type":"boolean","description":"Exclude people with ended contracts"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["asc","desc"]},"field":{"type":"string","enum":["CREATED_AT","FULL_NAME","KIND"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) @@ -30,12 +36,31 @@ var ( ListRisksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risks"],"properties":{"next_cursor":{"type":"string","format":"string"},"risks":{"type":"array","items":{"type":"object","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"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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":{"description":"Owner ID"},"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"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) ListVendorsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["asc","desc"]},"field":{"type":"string","enum":["CREATED_AT","UPDATED_AT","NAME"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) ListVendorsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["vendors"],"properties":{"next_cursor":{"type":"string","format":"string"},"vendors":{"type":"array","items":{"type":"object","required":["id","name","organization_id","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Vendor description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Vendor name"},"organization_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) + UpdateMeasureToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"category":{"type":"string","description":"Measure category"},"description":{"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"]}}}`) + UpdateMeasureToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["measure"],"properties":{"measure":{"type":"object","required":["id","category","name","state","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Measure category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) UpdateRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`) UpdateRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","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"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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":{"description":"Owner ID"},"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"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) UpdateVendorToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"description":{"type":"string","description":"Vendor description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Vendor name"}}}`) UpdateVendorToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["vendor"],"properties":{"vendor":{"type":"object","required":["id","name","organization_id","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Vendor description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Vendor name"},"organization_id":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) ) +// AddMeasureInput represents the schema +type AddMeasureInput struct { + // Measure category + Category string `json:"category"` + // Measure description + Description *string `json:"description,omitempty"` + // Measure name + Name string `json:"name"` + // Organization ID + OrganizationID gid.GID `json:"organization_id"` +} + +// AddMeasureOutput represents the schema +type AddMeasureOutput struct { + Measure *Measure `json:"measure"` +} + // AddPeopleInput represents the schema type AddPeopleInput struct { // Additional email addresses @@ -111,6 +136,17 @@ type AddVendorOutput struct { Vendor *Vendor `json:"vendor"` } +// GetMeasureInput represents the schema +type GetMeasureInput struct { + // Measure ID + ID gid.GID `json:"id"` +} + +// GetMeasureOutput represents the schema +type GetMeasureOutput struct { + Measure *Measure `json:"measure"` +} + // GetPeopleInput represents the schema type GetPeopleInput struct { // People ID @@ -133,6 +169,26 @@ type GetRiskOutput struct { Risk *Risk `json:"risk"` } +// ListMeasuresInput represents the schema +type ListMeasuresInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + Filter *ListMeasuresInputFilter `json:"filter,omitempty"` + // Measure order by + OrderBy *MeasureOrderBy `json:"order_by,omitempty"` + // Organization ID + OrganizationID gid.GID `json:"organization_id"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListMeasuresOutput represents the schema +type ListMeasuresOutput struct { + Measures []*Measure `json:"measures"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + // ListOrganizationsInput represents the schema type ListOrganizationsInput struct { // Organization ID @@ -204,6 +260,32 @@ type ListVendorsOutput struct { Vendors []*Vendor `json:"vendors"` } +// Measure represents the schema +type Measure struct { + // Measure category + Category string `json:"category"` + // Creation timestamp + CreatedAt time.Time `json:"created_at"` + // Measure description + Description *string `json:"description,omitempty"` + // Measure ID + ID gid.GID `json:"id"` + // Measure name + Name string `json:"name"` + // Measure state + State coredata.MeasureState `json:"state"` + // Update timestamp + UpdatedAt time.Time `json:"updated_at"` +} + +// MeasureOrderBy represents the schema +type MeasureOrderBy struct { + // Measure order direction + Direction page.OrderDirection `json:"direction"` + // Measure order field + Field coredata.MeasureOrderField `json:"field"` +} + // Organization represents the schema type Organization struct { // Creation timestamp @@ -298,6 +380,25 @@ type RiskOrderBy struct { Field coredata.RiskOrderField `json:"field"` } +// UpdateMeasureInput represents the schema +type UpdateMeasureInput struct { + // Measure category + Category *string `json:"category,omitempty"` + // Measure description + Description mcp.Omittable[*string] `json:"description,omitempty"` + // Measure ID + ID gid.GID `json:"id"` + // Measure name + Name *string `json:"name,omitempty"` + // Measure state + State *coredata.MeasureState `json:"state,omitempty"` +} + +// UpdateMeasureOutput represents the schema +type UpdateMeasureOutput struct { + Measure *Measure `json:"measure"` +} + // UpdateRiskInput represents the schema type UpdateRiskInput struct { // Risk category @@ -368,6 +469,14 @@ type VendorOrderBy struct { Field coredata.VendorOrderField `json:"field"` } +// ListMeasuresInputFilter represents the schema +type ListMeasuresInputFilter struct { + // Search query + Query *string `json:"query,omitempty"` + // Measure state filter + State *coredata.MeasureState `json:"state,omitempty"` +} + // ListPeopleInputFilter represents the schema type ListPeopleInputFilter struct { // Exclude people with ended contracts