Transform meetings page into context page with tabs
Add structured organization context with 5 markdown sections (Product, Architecture, Team, Processes, Customers) editable inline. Meetings are now a tab within the context page. Moved all GraphQL queries from hooks/graph/MeetingGraph.ts into colocated components following new best practices. Updated database schema, backend services, GraphQL resolvers, and MCP API to support the new context fields and structure. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
34
pkg/cmd/context/context.go
Normal file
34
pkg/cmd/context/context.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 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 context
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||
"go.probo.inc/probo/pkg/cmd/context/get"
|
||||
"go.probo.inc/probo/pkg/cmd/context/update"
|
||||
)
|
||||
|
||||
func NewCmdContext(f *cmdutil.Factory) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "context <command>",
|
||||
Short: "Manage organization context",
|
||||
}
|
||||
|
||||
cmd.AddCommand(get.NewCmdGet(f))
|
||||
cmd.AddCommand(update.NewCmdUpdate(f))
|
||||
|
||||
return cmd
|
||||
}
|
||||
155
pkg/cmd/context/get/get.go
Normal file
155
pkg/cmd/context/get/get.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2026 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 get
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/spf13/cobra"
|
||||
"go.probo.inc/probo/pkg/cli/api"
|
||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||
)
|
||||
|
||||
const getQuery = `
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Organization {
|
||||
id
|
||||
name
|
||||
context {
|
||||
product
|
||||
architecture
|
||||
team
|
||||
processes
|
||||
customers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type getResponse struct {
|
||||
Node *struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Context *struct {
|
||||
Product *string `json:"product"`
|
||||
Architecture *string `json:"architecture"`
|
||||
Team *string `json:"team"`
|
||||
Processes *string `json:"processes"`
|
||||
Customers *string `json:"customers"`
|
||||
} `json:"context"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
func NewCmdGet(f *cmdutil.Factory) *cobra.Command {
|
||||
var (
|
||||
flagOrg string
|
||||
flagOutput *string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get organization context",
|
||||
Example: ` prb context get --org <org-id>`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := f.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, hc, err := cfg.DefaultHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgID := flagOrg
|
||||
if orgID == "" {
|
||||
orgID = hc.Organization
|
||||
}
|
||||
if orgID == "" {
|
||||
return fmt.Errorf("organization ID is required: pass --org or run `prb auth login`")
|
||||
}
|
||||
|
||||
client := api.NewClient(
|
||||
host,
|
||||
hc.Token,
|
||||
"/api/console/v1/graphql",
|
||||
cfg.HTTPTimeoutDuration(),
|
||||
)
|
||||
|
||||
data, err := client.Do(
|
||||
getQuery,
|
||||
map[string]any{"id": orgID},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp getResponse
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return fmt.Errorf("cannot parse response: %w", err)
|
||||
}
|
||||
|
||||
if resp.Node == nil {
|
||||
return fmt.Errorf("organization %s not found", orgID)
|
||||
}
|
||||
|
||||
if *flagOutput == cmdutil.OutputJSON {
|
||||
return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node.Context)
|
||||
}
|
||||
|
||||
ctx := resp.Node.Context
|
||||
out := f.IOStreams.Out
|
||||
|
||||
bold := lipgloss.NewStyle().Bold(true)
|
||||
label := lipgloss.NewStyle().Foreground(lipgloss.Color("242"))
|
||||
|
||||
sections := []struct {
|
||||
title string
|
||||
value *string
|
||||
}{
|
||||
{"Product", ctx.Product},
|
||||
{"Architecture", ctx.Architecture},
|
||||
{"Team", ctx.Team},
|
||||
{"Processes", ctx.Processes},
|
||||
{"Customers", ctx.Customers},
|
||||
}
|
||||
|
||||
for _, s := range sections {
|
||||
_, _ = fmt.Fprintf(out, "%s\n", bold.Render(s.title))
|
||||
if s.value != nil && *s.value != "" {
|
||||
_, _ = fmt.Fprintf(out, "%s\n\n", *s.value)
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(out, "%s\n\n", label.Render("(empty)"))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID")
|
||||
flagOutput = cmdutil.AddOutputFlag(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
151
pkg/cmd/context/update/update.go
Normal file
151
pkg/cmd/context/update/update.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2026 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 update
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"go.probo.inc/probo/pkg/cli/api"
|
||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||
)
|
||||
|
||||
const updateMutation = `
|
||||
mutation($input: UpdateOrganizationContextInput!) {
|
||||
updateOrganizationContext(input: $input) {
|
||||
context {
|
||||
organizationId
|
||||
product
|
||||
architecture
|
||||
team
|
||||
processes
|
||||
customers
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type updateResponse struct {
|
||||
UpdateOrganizationContext struct {
|
||||
Context struct {
|
||||
OrganizationID string `json:"organizationId"`
|
||||
Product *string `json:"product"`
|
||||
Architecture *string `json:"architecture"`
|
||||
Team *string `json:"team"`
|
||||
Processes *string `json:"processes"`
|
||||
Customers *string `json:"customers"`
|
||||
} `json:"context"`
|
||||
} `json:"updateOrganizationContext"`
|
||||
}
|
||||
|
||||
func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
||||
var (
|
||||
flagOrg string
|
||||
flagProduct string
|
||||
flagArchitecture string
|
||||
flagTeam string
|
||||
flagProcesses string
|
||||
flagCustomers string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update organization context",
|
||||
Example: ` prb context update --org <org-id> --product "We build compliance software"
|
||||
prb context update --org <org-id> --architecture "Monolith deployed on AWS ECS"`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := f.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, hc, err := cfg.DefaultHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgID := flagOrg
|
||||
if orgID == "" {
|
||||
orgID = hc.Organization
|
||||
}
|
||||
if orgID == "" {
|
||||
return fmt.Errorf("organization ID is required: pass --org or run `prb auth login`")
|
||||
}
|
||||
|
||||
input := map[string]any{
|
||||
"organizationId": orgID,
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("product") {
|
||||
input["product"] = flagProduct
|
||||
}
|
||||
if cmd.Flags().Changed("architecture") {
|
||||
input["architecture"] = flagArchitecture
|
||||
}
|
||||
if cmd.Flags().Changed("team") {
|
||||
input["team"] = flagTeam
|
||||
}
|
||||
if cmd.Flags().Changed("processes") {
|
||||
input["processes"] = flagProcesses
|
||||
}
|
||||
if cmd.Flags().Changed("customers") {
|
||||
input["customers"] = flagCustomers
|
||||
}
|
||||
|
||||
if len(input) == 1 {
|
||||
return fmt.Errorf("at least one section flag is required (--product, --architecture, --team, --processes, --customers)")
|
||||
}
|
||||
|
||||
client := api.NewClient(
|
||||
host,
|
||||
hc.Token,
|
||||
"/api/console/v1/graphql",
|
||||
cfg.HTTPTimeoutDuration(),
|
||||
)
|
||||
|
||||
data, err := client.Do(
|
||||
updateMutation,
|
||||
map[string]any{"input": input},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp updateResponse
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return fmt.Errorf("cannot parse response: %w", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(
|
||||
f.IOStreams.Out,
|
||||
"Updated context for organization %s\n",
|
||||
resp.UpdateOrganizationContext.Context.OrganizationID,
|
||||
)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID")
|
||||
cmd.Flags().StringVar(&flagProduct, "product", "", "Product description (markdown)")
|
||||
cmd.Flags().StringVar(&flagArchitecture, "architecture", "", "Architecture description (markdown)")
|
||||
cmd.Flags().StringVar(&flagTeam, "team", "", "Team description (markdown)")
|
||||
cmd.Flags().StringVar(&flagProcesses, "processes", "", "Processes description (markdown)")
|
||||
cmd.Flags().StringVar(&flagCustomers, "customers", "", "Customers description (markdown)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||
"go.probo.inc/probo/pkg/cmd/completion"
|
||||
cmdconfig "go.probo.inc/probo/pkg/cmd/config"
|
||||
cmdcontext "go.probo.inc/probo/pkg/cmd/context"
|
||||
"go.probo.inc/probo/pkg/cmd/control"
|
||||
"go.probo.inc/probo/pkg/cmd/finding"
|
||||
"go.probo.inc/probo/pkg/cmd/framework"
|
||||
@@ -68,6 +69,7 @@ func NewCmdRoot(f *cmdutil.Factory) *cobra.Command {
|
||||
cmd.AddCommand(browse.NewCmdBrowse(f))
|
||||
cmd.AddCommand(completion.NewCmdCompletion(f))
|
||||
cmd.AddCommand(cmdconfig.NewCmdConfig(f))
|
||||
cmd.AddCommand(cmdcontext.NewCmdContext(f))
|
||||
cmd.AddCommand(control.NewCmdControl(f))
|
||||
cmd.AddCommand(finding.NewCmdFinding(f))
|
||||
cmd.AddCommand(framework.NewCmdFramework(f))
|
||||
|
||||
6
pkg/coredata/migrations/20260319T140000Z.sql
Normal file
6
pkg/coredata/migrations/20260319T140000Z.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE organization_contexts ADD COLUMN product TEXT;
|
||||
ALTER TABLE organization_contexts ADD COLUMN architecture TEXT;
|
||||
ALTER TABLE organization_contexts ADD COLUMN team TEXT;
|
||||
ALTER TABLE organization_contexts ADD COLUMN processes TEXT;
|
||||
ALTER TABLE organization_contexts ADD COLUMN customers TEXT;
|
||||
ALTER TABLE organization_contexts DROP COLUMN summary;
|
||||
@@ -29,7 +29,11 @@ import (
|
||||
type (
|
||||
OrganizationContext struct {
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Summary *string `db:"summary"`
|
||||
Product *string `db:"product"`
|
||||
Architecture *string `db:"architecture"`
|
||||
Team *string `db:"team"`
|
||||
Processes *string `db:"processes"`
|
||||
Customers *string `db:"customers"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
@@ -44,7 +48,11 @@ func (oc *OrganizationContext) LoadByOrganizationID(
|
||||
q := `
|
||||
SELECT
|
||||
organization_id,
|
||||
summary,
|
||||
product,
|
||||
architecture,
|
||||
team,
|
||||
processes,
|
||||
customers,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
@@ -88,13 +96,21 @@ func (oc *OrganizationContext) Insert(
|
||||
INSERT INTO organization_contexts (
|
||||
organization_id,
|
||||
tenant_id,
|
||||
summary,
|
||||
product,
|
||||
architecture,
|
||||
team,
|
||||
processes,
|
||||
customers,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
@organization_id,
|
||||
@tenant_id,
|
||||
@summary,
|
||||
@product,
|
||||
@architecture,
|
||||
@team,
|
||||
@processes,
|
||||
@customers,
|
||||
@created_at,
|
||||
@updated_at
|
||||
)
|
||||
@@ -103,7 +119,11 @@ INSERT INTO organization_contexts (
|
||||
args := pgx.StrictNamedArgs{
|
||||
"organization_id": oc.OrganizationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"summary": oc.Summary,
|
||||
"product": oc.Product,
|
||||
"architecture": oc.Architecture,
|
||||
"team": oc.Team,
|
||||
"processes": oc.Processes,
|
||||
"customers": oc.Customers,
|
||||
"created_at": oc.CreatedAt,
|
||||
"updated_at": oc.UpdatedAt,
|
||||
}
|
||||
@@ -124,7 +144,11 @@ func (oc *OrganizationContext) Update(
|
||||
q := `
|
||||
UPDATE organization_contexts
|
||||
SET
|
||||
summary = @summary,
|
||||
product = @product,
|
||||
architecture = @architecture,
|
||||
team = @team,
|
||||
processes = @processes,
|
||||
customers = @customers,
|
||||
updated_at = @updated_at
|
||||
WHERE
|
||||
%s
|
||||
@@ -135,7 +159,11 @@ WHERE
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"organization_id": oc.OrganizationID,
|
||||
"summary": oc.Summary,
|
||||
"product": oc.Product,
|
||||
"architecture": oc.Architecture,
|
||||
"team": oc.Team,
|
||||
"processes": oc.Processes,
|
||||
"customers": oc.Customers,
|
||||
"updated_at": oc.UpdatedAt,
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ type (
|
||||
|
||||
UpdateOrganizationContextRequest struct {
|
||||
OrganizationID gid.GID
|
||||
Summary **string
|
||||
Product **string
|
||||
Architecture **string
|
||||
Team **string
|
||||
Processes **string
|
||||
Customers **string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -72,7 +76,11 @@ func (uocr *UpdateOrganizationContextRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
v.Check(uocr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
|
||||
v.Check(uocr.Summary, "summary", validator.SafeText(30_000))
|
||||
v.Check(uocr.Product, "product", validator.SafeText(30_000))
|
||||
v.Check(uocr.Architecture, "architecture", validator.SafeText(30_000))
|
||||
v.Check(uocr.Team, "team", validator.SafeText(30_000))
|
||||
v.Check(uocr.Processes, "processes", validator.SafeText(30_000))
|
||||
v.Check(uocr.Customers, "customers", validator.SafeText(30_000))
|
||||
|
||||
return v.Error()
|
||||
}
|
||||
@@ -102,7 +110,7 @@ func (s OrganizationService) Get(
|
||||
return organization, nil
|
||||
}
|
||||
|
||||
func (s OrganizationService) GetContextSummary(
|
||||
func (s OrganizationService) GetContext(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
) (*coredata.OrganizationContext, error) {
|
||||
@@ -154,8 +162,34 @@ func (s OrganizationService) UpdateContext(
|
||||
return fmt.Errorf("cannot load organization context: %w", err)
|
||||
}
|
||||
|
||||
if req.Summary != nil {
|
||||
organizationContext.Summary = *req.Summary
|
||||
updated := false
|
||||
|
||||
if req.Product != nil {
|
||||
organizationContext.Product = *req.Product
|
||||
updated = true
|
||||
}
|
||||
|
||||
if req.Architecture != nil {
|
||||
organizationContext.Architecture = *req.Architecture
|
||||
updated = true
|
||||
}
|
||||
|
||||
if req.Team != nil {
|
||||
organizationContext.Team = *req.Team
|
||||
updated = true
|
||||
}
|
||||
|
||||
if req.Processes != nil {
|
||||
organizationContext.Processes = *req.Processes
|
||||
updated = true
|
||||
}
|
||||
|
||||
if req.Customers != nil {
|
||||
organizationContext.Customers = *req.Customers
|
||||
updated = true
|
||||
}
|
||||
|
||||
if updated {
|
||||
organizationContext.UpdatedAt = time.Now()
|
||||
|
||||
if err := organizationContext.Update(ctx, tx, s.svc.scope); err != nil {
|
||||
|
||||
@@ -3846,7 +3846,11 @@ type Mutation {
|
||||
# Input Types
|
||||
input UpdateOrganizationContextInput {
|
||||
organizationId: ID!
|
||||
summary: String @goField(omittable: true)
|
||||
product: String @goField(omittable: true)
|
||||
architecture: String @goField(omittable: true)
|
||||
team: String @goField(omittable: true)
|
||||
processes: String @goField(omittable: true)
|
||||
customers: String @goField(omittable: true)
|
||||
}
|
||||
|
||||
input UpdateTrustCenterInput {
|
||||
@@ -4753,7 +4757,11 @@ type UpdateOrganizationContextPayload {
|
||||
|
||||
type OrganizationContext {
|
||||
organizationId: ID!
|
||||
summary: String
|
||||
product: String
|
||||
architecture: String
|
||||
team: String
|
||||
processes: String
|
||||
customers: String
|
||||
}
|
||||
|
||||
type UpdateTrustCenterPayload {
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
func NewOrganizationContext(oc *coredata.OrganizationContext) *OrganizationContext {
|
||||
return &OrganizationContext{
|
||||
OrganizationID: oc.OrganizationID,
|
||||
Summary: oc.Summary,
|
||||
Product: oc.Product,
|
||||
Architecture: oc.Architecture,
|
||||
Team: oc.Team,
|
||||
Processes: oc.Processes,
|
||||
Customers: oc.Customers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2048,7 +2048,11 @@ func (r *mutationResolver) UpdateOrganizationContext(ctx context.Context, input
|
||||
|
||||
req := probo.UpdateOrganizationContextRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Summary: gqlutils.UnwrapOmittable(input.Summary),
|
||||
Product: gqlutils.UnwrapOmittable(input.Product),
|
||||
Architecture: gqlutils.UnwrapOmittable(input.Architecture),
|
||||
Team: gqlutils.UnwrapOmittable(input.Team),
|
||||
Processes: gqlutils.UnwrapOmittable(input.Processes),
|
||||
Customers: gqlutils.UnwrapOmittable(input.Customers),
|
||||
}
|
||||
|
||||
organizationContext, err := prb.Organizations.UpdateContext(ctx, req)
|
||||
@@ -6387,7 +6391,7 @@ func (r *organizationResolver) Context(ctx context.Context, obj *types.Organizat
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
orgContext, err := prb.Organizations.GetContextSummary(ctx, obj.ID)
|
||||
orgContext, err := prb.Organizations.GetContext(ctx, obj.ID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load organization context", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
|
||||
@@ -3241,3 +3241,43 @@ func (r *Resolver) UnarchiveDocumentTool(ctx context.Context, req *mcp.CallToolR
|
||||
Document: types.NewDocument(document, profileIDs(approverPage)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetOrganizationContextTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetOrganizationContextInput) (*mcp.CallToolResult, types.GetOrganizationContextOutput, error) {
|
||||
r.MustAuthorize(ctx, input.OrganizationID, probo.ActionOrganizationContextGet)
|
||||
|
||||
prb := r.ProboService(ctx, input.OrganizationID)
|
||||
|
||||
orgContext, err := prb.Organizations.GetContext(ctx, input.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, types.GetOrganizationContextOutput{}, fmt.Errorf("cannot get organization context: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.GetOrganizationContextOutput{
|
||||
OrganizationContext: types.NewOrganizationContext(orgContext),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) UpdateOrganizationContextTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateOrganizationContextInput) (*mcp.CallToolResult, types.UpdateOrganizationContextOutput, error) {
|
||||
r.MustAuthorize(ctx, input.OrganizationID, probo.ActionOrganizationContextUpdate)
|
||||
|
||||
prb := r.ProboService(ctx, input.OrganizationID)
|
||||
|
||||
orgContext, err := prb.Organizations.UpdateContext(
|
||||
ctx,
|
||||
probo.UpdateOrganizationContextRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Product: &input.Product,
|
||||
Architecture: &input.Architecture,
|
||||
Team: &input.Team,
|
||||
Processes: &input.Processes,
|
||||
Customers: &input.Customers,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.UpdateOrganizationContextOutput{}, fmt.Errorf("cannot update organization context: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UpdateOrganizationContextOutput{
|
||||
OrganizationContext: types.NewOrganizationContext(orgContext),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6323,6 +6323,79 @@ components:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Deleted applicability statement ID
|
||||
|
||||
OrganizationContext:
|
||||
type: object
|
||||
required:
|
||||
- organization_id
|
||||
properties:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
product:
|
||||
type: string
|
||||
description: Product description
|
||||
architecture:
|
||||
type: string
|
||||
description: Architecture description
|
||||
team:
|
||||
type: string
|
||||
description: Team description
|
||||
processes:
|
||||
type: string
|
||||
description: Processes description
|
||||
customers:
|
||||
type: string
|
||||
description: Customers description
|
||||
|
||||
GetOrganizationContextInput:
|
||||
type: object
|
||||
required:
|
||||
- organization_id
|
||||
properties:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
|
||||
GetOrganizationContextOutput:
|
||||
type: object
|
||||
required:
|
||||
- organization_context
|
||||
properties:
|
||||
organization_context:
|
||||
$ref: "#/components/schemas/OrganizationContext"
|
||||
|
||||
UpdateOrganizationContextInput:
|
||||
type: object
|
||||
required:
|
||||
- organization_id
|
||||
properties:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
product:
|
||||
type: string
|
||||
description: Product description
|
||||
architecture:
|
||||
type: string
|
||||
description: Architecture description
|
||||
team:
|
||||
type: string
|
||||
description: Team description
|
||||
processes:
|
||||
type: string
|
||||
description: Processes description
|
||||
customers:
|
||||
type: string
|
||||
description: Customers description
|
||||
|
||||
UpdateOrganizationContextOutput:
|
||||
type: object
|
||||
required:
|
||||
- organization_context
|
||||
properties:
|
||||
organization_context:
|
||||
$ref: "#/components/schemas/OrganizationContext"
|
||||
|
||||
tools:
|
||||
- name: listOrganizations
|
||||
description: List all organizations the user has access to
|
||||
@@ -7441,3 +7514,20 @@ tools:
|
||||
$ref: "#/components/schemas/DeleteApplicabilityStatementInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/DeleteApplicabilityStatementOutput"
|
||||
- name: getOrganizationContext
|
||||
description: Get the organization context containing structured sections about the company
|
||||
hints:
|
||||
readonly: true
|
||||
idempotent: true
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/GetOrganizationContextInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/GetOrganizationContextOutput"
|
||||
- name: updateOrganizationContext
|
||||
description: Update the organization context sections (product, architecture, team, processes, customers)
|
||||
hints:
|
||||
readonly: false
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/UpdateOrganizationContextInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/UpdateOrganizationContextOutput"
|
||||
|
||||
30
pkg/server/api/mcp/v1/types/organization_context.go
Normal file
30
pkg/server/api/mcp/v1/types/organization_context.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2026 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"
|
||||
)
|
||||
|
||||
func NewOrganizationContext(oc *coredata.OrganizationContext) *OrganizationContext {
|
||||
return &OrganizationContext{
|
||||
OrganizationID: oc.OrganizationID,
|
||||
Product: oc.Product,
|
||||
Architecture: oc.Architecture,
|
||||
Team: oc.Team,
|
||||
Processes: oc.Processes,
|
||||
Customers: oc.Customers,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user