mcp: add TrackerResource tools and drop SCRIPT/IFRAME from tracker_type
- Remove SCRIPT/IFRAME from the tracker_type enum in the MCP spec. - Add TrackerResource schema in components/schemas. - Add 6 tools: listTrackerResources, getTrackerResource, addTrackerResource, updateTrackerResource, deleteTrackerResource, moveTrackerResourceToCategory with input/output schemas. - Add types/tracker_resource.go helper and resolver implementations. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -5180,3 +5180,88 @@ func (r *Resolver) PublishDocumentTool(ctx context.Context, req *mcp.CallToolReq
|
|||||||
}
|
}
|
||||||
return nil, output, nil
|
return nil, output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) ListTrackerResourcesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTrackerResourcesInput) (*mcp.CallToolResult, types.ListTrackerResourcesOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerResourceList)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
|
||||||
|
cursor := types.NewCursor(input.Size, input.Cursor, page.OrderBy[coredata.TrackerResourceOrderField]{Field: coredata.TrackerResourceOrderFieldCreatedAt, Direction: page.OrderDirectionAsc})
|
||||||
|
resources, err := r.cookieBanner.ListTrackerResourcesForCategory(ctx, scope, input.CookieCategoryID, cursor)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot list tracker resources: %w", err))
|
||||||
|
}
|
||||||
|
p := page.NewPage(resources, cursor)
|
||||||
|
return nil, types.NewListTrackerResourcesOutput(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) GetTrackerResourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetTrackerResourceInput) (*mcp.CallToolResult, types.GetTrackerResourceOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerResourceGet)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.ID)
|
||||||
|
resource, err := r.cookieBanner.GetTrackerResource(ctx, scope, input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, types.GetTrackerResourceOutput{}, fmt.Errorf("cannot get tracker resource: %w", err)
|
||||||
|
}
|
||||||
|
return nil, types.GetTrackerResourceOutput{TrackerResource: types.NewTrackerResource(resource)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) AddTrackerResourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddTrackerResourceInput) (*mcp.CallToolResult, types.AddTrackerResourceOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerResourceCreate)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
|
||||||
|
description := ""
|
||||||
|
if input.Description != nil {
|
||||||
|
description = *input.Description
|
||||||
|
}
|
||||||
|
resource, err := r.cookieBanner.CreateTrackerResource(ctx, scope, cookiebanner.CreateTrackerResourceRequest{
|
||||||
|
CookieCategoryID: input.CookieCategoryID,
|
||||||
|
ResourceType: coredata.TrackerResourceType(input.ResourceType),
|
||||||
|
Origin: input.Origin,
|
||||||
|
Path: input.Path,
|
||||||
|
DisplayName: input.DisplayName,
|
||||||
|
Description: description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, types.AddTrackerResourceOutput{}, fmt.Errorf("cannot create tracker resource: %w", err)
|
||||||
|
}
|
||||||
|
return nil, types.AddTrackerResourceOutput{TrackerResource: types.NewTrackerResource(resource)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) UpdateTrackerResourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateTrackerResourceInput) (*mcp.CallToolResult, types.UpdateTrackerResourceOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerResourceUpdate)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.ID)
|
||||||
|
updateReq := cookiebanner.UpdateTrackerResourceRequest{TrackerResourceID: input.ID}
|
||||||
|
if v := UnwrapOmittable(input.DisplayName); v != nil && *v != nil {
|
||||||
|
updateReq.DisplayName = *v
|
||||||
|
}
|
||||||
|
if v := UnwrapOmittable(input.Description); v != nil && *v != nil {
|
||||||
|
updateReq.Description = *v
|
||||||
|
}
|
||||||
|
if v := UnwrapOmittable(input.Excluded); v != nil && *v != nil {
|
||||||
|
updateReq.Excluded = *v
|
||||||
|
}
|
||||||
|
resource, err := r.cookieBanner.UpdateTrackerResource(ctx, scope, updateReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, types.UpdateTrackerResourceOutput{}, fmt.Errorf("cannot update tracker resource: %w", err)
|
||||||
|
}
|
||||||
|
return nil, types.UpdateTrackerResourceOutput{TrackerResource: types.NewTrackerResource(resource)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) DeleteTrackerResourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteTrackerResourceInput) (*mcp.CallToolResult, types.DeleteTrackerResourceOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerResourceDelete)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.ID)
|
||||||
|
if err := r.cookieBanner.DeleteTrackerResource(ctx, scope, input.ID); err != nil {
|
||||||
|
return nil, types.DeleteTrackerResourceOutput{}, fmt.Errorf("cannot delete tracker resource: %w", err)
|
||||||
|
}
|
||||||
|
return nil, types.DeleteTrackerResourceOutput{DeletedID: input.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) MoveTrackerResourceToCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.MoveTrackerResourceToCategoryInput) (*mcp.CallToolResult, types.MoveTrackerResourceToCategoryOutput, error) {
|
||||||
|
r.MustAuthorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceUpdate)
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.TrackerResourceID)
|
||||||
|
result, err := r.cookieBanner.MoveTrackerResourceToCategory(ctx, scope, cookiebanner.MoveTrackerResourceToCategoryRequest{
|
||||||
|
TrackerResourceID: input.TrackerResourceID,
|
||||||
|
TargetCookieCategoryID: input.TargetCookieCategoryID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, types.MoveTrackerResourceToCategoryOutput{}, fmt.Errorf("cannot move tracker resource: %w", err)
|
||||||
|
}
|
||||||
|
return nil, types.MoveTrackerResourceToCategoryOutput{TrackerResource: types.NewTrackerResource(result.TrackerResource)}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9450,7 +9450,7 @@ components:
|
|||||||
description: Cookie category ID
|
description: Cookie category ID
|
||||||
tracker_type:
|
tracker_type:
|
||||||
type: string
|
type: string
|
||||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB, SCRIPT, IFRAME]
|
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB]
|
||||||
description: Type of tracker
|
description: Type of tracker
|
||||||
pattern:
|
pattern:
|
||||||
type: string
|
type: string
|
||||||
@@ -9492,6 +9492,68 @@ components:
|
|||||||
format: date-time
|
format: date-time
|
||||||
description: Last update timestamp
|
description: Last update timestamp
|
||||||
|
|
||||||
|
TrackerResource:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- organization_id
|
||||||
|
- cookie_banner_id
|
||||||
|
- cookie_category_id
|
||||||
|
- resource_type
|
||||||
|
- origin
|
||||||
|
- path
|
||||||
|
- display_name
|
||||||
|
- description
|
||||||
|
- excluded
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
description: Tracker resource ID
|
||||||
|
organization_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
description: Organization ID
|
||||||
|
cookie_banner_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
description: Cookie banner ID
|
||||||
|
cookie_category_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
description: Cookie category ID
|
||||||
|
resource_type:
|
||||||
|
type: string
|
||||||
|
enum: [SCRIPT, IFRAME]
|
||||||
|
description: Type of tracked resource
|
||||||
|
origin:
|
||||||
|
type: string
|
||||||
|
description: Resource origin (scheme://host[:port])
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: Resource path
|
||||||
|
display_name:
|
||||||
|
type: string
|
||||||
|
description: Human-friendly display name
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: Resource description
|
||||||
|
excluded:
|
||||||
|
type: boolean
|
||||||
|
description: Whether this resource is excluded from the consent banner
|
||||||
|
last_detected_at:
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
format: date-time
|
||||||
|
description: Timestamp when this resource was last detected
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Creation timestamp
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Last update timestamp
|
||||||
|
|
||||||
CookieBannerVersion:
|
CookieBannerVersion:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -9984,7 +10046,7 @@ components:
|
|||||||
$ref: "#/components/schemas/GID"
|
$ref: "#/components/schemas/GID"
|
||||||
tracker_type:
|
tracker_type:
|
||||||
type: string
|
type: string
|
||||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB, SCRIPT, IFRAME]
|
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB]
|
||||||
pattern:
|
pattern:
|
||||||
type: string
|
type: string
|
||||||
match_type:
|
match_type:
|
||||||
@@ -10071,6 +10133,143 @@ components:
|
|||||||
tracker_pattern:
|
tracker_pattern:
|
||||||
$ref: "#/components/schemas/TrackerPattern"
|
$ref: "#/components/schemas/TrackerPattern"
|
||||||
|
|
||||||
|
ListTrackerResourcesInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- cookie_category_id
|
||||||
|
properties:
|
||||||
|
cookie_category_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
size:
|
||||||
|
type: integer
|
||||||
|
cursor:
|
||||||
|
$ref: "#/components/schemas/CursorKey"
|
||||||
|
|
||||||
|
ListTrackerResourcesOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resources
|
||||||
|
properties:
|
||||||
|
next_cursor:
|
||||||
|
$ref: "#/components/schemas/CursorKey"
|
||||||
|
tracker_resources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/TrackerResource"
|
||||||
|
|
||||||
|
GetTrackerResourceInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
|
||||||
|
GetTrackerResourceOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resource
|
||||||
|
properties:
|
||||||
|
tracker_resource:
|
||||||
|
$ref: "#/components/schemas/TrackerResource"
|
||||||
|
|
||||||
|
AddTrackerResourceInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- cookie_category_id
|
||||||
|
- resource_type
|
||||||
|
- origin
|
||||||
|
- path
|
||||||
|
- display_name
|
||||||
|
properties:
|
||||||
|
cookie_category_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
resource_type:
|
||||||
|
type: string
|
||||||
|
enum: [SCRIPT, IFRAME]
|
||||||
|
origin:
|
||||||
|
type: string
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
display_name:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
AddTrackerResourceOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resource
|
||||||
|
properties:
|
||||||
|
tracker_resource:
|
||||||
|
$ref: "#/components/schemas/TrackerResource"
|
||||||
|
|
||||||
|
UpdateTrackerResourceInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
display_name:
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
go.probo.inc/mcpgen/omittable: true
|
||||||
|
description:
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
go.probo.inc/mcpgen/omittable: true
|
||||||
|
excluded:
|
||||||
|
type:
|
||||||
|
- boolean
|
||||||
|
- "null"
|
||||||
|
go.probo.inc/mcpgen/omittable: true
|
||||||
|
|
||||||
|
UpdateTrackerResourceOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resource
|
||||||
|
properties:
|
||||||
|
tracker_resource:
|
||||||
|
$ref: "#/components/schemas/TrackerResource"
|
||||||
|
|
||||||
|
DeleteTrackerResourceInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
|
||||||
|
DeleteTrackerResourceOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- deleted_id
|
||||||
|
properties:
|
||||||
|
deleted_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
|
||||||
|
MoveTrackerResourceToCategoryInput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resource_id
|
||||||
|
- target_cookie_category_id
|
||||||
|
properties:
|
||||||
|
tracker_resource_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
target_cookie_category_id:
|
||||||
|
$ref: "#/components/schemas/GID"
|
||||||
|
|
||||||
|
MoveTrackerResourceToCategoryOutput:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- tracker_resource
|
||||||
|
properties:
|
||||||
|
tracker_resource:
|
||||||
|
$ref: "#/components/schemas/TrackerResource"
|
||||||
|
|
||||||
PublishCookieBannerVersionInput:
|
PublishCookieBannerVersionInput:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -12353,6 +12552,57 @@ tools:
|
|||||||
$ref: "#/components/schemas/MoveTrackerPatternToCategoryInput"
|
$ref: "#/components/schemas/MoveTrackerPatternToCategoryInput"
|
||||||
outputSchema:
|
outputSchema:
|
||||||
$ref: "#/components/schemas/MoveTrackerPatternToCategoryOutput"
|
$ref: "#/components/schemas/MoveTrackerPatternToCategoryOutput"
|
||||||
|
- name: listTrackerResources
|
||||||
|
description: List all tracker resources for a category
|
||||||
|
hints:
|
||||||
|
readonly: true
|
||||||
|
idempotent: true
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/ListTrackerResourcesInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/ListTrackerResourcesOutput"
|
||||||
|
- name: getTrackerResource
|
||||||
|
description: Get a tracker resource by ID
|
||||||
|
hints:
|
||||||
|
readonly: true
|
||||||
|
idempotent: true
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/GetTrackerResourceInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/GetTrackerResourceOutput"
|
||||||
|
- name: addTrackerResource
|
||||||
|
description: Create a new tracker resource for a category
|
||||||
|
hints:
|
||||||
|
readonly: false
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/AddTrackerResourceInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/AddTrackerResourceOutput"
|
||||||
|
- name: updateTrackerResource
|
||||||
|
description: Update an existing tracker resource
|
||||||
|
hints:
|
||||||
|
readonly: false
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/UpdateTrackerResourceInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/UpdateTrackerResourceOutput"
|
||||||
|
- name: deleteTrackerResource
|
||||||
|
description: Delete a tracker resource
|
||||||
|
hints:
|
||||||
|
readonly: false
|
||||||
|
destructive: true
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/DeleteTrackerResourceInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/DeleteTrackerResourceOutput"
|
||||||
|
- name: moveTrackerResourceToCategory
|
||||||
|
description: Move a tracker resource to a different category
|
||||||
|
hints:
|
||||||
|
readonly: false
|
||||||
|
inputSchema:
|
||||||
|
$ref: "#/components/schemas/MoveTrackerResourceToCategoryInput"
|
||||||
|
outputSchema:
|
||||||
|
$ref: "#/components/schemas/MoveTrackerResourceToCategoryOutput"
|
||||||
- name: publishCookieBannerVersion
|
- name: publishCookieBannerVersion
|
||||||
description: Publish the current draft version of a cookie banner
|
description: Publish the current draft version of a cookie banner
|
||||||
hints:
|
hints:
|
||||||
|
|||||||
56
pkg/server/api/mcp/v1/types/tracker_resource.go
Normal file
56
pkg/server/api/mcp/v1/types/tracker_resource.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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"
|
||||||
|
"go.probo.inc/probo/pkg/page"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTrackerResource(tr *coredata.TrackerResource) *TrackerResource {
|
||||||
|
return &TrackerResource{
|
||||||
|
ID: tr.ID,
|
||||||
|
OrganizationID: tr.OrganizationID,
|
||||||
|
CookieBannerID: tr.CookieBannerID,
|
||||||
|
CookieCategoryID: tr.CookieCategoryID,
|
||||||
|
ResourceType: TrackerResourceResourceType(tr.ResourceType),
|
||||||
|
Origin: tr.Origin,
|
||||||
|
Path: tr.Path,
|
||||||
|
DisplayName: tr.DisplayName,
|
||||||
|
Description: tr.Description,
|
||||||
|
Excluded: tr.Excluded,
|
||||||
|
LastDetectedAt: tr.LastDetectedAt,
|
||||||
|
CreatedAt: tr.CreatedAt,
|
||||||
|
UpdatedAt: tr.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListTrackerResourcesOutput(pg *page.Page[*coredata.TrackerResource, coredata.TrackerResourceOrderField]) ListTrackerResourcesOutput {
|
||||||
|
resources := make([]*TrackerResource, 0, len(pg.Data))
|
||||||
|
for _, tr := range pg.Data {
|
||||||
|
resources = append(resources, NewTrackerResource(tr))
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextCursor *page.CursorKey
|
||||||
|
if len(pg.Data) > 0 {
|
||||||
|
cursorKey := pg.Data[len(pg.Data)-1].CursorKey(pg.Cursor.OrderBy.Field)
|
||||||
|
nextCursor = &cursorKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTrackerResourcesOutput{
|
||||||
|
NextCursor: nextCursor,
|
||||||
|
TrackerResources: resources,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user