From 72a6d335350318bbb47eb55ad78ec803a44f914a Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Sun, 23 Nov 2025 21:08:55 +0100 Subject: [PATCH] Add framework tools Signed-off-by: Bryan Frimin --- pkg/server/api/mcp/v1/schema.resolvers.go | 82 ++++++++++ pkg/server/api/mcp/v1/server/server.go | 44 ++++++ pkg/server/api/mcp/v1/specification.yaml | 175 ++++++++++++++++++++++ pkg/server/api/mcp/v1/types/framework.go | 48 ++++++ pkg/server/api/mcp/v1/types/types.go | 92 ++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 pkg/server/api/mcp/v1/types/framework.go diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index 34804c71b..679f94c2a 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -382,3 +382,85 @@ func (r *Resolver) UpdateMeasureTool(ctx context.Context, req *mcp.CallToolReque Measure: types.NewMeasure(measure), }, nil } + +func (r *Resolver) ListFrameworksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListFrameworksInput) (*mcp.CallToolResult, types.ListFrameworksOutput, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionListFrameworks) + + prb := r.ProboService(ctx, input.OrganizationID) + + pageOrderBy := page.OrderBy[coredata.FrameworkOrderField]{ + Field: coredata.FrameworkOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.FrameworkOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + page, err := prb.Frameworks.ListForOrganizationID(ctx, input.OrganizationID, cursor) + if err != nil { + panic(fmt.Errorf("cannot list organization frameworks: %w", err)) + } + + return nil, types.NewListFrameworksOutput(page), nil +} +func (r *Resolver) GetFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetFrameworkInput) (*mcp.CallToolResult, types.GetFrameworkOutput, error) { + r.MustBeAuthorized(ctx, input.ID, authz.ActionGet) + + prb := r.ProboService(ctx, input.ID) + + framework, err := prb.Frameworks.Get(ctx, input.ID) + if err != nil { + return nil, types.GetFrameworkOutput{}, fmt.Errorf("failed to get framework: %w", err) + } + + return nil, types.GetFrameworkOutput{ + Framework: types.NewFramework(framework), + }, nil +} +func (r *Resolver) AddFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddFrameworkInput) (*mcp.CallToolResult, types.AddFrameworkOutput, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateFramework) + + svc := r.ProboService(ctx, input.OrganizationID) + + framework, err := svc.Frameworks.Create( + ctx, + probo.CreateFrameworkRequest{ + OrganizationID: input.OrganizationID, + Name: input.Name, + Description: input.Description, + }, + ) + if err != nil { + return nil, types.AddFrameworkOutput{}, fmt.Errorf("failed to create framework: %w", err) + } + + return nil, types.AddFrameworkOutput{ + Framework: types.NewFramework(framework), + }, nil +} +func (r *Resolver) UpdateFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateFrameworkInput) (*mcp.CallToolResult, types.UpdateFrameworkOutput, error) { + r.MustBeAuthorized(ctx, input.ID, authz.ActionUpdateFramework) + + svc := r.ProboService(ctx, input.ID) + + framework, err := svc.Frameworks.Update( + ctx, + probo.UpdateFrameworkRequest{ + ID: input.ID, + Name: input.Name, + Description: UnwrapOmittable(input.Description), + }, + ) + if err != nil { + return nil, types.UpdateFrameworkOutput{}, fmt.Errorf("failed to update framework: %w", err) + } + + return nil, types.UpdateFrameworkOutput{ + Framework: types.NewFramework(framework), + }, nil +} diff --git a/pkg/server/api/mcp/v1/server/server.go b/pkg/server/api/mcp/v1/server/server.go index b7e237b67..c781b25c5 100644 --- a/pkg/server/api/mcp/v1/server/server.go +++ b/pkg/server/api/mcp/v1/server/server.go @@ -25,6 +25,10 @@ type ResolverInterface interface { 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) + 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) + UpdateFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateFrameworkInput) (*mcp.CallToolResult, types.UpdateFrameworkOutput, error) } // New creates a new MCP server instance with all handlers registered. @@ -194,4 +198,44 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) { }, resolver.UpdateMeasureTool, ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "listFrameworks", + Description: "List all frameworks for the organization", + InputSchema: types.ListFrameworksToolInputSchema, + OutputSchema: types.ListFrameworksToolOutputSchema, + }, + resolver.ListFrameworksTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "getFramework", + Description: "Get a framework by ID", + InputSchema: types.GetFrameworkToolInputSchema, + OutputSchema: types.GetFrameworkToolOutputSchema, + }, + resolver.GetFrameworkTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "addFramework", + Description: "Add a new framework to the organization", + InputSchema: types.AddFrameworkToolInputSchema, + OutputSchema: types.AddFrameworkToolOutputSchema, + }, + resolver.AddFrameworkTool, + ) + mcp.AddTool( + server, + &mcp.Tool{ + Name: "updateFramework", + Description: "Update an existing framework", + InputSchema: types.UpdateFrameworkToolInputSchema, + OutputSchema: types.UpdateFrameworkToolOutputSchema, + }, + resolver.UpdateFrameworkTool, + ) } diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 437f52d9f..bf990bfe7 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -851,6 +851,153 @@ components: measure: $ref: "#/components/schemas/Measure" + FrameworkOrderField: + type: string + enum: + - CREATED_AT + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.FrameworkOrderField + + FrameworkOrderBy: + type: object + required: + - field + - direction + properties: + field: + $ref: "#/components/schemas/FrameworkOrderField" + description: Framework order field + direction: + $ref: "#/components/schemas/OrderDirection" + description: Framework order direction + + Framework: + type: object + required: + - id + - organization_id + - name + - created_at + - updated_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Framework ID + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + name: + type: string + description: Framework name + description: + type: + - string + - "null" + description: Framework description + created_at: + type: string + format: date-time + description: Creation timestamp + updated_at: + type: string + format: date-time + description: Update timestamp + + ListFrameworksInput: + type: object + required: + - organization_id + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + order_by: + $ref: "#/components/schemas/FrameworkOrderBy" + description: Framework order by + size: + type: integer + description: Page size + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + + ListFrameworksOutput: + type: object + required: + - frameworks + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + frameworks: + type: array + items: + $ref: "#/components/schemas/Framework" + + GetFrameworkInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Framework ID + + GetFrameworkOutput: + type: object + required: + - framework + properties: + framework: + $ref: "#/components/schemas/Framework" + + AddFrameworkInput: + type: object + required: + - organization_id + - name + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + name: + type: string + description: Framework name + description: + type: string + description: Framework description + + AddFrameworkOutput: + type: object + required: + - framework + properties: + framework: + $ref: "#/components/schemas/Framework" + + UpdateFrameworkInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Framework ID + name: + type: string + description: Framework name + description: + type: ["string", "null"] + description: Framework description + go.probo.inc/mcpgen/omittable: true + + UpdateFrameworkOutput: + type: object + required: + - framework + properties: + framework: + $ref: "#/components/schemas/Framework" + tools: - name: listOrganizations description: List all organizations the user has access to @@ -957,3 +1104,31 @@ tools: $ref: "#/components/schemas/UpdateMeasureInput" outputSchema: $ref: "#/components/schemas/UpdateMeasureOutput" + - name: listFrameworks + description: List all frameworks for the organization + readonly: true + inputSchema: + $ref: "#/components/schemas/ListFrameworksInput" + outputSchema: + $ref: "#/components/schemas/ListFrameworksOutput" + - name: getFramework + description: Get a framework by ID + readonly: true + inputSchema: + $ref: "#/components/schemas/GetFrameworkInput" + outputSchema: + $ref: "#/components/schemas/GetFrameworkOutput" + - name: addFramework + description: Add a new framework to the organization + readonly: false + inputSchema: + $ref: "#/components/schemas/AddFrameworkInput" + outputSchema: + $ref: "#/components/schemas/AddFrameworkOutput" + - name: updateFramework + description: Update an existing framework + readonly: false + inputSchema: + $ref: "#/components/schemas/UpdateFrameworkInput" + outputSchema: + $ref: "#/components/schemas/UpdateFrameworkOutput" diff --git a/pkg/server/api/mcp/v1/types/framework.go b/pkg/server/api/mcp/v1/types/framework.go new file mode 100644 index 000000000..d9c1b9315 --- /dev/null +++ b/pkg/server/api/mcp/v1/types/framework.go @@ -0,0 +1,48 @@ +// 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 NewFramework(f *coredata.Framework) *Framework { + return &Framework{ + ID: f.ID, + Name: f.Name, + Description: f.Description, + CreatedAt: f.CreatedAt, + UpdatedAt: f.UpdatedAt, + } +} + +func NewListFrameworksOutput(frameworkPage *page.Page[*coredata.Framework, coredata.FrameworkOrderField]) ListFrameworksOutput { + frameworks := make([]*Framework, 0, len(frameworkPage.Data)) + for _, v := range frameworkPage.Data { + frameworks = append(frameworks, NewFramework(v)) + } + + var nextCursor *page.CursorKey + if len(frameworkPage.Data) > 0 { + cursorKey := frameworkPage.Data[len(frameworkPage.Data)-1].CursorKey(frameworkPage.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListFrameworksOutput{ + NextCursor: nextCursor, + Frameworks: frameworks, + } +} diff --git a/pkg/server/api/mcp/v1/types/types.go b/pkg/server/api/mcp/v1/types/types.go index a9a009f50..9fc9554ce 100644 --- a/pkg/server/api/mcp/v1/types/types.go +++ b/pkg/server/api/mcp/v1/types/types.go @@ -12,6 +12,8 @@ import ( // Tool input schemas var ( + AddFrameworkToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name"],"properties":{"description":{"type":"string","description":"Framework description"},"name":{"type":"string","description":"Framework name"},"organization_id":{"type":"string","format":"string"}}}`) + AddFrameworkToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["framework"],"properties":{"framework":{"type":"object","required":["id","organization_id","name","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) 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"}}}`) @@ -20,12 +22,16 @@ var ( 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"}}}}}`) + GetFrameworkToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) + GetFrameworkToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["framework"],"properties":{"framework":{"type":"object","required":["id","organization_id","name","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) 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"}}}}}`) + ListFrameworksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["asc","desc"]},"field":{"type":"string","enum":["CREATED_AT"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) + ListFrameworksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["frameworks"],"properties":{"frameworks":{"type":"array","items":{"type":"object","required":["id","organization_id","name","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}},"next_cursor":{"type":"string","format":"string"}}}`) 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"}}}`) @@ -36,6 +42,8 @@ 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"}}}}}}`) + UpdateFrameworkToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"description":{"description":"Framework description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Framework name"}}}`) + UpdateFrameworkToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["framework"],"properties":{"framework":{"type":"object","required":["id","organization_id","name","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"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"}}}}}`) 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"]}}}`) @@ -44,6 +52,21 @@ var ( 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"}}}}}`) ) +// AddFrameworkInput represents the schema +type AddFrameworkInput struct { + // Framework description + Description *string `json:"description,omitempty"` + // Framework name + Name string `json:"name"` + // Organization ID + OrganizationID gid.GID `json:"organization_id"` +} + +// AddFrameworkOutput represents the schema +type AddFrameworkOutput struct { + Framework *Framework `json:"framework"` +} + // AddMeasureInput represents the schema type AddMeasureInput struct { // Measure category @@ -136,6 +159,41 @@ type AddVendorOutput struct { Vendor *Vendor `json:"vendor"` } +// Framework represents the schema +type Framework struct { + // Creation timestamp + CreatedAt time.Time `json:"created_at"` + // Framework description + Description *string `json:"description,omitempty"` + // Framework ID + ID gid.GID `json:"id"` + // Framework name + Name string `json:"name"` + // Organization ID + OrganizationID gid.GID `json:"organization_id"` + // Update timestamp + UpdatedAt time.Time `json:"updated_at"` +} + +// FrameworkOrderBy represents the schema +type FrameworkOrderBy struct { + // Framework order direction + Direction page.OrderDirection `json:"direction"` + // Framework order field + Field coredata.FrameworkOrderField `json:"field"` +} + +// GetFrameworkInput represents the schema +type GetFrameworkInput struct { + // Framework ID + ID gid.GID `json:"id"` +} + +// GetFrameworkOutput represents the schema +type GetFrameworkOutput struct { + Framework *Framework `json:"framework"` +} + // GetMeasureInput represents the schema type GetMeasureInput struct { // Measure ID @@ -169,6 +227,25 @@ type GetRiskOutput struct { Risk *Risk `json:"risk"` } +// ListFrameworksInput represents the schema +type ListFrameworksInput struct { + // Page cursor + Cursor *page.CursorKey `json:"cursor,omitempty"` + // Framework order by + OrderBy *FrameworkOrderBy `json:"order_by,omitempty"` + // Organization ID + OrganizationID gid.GID `json:"organization_id"` + // Page size + Size *int `json:"size,omitempty"` +} + +// ListFrameworksOutput represents the schema +type ListFrameworksOutput struct { + Frameworks []*Framework `json:"frameworks"` + // Next cursor + NextCursor *page.CursorKey `json:"next_cursor,omitempty"` +} + // ListMeasuresInput represents the schema type ListMeasuresInput struct { // Page cursor @@ -380,6 +457,21 @@ type RiskOrderBy struct { Field coredata.RiskOrderField `json:"field"` } +// UpdateFrameworkInput represents the schema +type UpdateFrameworkInput struct { + // Framework description + Description mcp.Omittable[*string] `json:"description,omitempty"` + // Framework ID + ID gid.GID `json:"id"` + // Framework name + Name *string `json:"name,omitempty"` +} + +// UpdateFrameworkOutput represents the schema +type UpdateFrameworkOutput struct { + Framework *Framework `json:"framework"` +} + // UpdateMeasureInput represents the schema type UpdateMeasureInput struct { // Measure category