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
|
||||
}
|
||||
|
||||
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
|
||||
tracker_type:
|
||||
type: string
|
||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB, SCRIPT, IFRAME]
|
||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB]
|
||||
description: Type of tracker
|
||||
pattern:
|
||||
type: string
|
||||
@@ -9492,6 +9492,68 @@ components:
|
||||
format: date-time
|
||||
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:
|
||||
type: object
|
||||
required:
|
||||
@@ -9984,7 +10046,7 @@ components:
|
||||
$ref: "#/components/schemas/GID"
|
||||
tracker_type:
|
||||
type: string
|
||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB, SCRIPT, IFRAME]
|
||||
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, INDEXED_DB]
|
||||
pattern:
|
||||
type: string
|
||||
match_type:
|
||||
@@ -10071,6 +10133,143 @@ components:
|
||||
tracker_pattern:
|
||||
$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:
|
||||
type: object
|
||||
required:
|
||||
@@ -12353,6 +12552,57 @@ tools:
|
||||
$ref: "#/components/schemas/MoveTrackerPatternToCategoryInput"
|
||||
outputSchema:
|
||||
$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
|
||||
description: Publish the current draft version of a cookie banner
|
||||
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