💥 Migrate to official mcp sdk
This commit is contained in:
1 parent
24f5dd9118
commit
ec61960c1e
81 files changed
+4226
-3277
No files matched your search
+2
-1
@@ -17,8 +17,9 @@ go_deps.config(
|
||||
)
|
||||
use_repo(
|
||||
go_deps,
|
||||
"com_github_google_jsonschema_go",
|
||||
"com_github_jedib0t_go_pretty_v6",
|
||||
"com_github_mark3labs_mcp_go",
|
||||
"com_github_modelcontextprotocol_go_sdk",
|
||||
"com_github_ohler55_ojg",
|
||||
"com_github_savioxavier_termlink",
|
||||
"com_github_spf13_cobra",
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg",
|
||||
"//pkg/auth",
|
||||
"@com_github_mark3labs_mcp_go//server",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_savioxavier_termlink//:termlink",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@com_github_spf13_viper//:viper",
|
||||
|
||||
@@ -13,7 +13,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/activity",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+97
-82
@@ -3,6 +3,7 @@ package activity
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,14 +11,90 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/activity"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var defaultParts = []string{"id", "snippet", "contentDetails"}
|
||||
type listIn struct {
|
||||
ChannelId string `json:"channelId"`
|
||||
Home *string `json:"home,omitempty"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
PublishedAfter string `json:"publishedAfter"`
|
||||
PublishedBefore string `json:"publishedBefore"`
|
||||
RegionCode string `json:"regionCode"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"channelId", "home", "maxResults", "mine", "publishedAfter",
|
||||
"publishedBefore", "regionCode", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"channelId": {
|
||||
Type: "string", Description: ciUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"home": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: homeUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"publishedAfter": {
|
||||
Type: "string", Description: paUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"publishedBefore": {
|
||||
Type: "string", Description: pbUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"regionCode": {
|
||||
Type: "string", Description: rcUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","contentDetails"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "activity-list", Title: short, Description: long,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
activityCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringVarP(&channelId, "channelId", "c", "", ciUsage)
|
||||
listCmd.Flags().BoolVarP(home, "home", "H", true, homeUsage)
|
||||
@@ -31,7 +108,8 @@ func init() {
|
||||
)
|
||||
listCmd.Flags().StringVarP(®ionCode, "regionCode", "r", "", rcUsage)
|
||||
listCmd.Flags().StringSliceVarP(
|
||||
&parts, "parts", "p", defaultParts, pkg.PartsUsage,
|
||||
&parts, "parts", "p", []string{"id", "snippet", "contentDetails"},
|
||||
pkg.PartsUsage,
|
||||
)
|
||||
listCmd.Flags().StringVarP(&output, "output", "o", "table", pkg.TableUsage)
|
||||
listCmd.Flags().StringVarP(&jpath, "jsonpath", "j", "", pkg.JPUsage)
|
||||
@@ -50,80 +128,19 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"activity-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(ciUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"home", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(homeUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mineUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publishedAfter", mcp.DefaultString(""),
|
||||
mcp.Description(paUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publishedBefore", mcp.DefaultString(""),
|
||||
mcp.Description(pbUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"regionCode", mcp.DefaultString(""),
|
||||
mcp.Description(rcUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray(defaultParts),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
channelId, _ = args["channelId"].(string)
|
||||
homeRaw, _ := args["home"].(string)
|
||||
home = utils.BoolPtr(homeRaw)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
mineRaw, ok := args["mine"].(string)
|
||||
if !ok {
|
||||
mineRaw = "true" // Default to true if not provided
|
||||
}
|
||||
mine = utils.BoolPtr(mineRaw)
|
||||
publishedAfter, _ = args["publishedAfter"].(string)
|
||||
publishedBefore, _ = args["publishedBefore"].(string)
|
||||
regionCode, _ = args["regionCode"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
channelId = input.ChannelId
|
||||
home = utils.BoolPtr(*input.Home)
|
||||
maxResults = input.MaxResults
|
||||
mine = utils.BoolPtr(*input.Mine)
|
||||
publishedAfter = input.PublishedAfter
|
||||
publishedBefore = input.PublishedBefore
|
||||
regionCode = input.RegionCode
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "activity list started")
|
||||
|
||||
@@ -131,17 +148,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "activity list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "activity list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "activity list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -17,7 +17,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/caption",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@com_github_spf13_pflag//:pflag",
|
||||
],
|
||||
|
||||
+54
-39
@@ -3,12 +3,14 @@ package caption
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/caption"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,50 @@ const (
|
||||
deleteIdsUsage = "IDs of the captions to delete"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOf string `json:"onBehalfOf"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOf", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOf": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "caption-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
captionCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -44,39 +88,12 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"caption-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOf", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOf, _ = args["onBehalfOf"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOf = input.OnBehalfOf
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "caption delete started")
|
||||
|
||||
@@ -84,17 +101,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "caption delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "caption delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "caption delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+76
-54
@@ -3,12 +3,14 @@ package caption
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/caption"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,69 @@ const (
|
||||
downloadIdUsage = "ID of the caption to download"
|
||||
)
|
||||
|
||||
type downloadIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
File string `json:"file"`
|
||||
Tfmt string `json:"tfmt"`
|
||||
Tlang string `json:"tlang"`
|
||||
OnBehalfOf string `json:"onBehalfOf"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var downloadInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "file", "tfmt", "tlang", "onBehalfOf", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: downloadIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"file": {
|
||||
Type: "string",
|
||||
Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tfmt": {
|
||||
Type: "string",
|
||||
Enum: []any{"sbv", "srt", "vtt", ""},
|
||||
Description: tfmtUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tlang": {
|
||||
Type: "string",
|
||||
Description: tlangUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOf": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(downloadTool, downloadHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "caption-download", Title: downloadShort, Description: downloadLong,
|
||||
InputSchema: downloadInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, downloadHandler,
|
||||
)
|
||||
captionCmd.AddCommand(downloadCmd)
|
||||
|
||||
downloadCmd.Flags().StringSliceVarP(
|
||||
@@ -50,54 +113,15 @@ var downloadCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var downloadTool = mcp.NewTool(
|
||||
"caption-download",
|
||||
mcp.WithTitleAnnotation(downloadShort),
|
||||
mcp.WithDescription(downloadLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(downloadIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"tfmt", mcp.Enum("sbv", "srt", "vtt"),
|
||||
mcp.DefaultString(""), mcp.Description(tfmtUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"tlang", mcp.DefaultString(""),
|
||||
mcp.Description(tlangUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOf", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func downloadHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
file, _ = args["file"].(string)
|
||||
tfmt, _ = args["tfmt"].(string)
|
||||
tlang, _ = args["tlang"].(string)
|
||||
onBehalfOf, _ = args["onBehalfOf"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input downloadIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
file = input.File
|
||||
tfmt = input.Tfmt
|
||||
tlang = input.Tlang
|
||||
onBehalfOf = input.OnBehalfOf
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "caption download started")
|
||||
|
||||
@@ -105,17 +129,15 @@ func downloadHandler(
|
||||
err := download(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "caption download failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "caption download failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "caption download completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func download(writer io.Writer) error {
|
||||
|
||||
+146
-100
@@ -3,6 +3,7 @@ package caption
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/caption"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,130 @@ const (
|
||||
insertLong = "Insert caption to a video"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
File string `json:"file"`
|
||||
AudioTrackType string `json:"audioTrackType"`
|
||||
IsAutoSynced *string `json:"isAutoSynced,omitempty"`
|
||||
IsCC *string `json:"isCC,omitempty"`
|
||||
IsDraft *string `json:"isDraft,omitempty"`
|
||||
IsEasyReader *string `json:"isEasyReader,omitempty"`
|
||||
IsLarge *string `json:"isLarge,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Name string `json:"name"`
|
||||
TrackKind string `json:"trackKind"`
|
||||
VideoId string `json:"videoId"`
|
||||
OnBehalfOf string `json:"onBehalfOf"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"file", "audioTrackType", "isAutoSynced", "isCC", "isDraft",
|
||||
"isEasyReader", "isLarge", "language", "name", "trackKind",
|
||||
"videoId", "onBehalfOf", "onBehalfOfContentOwner", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"file": {
|
||||
Type: "string",
|
||||
Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"audioTrackType": {
|
||||
Type: "string",
|
||||
Enum: []any{"unknown", "primary", "commentary", "descriptive"},
|
||||
Description: attUsage,
|
||||
Default: json.RawMessage(`"unknown"`),
|
||||
},
|
||||
"isAutoSynced": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iasUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isCC": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iscUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isDraft": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: isdUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isEasyReader": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iserUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isLarge": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: islUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string",
|
||||
Description: languageUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"name": {
|
||||
Type: "string",
|
||||
Description: nameUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"trackKind": {
|
||||
Type: "string",
|
||||
Enum: []any{"standard", "ASR", "forced"},
|
||||
Description: tkUsage,
|
||||
Default: json.RawMessage(`"standard"`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string",
|
||||
Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOf": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string",
|
||||
Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage,
|
||||
Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string",
|
||||
Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "caption-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
captionCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(&file, "file", "f", "", fileUsage)
|
||||
@@ -66,100 +190,24 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"caption-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"audioTrackType",
|
||||
mcp.Enum("unknown", "primary", "commentary", "descriptive"),
|
||||
mcp.DefaultString("unknown"), mcp.Description(attUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isAutoSynced", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iasUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isCC", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iscUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isDraft", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(isdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isEasyReader", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iserUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isLarge", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(islUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(languageUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"name", mcp.DefaultString(""),
|
||||
mcp.Description(nameUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"trackKind", mcp.Enum("standard", "ASR", "forced"),
|
||||
mcp.DefaultString("standard"), mcp.Description(tkUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOf", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
file, _ = args["file"].(string)
|
||||
audioTrackType, _ = args["audioTrackType"].(string)
|
||||
isAutoSyncedRaw, _ := args["isAutoSynced"].(string)
|
||||
isAutoSynced = utils.BoolPtr(isAutoSyncedRaw)
|
||||
isCCRaw, _ := args["isCC"].(string)
|
||||
isCC = utils.BoolPtr(isCCRaw)
|
||||
isDraftRaw, _ := args["isDraft"].(string)
|
||||
isDraft = utils.BoolPtr(isDraftRaw)
|
||||
isEasyReaderRaw, _ := args["isEasyReader"].(string)
|
||||
isEasyReader = utils.BoolPtr(isEasyReaderRaw)
|
||||
isLargeRaw, _ := args["isLarge"].(string)
|
||||
isLarge = utils.BoolPtr(isLargeRaw)
|
||||
language, _ = args["language"].(string)
|
||||
name, _ = args["name"].(string)
|
||||
trackKind, _ = args["trackKind"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
onBehalfOf, _ = args["onBehalfOf"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
file = input.File
|
||||
audioTrackType = input.AudioTrackType
|
||||
isAutoSynced = utils.BoolPtr(*input.IsAutoSynced)
|
||||
isCC = utils.BoolPtr(*input.IsCC)
|
||||
isDraft = utils.BoolPtr(*input.IsDraft)
|
||||
isEasyReader = utils.BoolPtr(*input.IsEasyReader)
|
||||
isLarge = utils.BoolPtr(*input.IsLarge)
|
||||
language = input.Language
|
||||
name = input.Name
|
||||
trackKind = input.TrackKind
|
||||
videoId = input.VideoId
|
||||
onBehalfOf = input.OnBehalfOf
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "caption insert started")
|
||||
|
||||
@@ -167,17 +215,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "caption insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "caption insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "caption insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+86
-64
@@ -3,13 +3,15 @@ package caption
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/caption"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,78 @@ const (
|
||||
listIdsUsage = "IDs of the captions to list"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
VideoId string `json:"videoId"`
|
||||
OnBehalfOf string `json:"onBehalfOf"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "videoId", "onBehalfOf", "onBehalfOfContentOwner",
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string",
|
||||
Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOf": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string",
|
||||
Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage,
|
||||
Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string",
|
||||
Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "caption-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
captionCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -49,64 +121,16 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"caption-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOf", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
videoId, _ = args["videoId"].(string)
|
||||
onBehalfOf, _ = args["onBehalfOf"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
videoId = input.VideoId
|
||||
onBehalfOf = input.OnBehalfOf
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "caption list started")
|
||||
|
||||
@@ -114,17 +138,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "caption list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "caption list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "caption list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+145
-99
@@ -3,6 +3,7 @@ package caption
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/caption"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,129 @@ const (
|
||||
updateLong = "Update caption of a video"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
File string `json:"file"`
|
||||
AudioTrackType string `json:"audioTrackType"`
|
||||
IsAutoSynced *string `json:"isAutoSynced,omitempty"`
|
||||
IsCC *string `json:"isCC,omitempty"`
|
||||
IsDraft *string `json:"isDraft,omitempty"`
|
||||
IsEasyReader *string `json:"isEasyReader,omitempty"`
|
||||
IsLarge *string `json:"isLarge,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Name string `json:"name"`
|
||||
TrackKind string `json:"trackKind"`
|
||||
VideoId string `json:"videoId"`
|
||||
OnBehalfOf string `json:"onBehalfOf"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"file", "audioTrackType", "isAutoSynced", "isCC", "isDraft",
|
||||
"isEasyReader", "isLarge", "language", "name", "trackKind",
|
||||
"videoId", "onBehalfOf", "onBehalfOfContentOwner", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"file": {
|
||||
Type: "string",
|
||||
Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"audioTrackType": {
|
||||
Type: "string",
|
||||
Description: attUsage,
|
||||
Default: json.RawMessage(`"unknown"`),
|
||||
},
|
||||
"isAutoSynced": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iasUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isCC": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iscUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isDraft": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: isdUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isEasyReader": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: iserUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"isLarge": {
|
||||
Type: "string",
|
||||
Enum: []any{"true", "false", ""},
|
||||
Description: islUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string",
|
||||
Description: languageUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"name": {
|
||||
Type: "string",
|
||||
Description: nameUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"trackKind": {
|
||||
Type: "string",
|
||||
Enum: []any{"standard", "ASR", "forced"},
|
||||
Description: tkUsage,
|
||||
Default: json.RawMessage(`"standard"`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string",
|
||||
Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOf": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string",
|
||||
Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage,
|
||||
Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string",
|
||||
Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "caption-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
captionCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringVarP(&file, "file", "f", "", fileUsage)
|
||||
@@ -65,99 +188,24 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"caption-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"audioTrackType", mcp.DefaultString("unknown"),
|
||||
mcp.Description(attUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isAutoSynced", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iasUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isCC", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iscUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isDraft", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(isdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isEasyReader", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(iserUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"isLarge", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(islUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(languageUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"name", mcp.DefaultString(""),
|
||||
mcp.Description(nameUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"trackKind", mcp.Enum("standard", "ASR", "forced"),
|
||||
mcp.DefaultString("standard"), mcp.Description(tkUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOf", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
file, _ = args["file"].(string)
|
||||
audioTrackType, _ = args["audioTrackType"].(string)
|
||||
isAutoSyncedRaw, _ := args["isAutoSynced"].(string)
|
||||
isAutoSynced = utils.BoolPtr(isAutoSyncedRaw)
|
||||
isCCRaw, _ := args["isCC"].(string)
|
||||
isCC = utils.BoolPtr(isCCRaw)
|
||||
isDraftRaw, _ := args["isDraft"].(string)
|
||||
isDraft = utils.BoolPtr(isDraftRaw)
|
||||
isEasyReaderRaw, _ := args["isEasyReader"].(string)
|
||||
isEasyReader = utils.BoolPtr(isEasyReaderRaw)
|
||||
isLargeRaw, _ := args["isLarge"].(string)
|
||||
isLarge = utils.BoolPtr(isLargeRaw)
|
||||
language, _ = args["language"].(string)
|
||||
name, _ = args["name"].(string)
|
||||
trackKind, _ = args["trackKind"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
onBehalfOf, _ = args["onBehalfOf"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
file = input.File
|
||||
audioTrackType = input.AudioTrackType
|
||||
isAutoSynced = utils.BoolPtr(*input.IsAutoSynced)
|
||||
isCC = utils.BoolPtr(*input.IsCC)
|
||||
isDraft = utils.BoolPtr(*input.IsDraft)
|
||||
isEasyReader = utils.BoolPtr(*input.IsEasyReader)
|
||||
isLarge = utils.BoolPtr(*input.IsLarge)
|
||||
language = input.Language
|
||||
name = input.Name
|
||||
trackKind = input.TrackKind
|
||||
videoId = input.VideoId
|
||||
onBehalfOf = input.OnBehalfOf
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "caption update started")
|
||||
|
||||
@@ -165,17 +213,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "caption update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "caption update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "caption update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -14,7 +14,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/channel",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@com_github_spf13_pflag//:pflag",
|
||||
],
|
||||
|
||||
+118
-101
@@ -3,6 +3,7 @@ package channel
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channel"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,8 +22,104 @@ const (
|
||||
listIdsUsage = "Return the channels with the specified IDs"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
CategoryId string `json:"categoryId"`
|
||||
ForHandle string `json:"forHandle"`
|
||||
ForUsername string `json:"forUsername"`
|
||||
Hl string `json:"hl"`
|
||||
Ids []string `json:"ids"`
|
||||
ManagedByMe *string `json:"managedByMe,omitempty"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
MySubscribers *string `json:"mySubscribers,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"categoryId", "forHandle", "forUsername", "hl", "ids",
|
||||
"managedByMe", "maxResults", "mine", "mySubscribers",
|
||||
"onBehalfOfContentOwner", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"categoryId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forHandle": {
|
||||
Type: "string", Description: fhUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forUsername": {
|
||||
Type: "string", Description: fuUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"managedByMe": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mbmUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"mySubscribers": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: msUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","status"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "channel-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
channelCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVarP(
|
||||
@@ -68,101 +166,22 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"channel-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"categoryId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forHandle", mcp.DefaultString(""),
|
||||
mcp.Description(fhUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forUsername", mcp.DefaultString(""),
|
||||
mcp.Description(fuUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"managedByMe", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mbmUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mineUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mySubscribers", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(msUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet", "status"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
categoryId, _ = args["categoryId"].(string)
|
||||
forHandle, _ = args["forHandle"].(string)
|
||||
forUsername, _ = args["forUsername"].(string)
|
||||
hl, _ = args["hl"].(string)
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
managedByMeRaw, _ := args["managedByMe"].(string)
|
||||
managedByMe = utils.BoolPtr(managedByMeRaw)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
mineRaw, ok := args["mine"].(string)
|
||||
if !ok {
|
||||
mineRaw = "true"
|
||||
}
|
||||
mine = utils.BoolPtr(mineRaw)
|
||||
mySubscribersRaw, _ := args["mySubscribers"].(string)
|
||||
mySubscribers = utils.BoolPtr(mySubscribersRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
categoryId = input.CategoryId
|
||||
forHandle = input.ForHandle
|
||||
forUsername = input.ForUsername
|
||||
hl = input.Hl
|
||||
ids = input.Ids
|
||||
managedByMe = utils.BoolPtr(*input.ManagedByMe)
|
||||
maxResults = input.MaxResults
|
||||
mine = utils.BoolPtr(*input.Mine)
|
||||
mySubscribers = utils.BoolPtr(*input.MySubscribers)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "channel list started")
|
||||
|
||||
@@ -170,17 +189,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "channel list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "channel list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "channel list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+83
-69
@@ -3,13 +3,15 @@ package channel
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channel"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,74 @@ const (
|
||||
updateIdUsage = "ID of the channel to update"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Country string `json:"country"`
|
||||
CustomUrl string `json:"customUrl"`
|
||||
DefaultLanguage string `json:"defaultLanguage"`
|
||||
Description string `json:"description"`
|
||||
Title string `json:"title"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "country", "customUrl", "defaultLanguage",
|
||||
"description", "title", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: updateIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"country": {
|
||||
Type: "string", Description: countryUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"customUrl": {
|
||||
Type: "string", Description: curlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"defaultLanguage": {
|
||||
Type: "string", Description: dlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "channel-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
channelCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringSliceVarP(&ids, "id", "i", []string{}, updateIdUsage)
|
||||
@@ -52,69 +120,17 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"channel-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(updateIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"country", mcp.DefaultString(""),
|
||||
mcp.Description(countryUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"customUrl", mcp.DefaultString(""),
|
||||
mcp.Description(curlUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"defaultLanguage", mcp.DefaultString(""),
|
||||
mcp.Description(dlUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
country, _ = args["country"].(string)
|
||||
customUrl, _ = args["customUrl"].(string)
|
||||
defaultLanguage, _ = args["defaultLanguage"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
title, _ = args["title"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
country = input.Country
|
||||
customUrl = input.CustomUrl
|
||||
defaultLanguage = input.DefaultLanguage
|
||||
description = input.Description
|
||||
title = input.Title
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "channel update started")
|
||||
|
||||
@@ -122,17 +138,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "channel update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "channel update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "channel update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/channelBanner",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+68
-49
@@ -3,18 +3,73 @@ package channelBanner
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channelBanner"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
ChannelId string `json:"channelId"`
|
||||
File string `json:"file"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
OnBehalfOfContentOwnerChannel string `json:"onBehalfOfContentOwnerChannel"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"channelId", "file", "onBehalfOfContentOwner",
|
||||
"onBehalfOfContentOwnerChannel", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"file": {
|
||||
Type: "string", Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwnerChannel": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "channelBanner-insert", Title: short, Description: long,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
channelBannerCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(&channelId, "channelId", "c", "", cidUsage)
|
||||
@@ -45,49 +100,15 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"channelBanner-insert",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwnerChannel", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
channelId, _ = args["channelId"].(string)
|
||||
file, _ = args["file"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
onBehalfOfContentOwnerChannel, _ = args["onBehalfOfContentOwnerChannel"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
channelId = input.ChannelId
|
||||
file = input.File
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
onBehalfOfContentOwnerChannel = input.OnBehalfOfContentOwnerChannel
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "channelBanner insert started")
|
||||
|
||||
@@ -95,17 +116,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "channelBanner insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "channelBanner insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "channelBanner insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
@@ -14,7 +14,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/channelSection",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
@@ -3,12 +3,14 @@ package channelSection
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channelSection"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,43 @@ const (
|
||||
deleteIdsUsage = "Delete the channel sections with the given ids"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "channelSection-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
channelSectionCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -43,34 +80,11 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"channelSection-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "channelSection delete started")
|
||||
|
||||
@@ -78,17 +92,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "channelSection delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "channelSection delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "channelSection delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+86
-70
@@ -3,6 +3,7 @@ package channelSection
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channelSection"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,8 +22,77 @@ const (
|
||||
listIdsUsage = "Return the channel sections with the given ids"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
ChannelId string `json:"channelId"`
|
||||
Hl string `json:"hl"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "channelId", "hl", "mine", "onBehalfOfContentOwner",
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "channelSection-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
channelSectionCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -51,70 +122,17 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"channelSection-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mineUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
channelId, _ = args["channelId"].(string)
|
||||
hl, _ = args["hl"].(string)
|
||||
mineRaw, _ := args["mine"].(string)
|
||||
mine = utils.BoolPtr(mineRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
channelId = input.ChannelId
|
||||
hl = input.Hl
|
||||
mine = utils.BoolPtr(*input.Mine)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "channelSection list started")
|
||||
|
||||
@@ -122,17 +140,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "channelSection list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "channelSection list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "channelSection list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -18,7 +18,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/comment",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+38
-29
@@ -3,12 +3,14 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -17,8 +19,36 @@ const (
|
||||
deleteLong = "Delete YouTube comments by ids"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"ids"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
commentCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
@@ -38,29 +68,10 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"comment-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.IDs
|
||||
|
||||
slog.InfoContext(ctx, "comment delete started")
|
||||
|
||||
@@ -68,17 +79,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+80
-60
@@ -3,6 +3,7 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,8 +22,71 @@ const (
|
||||
insertPidUsage = "ID of the parent comment"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
AuthorChannelId string `json:"authorChannelId"`
|
||||
ChannelId string `json:"channelId"`
|
||||
CanRate string `json:"canRate"`
|
||||
ParentId string `json:"parentId"`
|
||||
TextOriginal string `json:"textOriginal"`
|
||||
VideoId string `json:"videoId"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"authorChannelId", "channelId", "canRate", "parentId",
|
||||
"textOriginal", "videoId", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"authorChannelId": {
|
||||
Type: "string", Description: acidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"canRate": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: crUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parentId": {
|
||||
Type: "string", Description: insertPidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"textOriginal": {
|
||||
Type: "string", Description: toUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string", Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
commentCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(
|
||||
@@ -61,60 +126,17 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"comment-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"authorChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(acidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"canRate", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(crUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"parentId", mcp.DefaultString(""),
|
||||
mcp.Description(insertPidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"textOriginal", mcp.DefaultString(""),
|
||||
mcp.Description(toUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
authorChannelId, _ = args["authorChannelId"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
canRateRaw, _ := args["canRate"].(string)
|
||||
canRate = utils.BoolPtr(canRateRaw)
|
||||
parentId, _ = args["parentId"].(string)
|
||||
textOriginal, _ = args["textOriginal"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
authorChannelId = input.AuthorChannelId
|
||||
channelId = input.ChannelId
|
||||
canRate = utils.BoolPtr(input.CanRate)
|
||||
parentId = input.ParentId
|
||||
textOriginal = input.TextOriginal
|
||||
videoId = input.VideoId
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "comment insert started")
|
||||
|
||||
@@ -122,17 +144,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+81
-66
@@ -3,13 +3,15 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,73 @@ const (
|
||||
listPidUsage = "Returns replies to the specified comment"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
IDs []string `json:"ids"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
ParentId string `json:"parentId"`
|
||||
TextFormat string `json:"textFormat"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "maxResults", "parentId", "textFormat",
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"parentId": {
|
||||
Type: "string", Description: listPidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"textFormat": {
|
||||
Type: "string", Enum: []any{"textFormatUnspecified", "html", "plainText"},
|
||||
Description: tfUsage, Default: json.RawMessage(`"html"`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
commentCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
@@ -51,66 +118,16 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"comment-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"parentId", mcp.DefaultString(""),
|
||||
mcp.Description(listPidUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"textFormat", mcp.Enum("textFormatUnspecified", "html", "plainText"),
|
||||
mcp.DefaultString("html"), mcp.Description(tfUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
parentId, _ = args["parentId"].(string)
|
||||
textFormat, _ = args["textFormat"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.IDs
|
||||
maxResults = input.MaxResults
|
||||
parentId = input.ParentId
|
||||
textFormat = input.TextFormat
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "comment list started")
|
||||
|
||||
@@ -118,17 +135,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+50
-39
@@ -3,13 +3,15 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,46 @@ const (
|
||||
masLong = "Mark YouTube comments as spam by ids"
|
||||
)
|
||||
|
||||
type markAsSpamIn struct {
|
||||
IDs []string `json:"ids"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var markAsSpamInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"ids", "output", "jsonpath"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(markAsSpamTool, markAsSpamHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-markAsSpam", Title: masShort, Description: masLong,
|
||||
InputSchema: markAsSpamInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, markAsSpamHandler,
|
||||
)
|
||||
commentCmd.AddCommand(markAsSpamCmd)
|
||||
|
||||
markAsSpamCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
@@ -42,39 +82,12 @@ var markAsSpamCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var markAsSpamTool = mcp.NewTool(
|
||||
"comment-markAsSpam",
|
||||
mcp.WithTitleAnnotation(masShort),
|
||||
mcp.WithDescription(masLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func markAsSpamHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input markAsSpamIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.IDs
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "comment markAsSpam started")
|
||||
|
||||
@@ -82,17 +95,15 @@ func markAsSpamHandler(
|
||||
err := markAsSpam(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment markAsSpam failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment markAsSpam failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment markAsSpam completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func markAsSpam(writer io.Writer) error {
|
||||
|
||||
@@ -3,6 +3,7 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,60 @@ const (
|
||||
smsLong = "Set YouTube comments moderation status by ids"
|
||||
)
|
||||
|
||||
type setModerationStatusIn struct {
|
||||
IDs []string `json:"ids"`
|
||||
ModerationStatus string `json:"moderationStatus"`
|
||||
BanAuthor string `json:"banAuthor"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var setModerationStatusInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "moderationStatus", "banAuthor",
|
||||
"output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"moderationStatus": {
|
||||
Type: "string", Enum: []any{"heldForReview", "published", "rejected", ""},
|
||||
Description: msUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"banAuthor": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: baUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(setModerationStatusTool, setModerationStatusHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-setModerationStatus", Title: smsShort, Description: smsLong,
|
||||
InputSchema: setModerationStatusInSchema,
|
||||
Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, setModerationStatusHandler,
|
||||
)
|
||||
commentCmd.AddCommand(setModerationStatusCmd)
|
||||
|
||||
setModerationStatusCmd.Flags().StringSliceVarP(
|
||||
@@ -56,50 +110,14 @@ var setModerationStatusCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var setModerationStatusTool = mcp.NewTool(
|
||||
"comment-setModerationStatus",
|
||||
mcp.WithTitleAnnotation(smsShort),
|
||||
mcp.WithDescription(smsLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"moderationStatus", mcp.Enum("heldForReview", "published", "rejected"),
|
||||
mcp.DefaultString(""), mcp.Description(msUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"banAuthor", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(baUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func setModerationStatusHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
moderationStatus, _ = args["moderationStatus"].(string)
|
||||
banAuthorRaw, _ := args["banAuthor"].(string)
|
||||
banAuthor = utils.BoolPtr(banAuthorRaw)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input setModerationStatusIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.IDs
|
||||
moderationStatus = input.ModerationStatus
|
||||
banAuthor = utils.BoolPtr(input.BanAuthor)
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "comment setModerationStatus started")
|
||||
|
||||
@@ -107,17 +125,15 @@ func setModerationStatusHandler(
|
||||
err := setModerationStatus(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment setModerationStatus failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment setModerationStatus failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment setModerationStatus completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func setModerationStatus(writer io.Writer) error {
|
||||
|
||||
+71
-55
@@ -3,6 +3,7 @@ package comment
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/comment"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,8 +22,64 @@ const (
|
||||
updateIdUsage = "ID of the comment"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
IDs []string `json:"ids"`
|
||||
CanRate string `json:"canRate"`
|
||||
TextOriginal string `json:"textOriginal"`
|
||||
ViewerRating string `json:"viewerRating"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "canRate", "textOriginal", "viewerRating",
|
||||
"output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: updateIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"canRate": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: crUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"textOriginal": {
|
||||
Type: "string", Description: toUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"viewerRating": {
|
||||
Type: "string", Enum: []any{"none", "like", "dislike", ""},
|
||||
Description: vrUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "comment-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
commentCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringSliceVarP(&ids, "id", "i", []string{}, updateIdUsage)
|
||||
@@ -51,55 +109,15 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"comment-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(updateIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"canRate", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(crUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"textOriginal", mcp.DefaultString(""),
|
||||
mcp.Description(toUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"viewerRating", mcp.Enum("none", "like", "dislike"),
|
||||
mcp.DefaultString(""), mcp.Description(vrUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
canRateRaw, _ := args["canRate"].(string)
|
||||
canRate = utils.BoolPtr(canRateRaw)
|
||||
textOriginal, _ = args["textOriginal"].(string)
|
||||
viewerRating, _ = args["viewerRating"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.IDs
|
||||
canRate = utils.BoolPtr(input.CanRate)
|
||||
textOriginal = input.TextOriginal
|
||||
viewerRating = input.ViewerRating
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "comment update started")
|
||||
|
||||
@@ -107,17 +125,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "comment update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "comment update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "comment update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -13,7 +13,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/commentThread",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+68
-49
@@ -3,13 +3,15 @@ package commentThread
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/commentThread"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,61 @@ const (
|
||||
insertVidUsage = "ID of the video"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
AuthorChannelId string `json:"authorChannelId"`
|
||||
ChannelId string `json:"channelId"`
|
||||
TextOriginal string `json:"textOriginal"`
|
||||
VideoId string `json:"videoId"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"authorChannelId", "channelId", "textOriginal", "videoId",
|
||||
"output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"authorChannelId": {
|
||||
Type: "string", Description: acidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"textOriginal": {
|
||||
Type: "string", Description: toUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string", Description: insertVidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "commentThread-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
commentThreadCmd.AddCommand(insertCmd)
|
||||
insertCmd.Flags().StringVarP(
|
||||
&authorChannelId, "authorChannelId", "a", "", acidUsage,
|
||||
@@ -50,49 +105,15 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"commentThread-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"authorChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(acidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"textOriginal", mcp.DefaultString(""),
|
||||
mcp.Description(toUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(insertVidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
authorChannelId, _ = args["authorChannelId"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
textOriginal, _ = args["textOriginal"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
authorChannelId = input.AuthorChannelId
|
||||
channelId = input.ChannelId
|
||||
textOriginal = input.TextOriginal
|
||||
videoId = input.VideoId
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "commentThread insert started")
|
||||
|
||||
@@ -100,17 +121,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "commentThread insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "commentThread insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "commentThread insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+113
-91
@@ -3,13 +3,15 @@ package commentThread
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/commentThread"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,100 @@ const (
|
||||
listVidUsage = "Returns the comment threads of the specified video"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
AllThreadsRelatedToChannelId string `json:"allThreadsRelatedToChannelId"`
|
||||
ChannelId string `json:"channelId"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
ModerationStatus string `json:"moderationStatus"`
|
||||
Order string `json:"order"`
|
||||
SearchTerms string `json:"searchTerms"`
|
||||
TextFormat string `json:"textFormat"`
|
||||
VideoId string `json:"videoId"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "allThreadsRelatedToChannelId", "channelId", "maxResults",
|
||||
"moderationStatus", "order", "searchTerms", "textFormat",
|
||||
"videoId", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"allThreadsRelatedToChannelId": {
|
||||
Type: "string", Description: atrtcidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"moderationStatus": {
|
||||
Type: "string",
|
||||
Enum: []any{"published", "heldForReview", "likelySpam", "rejected"},
|
||||
Description: msUsage, Default: json.RawMessage(`"published"`),
|
||||
},
|
||||
"order": {
|
||||
Type: "string", Enum: []any{"orderUnspecified", "time", "relevance"},
|
||||
Description: orderUsage, Default: json.RawMessage(`"time"`),
|
||||
},
|
||||
"searchTerms": {
|
||||
Type: "string", Description: stUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"textFormat": {
|
||||
Type: "string", Enum: []any{"textFormatUnspecified", "html"},
|
||||
Description: tfUsage, Default: json.RawMessage(`"html"`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string", Description: listVidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "commentThread-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
commentThreadCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
@@ -57,91 +151,21 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"commentThread-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"allThreadsRelatedToChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(atrtcidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"moderationStatus",
|
||||
mcp.Enum("published", "heldForReview", "likelySpam", "rejected"),
|
||||
mcp.DefaultString("published"), mcp.Description(msUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"order", mcp.Enum("orderUnspecified", "time", "relevance"),
|
||||
mcp.DefaultString("time"), mcp.Description(orderUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"searchTerms", mcp.DefaultString(""),
|
||||
mcp.Description(stUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"textFormat", mcp.Enum("textFormatUnspecified", "html"),
|
||||
mcp.DefaultString("html"), mcp.Description(tfUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(listVidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
allThreadsRelatedToChannelId, _ = args["allThreadsRelatedToChannelId"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
moderationStatus, _ = args["moderationStatus"].(string)
|
||||
order, _ = args["order"].(string)
|
||||
searchTerms, _ = args["searchTerms"].(string)
|
||||
textFormat, _ = args["textFormat"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
allThreadsRelatedToChannelId = input.AllThreadsRelatedToChannelId
|
||||
channelId = input.ChannelId
|
||||
maxResults = input.MaxResults
|
||||
moderationStatus = input.ModerationStatus
|
||||
order = input.Order
|
||||
searchTerms = input.SearchTerms
|
||||
textFormat = input.TextFormat
|
||||
videoId = input.VideoId
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "commentThread list started")
|
||||
|
||||
@@ -149,17 +173,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "commentThread list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "commentThread list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "commentThread list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -13,7 +13,7 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/i18nLanguage",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+42
-41
@@ -10,13 +10,33 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/i18nLanguage"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddResource(hlResource, hlHandler)
|
||||
cmd.MCP.AddResourceTemplate(langsResource, langsHandler)
|
||||
cmd.Server.AddResource(
|
||||
&mcp.Resource{
|
||||
URI: hlURI,
|
||||
Name: hlName,
|
||||
Description: hlDesc,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
Annotations: &mcp.Annotations{
|
||||
Audience: []mcp.Role{"user", "assistant"}, Priority: 0.51,
|
||||
},
|
||||
}, hlHandler,
|
||||
)
|
||||
cmd.Server.AddResourceTemplate(
|
||||
&mcp.ResourceTemplate{
|
||||
Name: langName,
|
||||
Description: long,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
URITemplate: langURI,
|
||||
Annotations: &mcp.Annotations{
|
||||
Audience: []mcp.Role{"user", "assistant"}, Priority: 0.51,
|
||||
},
|
||||
}, langsHandler,
|
||||
)
|
||||
i18nLanguageCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringVarP(&hl, "hl", "l", "", hlUsage)
|
||||
listCmd.Flags().StringSliceVarP(
|
||||
@@ -39,16 +59,9 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var hlResource = mcp.NewResource(
|
||||
hlURI, hlName,
|
||||
mcp.WithMIMEType(pkg.JsonMIME),
|
||||
mcp.WithResourceDescription(hlDesc),
|
||||
mcp.WithAnnotations([]mcp.Role{"user", "assistant"}, 0.51),
|
||||
)
|
||||
|
||||
func hlHandler(
|
||||
ctx context.Context, request mcp.ReadResourceRequest,
|
||||
) ([]mcp.ResourceContents, error) {
|
||||
ctx context.Context, request *mcp.ReadResourceRequest,
|
||||
) (*mcp.ReadResourceResult, error) {
|
||||
parts = defaultParts
|
||||
output = "json"
|
||||
jpath = "$.*.snippet.hl"
|
||||
@@ -59,9 +72,8 @@ func hlHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "i18nLanguage hl list failed",
|
||||
"error", err,
|
||||
"uri", request.Params.URI,
|
||||
ctx, "i18nLanguage hl list failed", "error", err, "uri",
|
||||
request.Params.URI,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,26 +83,18 @@ func hlHandler(
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
|
||||
contents := []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: hlURI,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
Text: writer.String(),
|
||||
return &mcp.ReadResourceResult{
|
||||
Contents: []*mcp.ResourceContents{
|
||||
{
|
||||
URI: hlURI, MIMEType: pkg.JsonMIME, Text: writer.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return contents, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
var langsResource = mcp.NewResourceTemplate(
|
||||
langURI, langName,
|
||||
mcp.WithTemplateMIMEType(pkg.JsonMIME),
|
||||
mcp.WithTemplateDescription(long),
|
||||
mcp.WithTemplateAnnotations([]mcp.Role{"user", "assistant"}, 0.51),
|
||||
)
|
||||
|
||||
func langsHandler(
|
||||
ctx context.Context, request mcp.ReadResourceRequest,
|
||||
) ([]mcp.ResourceContents, error) {
|
||||
ctx context.Context, request *mcp.ReadResourceRequest,
|
||||
) (*mcp.ReadResourceResult, error) {
|
||||
parts = defaultParts
|
||||
hl = utils.ExtractHl(request.Params.URI)
|
||||
output = "json"
|
||||
@@ -101,9 +105,7 @@ func langsHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "i18nLanguage list failed",
|
||||
"error", err,
|
||||
"uri", request.Params.URI,
|
||||
ctx, "i18nLanguage list failed", "error", err, "uri", request.Params.URI,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
@@ -113,14 +115,13 @@ func langsHandler(
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
|
||||
contents := []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: request.Params.URI,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
Text: writer.String(),
|
||||
return &mcp.ReadResourceResult{
|
||||
Contents: []*mcp.ResourceContents{
|
||||
{
|
||||
URI: request.Params.URI, MIMEType: pkg.JsonMIME, Text: writer.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return contents, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -13,7 +13,7 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/i18nRegion",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+21
-21
@@ -10,12 +10,22 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/i18nRegion"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddResourceTemplate(RegionsResource, regionsHandler)
|
||||
cmd.Server.AddResourceTemplate(
|
||||
&mcp.ResourceTemplate{
|
||||
Name: regionName,
|
||||
Description: long,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
URITemplate: regionURI,
|
||||
Annotations: &mcp.Annotations{
|
||||
Audience: []mcp.Role{"user", "assistant"}, Priority: 0.51,
|
||||
},
|
||||
}, regionsHandler,
|
||||
)
|
||||
i18nRegionCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringVarP(&hl, "hl", "l", "", hlUsage)
|
||||
listCmd.Flags().StringSliceVarP(
|
||||
@@ -38,16 +48,9 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var RegionsResource = mcp.NewResourceTemplate(
|
||||
regionURI, regionName,
|
||||
mcp.WithTemplateMIMEType(pkg.JsonMIME),
|
||||
mcp.WithTemplateDescription(long),
|
||||
mcp.WithTemplateAnnotations([]mcp.Role{"user", "assistant"}, 0.51),
|
||||
)
|
||||
|
||||
func regionsHandler(
|
||||
ctx context.Context, request mcp.ReadResourceRequest,
|
||||
) ([]mcp.ResourceContents, error) {
|
||||
ctx context.Context, request *mcp.ReadResourceRequest,
|
||||
) (*mcp.ReadResourceResult, error) {
|
||||
parts = defaultParts
|
||||
hl = utils.ExtractHl(request.Params.URI)
|
||||
output = "json"
|
||||
@@ -58,9 +61,7 @@ func regionsHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "i18nRegion list failed",
|
||||
"error", err,
|
||||
"uri", request.Params.URI,
|
||||
ctx, "i18nRegion list failed", "error", err, "uri", request.Params.URI,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
@@ -70,14 +71,13 @@ func regionsHandler(
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
|
||||
contents := []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: request.Params.URI,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
Text: writer.String(),
|
||||
return &mcp.ReadResourceResult{
|
||||
Contents: []*mcp.ResourceContents{
|
||||
{
|
||||
URI: request.Params.URI, MIMEType: pkg.JsonMIME, Text: writer.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return contents, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+23
-22
@@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -23,12 +24,14 @@ var (
|
||||
port int
|
||||
)
|
||||
|
||||
var MCP = server.NewMCPServer(
|
||||
"yutu", Version,
|
||||
server.WithToolCapabilities(true),
|
||||
server.WithResourceCapabilities(true, true),
|
||||
server.WithLogging(),
|
||||
server.WithRecovery(),
|
||||
var Server = mcp.NewServer(
|
||||
&mcp.Implementation{Name: "yutu", Version: Version},
|
||||
&mcp.ServerOptions{
|
||||
PageSize: 99,
|
||||
KeepAlive: 13 * time.Second,
|
||||
HasResources: true,
|
||||
HasTools: true,
|
||||
},
|
||||
)
|
||||
|
||||
var mcpCmd = &cobra.Command{
|
||||
@@ -36,9 +39,8 @@ var mcpCmd = &cobra.Command{
|
||||
Short: mcpShort,
|
||||
Long: mcpLong,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := context.Background()
|
||||
var err error
|
||||
interval := 13 * time.Second
|
||||
ctx := context.Background()
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
slog.InfoContext(
|
||||
ctx, "starting MCP server",
|
||||
@@ -48,33 +50,32 @@ var mcpCmd = &cobra.Command{
|
||||
|
||||
switch mode {
|
||||
case "stdio":
|
||||
err = server.ServeStdio(MCP)
|
||||
t := &mcp.LoggingTransport{
|
||||
Transport: &mcp.StdioTransport{},
|
||||
Writer: os.Stderr,
|
||||
}
|
||||
err = Server.Run(ctx, t)
|
||||
case "http":
|
||||
httpServer := server.NewStreamableHTTPServer(
|
||||
MCP,
|
||||
server.WithHeartbeatInterval(interval),
|
||||
handler := mcp.NewStreamableHTTPHandler(
|
||||
func(*http.Request) *mcp.Server {
|
||||
return Server
|
||||
}, nil,
|
||||
)
|
||||
|
||||
slog.InfoContext(
|
||||
ctx, "http server configuration",
|
||||
"url", fmt.Sprintf("http://localhost:%d/mcp", port),
|
||||
"heartbeat_interval", interval,
|
||||
)
|
||||
err = httpServer.Start(addr)
|
||||
err = http.ListenAndServe(addr, handler)
|
||||
default:
|
||||
slog.ErrorContext(
|
||||
ctx, "invalid server mode",
|
||||
"mode", mode,
|
||||
"valid_modes", []string{"stdio", "http"},
|
||||
ctx, "invalid mode", "mode", mode, "valid_modes", "stdio, http",
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "starting server failed",
|
||||
"error", err,
|
||||
"mode", mode,
|
||||
ctx, "starting server failed", "error", err, "mode", mode,
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/member",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+79
-60
@@ -3,18 +3,83 @@ package member
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/member"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
MemberChannelId string `json:"memberChannelId"`
|
||||
HasAccessToLevel string `json:"hasAccessToLevel"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mode string `json:"mode"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"memberChannelId", "hasAccessToLevel", "maxResults", "mode",
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"memberChannelId": {
|
||||
Type: "string", Description: mcidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hasAccessToLevel": {
|
||||
Type: "string", Description: hatlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mode": {
|
||||
Type: "string",
|
||||
Enum: []any{"listMembersModeUnknown", "updates", "all_current"},
|
||||
Description: mmUsage, Default: json.RawMessage(`"all_current"`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "member-list", Title: short, Description: long,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
memberCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVarP(
|
||||
@@ -45,60 +110,16 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"member-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"memberChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(mcidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"hasAccessToLevel", mcp.DefaultString(""),
|
||||
mcp.Description(hatlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mode", mcp.Enum("listMembersModeUnknown", "updates", "all_current"),
|
||||
mcp.DefaultString("all_current"), mcp.Description(mmUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
memberChannelId, _ = args["memberChannelId"].(string)
|
||||
hasAccessToLevel, _ = args["hasAccessToLevel"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
mode, _ = args["mode"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
memberChannelId = input.MemberChannelId
|
||||
hasAccessToLevel = input.HasAccessToLevel
|
||||
maxResults = input.MaxResults
|
||||
mode = input.Mode
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "member list started")
|
||||
|
||||
@@ -106,17 +127,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "member list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "member list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "member list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/membershipsLevel",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
@@ -3,22 +3,64 @@ package membershipsLevel
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/membershipsLevel"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id", "snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "membershipsLevel-list", Title: short, Description: long,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
membershipsLevelCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(
|
||||
&parts, "parts", "p", []string{"id, snippet"}, pkg.PartsUsage,
|
||||
&parts, "parts", "p", []string{"id", "snippet"}, pkg.PartsUsage,
|
||||
)
|
||||
listCmd.Flags().StringVarP(&output, "output", "o", "table", pkg.TableUsage)
|
||||
listCmd.Flags().StringVarP(&jpath, "jsonpath", "j", "", pkg.JPUsage)
|
||||
@@ -37,39 +79,12 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"membershipsLevel-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id, snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "membershipsLevel list started")
|
||||
|
||||
@@ -77,17 +92,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "membershipsLevel list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "membershipsLevel list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "membershipsLevel list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -16,7 +16,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/playlist",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+46
-34
@@ -3,12 +3,14 @@ package playlist
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlist"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,43 @@ const (
|
||||
deleteIdsUsage = "IDs of the playlists to delete"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlist-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
playlistCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -43,34 +80,11 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"playlist-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "playlist delete started")
|
||||
|
||||
@@ -78,17 +92,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlist delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlist delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlist delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+83
-64
@@ -3,13 +3,15 @@ package playlist
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlist"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,74 @@ const (
|
||||
insertCidUsage = "Channel id of the playlist"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags"`
|
||||
Language string `json:"language"`
|
||||
ChannelId string `json:"channelId"`
|
||||
Privacy string `json:"privacy"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"title", "description", "tags", "language",
|
||||
"channelId", "privacy", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tags": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: tagsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string", Description: languageUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: insertCidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlist-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
playlistCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(&title, "title", "t", "", titleUsage)
|
||||
@@ -50,64 +118,17 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"playlist-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"tags", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(tagsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(languageUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(insertCidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
tagsRaw, _ := args["tags"].([]any)
|
||||
tags = make([]string, len(tagsRaw))
|
||||
for i, tag := range tagsRaw {
|
||||
tags[i] = tag.(string)
|
||||
}
|
||||
language, _ = args["language"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
tags = input.Tags
|
||||
language = input.Language
|
||||
channelId = input.ChannelId
|
||||
privacy = input.Privacy
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlist insert started")
|
||||
|
||||
@@ -115,17 +136,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlist insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlist insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlist insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+100
-84
@@ -3,6 +3,7 @@ package playlist
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlist"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -21,8 +23,89 @@ const (
|
||||
listCidUsage = "Return the playlists owned by the specified channel id"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
ChannelId string `json:"channelId"`
|
||||
Hl string `json:"hl"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
OnBehalfOfContentOwnerChannel string `json:"onBehalfOfContentOwnerChannel"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "channelId", "hl", "maxResults", "mine",
|
||||
"onBehalfOfContentOwner", "onBehalfOfContentOwnerChannel",
|
||||
"parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: listCidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwnerChannel": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","status"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlist-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
playlistCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -56,84 +139,19 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"playlist-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(listCidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mineUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwnerChannel", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet", "status"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids := make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
channelId, _ = args["channelId"].(string)
|
||||
hl, _ = args["hl"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
mineRaw, ok := args["mine"].(string)
|
||||
if !ok {
|
||||
mineRaw = "true"
|
||||
}
|
||||
mine = utils.BoolPtr(mineRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
onBehalfOfContentOwnerChannel, _ = args["onBehalfOfContentOwnerChannel"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
channelId = input.ChannelId
|
||||
hl = input.Hl
|
||||
maxResults = input.MaxResults
|
||||
mine = utils.BoolPtr(*input.Mine)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
onBehalfOfContentOwnerChannel = input.OnBehalfOfContentOwnerChannel
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlist list started")
|
||||
|
||||
@@ -141,17 +159,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlist list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlist list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlist list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+86
-69
@@ -3,13 +3,15 @@ package playlist
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlist"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,77 @@ const (
|
||||
updateIdUsage = "ID of the playlist to update"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags"`
|
||||
Language string `json:"language"`
|
||||
Privacy string `json:"privacy"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "title", "description", "tags",
|
||||
"language", "privacy", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: updateIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tags": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: tagsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string", Description: languageUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlist-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
playlistCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringSliceVarP(&ids, "id", "i", []string{}, updateIdUsage)
|
||||
@@ -48,69 +119,17 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"playlist-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(updateIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"tags", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(tagsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(languageUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
tagsRaw, _ := args["tags"].([]any)
|
||||
tags = make([]string, len(tagsRaw))
|
||||
for i, tag := range tagsRaw {
|
||||
tags[i] = tag.(string)
|
||||
}
|
||||
language, _ = args["language"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
tags = input.Tags
|
||||
language = input.Language
|
||||
privacy = input.Privacy
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlist update started")
|
||||
|
||||
@@ -118,17 +137,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlist update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlist update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlist update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -15,7 +15,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/playlistImage",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+46
-34
@@ -3,22 +3,59 @@ package playlistImage
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistImage"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: idsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
deleteShort = "Delete YouTube playlist images"
|
||||
deleteLong = "Delete YouTube playlist images by ids"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistImage-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
playlistImageCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
@@ -42,34 +79,11 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"playlistImage-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(idsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "playlistImage delete started")
|
||||
|
||||
@@ -77,17 +91,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistImage delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistImage delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistImage delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+75
-56
@@ -3,23 +3,84 @@ package playlistImage
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistImage"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
File string `json:"file"`
|
||||
PlaylistId string `json:"playlistId"`
|
||||
Type string `json:"type"`
|
||||
Height int64 `json:"height"`
|
||||
Width int64 `json:"width"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"file", "playlistId", "type", "height", "width", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"file": {
|
||||
Type: "string", Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"playlistId": {
|
||||
Type: "string", Description: pidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"type": {
|
||||
Type: "string", Description: typeUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"height": {
|
||||
Type: "number", Description: heightUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"width": {
|
||||
Type: "number", Description: widthUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
insertShort = "Insert a YouTube playlist image"
|
||||
insertLong = "Insert a YouTube playlist image for a given playlist id"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistImage-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
playlistImageCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(&file, "file", "f", "", fileUsage)
|
||||
@@ -47,56 +108,16 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"playlistImage-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(pidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"type", mcp.DefaultString(""),
|
||||
mcp.Description(typeUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"height", mcp.DefaultNumber(0),
|
||||
mcp.Description(heightUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"width", mcp.DefaultNumber(0),
|
||||
mcp.Description(widthUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
file, _ = args["file"].(string)
|
||||
playlistId, _ = args["playlistId"].(string)
|
||||
type_, _ = args["type"].(string)
|
||||
heightRaw, _ := args["height"].(float64)
|
||||
height = int64(heightRaw)
|
||||
widthRaw, _ := args["width"].(float64)
|
||||
width = int64(widthRaw)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
file = input.File
|
||||
playlistId = input.PlaylistId
|
||||
type_ = input.Type
|
||||
height = input.Height
|
||||
width = input.Width
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlistImage insert started")
|
||||
|
||||
@@ -104,17 +125,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistImage insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistImage insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistImage insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+78
-61
@@ -3,25 +3,88 @@ package playlistImage
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistImage"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Parent string `json:"parent"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
OnBehalfOfContentOwnerChannel string `json:"onBehalfOfContentOwnerChannel"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"parent", "maxResults", "parts", "output", "jsonpath",
|
||||
"onBehalfOfContentOwner", "onBehalfOfContentOwnerChannel",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"parent": {
|
||||
Type: "string", Description: parentUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","kind","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwnerChannel": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
listShort = "List YouTube playlist images"
|
||||
listLong = "List YouTube playlist images' info"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistImage-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
playlistImageCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVarP(&parent, "parent", "P", "", parentUsage)
|
||||
listCmd.Flags().Int64VarP(&maxResults, "maxResults", "n", 5, pkg.MRUsage)
|
||||
listCmd.Flags().StringSliceVarP(
|
||||
@@ -50,60 +113,16 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"playlistImage-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"parent", mcp.DefaultString(""),
|
||||
mcp.Description(parentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "kind", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwnerChannel", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
parent, _ = args["parent"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
onBehalfOfContentOwnerChannel, _ = args["onBehalfOfContentOwnerChannel"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
parent = input.Parent
|
||||
maxResults = input.MaxResults
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
onBehalfOfContentOwnerChannel = input.OnBehalfOfContentOwnerChannel
|
||||
|
||||
slog.InfoContext(ctx, "playlistImage list started")
|
||||
|
||||
@@ -111,17 +130,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistImage list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistImage list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistImage list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+69
-51
@@ -3,23 +3,79 @@ package playlistImage
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistImage"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
PlaylistId string `json:"playlistId"`
|
||||
Type string `json:"type"`
|
||||
Height int64 `json:"height"`
|
||||
Width int64 `json:"width"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"playlistId", "type", "height", "width", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"playlistId": {
|
||||
Type: "string", Description: pidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"type": {
|
||||
Type: "string", Description: typeUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"height": {
|
||||
Type: "number", Description: heightUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"width": {
|
||||
Type: "number", Description: widthUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
updateShort = "Update a playlist image"
|
||||
updateLong = "Update a playlist image for a given playlist id"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistImage-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
playlistImageCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringVarP(&playlistId, "playlistId", "p", "", pidUsage)
|
||||
@@ -45,51 +101,15 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"playlistImage-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(pidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"type", mcp.DefaultString(""),
|
||||
mcp.Description(typeUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"height", mcp.DefaultNumber(0),
|
||||
mcp.Description(heightUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"width", mcp.DefaultNumber(0),
|
||||
mcp.Description(widthUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
playlistId, _ = args["playlistId"].(string)
|
||||
type_, _ = args["type"].(string)
|
||||
heightRaw, _ := args["height"].(float64)
|
||||
height = int64(heightRaw)
|
||||
widthRaw, _ := args["width"].(float64)
|
||||
width = int64(widthRaw)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
playlistId = input.PlaylistId
|
||||
type_ = input.Type
|
||||
height = input.Height
|
||||
width = input.Width
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlistImage update started")
|
||||
|
||||
@@ -97,17 +117,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistImage update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistImage update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistImage update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -15,7 +15,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/playlistItem",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+46
-34
@@ -3,12 +3,14 @@ package playlistItem
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistItem"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,43 @@ const (
|
||||
deleteIdsUsage = "IDs of the playlist items to delete"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistItem-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
playlistItemCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -43,34 +80,11 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"playlistItem-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "playlistItem delete started")
|
||||
|
||||
@@ -78,17 +92,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistItem delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistItem delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistItem delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+105
-79
@@ -3,13 +3,15 @@ package playlistItem
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistItem"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,92 @@ const (
|
||||
insertPidUsage = "The id that YouTube uses to uniquely identify the playlist that the item is in"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Kind string `json:"kind"`
|
||||
KVideoId string `json:"kVideoId"`
|
||||
KChannelId string `json:"kChannelId"`
|
||||
KPlaylistId string `json:"kPlaylistId"`
|
||||
PlaylistId string `json:"playlistId"`
|
||||
ChannelId string `json:"channelId"`
|
||||
Privacy string `json:"privacy"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"title", "description", "kind", "kVideoId", "kChannelId",
|
||||
"kPlaylistId", "playlistId", "channelId", "privacy",
|
||||
"onBehalfOfContentOwner", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"kind": {
|
||||
Type: "string", Enum: []any{"video", "channel", "playlist", ""},
|
||||
Description: kindUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"kVideoId": {
|
||||
Type: "string", Description: kvidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"kChannelId": {
|
||||
Type: "string", Description: kcidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"kPlaylistId": {
|
||||
Type: "string", Description: kpidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"playlistId": {
|
||||
Type: "string", Description: insertPidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistItem-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
playlistItemCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(&title, "title", "t", "", titleUsage)
|
||||
@@ -58,79 +144,21 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"playlistItem-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"kind", mcp.Enum("video", "channel", "playlist"),
|
||||
mcp.DefaultString(""), mcp.Description(kindUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"kVideoId", mcp.DefaultString(""),
|
||||
mcp.Description(kvidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"kChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(kcidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"kPlaylistId", mcp.DefaultString(""),
|
||||
mcp.Description(kpidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(insertPidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
kind, _ = args["kind"].(string)
|
||||
kVideoId, _ = args["kVideoId"].(string)
|
||||
kChannelId, _ = args["kChannelId"].(string)
|
||||
kPlaylistId, _ = args["kPlaylistId"].(string)
|
||||
playlistId, _ = args["playlistId"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
kind = input.Kind
|
||||
kVideoId = input.KVideoId
|
||||
kChannelId = input.KChannelId
|
||||
kPlaylistId = input.KPlaylistId
|
||||
playlistId = input.PlaylistId
|
||||
channelId = input.ChannelId
|
||||
privacy = input.Privacy
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlistItem insert started")
|
||||
|
||||
@@ -138,17 +166,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistItem insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistItem insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistItem insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+87
-70
@@ -3,13 +3,15 @@ package playlistItem
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistItem"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,8 +22,78 @@ const (
|
||||
listPidUsage = "Return the playlist items within the given playlist"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
PlaylistId string `json:"playlistId"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
VideoId string `json:"videoId"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "playlistId", "maxResults", "videoId",
|
||||
"onBehalfOfContentOwner", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"playlistId": {
|
||||
Type: "string", Description: listPidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string", Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","status"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistItem-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
playlistItemCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -51,70 +123,17 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"playlistItem-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(listPidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet", "status"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
playlistId, _ = args["playlistId"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
playlistId = input.PlaylistId
|
||||
maxResults = input.MaxResults
|
||||
videoId = input.VideoId
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlistItem list started")
|
||||
|
||||
@@ -122,17 +141,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistItem list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistItem list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistItem list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+77
-59
@@ -3,13 +3,15 @@ package playlistItem
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/playlistItem"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,69 @@ const (
|
||||
updateIdUsage = "ID of the playlist item to update"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Privacy string `json:"privacy"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "title", "description", "privacy",
|
||||
"onBehalfOfContentOwner", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: updateIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "playlistItem-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
playlistItemCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringSliceVarP(&ids, "id", "i", []string{}, updateIdUsage)
|
||||
@@ -49,59 +112,16 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"playlistItem-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(updateIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
privacy = input.Privacy
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "playlistItem update started")
|
||||
|
||||
@@ -109,17 +129,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "playlistItem update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "playlistItem update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "playlistItem update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -13,7 +13,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/search",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+253
-199
@@ -3,6 +3,7 @@ package search
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,12 +11,225 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/search"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
ChannelId string `json:"channelId"`
|
||||
ChannelType string `json:"channelType"`
|
||||
EventType string `json:"eventType"`
|
||||
ForContentOwner *string `json:"forContentOwner,omitempty"`
|
||||
ForDeveloper *string `json:"forDeveloper,omitempty"`
|
||||
ForMine *string `json:"forMine,omitempty"`
|
||||
Location string `json:"location"`
|
||||
LocationRadius string `json:"locationRadius"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Order string `json:"order"`
|
||||
PublishedAfter string `json:"publishedAfter"`
|
||||
PublishedBefore string `json:"publishedBefore"`
|
||||
Q string `json:"q"`
|
||||
RegionCode string `json:"regionCode"`
|
||||
RelevanceLanguage string `json:"relevanceLanguage"`
|
||||
SafeSearch string `json:"safeSearch"`
|
||||
TopicId string `json:"topicId"`
|
||||
Types []string `json:"types"`
|
||||
VideoCaption string `json:"videoCaption"`
|
||||
VideoCategoryId string `json:"videoCategoryId"`
|
||||
VideoDefinition string `json:"videoDefinition"`
|
||||
VideoDimension string `json:"videoDimension"`
|
||||
VideoDuration string `json:"videoDuration"`
|
||||
VideoEmbeddable string `json:"videoEmbeddable"`
|
||||
VideoLicense string `json:"videoLicense"`
|
||||
VideoPaidProductPlacement string `json:"videoPaidProductPlacement"`
|
||||
VideoSyndicated string `json:"videoSyndicated"`
|
||||
VideoType string `json:"videoType"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"channelId", "channelType", "eventType", "forContentOwner", "forDeveloper",
|
||||
"forMine", "location", "locationRadius", "maxResults",
|
||||
"onBehalfOfContentOwner",
|
||||
"order", "publishedAfter", "publishedBefore", "q", "regionCode",
|
||||
"relevanceLanguage", "safeSearch", "topicId", "types", "videoCaption",
|
||||
"videoCategoryId", "videoDefinition", "videoDimension", "videoDuration",
|
||||
"videoEmbeddable", "videoLicense", "videoPaidProductPlacement",
|
||||
"videoSyndicated",
|
||||
"videoType", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"channelId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelType": {
|
||||
Type: "string", Enum: []any{"channelTypeUnspecified", "any", "show"},
|
||||
Description: ctUsage, Default: json.RawMessage(`"channelTypeUnspecified"`),
|
||||
},
|
||||
"eventType": {
|
||||
Type: "string", Enum: []any{"none", "upcoming", "live", "completed"},
|
||||
Description: etUsage, Default: json.RawMessage(`"none"`),
|
||||
},
|
||||
"forContentOwner": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: fcoUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forDeveloper": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: fdUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forMine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: fmUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"location": {
|
||||
Type: "string", Description: locationUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"locationRadius": {
|
||||
Type: "string", Description: lrUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"order": {
|
||||
Type: "string", Description: orderUsage,
|
||||
Default: json.RawMessage(`"relevance"`),
|
||||
},
|
||||
"publishedAfter": {
|
||||
Type: "string", Description: paUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"publishedBefore": {
|
||||
Type: "string", Description: pbUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"q": {
|
||||
Type: "string", Description: qUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"regionCode": {
|
||||
Type: "string", Description: rcUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"relevanceLanguage": {
|
||||
Type: "string", Description: rlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"safeSearch": {
|
||||
Type: "string",
|
||||
Enum: []any{
|
||||
"safeSearchSettingUnspecified", "none", "moderate", "strict",
|
||||
},
|
||||
Description: ssUsage, Default: json.RawMessage(`"moderate"`),
|
||||
},
|
||||
"topicId": {
|
||||
Type: "string", Description: tidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"types": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: typesUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"videoCaption": {
|
||||
Type: "string",
|
||||
Enum: []any{
|
||||
"videoCaptionUnspecified", "any", "closedCaption", "none",
|
||||
},
|
||||
Description: vcUsage, Default: json.RawMessage(`"any"`),
|
||||
},
|
||||
"videoCategoryId": {
|
||||
Type: "string", Description: vcidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoDefinition": {
|
||||
Type: "string", Description: vdeUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoDimension": {
|
||||
Type: "string", Enum: []any{"any", "2d", "3d"},
|
||||
Description: vdiUsage, Default: json.RawMessage(`"any"`),
|
||||
},
|
||||
"videoDuration": {
|
||||
Type: "string",
|
||||
Enum: []any{
|
||||
"videoDurationUnspecified", "any", "short", "medium", "long",
|
||||
},
|
||||
Description: vduUsage, Default: json.RawMessage(`"any"`),
|
||||
},
|
||||
"videoEmbeddable": {
|
||||
Type: "string",
|
||||
Enum: []any{"videoEmbeddableUnspecified", "any", "true", ""},
|
||||
Description: veUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoLicense": {
|
||||
Type: "string", Enum: []any{"any", "youtube", "creativeCommon", ""},
|
||||
Description: vlUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoPaidProductPlacement": {
|
||||
Type: "string",
|
||||
Enum: []any{
|
||||
"videoPaidProductPlacementUnspecified", "any", "true", "",
|
||||
},
|
||||
Description: vpppUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoSyndicated": {
|
||||
Type: "string",
|
||||
Enum: []any{"videoSyndicatedUnspecified", "any", "true", ""},
|
||||
Description: vsUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoType": {
|
||||
Type: "string",
|
||||
Enum: []any{"videoTypeUnspecified", "any", "movie", "episode", ""},
|
||||
Description: vtUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "search-list", Title: short, Description: long,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
searchCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVar(&channelId, "channelId", "", cidUsage)
|
||||
@@ -75,199 +289,41 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"search-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelType",
|
||||
mcp.Enum("channelTypeUnspecified", "any", "show"),
|
||||
mcp.DefaultString("channelTypeUnspecified"),
|
||||
mcp.Description(ctUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"eventType", mcp.Enum("none", "upcoming", "live", "completed"),
|
||||
mcp.DefaultString("none"), mcp.Description(etUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forContentOwner", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(fcoUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forDeveloper", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(fdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forMine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(fmUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"location", mcp.DefaultString(""),
|
||||
mcp.Description(locationUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"locationRadius", mcp.DefaultString(""),
|
||||
mcp.Description(lrUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"order", mcp.DefaultString("relevance"),
|
||||
mcp.Description(orderUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publishedAfter", mcp.DefaultString(""),
|
||||
mcp.Description(paUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publishedBefore", mcp.DefaultString(""),
|
||||
mcp.Description(pbUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"q", mcp.DefaultString(""),
|
||||
mcp.Description(qUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"regionCode", mcp.DefaultString(""),
|
||||
mcp.Description(rcUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"relevanceLanguage", mcp.DefaultString(""),
|
||||
mcp.Description(rlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"safeSearch",
|
||||
mcp.Enum("safeSearchSettingUnspecified", "none", "moderate", "strict"),
|
||||
mcp.DefaultString("moderate"), mcp.Description(ssUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"topicId", mcp.DefaultString(""),
|
||||
mcp.Description(tidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"types", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(typesUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoCaption",
|
||||
mcp.Enum("videoCaptionUnspecified", "any", "closedCaption", "none"),
|
||||
mcp.DefaultString("any"), mcp.Description(vcUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoCategoryId", mcp.DefaultString(""),
|
||||
mcp.Description(vcidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoDefinition", mcp.DefaultString(""),
|
||||
mcp.Description(vdeUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoDimension", mcp.Enum("any", "2d", "3d"),
|
||||
mcp.DefaultString("any"), mcp.Description(vdiUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoDuration",
|
||||
mcp.Enum("videoDurationUnspecified", "any", "short", "medium", "long"),
|
||||
mcp.DefaultString("any"), mcp.Description(vduUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoEmbeddable", mcp.Enum("videoEmbeddableUnspecified", "any", "true"),
|
||||
mcp.DefaultString(""), mcp.Description(veUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoLicense", mcp.Enum("any", "youtube", "creativeCommon"),
|
||||
mcp.DefaultString(""), mcp.Description(vlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoPaidProductPlacement",
|
||||
mcp.Enum("videoPaidProductPlacementUnspecified", "any", "true"),
|
||||
mcp.DefaultString(""), mcp.Description(vpppUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoSyndicated", mcp.Enum("videoSyndicatedUnspecified", "any", "true"),
|
||||
mcp.DefaultString(""), mcp.Description(vsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoType", mcp.Enum("videoTypeUnspecified", "any", "movie", "episode"),
|
||||
mcp.DefaultString(""), mcp.Description(vtUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
channelId, _ = args["channelId"].(string)
|
||||
channelType, _ = args["channelType"].(string)
|
||||
eventType, _ = args["eventType"].(string)
|
||||
forContentOwnerRaw, _ := args["forContentOwner"].(string)
|
||||
forContentOwner = utils.BoolPtr(forContentOwnerRaw)
|
||||
forDeveloperRaw, _ := args["forDeveloper"].(string)
|
||||
forDeveloper = utils.BoolPtr(forDeveloperRaw)
|
||||
forMineRaw, _ := args["forMine"].(string)
|
||||
forMine = utils.BoolPtr(forMineRaw)
|
||||
location, _ = args["location"].(string)
|
||||
locationRadius, _ = args["locationRadius"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
order, _ = args["order"].(string)
|
||||
publishedAfter, _ = args["publishedAfter"].(string)
|
||||
publishedBefore, _ = args["publishedBefore"].(string)
|
||||
q, _ = args["q"].(string)
|
||||
regionCode, _ = args["regionCode"].(string)
|
||||
relevanceLanguage, _ = args["relevanceLanguage"].(string)
|
||||
safeSearch, _ = args["safeSearch"].(string)
|
||||
topicId, _ = args["topicId"].(string)
|
||||
typesRaw, _ := args["types"].([]any)
|
||||
types = make([]string, len(typesRaw))
|
||||
for i, typ := range typesRaw {
|
||||
types[i] = typ.(string)
|
||||
}
|
||||
videoCaption, _ = args["videoCaption"].(string)
|
||||
videoCategoryId, _ = args["videoCategoryId"].(string)
|
||||
videoDefinition, _ = args["videoDefinition"].(string)
|
||||
videoDimension, _ = args["videoDimension"].(string)
|
||||
videoDuration, _ = args["videoDuration"].(string)
|
||||
videoEmbeddable, _ = args["videoEmbeddable"].(string)
|
||||
videoLicense, _ = args["videoLicense"].(string)
|
||||
videoPaidProductPlacement, _ = args["videoPaidProductPlacement"].(string)
|
||||
videoSyndicated, _ = args["videoSyndicated"].(string)
|
||||
videoType, _ = args["videoType"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
channelId = input.ChannelId
|
||||
channelType = input.ChannelType
|
||||
eventType = input.EventType
|
||||
forContentOwner = utils.BoolPtr(*input.ForContentOwner)
|
||||
forDeveloper = utils.BoolPtr(*input.ForDeveloper)
|
||||
forMine = utils.BoolPtr(*input.ForMine)
|
||||
location = input.Location
|
||||
locationRadius = input.LocationRadius
|
||||
maxResults = input.MaxResults
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
order = input.Order
|
||||
publishedAfter = input.PublishedAfter
|
||||
publishedBefore = input.PublishedBefore
|
||||
q = input.Q
|
||||
regionCode = input.RegionCode
|
||||
relevanceLanguage = input.RelevanceLanguage
|
||||
safeSearch = input.SafeSearch
|
||||
topicId = input.TopicId
|
||||
types = input.Types
|
||||
videoCaption = input.VideoCaption
|
||||
videoCategoryId = input.VideoCategoryId
|
||||
videoDefinition = input.VideoDefinition
|
||||
videoDimension = input.VideoDimension
|
||||
videoDuration = input.VideoDuration
|
||||
videoEmbeddable = input.VideoEmbeddable
|
||||
videoLicense = input.VideoLicense
|
||||
videoPaidProductPlacement = input.VideoPaidProductPlacement
|
||||
videoSyndicated = input.VideoSyndicated
|
||||
videoType = input.VideoType
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "search list started")
|
||||
|
||||
@@ -275,17 +331,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "search list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "search list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "search list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -15,7 +15,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/subscription",
|
||||
"//pkg/utils",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+38
-29
@@ -3,12 +3,14 @@ package subscription
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/subscription"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -18,8 +20,36 @@ const (
|
||||
deleteIdsUsage = "IDs of the subscriptions to delete"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"ids"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "subscription-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
subscriptionCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -39,29 +69,10 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"subscription-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
|
||||
slog.InfoContext(ctx, "subscription delete started")
|
||||
|
||||
@@ -69,17 +80,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "subscription delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "subscription delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "subscription delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+68
-49
@@ -3,13 +3,15 @@ package subscription
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/subscription"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,8 +21,61 @@ const (
|
||||
insertCidUsage = "ID of the channel to be subscribed"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
SubscriberChannelId string `json:"subscriberChannelId"`
|
||||
Description string `json:"description"`
|
||||
ChannelId string `json:"channelId"`
|
||||
Title string `json:"title"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"subscriberChannelId", "description", "channelId", "title",
|
||||
"output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"subscriberChannelId": {
|
||||
Type: "string", Description: scidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: insertCidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "subscription-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
subscriptionCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().StringVarP(
|
||||
@@ -49,49 +104,15 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"subscription-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"subscriberChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(scidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(insertCidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
subscriberChannelId, _ = args["subscriberChannelId"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
title, _ = args["title"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
subscriberChannelId = input.SubscriberChannelId
|
||||
description = input.Description
|
||||
channelId = input.ChannelId
|
||||
title = input.Title
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "subscription insert started")
|
||||
|
||||
@@ -99,17 +120,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "subscription insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "subscription insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "subscription insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+120
-103
@@ -3,6 +3,7 @@ package subscription
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/subscription"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -21,8 +23,106 @@ const (
|
||||
listCidUsage = "Return the subscriptions of the given channel owner"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
ChannelId string `json:"channelId"`
|
||||
ForChannelId string `json:"forChannelId"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
MyRecentSubscribers *string `json:"myRecentSubscribers,omitempty"`
|
||||
MySubscribers *string `json:"mySubscribers,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
OnBehalfOfContentOwnerChannel string `json:"onBehalfOfContentOwnerChannel"`
|
||||
Order string `json:"order"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "channelId", "forChannelId", "maxResults", "mine",
|
||||
"myRecentSubscribers", "mySubscribers", "onBehalfOfContentOwner",
|
||||
"onBehalfOfContentOwnerChannel", "order", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: listCidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forChannelId": {
|
||||
Type: "string", Description: fcidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"myRecentSubscribers": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mrsUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"mySubscribers": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: msUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwnerChannel": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"order": {
|
||||
Type: "string", Enum: []any{
|
||||
"subscriptionOrderUnspecified", "relevance", "unread", "alphabetical",
|
||||
},
|
||||
Description: orderUsage, Default: json.RawMessage(`"relevance"`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "subscription-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
subscriptionCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -61,103 +161,22 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"subscription-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(listCidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forChannelId", mcp.DefaultString(""),
|
||||
mcp.Description(fcidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mine", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mineUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"myRecentSubscribers", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(mrsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"mySubscribers", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(msUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwnerChannel", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"order", mcp.Enum(
|
||||
"subscriptionOrderUnspecified", "relevance", "unread", "alphabetical",
|
||||
),
|
||||
mcp.DefaultString("relevance"), mcp.Description(orderUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
channelId, _ = args["channelId"].(string)
|
||||
forChannelId, _ = args["forChannelId"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
mineRaw, ok := args["mine"].(string)
|
||||
if !ok {
|
||||
mineRaw = "true"
|
||||
}
|
||||
mine = utils.BoolPtr(mineRaw)
|
||||
myRecentSubscribersRaw, _ := args["myRecentSubscribers"].(string)
|
||||
myRecentSubscribers = utils.BoolPtr(myRecentSubscribersRaw)
|
||||
mySubscribersRaw, _ := args["mySubscribers"].(string)
|
||||
mySubscribers = utils.BoolPtr(mySubscribersRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
onBehalfOfContentOwnerChannel, _ = args["onBehalfOfContentOwnerChannel"].(string)
|
||||
order, _ = args["order"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
channelId = input.ChannelId
|
||||
forChannelId = input.ForChannelId
|
||||
maxResults = input.MaxResults
|
||||
mine = utils.BoolPtr(*input.Mine)
|
||||
myRecentSubscribers = utils.BoolPtr(*input.MyRecentSubscribers)
|
||||
mySubscribers = utils.BoolPtr(*input.MySubscribers)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
onBehalfOfContentOwnerChannel = input.OnBehalfOfContentOwnerChannel
|
||||
order = input.Order
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "subscription list started")
|
||||
|
||||
@@ -165,17 +184,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "subscription list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "subscription list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "subscription list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/superChatEvent",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+65
-50
@@ -3,18 +3,71 @@ package superChatEvent
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/superChatEvent"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Hl string `json:"hl"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"hl", "maxResults", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "superChatEvent-list", Title: short, Description: long,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
superChatEventCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVarP(&hl, "hl", "l", "", hlUsage)
|
||||
@@ -39,50 +92,14 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"superChatEvent-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
hl, _ = args["hl"].(string)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
hl = input.Hl
|
||||
maxResults = input.MaxResults
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "superChatEvent list started")
|
||||
|
||||
@@ -90,17 +107,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "superChatEvent list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "superChatEvent list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "superChatEvent list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/thumbnail",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+55
-39
@@ -3,18 +3,62 @@ package thumbnail
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/thumbnail"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type setIn struct {
|
||||
File string `json:"file"`
|
||||
VideoId string `json:"videoId"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var setInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"file", "videoId", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"file": {
|
||||
Type: "string", Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoId": {
|
||||
Type: "string", Description: vidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(setTool, setHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "thumbnail-set", Title: short, Description: long,
|
||||
InputSchema: setInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, setHandler,
|
||||
)
|
||||
thumbnailCmd.AddCommand(setCmd)
|
||||
|
||||
setCmd.Flags().StringVarP(&file, "file", "f", "", fileUsage)
|
||||
@@ -39,39 +83,13 @@ var setCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var setTool = mcp.NewTool(
|
||||
"thumbnail-set",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoId", mcp.DefaultString(""),
|
||||
mcp.Description(vidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func setHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
file, _ = args["file"].(string)
|
||||
videoId, _ = args["videoId"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input setIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
file = input.File
|
||||
videoId = input.VideoId
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "thumbnail set started")
|
||||
|
||||
@@ -79,17 +97,15 @@ func setHandler(
|
||||
err := set(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "thumbnail set failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "thumbnail set failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "thumbnail set completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func set(writer io.Writer) error {
|
||||
|
||||
@@ -19,7 +19,8 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/utils",
|
||||
"//pkg/video",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@com_github_spf13_pflag//:pflag",
|
||||
],
|
||||
|
||||
+38
-29
@@ -3,15 +3,35 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type deleteIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
}
|
||||
|
||||
var deleteInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"ids"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: deleteIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
deleteShort = "Delete a video on YouTube"
|
||||
deleteLong = "Delete a video on YouTube by ids"
|
||||
@@ -19,7 +39,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(deleteTool, deleteHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-delete", Title: deleteShort, Description: deleteLong,
|
||||
InputSchema: deleteInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, deleteHandler,
|
||||
)
|
||||
videoCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
@@ -39,29 +69,10 @@ var deleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var deleteTool = mcp.NewTool(
|
||||
"video-delete",
|
||||
mcp.WithTitleAnnotation(deleteShort),
|
||||
mcp.WithDescription(deleteLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(deleteIdsUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func deleteHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input deleteIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
|
||||
slog.InfoContext(ctx, "video delete started")
|
||||
|
||||
@@ -69,17 +80,15 @@ func deleteHandler(
|
||||
err := del(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video delete failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video delete failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video delete completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func del(writer io.Writer) error {
|
||||
|
||||
+58
-44
@@ -3,16 +3,53 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type getRatingIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var getRatingInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "onBehalfOfContentOwner", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: grIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
getRatingShort = "Get the rating of videos"
|
||||
getRatingLong = "Get the rating of videos by ids"
|
||||
@@ -20,7 +57,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(getRatingTool, getRatingHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-getRating", Title: getRatingShort, Description: getRatingLong,
|
||||
InputSchema: getRatingInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, getRatingHandler,
|
||||
)
|
||||
videoCmd.AddCommand(getRatingCmd)
|
||||
|
||||
getRatingCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, grIdsUsage)
|
||||
@@ -46,44 +93,13 @@ var getRatingCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var getRatingTool = mcp.NewTool(
|
||||
"video-getRating",
|
||||
mcp.WithTitleAnnotation(getRatingShort),
|
||||
mcp.WithDescription(getRatingLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(grIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func getRatingHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input getRatingIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "video getRating started")
|
||||
|
||||
@@ -91,17 +107,15 @@ func getRatingHandler(
|
||||
err := getRating(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video getRating failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video getRating failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video getRating completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func getRating(writer io.Writer) error {
|
||||
|
||||
+171
-140
@@ -3,6 +3,7 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,10 +11,141 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type insertIn struct {
|
||||
AutoLevels *string `json:"autoLevels,omitempty"`
|
||||
File string `json:"file"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags"`
|
||||
Language string `json:"language"`
|
||||
License string `json:"license"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
ChannelId string `json:"channelId"`
|
||||
PlaylistId string `json:"playlistId"`
|
||||
CategoryId string `json:"categoryId"`
|
||||
Privacy string `json:"privacy"`
|
||||
ForKids *string `json:"forKids,omitempty"`
|
||||
Embeddable *string `json:"embeddable,omitempty"`
|
||||
PublishAt string `json:"publishAt"`
|
||||
Stabilize *string `json:"stabilize,omitempty"`
|
||||
NotifySubscribers *string `json:"notifySubscribers,omitempty"`
|
||||
PublicStatsViewable *string `json:"publicStatsViewable,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
OnBehalfOfContentOwnerChannel string `json:"onBehalfOfContentOwnerChannel"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var insertInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"autoLevels", "file", "title", "description", "tags",
|
||||
"language", "license", "thumbnail", "channelId", "playlistId",
|
||||
"categoryId", "privacy", "forKids", "embeddable", "publishAt",
|
||||
"stabilize", "notifySubscribers", "publicStatsViewable",
|
||||
"onBehalfOfContentOwner", "onBehalfOfContentOwnerChannel",
|
||||
"output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"autoLevels": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: alUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"file": {
|
||||
Type: "string", Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tags": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: tagsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string", Description: insertLangUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"license": {
|
||||
Type: "string", Enum: []any{"youtube", "creativeCommon"},
|
||||
Description: licenseUsage, Default: json.RawMessage(`"youtube"`),
|
||||
},
|
||||
"thumbnail": {
|
||||
Type: "string", Description: thumbnailUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"channelId": {
|
||||
Type: "string", Description: chidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"playlistId": {
|
||||
Type: "string", Description: pidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"categoryId": {
|
||||
Type: "string", Description: caidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forKids": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: fkUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"embeddable": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: embeddableUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"publishAt": {
|
||||
Type: "string", Description: paUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"stabilize": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: stabilizeUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"notifySubscribers": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: nsUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"publicStatsViewable": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: psvUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwnerChannel": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
insertShort = "Upload a video to YouTube"
|
||||
insertLong = "Upload a video to YouTube, with the specified title, description, tags, etc"
|
||||
@@ -21,7 +153,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(insertTool, insertHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-insert", Title: insertShort, Description: insertLong,
|
||||
InputSchema: insertInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, insertHandler,
|
||||
)
|
||||
videoCmd.AddCommand(insertCmd)
|
||||
|
||||
insertCmd.Flags().BoolVarP(
|
||||
@@ -77,140 +219,31 @@ var insertCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var insertTool = mcp.NewTool(
|
||||
"video-insert",
|
||||
mcp.WithTitleAnnotation(insertShort),
|
||||
mcp.WithDescription(insertLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"autoLevels", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(alUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"tags", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(tagsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(insertLangUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"license", mcp.Enum("youtube", "creativeCommon"),
|
||||
mcp.DefaultString("youtube"), mcp.Description(licenseUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"thumbnail", mcp.DefaultString(""),
|
||||
mcp.Description(thumbnailUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(chidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(pidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"categoryId", mcp.DefaultString(""),
|
||||
mcp.Description(caidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"forKids", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(fkUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"embeddable", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(embeddableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publishAt", mcp.DefaultString(""),
|
||||
mcp.Description(paUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"stabilize", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(stabilizeUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"notifySubscribers", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(nsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"publicStatsViewable", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(psvUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwnerChannel", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func insertHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
autoLevelsRaw, _ := args["autoLevels"].(string)
|
||||
autoLevels = utils.BoolPtr(autoLevelsRaw)
|
||||
file, _ = args["file"].(string)
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
tagsRaw, _ := args["tags"].([]any)
|
||||
tags = make([]string, len(tagsRaw))
|
||||
for i, tag := range tagsRaw {
|
||||
tags[i] = tag.(string)
|
||||
}
|
||||
language, _ = args["language"].(string)
|
||||
license, _ = args["license"].(string)
|
||||
thumbnail, _ = args["thumbnail"].(string)
|
||||
channelId, _ = args["channelId"].(string)
|
||||
playListId, _ = args["playlistId"].(string)
|
||||
categoryId, _ = args["categoryId"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
forKidsRaw, _ := args["forKids"].(string)
|
||||
forKids = utils.BoolPtr(forKidsRaw)
|
||||
embeddableRaw, _ := args["embeddable"].(string)
|
||||
embeddable = utils.BoolPtr(embeddableRaw)
|
||||
publishAt, _ = args["publishAt"].(string)
|
||||
stabilizeRaw, _ := args["stabilize"].(string)
|
||||
stabilize = utils.BoolPtr(stabilizeRaw)
|
||||
notifySubscribersRaw, _ := args["notifySubscribers"].(string)
|
||||
notifySubscribers = utils.BoolPtr(notifySubscribersRaw)
|
||||
publicStatsViewableRaw, _ := args["publicStatsViewable"].(string)
|
||||
publicStatsViewable = utils.BoolPtr(publicStatsViewableRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
onBehalfOfContentOwnerChannel, _ = args["onBehalfOfContentOwnerChannel"].(string)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input insertIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
autoLevels = utils.BoolPtr(*input.AutoLevels)
|
||||
file = input.File
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
tags = input.Tags
|
||||
language = input.Language
|
||||
license = input.License
|
||||
thumbnail = input.Thumbnail
|
||||
channelId = input.ChannelId
|
||||
playListId = input.PlaylistId
|
||||
categoryId = input.CategoryId
|
||||
privacy = input.Privacy
|
||||
forKids = utils.BoolPtr(*input.ForKids)
|
||||
embeddable = utils.BoolPtr(*input.Embeddable)
|
||||
publishAt = input.PublishAt
|
||||
stabilize = utils.BoolPtr(*input.Stabilize)
|
||||
notifySubscribers = utils.BoolPtr(*input.NotifySubscribers)
|
||||
publicStatsViewable = utils.BoolPtr(*input.PublicStatsViewable)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
onBehalfOfContentOwnerChannel = input.OnBehalfOfContentOwnerChannel
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "video insert started")
|
||||
|
||||
@@ -218,17 +251,15 @@ func insertHandler(
|
||||
err := insert(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video insert failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video insert failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video insert completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func insert(writer io.Writer) error {
|
||||
|
||||
+126
-102
@@ -3,16 +3,111 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Chart string `json:"chart"`
|
||||
Hl string `json:"hl"`
|
||||
Locale string `json:"locale"`
|
||||
VideoCategoryId string `json:"videoCategoryId"`
|
||||
RegionCode string `json:"regionCode"`
|
||||
MaxHeight int64 `json:"maxHeight"`
|
||||
MaxWidth int64 `json:"maxWidth"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
MyRating string `json:"myRating"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "chart", "hl", "locale", "videoCategoryId",
|
||||
"regionCode", "maxHeight", "maxWidth", "maxResults",
|
||||
"onBehalfOfContentOwner", "myRating", "parts", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"chart": {
|
||||
Type: "string", Enum: []any{"chartUnspecified", "mostPopular", ""},
|
||||
Description: chartUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"locale": {
|
||||
Type: "string", Description: localUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"videoCategoryId": {
|
||||
Type: "string", Description: caidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"regionCode": {
|
||||
Type: "string", Description: rcUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"maxHeight": {
|
||||
Type: "number", Description: mhUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"maxWidth": {
|
||||
Type: "number", Description: mwUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
Default: json.RawMessage("5"),
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"myRating": {
|
||||
Type: "string", Description: listMrUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","status"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
listShort = "List video's info"
|
||||
listLong = "List video's info, such as title, description, etc"
|
||||
@@ -21,7 +116,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-list", Title: listShort, Description: listLong,
|
||||
InputSchema: listInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
videoCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, listIdsUsage)
|
||||
@@ -57,102 +162,23 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"video-list",
|
||||
mcp.WithTitleAnnotation(listShort),
|
||||
mcp.WithDescription(listLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(listIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"chart", mcp.Enum("chartUnspecified", "mostPopular"),
|
||||
mcp.DefaultString(""), mcp.Description(chartUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"locale", mcp.DefaultString(""),
|
||||
mcp.Description(localUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"videoCategoryId", mcp.DefaultString(""),
|
||||
mcp.Description(caidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"regionCode", mcp.DefaultString(""),
|
||||
mcp.Description(rcUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxHeight", mcp.DefaultNumber(0),
|
||||
mcp.Description(mhUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxWidth", mcp.DefaultNumber(0),
|
||||
mcp.Description(mwUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"maxResults", mcp.DefaultNumber(5),
|
||||
mcp.Description(pkg.MRUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"myRating", mcp.DefaultString(""),
|
||||
mcp.Description(listMrUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet", "status"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
chart, _ = args["chart"].(string)
|
||||
hl, _ = args["hl"].(string)
|
||||
locale, _ = args["locale"].(string)
|
||||
categoryId, _ = args["videoCategoryId"].(string)
|
||||
regionCode, _ = args["regionCode"].(string)
|
||||
maxHeightRaw, _ := args["maxHeight"].(float64)
|
||||
maxHeight = int64(maxHeightRaw)
|
||||
maxWidthRaw, _ := args["maxWidth"].(float64)
|
||||
maxWidth = int64(maxWidthRaw)
|
||||
maxResultsRaw, _ := args["maxResults"].(float64)
|
||||
maxResults = int64(maxResultsRaw)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
rating, _ = args["myRating"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
chart = input.Chart
|
||||
hl = input.Hl
|
||||
locale = input.Locale
|
||||
categoryId = input.VideoCategoryId
|
||||
regionCode = input.RegionCode
|
||||
maxHeight = input.MaxHeight
|
||||
maxWidth = input.MaxWidth
|
||||
maxResults = input.MaxResults
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
rating = input.MyRating
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "video list started")
|
||||
|
||||
@@ -160,17 +186,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
+44
-34
@@ -3,15 +3,40 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rateIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Rating string `json:"rating"`
|
||||
}
|
||||
|
||||
var rateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"ids", "rating"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: rateIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"rating": {
|
||||
Type: "string", Enum: []any{"like", "dislike", "none", ""},
|
||||
Description: rateRUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
rateShort = "Rate a video on YouTube"
|
||||
rateLong = "Rate a video on YouTube, with the specified rating"
|
||||
@@ -20,7 +45,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(rateTool, rateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-rate", Title: rateShort, Description: rateLong,
|
||||
InputSchema: rateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, rateHandler,
|
||||
)
|
||||
videoCmd.AddCommand(rateCmd)
|
||||
|
||||
rateCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, rateIdsUsage)
|
||||
@@ -43,34 +78,11 @@ var rateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var rateTool = mcp.NewTool(
|
||||
"video-rate",
|
||||
mcp.WithTitleAnnotation(rateShort),
|
||||
mcp.WithDescription(rateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(rateIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"rating", mcp.Enum("like", "dislike", "none"),
|
||||
mcp.DefaultString(""), mcp.Description(rateRUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func rateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
rating, _ = args["rating"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input rateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
rating = input.Rating
|
||||
|
||||
slog.InfoContext(ctx, "video rate started")
|
||||
|
||||
@@ -78,17 +90,15 @@ func rateHandler(
|
||||
err := rate(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video rate failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video rate failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video rate completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func rate(writer io.Writer) error {
|
||||
|
||||
+72
-53
@@ -3,15 +3,63 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type reportAbuseIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
ReasonId string `json:"reasonId"`
|
||||
SecondaryReasonId string `json:"secondaryReasonId"`
|
||||
Comments string `json:"comments"`
|
||||
Language string `json:"language"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var reportAbuseInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "reasonId", "secondaryReasonId", "comments",
|
||||
"language", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: raIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"reasonId": {
|
||||
Type: "string", Description: ridUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"secondaryReasonId": {
|
||||
Type: "string", Description: sridUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"comments": {
|
||||
Type: "string", Description: commentsUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string", Description: raLangUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
reportAbuseShort = "Report abuse on a video"
|
||||
reportAbuseLong = "Report abuse on a video"
|
||||
@@ -20,7 +68,18 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(reportAbuseTool, reportAbuseHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-reportAbuse", Title: reportAbuseShort,
|
||||
Description: reportAbuseLong,
|
||||
InputSchema: reportAbuseInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, reportAbuseHandler,
|
||||
)
|
||||
videoCmd.AddCommand(reportAbuseCmd)
|
||||
|
||||
reportAbuseCmd.Flags().StringSliceVarP(
|
||||
@@ -55,53 +114,15 @@ var reportAbuseCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var reportAbuseTool = mcp.NewTool(
|
||||
"video-reportAbuse",
|
||||
mcp.WithTitleAnnotation(reportAbuseShort),
|
||||
mcp.WithDescription(reportAbuseLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(raIdsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"reasonId", mcp.DefaultString(""), mcp.Description(ridUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"secondaryReasonId", mcp.DefaultString(""), mcp.Description(sridUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"comments", mcp.DefaultString(""), mcp.Description(commentsUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""), mcp.Description(raLangUsage),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""), mcp.Description(""),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func reportAbuseHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
reasonId, _ = args["reasonId"].(string)
|
||||
secondaryReasonId, _ = args["secondaryReasonId"].(string)
|
||||
comments, _ = args["comments"].(string)
|
||||
language, _ = args["language"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input reportAbuseIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
reasonId = input.ReasonId
|
||||
secondaryReasonId = input.SecondaryReasonId
|
||||
comments = input.Comments
|
||||
language = input.Language
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "video reportAbuse started")
|
||||
|
||||
@@ -109,17 +130,15 @@ func reportAbuseHandler(
|
||||
err := reportAbuse(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video reportAbuse failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video reportAbuse failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video reportAbuse completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func reportAbuse(writer io.Writer) error {
|
||||
|
||||
+117
-95
@@ -3,6 +3,7 @@ package video
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,10 +11,96 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/eat-pray-ai/yutu/pkg/video"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type updateIn struct {
|
||||
Ids []string `json:"ids"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags"`
|
||||
Language string `json:"language"`
|
||||
License string `json:"license"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
PlaylistId string `json:"playlistId"`
|
||||
CategoryId string `json:"categoryId"`
|
||||
Privacy string `json:"privacy"`
|
||||
Embeddable *string `json:"embeddable,omitempty"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var updateInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"ids", "title", "description", "tags", "language",
|
||||
"license", "thumbnail", "playlistId", "categoryId",
|
||||
"privacy", "embeddable", "output", "jsonpath",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: updateIdUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"title": {
|
||||
Type: "string", Description: titleUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"description": {
|
||||
Type: "string", Description: descUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"tags": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: tagsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"language": {
|
||||
Type: "string", Description: updateLangUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"license": {
|
||||
Type: "string", Enum: []any{"youtube", "creativeCommon"},
|
||||
Description: licenseUsage, Default: json.RawMessage(`"youtube"`),
|
||||
},
|
||||
"thumbnail": {
|
||||
Type: "string", Description: thumbnailUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"playlistId": {
|
||||
Type: "string", Description: pidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"categoryId": {
|
||||
Type: "string", Description: caidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"privacy": {
|
||||
Type: "string", Enum: []any{"public", "private", "unlisted", ""},
|
||||
Description: privacyUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"embeddable": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: embeddableUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string", Enum: []any{"json", "yaml", "silent", ""},
|
||||
Description: pkg.SilentUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
updateShort = "Update a video on YouTube"
|
||||
updateLong = "Update a video on YouTube, with the specified title, description, tags, etc"
|
||||
@@ -22,7 +109,17 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(updateTool, updateHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "video-update", Title: updateShort, Description: updateLong,
|
||||
InputSchema: updateInSchema, Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: false,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, updateHandler,
|
||||
)
|
||||
videoCmd.AddCommand(updateCmd)
|
||||
|
||||
updateCmd.Flags().StringSliceVarP(&ids, "id", "i", []string{}, updateIdUsage)
|
||||
@@ -57,95 +154,22 @@ var updateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var updateTool = mcp.NewTool(
|
||||
"video-update",
|
||||
mcp.WithTitleAnnotation(updateShort),
|
||||
mcp.WithDescription(updateLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithArray(
|
||||
"ids", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(updateIdUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"title", mcp.DefaultString(""),
|
||||
mcp.Description(titleUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"description", mcp.DefaultString(""),
|
||||
mcp.Description(descUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"tags", mcp.DefaultArray([]string{}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(tagsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"language", mcp.DefaultString(""),
|
||||
mcp.Description(updateLangUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"license", mcp.Enum("youtube", "creativeCommon"),
|
||||
mcp.DefaultString("youtube"), mcp.Description(licenseUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"thumbnail", mcp.DefaultString(""),
|
||||
mcp.Description(thumbnailUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"playlistId", mcp.DefaultString(""),
|
||||
mcp.Description(pidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"categoryId", mcp.DefaultString(""),
|
||||
mcp.Description(caidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"privacy", mcp.Enum("public", "private", "unlisted"),
|
||||
mcp.DefaultString(""), mcp.Description(privacyUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"embeddable", mcp.Enum("true", "false", ""),
|
||||
mcp.DefaultString(""), mcp.Description(embeddableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "silent", ""),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.SilentUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func updateHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
idsRaw, _ := args["ids"].([]any)
|
||||
ids = make([]string, len(idsRaw))
|
||||
for i, id := range idsRaw {
|
||||
ids[i] = id.(string)
|
||||
}
|
||||
title, _ = args["title"].(string)
|
||||
description, _ = args["description"].(string)
|
||||
tagsRaw, _ := args["tags"].([]any)
|
||||
tags = make([]string, len(tagsRaw))
|
||||
for i, tag := range tagsRaw {
|
||||
tags[i] = tag.(string)
|
||||
}
|
||||
language, _ = args["language"].(string)
|
||||
license, _ = args["license"].(string)
|
||||
thumbnail, _ = args["thumbnail"].(string)
|
||||
playListId, _ = args["playlistId"].(string)
|
||||
categoryId, _ = args["categoryId"].(string)
|
||||
privacy, _ = args["privacy"].(string)
|
||||
embeddableRaw, _ := args["embeddable"].(string)
|
||||
embeddable = utils.BoolPtr(embeddableRaw)
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input updateIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
ids = input.Ids
|
||||
title = input.Title
|
||||
description = input.Description
|
||||
tags = input.Tags
|
||||
language = input.Language
|
||||
license = input.License
|
||||
thumbnail = input.Thumbnail
|
||||
playListId = input.PlaylistId
|
||||
categoryId = input.CategoryId
|
||||
privacy = input.Privacy
|
||||
embeddable = utils.BoolPtr(*input.Embeddable)
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "video update started")
|
||||
|
||||
@@ -153,17 +177,15 @@ func updateHandler(
|
||||
err := update(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "video update failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "video update failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "video update completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
"//cmd",
|
||||
"//pkg",
|
||||
"//pkg/videoAbuseReportReason",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
@@ -3,18 +3,71 @@ package videoAbuseReportReason
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/videoAbuseReportReason"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listIn struct {
|
||||
Hl string `json:"hl"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
Jsonpath string `json:"jsonpath"`
|
||||
}
|
||||
|
||||
var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"hl", "parts", "output", "jsonpath"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"hl": {
|
||||
Type: "string",
|
||||
Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"parts": {
|
||||
Type: "array",
|
||||
Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet"]`),
|
||||
},
|
||||
"output": {
|
||||
Type: "string",
|
||||
Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage,
|
||||
Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string",
|
||||
Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(listTool, listHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "videoAbuseReportReason-list",
|
||||
Title: short,
|
||||
Description: long,
|
||||
InputSchema: listInSchema,
|
||||
Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: true,
|
||||
},
|
||||
}, listHandler,
|
||||
)
|
||||
videoAbuseReportReasonCmd.AddCommand(listCmd)
|
||||
|
||||
listCmd.Flags().StringVarP(&hl, "hl", "l", "", hlUsage)
|
||||
@@ -38,44 +91,13 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listTool = mcp.NewTool(
|
||||
"videoAbuseReportReason-list",
|
||||
mcp.WithTitleAnnotation(short),
|
||||
mcp.WithDescription(long),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithString(
|
||||
"hl", mcp.DefaultString(""),
|
||||
mcp.Description(hlUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithArray(
|
||||
"parts", mcp.DefaultArray([]string{"id", "snippet"}),
|
||||
mcp.Items(map[string]any{"type": "string"}),
|
||||
mcp.Description(pkg.PartsUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"output", mcp.Enum("json", "yaml", "table"),
|
||||
mcp.DefaultString("yaml"), mcp.Description(pkg.TableUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"jsonpath", mcp.DefaultString(""),
|
||||
mcp.Description(pkg.JPUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func listHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
hl, _ = args["hl"].(string)
|
||||
partsRaw, _ := args["parts"].([]any)
|
||||
parts = make([]string, len(partsRaw))
|
||||
for i, part := range partsRaw {
|
||||
parts[i] = part.(string)
|
||||
}
|
||||
output, _ = args["output"].(string)
|
||||
jpath, _ = args["jsonpath"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input listIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
hl = input.Hl
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jpath = input.Jsonpath
|
||||
|
||||
slog.InfoContext(ctx, "videoAbuseReportReason list started")
|
||||
|
||||
@@ -83,17 +105,15 @@ func listHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "videoAbuseReportReason list failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "videoAbuseReportReason list failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "videoAbuseReportReason list completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -13,7 +13,7 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/utils",
|
||||
"//pkg/videoCategory",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+22
-21
@@ -10,12 +10,23 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/eat-pray-ai/yutu/pkg/videoCategory"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddResourceTemplate(categoriesResource, categoriesHandler)
|
||||
cmd.Server.AddResourceTemplate(
|
||||
&mcp.ResourceTemplate{
|
||||
Name: vcName,
|
||||
Title: short,
|
||||
Description: long,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
URITemplate: vcURI,
|
||||
Annotations: &mcp.Annotations{
|
||||
Audience: []mcp.Role{"user", "assistant"}, Priority: 0.51,
|
||||
},
|
||||
}, categoriesHandler,
|
||||
)
|
||||
videoCategoryCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
listCmd.Flags().StringVarP(&hl, "hl", "l", "", hlUsage)
|
||||
@@ -40,16 +51,9 @@ var listCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var categoriesResource = mcp.NewResourceTemplate(
|
||||
vcURI, vcName,
|
||||
mcp.WithTemplateMIMEType(pkg.JsonMIME),
|
||||
mcp.WithTemplateDescription(long),
|
||||
mcp.WithTemplateAnnotations([]mcp.Role{"user", "assistant"}, 0.51),
|
||||
)
|
||||
|
||||
func categoriesHandler(
|
||||
ctx context.Context, request mcp.ReadResourceRequest,
|
||||
) ([]mcp.ResourceContents, error) {
|
||||
ctx context.Context, request *mcp.ReadResourceRequest,
|
||||
) (*mcp.ReadResourceResult, error) {
|
||||
parts = defaultParts
|
||||
hl = utils.ExtractHl(request.Params.URI)
|
||||
output = "json"
|
||||
@@ -60,9 +64,7 @@ func categoriesHandler(
|
||||
err := list(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "videoCategory list failed",
|
||||
"error", err,
|
||||
"uri", request.Params.URI,
|
||||
ctx, "videoCategory list failed", "error", err, "uri", request.Params.URI,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
@@ -72,14 +74,13 @@ func categoriesHandler(
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
|
||||
contents := []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: request.Params.URI,
|
||||
MIMEType: pkg.JsonMIME,
|
||||
Text: writer.String(),
|
||||
return &mcp.ReadResourceResult{
|
||||
Contents: []*mcp.ResourceContents{
|
||||
{
|
||||
URI: request.Params.URI, MIMEType: pkg.JsonMIME, Text: writer.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return contents, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
|
||||
@@ -12,7 +12,8 @@ go_library(
|
||||
deps = [
|
||||
"//cmd",
|
||||
"//pkg/watermark",
|
||||
"@com_github_mark3labs_mcp_go//mcp",
|
||||
"@com_github_google_jsonschema_go//jsonschema",
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
+86
-57
@@ -3,12 +3,14 @@ package watermark
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/watermark"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -17,8 +19,78 @@ const (
|
||||
setLong = "Set watermark for channel's video by channel id"
|
||||
)
|
||||
|
||||
type setIn struct {
|
||||
ChannelId string `json:"channelId"`
|
||||
File string `json:"file"`
|
||||
InVideoPosition string `json:"inVideoPosition"`
|
||||
DurationMs int64 `json:"durationMs"`
|
||||
OffsetMs int64 `json:"offsetMs"`
|
||||
OffsetType string `json:"offsetType"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
}
|
||||
|
||||
var setInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{
|
||||
"channelId", "file", "inVideoPosition", "durationMs",
|
||||
"offsetMs", "offsetType", "onBehalfOfContentOwner",
|
||||
},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"channelId": {
|
||||
Type: "string",
|
||||
Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"file": {
|
||||
Type: "string",
|
||||
Description: fileUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"inVideoPosition": {
|
||||
Type: "string",
|
||||
Enum: []any{"topLeft", "topRight", "bottomLeft", "bottomRight", ""},
|
||||
Description: ivpUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"durationMs": {
|
||||
Type: "number",
|
||||
Description: dmUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
},
|
||||
"offsetMs": {
|
||||
Type: "number",
|
||||
Description: omUsage,
|
||||
Default: json.RawMessage("0"),
|
||||
},
|
||||
"offsetType": {
|
||||
Type: "string",
|
||||
Enum: []any{"offsetFromStart", "offsetFromEnd", ""},
|
||||
Description: otUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(setTool, setHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "watermark-set",
|
||||
Title: setShort,
|
||||
Description: setLong,
|
||||
InputSchema: setInSchema,
|
||||
Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(false),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, setHandler,
|
||||
)
|
||||
watermarkCmd.AddCommand(setCmd)
|
||||
|
||||
setCmd.Flags().StringVarP(&channelId, "channelId", "c", "", cidUsage)
|
||||
@@ -50,57 +122,16 @@ var setCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var setTool = mcp.NewTool(
|
||||
"watermark-set",
|
||||
mcp.WithTitleAnnotation(setShort),
|
||||
mcp.WithDescription(setLong),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"file", mcp.DefaultString(""),
|
||||
mcp.Description(fileUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"inVideoPosition",
|
||||
mcp.Enum("topLeft", "topRight", "bottomLeft", "bottomRight"),
|
||||
mcp.DefaultString(""), mcp.Description(ivpUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"durationMs", mcp.DefaultNumber(0),
|
||||
mcp.Description(dmUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber(
|
||||
"offsetMs", mcp.DefaultNumber(0),
|
||||
mcp.Description(omUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"offsetType", mcp.Enum("offsetFromStart", "offsetFromEnd"),
|
||||
mcp.DefaultString(""), mcp.Description(otUsage), mcp.Required(),
|
||||
),
|
||||
mcp.WithString(
|
||||
"onBehalfOfContentOwner", mcp.DefaultString(""),
|
||||
mcp.Description(""), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func setHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
channelId, _ = args["channelId"].(string)
|
||||
file, _ = args["file"].(string)
|
||||
inVideoPosition, _ = args["inVideoPosition"].(string)
|
||||
durationMsRaw, _ := args["durationMs"].(float64)
|
||||
durationMs = uint64(durationMsRaw)
|
||||
offsetMsRaw, _ := args["offsetMs"].(float64)
|
||||
offsetMs = uint64(offsetMsRaw)
|
||||
offsetType, _ = args["offsetType"].(string)
|
||||
onBehalfOfContentOwner, _ = args["onBehalfOfContentOwner"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input setIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
channelId = input.ChannelId
|
||||
file = input.File
|
||||
inVideoPosition = input.InVideoPosition
|
||||
durationMs = uint64(input.DurationMs)
|
||||
offsetMs = uint64(input.OffsetMs)
|
||||
offsetType = input.OffsetType
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
|
||||
slog.InfoContext(ctx, "watermark set started")
|
||||
|
||||
@@ -108,17 +139,15 @@ func setHandler(
|
||||
err := set(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "watermark set failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "watermark set failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "watermark set completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func set(writer io.Writer) error {
|
||||
|
||||
+39
-24
@@ -3,12 +3,14 @@ package watermark
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg/watermark"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -17,8 +19,37 @@ const (
|
||||
unsetLong = "Unset watermark for channel's video by channel id"
|
||||
)
|
||||
|
||||
type unsetIn struct {
|
||||
ChannelId string `json:"channelId"`
|
||||
}
|
||||
|
||||
var unsetInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{"channelId"},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"channelId": {
|
||||
Type: "string",
|
||||
Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.MCP.AddTool(unsetTool, unsetHandler)
|
||||
mcp.AddTool(
|
||||
cmd.Server, &mcp.Tool{
|
||||
Name: "watermark-unset",
|
||||
Title: unsetShort,
|
||||
Description: unsetLong,
|
||||
InputSchema: unsetInSchema,
|
||||
Annotations: &mcp.ToolAnnotations{
|
||||
DestructiveHint: jsonschema.Ptr(true),
|
||||
IdempotentHint: true,
|
||||
OpenWorldHint: jsonschema.Ptr(true),
|
||||
ReadOnlyHint: false,
|
||||
},
|
||||
}, unsetHandler,
|
||||
)
|
||||
watermarkCmd.AddCommand(unsetCmd)
|
||||
|
||||
unsetCmd.Flags().StringVarP(&channelId, "channelId", "c", "", cidUsage)
|
||||
@@ -38,24 +69,10 @@ var unsetCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var unsetTool = mcp.NewTool(
|
||||
"watermark-unset",
|
||||
mcp.WithTitleAnnotation(unsetShort),
|
||||
mcp.WithDescription(unsetLong),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithString(
|
||||
"channelId", mcp.DefaultString(""),
|
||||
mcp.Description(cidUsage), mcp.Required(),
|
||||
),
|
||||
)
|
||||
|
||||
func unsetHandler(
|
||||
ctx context.Context, request mcp.CallToolRequest,
|
||||
) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
channelId, _ = args["channelId"].(string)
|
||||
ctx context.Context, _ *mcp.CallToolRequest, input unsetIn,
|
||||
) (*mcp.CallToolResult, any, error) {
|
||||
channelId = input.ChannelId
|
||||
|
||||
slog.InfoContext(ctx, "watermark unset started")
|
||||
|
||||
@@ -63,17 +80,15 @@ func unsetHandler(
|
||||
err := unset(&writer)
|
||||
if err != nil {
|
||||
slog.ErrorContext(
|
||||
ctx, "watermark unset failed",
|
||||
"error", err,
|
||||
"args", args,
|
||||
ctx, "watermark unset failed", "error", err, "input", input,
|
||||
)
|
||||
return mcp.NewToolResultError(err.Error()), err
|
||||
return nil, nil, err
|
||||
}
|
||||
slog.InfoContext(
|
||||
ctx, "watermark unset completed successfully",
|
||||
"resultSize", writer.Len(),
|
||||
)
|
||||
return mcp.NewToolResultText(writer.String()), nil
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func unset(writer io.Writer) error {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module github.com/eat-pray-ai/yutu
|
||||
|
||||
go 1.25.0
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/mark3labs/mcp-go v0.39.1
|
||||
github.com/google/jsonschema-go v0.3.0
|
||||
github.com/savioxavier/termlink v1.4.3
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/viper v1.21.0
|
||||
@@ -13,8 +13,6 @@ require (
|
||||
require (
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -23,40 +21,37 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.17 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8
|
||||
github.com/modelcontextprotocol/go-sdk v0.7.0
|
||||
github.com/ohler55/ojg v1.26.10
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
google.golang.org/api v0.249.0
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
google.golang.org/api v0.250.0
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -2,12 +2,8 @@ cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -28,6 +24,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
|
||||
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@@ -38,20 +36,16 @@ github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mark3labs/mcp-go v0.39.1 h1:2oPxk7aDbQhouakkYyKl2T4hKFU1c6FDaubWyGyVE1k=
|
||||
github.com/mark3labs/mcp-go v0.39.1/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ=
|
||||
github.com/mattn/go-runewidth v0.0.17/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/modelcontextprotocol/go-sdk v0.7.0 h1:XEQfn3bDx2cAdSUKty3tYEMll5dtRgBUDX88Q65fai0=
|
||||
github.com/modelcontextprotocol/go-sdk v0.7.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs=
|
||||
github.com/ohler55/ojg v1.26.10 h1:qXq8A0AjzwvO+rKJWv9apNVWxyu3He8lgGZZ+AoEdLA=
|
||||
github.com/ohler55/ojg v1.26.10/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
@@ -61,15 +55,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/savioxavier/termlink v1.4.3 h1:Gh6vrG7jSn21cRiYdQqFXYcdXfM+Fg14aG487JTfKpA=
|
||||
github.com/savioxavier/termlink v1.4.3/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
@@ -85,12 +77,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
@@ -105,32 +95,34 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
|
||||
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w=
|
||||
google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ=
|
||||
google.golang.org/api v0.250.0 h1:qvkwrf/raASj82UegU2RSDGWi/89WkLckn4LuO4lVXM=
|
||||
google.golang.org/api v0.250.0/go.mod h1:Y9Uup8bDLJJtMzJyQnu+rLRJLA0wn+wTtc6vTlOvfXo=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
importpath = "github.com/eat-pray-ai/yutu/pkg/utils",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_modelcontextprotocol_go_sdk//mcp",
|
||||
"@com_github_ohler55_ojg//jp",
|
||||
"@com_github_spf13_pflag//:pflag",
|
||||
"@in_gopkg_yaml_v3//:yaml_v3",
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/ohler55/ojg/jp"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
@@ -100,3 +101,10 @@ func ExtractHl(uri string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func Errf(format string, args ...any) *mcp.CallToolResult {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: fmt.Sprintf(format, args...)}},
|
||||
IsError: true,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user