mcp
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.k6n.net/mats/go-cart-actor/pkg/promotions"
|
||||
)
|
||||
|
||||
// AdminTool is one admin promotion tool definition — name, description, input
|
||||
// schema, and the invoke function. It mirrors cmsmount.MCPTool but lives in
|
||||
// this package so the promotions module doesn't import the CMS module.
|
||||
type AdminTool struct {
|
||||
Name string
|
||||
Description string
|
||||
InputSchema json.RawMessage
|
||||
Invoke func(args json.RawMessage) (any, error)
|
||||
}
|
||||
|
||||
// AdminTools returns the admin-only promotion MCP tool definitions (upsert,
|
||||
// set_status, delete). Pass these to cmsmount.AddMCPTools to merge them into
|
||||
// the CMS MCP behind full auth.
|
||||
func AdminTools(store *promotions.Store, eval *promotions.PromotionService) []AdminTool {
|
||||
if eval == nil {
|
||||
eval = promotions.NewPromotionService(nil)
|
||||
}
|
||||
return []AdminTool{
|
||||
{
|
||||
Name: "upsert_promotion",
|
||||
Description: "Create a promotion or replace the existing one with the same id. " +
|
||||
"`promotion` is a full promotion-rule object (id, name, status, priority, conditions, actions). " +
|
||||
"For a tiered volume discount (Volymrabatt) use a cart_total condition plus a tiered_discount action " +
|
||||
"whose config.tiers each have minTotal/maxTotal (öre incl. VAT) and percent.",
|
||||
InputSchema: object(props{
|
||||
"promotion": obj("the full promotion rule object"),
|
||||
}, []string{"promotion"}),
|
||||
Invoke: func(args json.RawMessage) (any, error) {
|
||||
var a struct {
|
||||
Promotion json.RawMessage `json:"promotion"`
|
||||
}
|
||||
if err := decode(args, &a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rule promotions.PromotionRule
|
||||
if err := json.Unmarshal(a.Promotion, &rule); err != nil {
|
||||
return nil, fmt.Errorf("invalid promotion: %w", err)
|
||||
}
|
||||
if rule.ID == "" {
|
||||
return nil, fmt.Errorf("promotion.id is required")
|
||||
}
|
||||
if rule.Status == "" {
|
||||
rule.Status = promotions.StatusActive
|
||||
}
|
||||
replaced, err := store.Upsert(rule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"id": rule.ID, "replaced": replaced}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set_promotion_status",
|
||||
Description: "Set a promotion's status (active|inactive|scheduled|expired) to toggle it on or off.",
|
||||
InputSchema: object(props{
|
||||
"id": str("the promotion id"),
|
||||
"status": str("new status: active|inactive|scheduled|expired"),
|
||||
}, []string{"id", "status"}),
|
||||
Invoke: func(args json.RawMessage) (any, error) {
|
||||
var a struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
if err := decode(args, &a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !validStatus(a.Status) {
|
||||
return nil, fmt.Errorf("invalid status %q", a.Status)
|
||||
}
|
||||
found, err := store.SetStatus(a.ID, promotions.PromotionStatus(a.Status))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("promotion %q not found", a.ID)
|
||||
}
|
||||
return map[string]any{"id": a.ID, "status": a.Status}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete_promotion",
|
||||
Description: "Delete a promotion rule by id.",
|
||||
InputSchema: object(props{"id": str("the promotion id")}, []string{"id"}),
|
||||
Invoke: func(args json.RawMessage) (any, error) {
|
||||
var a struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
if err := decode(args, &a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
found, err := store.Delete(a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("promotion %q not found", a.ID)
|
||||
}
|
||||
return map[string]any{"id": a.ID, "deleted": true}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user