@@ -192,3 +192,103 @@ func (r *Resolver) AddPeopleTool(ctx context.Context, req *mcp.CallToolRequest,
|
||||
People: types.NewPeople(people),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRisksInput) (*mcp.CallToolResult, types.ListRisksOutput, error) {
|
||||
r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionListRisks)
|
||||
|
||||
prb := r.ProboService(ctx, input.OrganizationID)
|
||||
|
||||
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)
|
||||
|
||||
var riskFilter = coredata.NewRiskFilter(nil, nil)
|
||||
if input.Filter != nil {
|
||||
riskFilter = coredata.NewRiskFilter(input.Filter.Query, &input.Filter.SnapshotID)
|
||||
}
|
||||
|
||||
page, err := prb.Risks.ListForOrganizationID(ctx, input.OrganizationID, cursor, riskFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list organization risks: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.NewListRisksOutput(page), nil
|
||||
}
|
||||
func (r *Resolver) GetRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetRiskInput) (*mcp.CallToolResult, types.GetRiskOutput, error) {
|
||||
r.MustBeAuthorized(ctx, input.ID, authz.ActionGet)
|
||||
|
||||
prb := r.ProboService(ctx, input.ID)
|
||||
|
||||
risk, err := prb.Risks.Get(ctx, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.GetRiskOutput{}, fmt.Errorf("failed to get risk: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.GetRiskOutput{
|
||||
Risk: types.NewRisk(risk),
|
||||
}, nil
|
||||
}
|
||||
func (r *Resolver) AddRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddRiskInput) (*mcp.CallToolResult, types.AddRiskOutput, error) {
|
||||
r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateRisk)
|
||||
|
||||
svc := r.ProboService(ctx, input.OrganizationID)
|
||||
|
||||
risk, err := svc.Risks.Create(
|
||||
ctx,
|
||||
probo.CreateRiskRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
Category: input.Category,
|
||||
Treatment: input.Treatment,
|
||||
InherentLikelihood: input.InherentLikelihood,
|
||||
InherentImpact: input.InherentImpact,
|
||||
ResidualLikelihood: input.ResidualLikelihood,
|
||||
ResidualImpact: input.ResidualImpact,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.AddRiskOutput{}, fmt.Errorf("failed to create risk: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.AddRiskOutput{
|
||||
Risk: types.NewRisk(risk),
|
||||
}, nil
|
||||
}
|
||||
func (r *Resolver) UpdateRiskTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateRiskInput) (*mcp.CallToolResult, types.UpdateRiskOutput, error) {
|
||||
r.MustBeAuthorized(ctx, input.ID, authz.ActionUpdateRisk)
|
||||
|
||||
svc := r.ProboService(ctx, input.ID)
|
||||
|
||||
risk, err := svc.Risks.Update(
|
||||
ctx,
|
||||
probo.UpdateRiskRequest{
|
||||
ID: input.ID,
|
||||
Name: input.Name,
|
||||
Description: UnwrapOmittable(input.Description),
|
||||
Category: input.Category,
|
||||
Treatment: input.Treatment,
|
||||
OwnerID: UnwrapOmittable(input.OwnerID),
|
||||
InherentLikelihood: input.InherentLikelihood,
|
||||
InherentImpact: input.InherentImpact,
|
||||
ResidualLikelihood: input.ResidualLikelihood,
|
||||
ResidualImpact: input.ResidualImpact,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.UpdateRiskOutput{}, fmt.Errorf("failed to update risk: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UpdateRiskOutput{
|
||||
Risk: types.NewRisk(risk),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ type ResolverInterface interface {
|
||||
UpdateVendorTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateVendorInput) (*mcp.CallToolResult, types.UpdateVendorOutput, error)
|
||||
GetPeopleTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetPeopleInput) (*mcp.CallToolResult, types.GetPeopleOutput, error)
|
||||
AddPeopleTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddPeopleInput) (*mcp.CallToolResult, types.AddPeopleOutput, error)
|
||||
ListRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRisksInput) (*mcp.CallToolResult, types.ListRisksOutput, error)
|
||||
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)
|
||||
}
|
||||
|
||||
// New creates a new MCP server instance with all handlers registered.
|
||||
@@ -106,4 +110,44 @@ func registerToolHandlers(server *mcp.Server, resolver ResolverInterface) {
|
||||
},
|
||||
resolver.AddPeopleTool,
|
||||
)
|
||||
mcp.AddTool(
|
||||
server,
|
||||
&mcp.Tool{
|
||||
Name: "listRisks",
|
||||
Description: "List all risks for the organization",
|
||||
InputSchema: types.ListRisksToolInputSchema,
|
||||
OutputSchema: types.ListRisksToolOutputSchema,
|
||||
},
|
||||
resolver.ListRisksTool,
|
||||
)
|
||||
mcp.AddTool(
|
||||
server,
|
||||
&mcp.Tool{
|
||||
Name: "getRisk",
|
||||
Description: "Get a risk by ID",
|
||||
InputSchema: types.GetRiskToolInputSchema,
|
||||
OutputSchema: types.GetRiskToolOutputSchema,
|
||||
},
|
||||
resolver.GetRiskTool,
|
||||
)
|
||||
mcp.AddTool(
|
||||
server,
|
||||
&mcp.Tool{
|
||||
Name: "addRisk",
|
||||
Description: "Add a new risk to the organization",
|
||||
InputSchema: types.AddRiskToolInputSchema,
|
||||
OutputSchema: types.AddRiskToolOutputSchema,
|
||||
},
|
||||
resolver.AddRiskTool,
|
||||
)
|
||||
mcp.AddTool(
|
||||
server,
|
||||
&mcp.Tool{
|
||||
Name: "updateRisk",
|
||||
Description: "Update an existing risk",
|
||||
InputSchema: types.UpdateRiskToolInputSchema,
|
||||
OutputSchema: types.UpdateRiskToolOutputSchema,
|
||||
},
|
||||
resolver.UpdateRiskTool,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -397,6 +397,280 @@ components:
|
||||
people:
|
||||
$ref: "#/components/schemas/People"
|
||||
|
||||
RiskOrderField:
|
||||
type: string
|
||||
enum:
|
||||
- CREATED_AT
|
||||
- UPDATED_AT
|
||||
- NAME
|
||||
- CATEGORY
|
||||
- TREATMENT
|
||||
- INHERENT_RISK_SCORE
|
||||
- RESIDUAL_RISK_SCORE
|
||||
- OWNER_FULL_NAME
|
||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.RiskOrderField
|
||||
|
||||
RiskOrderBy:
|
||||
type: object
|
||||
required:
|
||||
- field
|
||||
- direction
|
||||
properties:
|
||||
field:
|
||||
$ref: "#/components/schemas/RiskOrderField"
|
||||
description: Risk order field
|
||||
direction:
|
||||
$ref: "#/components/schemas/OrderDirection"
|
||||
description: Risk order direction
|
||||
|
||||
RiskTreatment:
|
||||
type: string
|
||||
enum:
|
||||
- MITIGATED
|
||||
- ACCEPTED
|
||||
- AVOIDED
|
||||
- TRANSFERRED
|
||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.RiskTreatment
|
||||
|
||||
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:
|
||||
id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Risk ID
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
snapshot_id:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
description: Snapshot ID
|
||||
name:
|
||||
type: string
|
||||
description: Risk name
|
||||
description:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
description: Risk description
|
||||
category:
|
||||
type: string
|
||||
description: Risk category
|
||||
treatment:
|
||||
$ref: "#/components/schemas/RiskTreatment"
|
||||
description: Risk treatment
|
||||
inherent_likelihood:
|
||||
type: integer
|
||||
description: Inherent likelihood
|
||||
inherent_impact:
|
||||
type: integer
|
||||
description: Inherent impact
|
||||
inherent_risk_score:
|
||||
type: integer
|
||||
description: Inherent risk score
|
||||
residual_likelihood:
|
||||
type: integer
|
||||
description: Residual likelihood
|
||||
residual_impact:
|
||||
type: integer
|
||||
description: Residual impact
|
||||
residual_risk_score:
|
||||
type: integer
|
||||
description: Residual risk score
|
||||
note:
|
||||
type: string
|
||||
description: Risk note
|
||||
owner_id:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
description: Owner ID
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Creation timestamp
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Update timestamp
|
||||
|
||||
ListRisksInput:
|
||||
type: object
|
||||
required:
|
||||
- organization_id
|
||||
properties:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
order_by:
|
||||
$ref: "#/components/schemas/RiskOrderBy"
|
||||
description: Risk 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
|
||||
snapshot_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Snapshot ID
|
||||
|
||||
ListRisksOutput:
|
||||
type: object
|
||||
required:
|
||||
- risks
|
||||
properties:
|
||||
next_cursor:
|
||||
$ref: "#/components/schemas/CursorKey"
|
||||
description: Next cursor
|
||||
risks:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Risk"
|
||||
|
||||
GetRiskInput:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Risk ID
|
||||
|
||||
GetRiskOutput:
|
||||
type: object
|
||||
required:
|
||||
- risk
|
||||
properties:
|
||||
risk:
|
||||
$ref: "#/components/schemas/Risk"
|
||||
|
||||
AddRiskInput:
|
||||
type: object
|
||||
required:
|
||||
- organization_id
|
||||
- name
|
||||
- category
|
||||
- treatment
|
||||
- inherent_likelihood
|
||||
- inherent_impact
|
||||
properties:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
name:
|
||||
type: string
|
||||
description: Risk name
|
||||
description:
|
||||
type: string
|
||||
description: Risk description
|
||||
category:
|
||||
type: string
|
||||
description: Risk category
|
||||
owner_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Owner ID
|
||||
treatment:
|
||||
$ref: "#/components/schemas/RiskTreatment"
|
||||
description: Risk treatment
|
||||
inherent_likelihood:
|
||||
type: integer
|
||||
description: Inherent likelihood
|
||||
inherent_impact:
|
||||
type: integer
|
||||
description: Inherent impact
|
||||
residual_likelihood:
|
||||
type: integer
|
||||
description: Residual likelihood
|
||||
residual_impact:
|
||||
type: integer
|
||||
description: Residual impact
|
||||
note:
|
||||
type: string
|
||||
description: Risk note
|
||||
|
||||
AddRiskOutput:
|
||||
type: object
|
||||
required:
|
||||
- risk
|
||||
properties:
|
||||
risk:
|
||||
$ref: "#/components/schemas/Risk"
|
||||
|
||||
UpdateRiskInput:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Risk ID
|
||||
name:
|
||||
type: string
|
||||
description: Risk name
|
||||
description:
|
||||
type: ["string", "null"]
|
||||
description: Risk description
|
||||
go.probo.inc/mcpgen/omittable: true
|
||||
category:
|
||||
type: string
|
||||
description: Risk category
|
||||
owner_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
$ref: "#/components/schemas/GID"
|
||||
- type: "null"
|
||||
description: Owner ID
|
||||
go.probo.inc/mcpgen/omittable: true
|
||||
treatment:
|
||||
$ref: "#/components/schemas/RiskTreatment"
|
||||
description: Risk treatment
|
||||
inherent_likelihood:
|
||||
type: integer
|
||||
description: Inherent likelihood
|
||||
inherent_impact:
|
||||
type: integer
|
||||
description: Inherent impact
|
||||
residual_likelihood:
|
||||
type: integer
|
||||
description: Residual likelihood
|
||||
residual_impact:
|
||||
type: integer
|
||||
description: Residual impact
|
||||
note:
|
||||
type: string
|
||||
description: Risk note
|
||||
|
||||
UpdateRiskOutput:
|
||||
type: object
|
||||
required:
|
||||
- risk
|
||||
properties:
|
||||
risk:
|
||||
$ref: "#/components/schemas/Risk"
|
||||
|
||||
tools:
|
||||
- name: listOrganizations
|
||||
description: List all organizations the user has access to
|
||||
@@ -447,3 +721,31 @@ tools:
|
||||
$ref: "#/components/schemas/AddPeopleInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/AddPeopleOutput"
|
||||
- name: listRisks
|
||||
description: List all risks for the organization
|
||||
readonly: true
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/ListRisksInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/ListRisksOutput"
|
||||
- name: getRisk
|
||||
description: Get a risk by ID
|
||||
readonly: true
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/GetRiskInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/GetRiskOutput"
|
||||
- name: addRisk
|
||||
description: Add a new risk to the organization
|
||||
readonly: false
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/AddRiskInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/AddRiskOutput"
|
||||
- name: updateRisk
|
||||
description: Update an existing risk
|
||||
readonly: false
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/UpdateRiskInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/UpdateRiskOutput"
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
func NewCursor[O page.OrderField](
|
||||
first *int64,
|
||||
first *int,
|
||||
after *page.CursorKey,
|
||||
orderBy page.OrderBy[O],
|
||||
) *page.Cursor[O] {
|
||||
|
||||
50
pkg/server/api/mcp/v1/types/risk.go
Normal file
50
pkg/server/api/mcp/v1/types/risk.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// 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 NewRisk(r *coredata.Risk) *Risk {
|
||||
return &Risk{
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
Description: r.Description,
|
||||
Category: r.Category,
|
||||
Treatment: r.Treatment,
|
||||
InherentLikelihood: r.InherentLikelihood,
|
||||
InherentImpact: r.InherentImpact,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListRisksOutput(riskPage *page.Page[*coredata.Risk, coredata.RiskOrderField]) ListRisksOutput {
|
||||
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 ListRisksOutput{
|
||||
NextCursor: nextCursor,
|
||||
Risks: risks,
|
||||
}
|
||||
}
|
||||
@@ -14,16 +14,24 @@ import (
|
||||
var (
|
||||
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"}}}}}`)
|
||||
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"}}}}}`)
|
||||
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"}}}`)
|
||||
ListPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"next_cursor":{"type":"string","format":"string"},"people":{"type":"array","items":{"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"}}}}}}`)
|
||||
ListRisksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"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","required":["field","direction"],"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"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`)
|
||||
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"}}}}}}`)
|
||||
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"}}}}}`)
|
||||
)
|
||||
@@ -53,6 +61,37 @@ type AddPeopleOutput struct {
|
||||
People *People `json:"people"`
|
||||
}
|
||||
|
||||
// AddRiskInput represents the schema
|
||||
type AddRiskInput struct {
|
||||
// Risk category
|
||||
Category string `json:"category"`
|
||||
// Risk description
|
||||
Description *string `json:"description,omitempty"`
|
||||
// Inherent impact
|
||||
InherentImpact int `json:"inherent_impact"`
|
||||
// Inherent likelihood
|
||||
InherentLikelihood int `json:"inherent_likelihood"`
|
||||
// Risk name
|
||||
Name string `json:"name"`
|
||||
// Risk note
|
||||
Note *string `json:"note,omitempty"`
|
||||
// Organization ID
|
||||
OrganizationID gid.GID `json:"organization_id"`
|
||||
// Owner ID
|
||||
OwnerID *gid.GID `json:"owner_id,omitempty"`
|
||||
// Residual impact
|
||||
ResidualImpact *int `json:"residual_impact,omitempty"`
|
||||
// Residual likelihood
|
||||
ResidualLikelihood *int `json:"residual_likelihood,omitempty"`
|
||||
// Risk treatment
|
||||
Treatment coredata.RiskTreatment `json:"treatment"`
|
||||
}
|
||||
|
||||
// AddRiskOutput represents the schema
|
||||
type AddRiskOutput struct {
|
||||
Risk *Risk `json:"risk"`
|
||||
}
|
||||
|
||||
// AddVendorInput represents the schema
|
||||
type AddVendorInput struct {
|
||||
// Creation timestamp
|
||||
@@ -83,6 +122,17 @@ type GetPeopleOutput struct {
|
||||
People *People `json:"people"`
|
||||
}
|
||||
|
||||
// GetRiskInput represents the schema
|
||||
type GetRiskInput struct {
|
||||
// Risk ID
|
||||
ID gid.GID `json:"id"`
|
||||
}
|
||||
|
||||
// GetRiskOutput represents the schema
|
||||
type GetRiskOutput struct {
|
||||
Risk *Risk `json:"risk"`
|
||||
}
|
||||
|
||||
// ListOrganizationsInput represents the schema
|
||||
type ListOrganizationsInput struct {
|
||||
// Organization ID
|
||||
@@ -104,7 +154,7 @@ type ListPeopleInput struct {
|
||||
// Organization ID
|
||||
OrganizationID gid.GID `json:"organization_id"`
|
||||
// Page size
|
||||
Size *int64 `json:"size,omitempty"`
|
||||
Size *int `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
// ListPeopleOutput represents the schema
|
||||
@@ -114,6 +164,26 @@ type ListPeopleOutput struct {
|
||||
People []*People `json:"people"`
|
||||
}
|
||||
|
||||
// ListRisksInput represents the schema
|
||||
type ListRisksInput struct {
|
||||
// Page cursor
|
||||
Cursor *page.CursorKey `json:"cursor,omitempty"`
|
||||
Filter *ListRisksInputFilter `json:"filter,omitempty"`
|
||||
// Risk order by
|
||||
OrderBy *RiskOrderBy `json:"order_by,omitempty"`
|
||||
// Organization ID
|
||||
OrganizationID gid.GID `json:"organization_id"`
|
||||
// Page size
|
||||
Size *int `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
// ListRisksOutput represents the schema
|
||||
type ListRisksOutput struct {
|
||||
// Next cursor
|
||||
NextCursor *page.CursorKey `json:"next_cursor,omitempty"`
|
||||
Risks []*Risk `json:"risks"`
|
||||
}
|
||||
|
||||
// ListVendorsInput represents the schema
|
||||
type ListVendorsInput struct {
|
||||
// Page cursor
|
||||
@@ -124,7 +194,7 @@ type ListVendorsInput struct {
|
||||
// Organization ID
|
||||
OrganizationID gid.GID `json:"organization_id"`
|
||||
// Page size
|
||||
Size *int64 `json:"size,omitempty"`
|
||||
Size *int `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
// ListVendorsOutput represents the schema
|
||||
@@ -182,6 +252,83 @@ type PeopleOrderBy struct {
|
||||
Field coredata.PeopleOrderField `json:"field"`
|
||||
}
|
||||
|
||||
// Risk represents the schema
|
||||
type Risk struct {
|
||||
// Risk category
|
||||
Category string `json:"category"`
|
||||
// Creation timestamp
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
// Risk description
|
||||
Description *string `json:"description,omitempty"`
|
||||
// Risk ID
|
||||
ID gid.GID `json:"id"`
|
||||
// Inherent impact
|
||||
InherentImpact int `json:"inherent_impact"`
|
||||
// Inherent likelihood
|
||||
InherentLikelihood int `json:"inherent_likelihood"`
|
||||
// Inherent risk score
|
||||
InherentRiskScore int `json:"inherent_risk_score"`
|
||||
// Risk name
|
||||
Name string `json:"name"`
|
||||
// Risk note
|
||||
Note string `json:"note"`
|
||||
// Organization ID
|
||||
OrganizationID gid.GID `json:"organization_id"`
|
||||
// Owner ID
|
||||
OwnerID *string `json:"owner_id,omitempty"`
|
||||
// Residual impact
|
||||
ResidualImpact int `json:"residual_impact"`
|
||||
// Residual likelihood
|
||||
ResidualLikelihood int `json:"residual_likelihood"`
|
||||
// Residual risk score
|
||||
ResidualRiskScore int `json:"residual_risk_score"`
|
||||
// Snapshot ID
|
||||
SnapshotID *string `json:"snapshot_id,omitempty"`
|
||||
// Risk treatment
|
||||
Treatment coredata.RiskTreatment `json:"treatment"`
|
||||
// Update timestamp
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// RiskOrderBy represents the schema
|
||||
type RiskOrderBy struct {
|
||||
// Risk order direction
|
||||
Direction page.OrderDirection `json:"direction"`
|
||||
// Risk order field
|
||||
Field coredata.RiskOrderField `json:"field"`
|
||||
}
|
||||
|
||||
// UpdateRiskInput represents the schema
|
||||
type UpdateRiskInput struct {
|
||||
// Risk category
|
||||
Category *string `json:"category,omitempty"`
|
||||
// Risk description
|
||||
Description mcp.Omittable[*string] `json:"description,omitempty"`
|
||||
// Risk ID
|
||||
ID gid.GID `json:"id"`
|
||||
// Inherent impact
|
||||
InherentImpact *int `json:"inherent_impact,omitempty"`
|
||||
// Inherent likelihood
|
||||
InherentLikelihood *int `json:"inherent_likelihood,omitempty"`
|
||||
// Risk name
|
||||
Name *string `json:"name,omitempty"`
|
||||
// Risk note
|
||||
Note *string `json:"note,omitempty"`
|
||||
// Owner ID
|
||||
OwnerID mcp.Omittable[*gid.GID] `json:"owner_id,omitempty"`
|
||||
// Residual impact
|
||||
ResidualImpact *int `json:"residual_impact,omitempty"`
|
||||
// Residual likelihood
|
||||
ResidualLikelihood *int `json:"residual_likelihood,omitempty"`
|
||||
// Risk treatment
|
||||
Treatment *coredata.RiskTreatment `json:"treatment,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateRiskOutput represents the schema
|
||||
type UpdateRiskOutput struct {
|
||||
Risk *Risk `json:"risk"`
|
||||
}
|
||||
|
||||
// UpdateVendorInput represents the schema
|
||||
type UpdateVendorInput struct {
|
||||
// Vendor description
|
||||
@@ -227,6 +374,14 @@ type ListPeopleInputFilter struct {
|
||||
ExcludeContractEnded *bool `json:"exclude_contract_ended,omitempty"`
|
||||
}
|
||||
|
||||
// ListRisksInputFilter represents the schema
|
||||
type ListRisksInputFilter struct {
|
||||
// Search query
|
||||
Query *string `json:"query,omitempty"`
|
||||
// Snapshot ID
|
||||
SnapshotID *gid.GID `json:"snapshot_id,omitempty"`
|
||||
}
|
||||
|
||||
// ListVendorsInputFilter represents the schema
|
||||
type ListVendorsInputFilter struct {
|
||||
// Snapshot ID
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"go.gearno.de/kit/log"
|
||||
mcpgenmcp "go.probo.inc/mcpgen/mcp"
|
||||
"go.probo.inc/probo/pkg/auth"
|
||||
"go.probo.inc/probo/pkg/authz"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
@@ -65,3 +66,11 @@ func NewMux(logger *log.Logger, proboSvc *probo.Service, authSvc *auth.Service,
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func UnwrapOmittable[T any](field mcpgenmcp.Omittable[T]) *T {
|
||||
if !field.IsSet() {
|
||||
return nil
|
||||
}
|
||||
value, _ := field.Value()
|
||||
return &value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user